#!/usr/bin/python3

# This file is part of Cockpit.
#
# Copyright (C) 2021 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import os
import sys

# import Cockpit's machinery for test VMs and its browser test API
TEST_DIR = os.path.dirname(__file__)
sys.path.append(os.path.join(TEST_DIR, "common"))
sys.path.append(os.path.join(os.path.dirname(TEST_DIR), "bots/machine"))

from machineslib import VirtualMachinesCase  # noqa
from testlib import no_retry_when_changed, nondestructive, test_main, wait  # noqa


@nondestructive
class TestMachinesConsoles(VirtualMachinesCase):

    @no_retry_when_changed
    def testExternalConsole(self):
        b = self.browser

        self.createVm("subVmTest1")

        self.login_and_go("/machines")
        b.wait_in_text("body", "Virtual machines")
        self.waitVmRow("subVmTest1")

        b.wait_in_text("#vm-subVmTest1-state", "Running")  # running or paused
        self.goToVmPage("subVmTest1")

        # since VNC is not defined for this VM, the view for "Desktop Viewer" is rendered by default
        b.wait_in_text(".pf-c-console__manual-connection dl > div:first-child dd", "127.0.0.1")
        b.wait_in_text(".pf-c-console__manual-connection dl > div:nth-child(2) dd", "5900")

        b.click(".pf-c-console__remote-viewer-launch-vv")  # "Launch Remote Viewer" button
        b.wait_visible("#dynamically-generated-file")  # is .vv file generated for download?
        self.assertEqual(b.attr("#dynamically-generated-file", "href"),
                         u"data:application/x-virt-viewer,%5Bvirt-viewer%5D%0Atype%3Dspice%0Ahost%3D127.0.0.1%0Aport%3D5900%0Adelete-this-file%3D1%0Afullscreen%3D0%0A")

        # Check "More information"
        b.click('.pf-c-console__remote-viewer .pf-c-expandable-section__toggle')
        b.wait_in_text('.pf-c-expandable-section__content',
                       'Clicking "Launch remote viewer" will download')

        # HACK: clicking 'Launch Remote Viewer' kills execution context and thus CDP fails
        b.reload()
        b.enter_page("/machines")
        b.wait_in_text("body", "Virtual machines")

    def testInlineConsole(self, urlroot=""):
        b = self.browser

        args = self.createVm("subVmTest1", graphics='vnc')

        if urlroot != "":
            self.machine.execute('mkdir -p /etc/cockpit/ && echo "[WebService]\nUrlRoot=%s" > /etc/cockpit/cockpit.conf' % urlroot)

        self.login_and_go("/machines", urlroot=urlroot)
        b.wait_in_text("body", "Virtual machines")
        self.waitVmRow("subVmTest1")

        b.wait_in_text("#vm-subVmTest1-state", "Running")  # running or paused
        self.goToVmPage("subVmTest1")

        # since VNC is defined for this VM, the view for "In-Browser Viewer" is rendered by default
        b.wait_visible(".pf-c-console__vnc canvas")

        # make sure the log file is full - then empty it and reboot the VM - the log file should fill up again
        wait(lambda: "login as 'cirros' user." in self.machine.execute("cat {0}".format(args["logfile"])), delay=3)
        self.machine.execute("echo '' > {0}".format(args["logfile"]))
        b.click("#{0}-system-vnc-sendkey button".format("subVmTest1"))
        b.click("#ctrl-alt-Delete")
        wait(lambda: "Requesting system reboot" in self.machine.execute("cat {0}".format(args["logfile"])), delay=3)

    def testInlineConsoleWithUrlRoot(self, urlroot=""):
        self.testInlineConsole(urlroot="/webcon")

    @no_retry_when_changed
    def testSerialConsole(self):
        b = self.browser
        name = "vmWithSerialConsole"

        self.createVm(name, graphics='vnc', ptyconsole=True)

        self.login_and_go("/machines")
        b.wait_in_text("body", "Virtual machines")
        self.waitVmRow(name)

        self.goToVmPage(name)
        b.wait_in_text("#vm-{0}-state".format(name), "Running")  # running or paused

        b.click("#pf-c-console__type-selector")
        b.wait_visible("#pf-c-console__type-selector + .pf-c-select__menu")
        b.click("#SerialConsole button")
        b.wait_not_present("#pf-c-console__type-selector + .pf-c-select__menu")

        b.wait_in_text("#{0}-terminal .xterm-accessibility-tree > div:last-child".format(name), "cirros login")

        b.click("#{0}-serialconsole-disconnect".format(name))
        b.wait_text("#{0}-terminal".format(name), "Disconnected from serial console. Click the connect button.")

        b.click("#{0}-serialconsole-connect".format(name))
        b.wait_in_text("#{0}-terminal .xterm-accessibility-tree > div:nth-child(1)".format(name), "Connected to domain")

        # disconnecting the serial console closes the pty channel
        self.allow_journal_messages("connection unexpectedly closed by peer",
                                    ".*Connection reset by peer")
        self.allow_browser_errors("Disconnection timed out.")

    def testSwitchConsoleFromSerialToGraphical(self):
        b = self.browser
        name = "vmForSwitching"

        self.createVm(name, graphics="vnc", ptyconsole=True)

        self.login_and_go("/machines")
        b.wait_in_text("body", "Virtual machines")

        self.waitVmRow(name)
        self.goToVmPage(name)
        b.wait_in_text("#vm-{0}-state".format(name), "Running")

        b.wait_visible("#vm-{0}-consoles".format(name))
        b.wait_visible(".pf-c-console__vnc canvas")

        b.click("#pf-c-console__type-selector")
        b.wait_visible("#pf-c-console__type-selector + .pf-c-select__menu")
        b.click("#SerialConsole button")
        b.wait_not_present("#pf-c-console__type-selector + .pf-c-select__menu")

        b.wait_not_present(".pf-c-console__vnc canvas")
        b.wait_visible("#{}-terminal".format(name))

        b.wait_not_present("#navbar-oops")

        self.allowed_messages.append("connection unexpectedly closed by peer")


if __name__ == '__main__':
    test_main()
