#!/usr/bin/python3 -cimport os, sys; os.execv(os.path.dirname(sys.argv[1]) + "/common/pywrap", sys.argv)

# 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 machineslib
import testlib


@testlib.nondestructive
class TestMachinesFilesystems(machineslib.VirtualMachinesCase):
    def testBasicSystemConnection(self):
        self._testBasic()

    def testBasicSessionConnection(self):
        self._testBasic("session")

    def _testBasic(self, connection="system"):
        b = self.browser
        m = self.machine

        m.execute("mkdir -p /tmp/dir1 /tmp/dir2")

        self.login_and_go("/machines")
        self.waitPageInit()
        self.createVm("subVmTest1", connection=connection)
        self.goToVmPage("subVmTest1", connectionName=connection)
        # wait until page initialized
        b.wait_visible("#vm-subVmTest1-hostdevs")

        if m.image in ["ubuntu-2204", "rhel-8-10"] and connection == "session":
            # libvirt is too old
            b.wait_not_present("#vm-subVmTest1-filesystems-add")
            return

        # Initially, the VM is missing a suitable memoryBacking
        # config, which can't be modified while it is running.  The
        # button should be disabled.

        b.wait_visible("#vm-subVmTest1-filesystems-add[aria-disabled=true]")

        # Stopping the machine enables it.

        self.performAction("subVmTest1", "forceOff", connectionName=connection)
        b.wait_visible("#vm-subVmTest1-filesystems-add:not([aria-disabled=true])")

        # Form validation
        b.click("#vm-subVmTest1-filesystems-add")

        b.click("#vm-subVmTest1-filesystems-modal-add")
        b.wait_visible("#vm-subVmTest1-filesystems-modal-source-helper")
        b.wait_visible("#vm-subVmTest1-filesystems-modal-mountTag-helper")

        b.set_input_text("#vm-subVmTest1-filesystems-modal-mountTag", "test")
        b.click("#vm-subVmTest1-filesystems-modal-add")
        b.wait_text("#vm-subVmTest1-filesystems-modal-source-helper", "Source must not be empty")
        b.wait_not_present("#vm-subVmTest1-filesystems-modal-mountTag-helper")

        b.set_input_text("#vm-subVmTest1-filesystems-modal-mountTag", "")
        b.set_file_autocomplete_val("#vm-subVmTest1-filesystems-modal-source-group", "/tmp/")
        b.click("#vm-subVmTest1-filesystems-modal-add")
        b.wait_not_present("#vm-subVmTest1-filesystems-modal-source-helper")
        b.wait_text("#vm-subVmTest1-filesystems-modal-mountTag-helper", "Mount tag must not be empty")

        b.click("#vm-subVmTest1-filesystems-modal-cancel")

        # Add a new shared filesystem while the VM is off. This will
        # add the necessary memoryBacking config.

        b.click("#vm-subVmTest1-filesystems-add")
        b.set_file_autocomplete_val("#vm-subVmTest1-filesystems-modal-source-group", "/tmp/dir1/")
        b.set_input_text("#vm-subVmTest1-filesystems-modal-mountTag", "dir1")
        b.click(".pf-v6-c-expandable-section__toggle button")
        b.wait_visible("#vm-subVmTest1-filesystems-modal-xattr:not(:checked)")
        # Check "Extended attributes" text
        b.wait_text('#vm-subVmTest1-filesystems-modal-xattr + label',
                    "Use extended attributes on files and directories")
        b.set_checked("#vm-subVmTest1-filesystems-modal-xattr", True)

        b.click("#vm-subVmTest1-filesystems-modal-add")
        b.wait_not_present("#vm-subVmTest1-filesystems-modal-add")
        b.wait_in_text("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir1/-dir1'] td[data-label='Source path']",
                       "/tmp/dir1/")
        b.wait_in_text("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir1/-dir1'] td[data-label='Mount tag']",
                       "dir1")

        def xmllint_element(prop):
            domain_xml = f"virsh -c qemu:///{connection} dumpxml subVmTest1"
            cmd = f"{domain_xml} | xmllint --xpath \"string(//domain/{prop})\" -"
            return self.run_admin(cmd, connection).strip()

        self.assertEqual('/tmp/dir1/', xmllint_element('devices/filesystem/source/@dir'))
        self.assertEqual('dir1', xmllint_element('devices/filesystem/target/@dir'))
        self.assertEqual('on', xmllint_element('devices/filesystem/binary/@xattr'))
        self.assertEqual('shared', xmllint_element('memoryBacking/access/@mode'))

        # Start the machine. Adding filesystems should still be
        # possible since it now has a memoryBacking config.

        b.click(f"#vm-subVmTest1-{connection}-run")
        b.wait_in_text(f"#vm-subVmTest1-{connection}-state", "Running")

        # Add a new shared filesystem
        b.click("#vm-subVmTest1-filesystems-add")
        b.set_file_autocomplete_val("#vm-subVmTest1-filesystems-modal-source-group", "/tmp/dir2/")
        b.set_input_text("#vm-subVmTest1-filesystems-modal-mountTag", "dir2")
        b.click(".pf-v6-c-expandable-section__toggle button")
        b.set_checked("#vm-subVmTest1-filesystems-modal-xattr", False)

        b.click("#vm-subVmTest1-filesystems-modal-add")
        b.wait_not_present("#vm-subVmTest1-filesystems-modal-add")
        b.wait_in_text("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir2/-dir2'] td[data-label='Source path']",
                       "/tmp/dir2/")
        b.wait_in_text("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir2/-dir2'] td[data-label='Mount tag']",
                       "dir2")

        self.assertEqual('/tmp/dir2/', xmllint_element('devices/filesystem[2]/source/@dir'))
        self.assertEqual('dir2', xmllint_element('devices/filesystem[2]/target/@dir'))
        self.assertEqual('', xmllint_element('devices/filesystem[2]/binary/@xattr'))

        # Try to add a new shared filesystem with the same mount tag
        b.click("#vm-subVmTest1-filesystems-add")
        b.set_file_autocomplete_val("#vm-subVmTest1-filesystems-modal-source-group", "/tmp/dir1/")
        b.set_input_text("#vm-subVmTest1-filesystems-modal-mountTag", "dir1")
        b.click("#vm-subVmTest1-filesystems-modal-add")
        b.wait_in_text(".pf-v6-c-alert", "Failed to add shared directory")
        b.wait_in_text(".pf-v6-c-alert", "filesystem target 'dir1' specified twice")
        b.click("#vm-subVmTest1-filesystems-modal-cancel")

        # Try to add a new shared filesystem with non existing source directory
        b.click("#vm-subVmTest1-filesystems-add")
        b.set_input_text("#vm-subVmTest1-filesystems-modal-source-group input", "dir3")
        b.wait_in_text(".pf-v6-c-menu ul", "No such file or directory")
        b.click("#vm-subVmTest1-filesystems-modal-source button[aria-label='Clear input value']")
        b.click("#vm-subVmTest1-filesystems-modal-source button[aria-label='Typeahead menu toggle']")
        b.blur("#vm-subVmTest1-filesystems-modal-source-group input")
        b.wait_not_present(".pf-v6-c-menu")
        b.click("#vm-subVmTest1-filesystems-modal-cancel")
        b.wait_not_present(".pf-v6-c-modal-box__body ")

    def testDelete(self):
        b = self.browser
        m = self.machine

        m.execute("mkdir -p /tmp/dir1")

        self.createVm("subVmTest1", running=False)
        self.login_and_go("/machines")
        self.waitPageInit()
        self.goToVmPage("subVmTest1")

        m.execute("virt-xml subVmTest1 --add-device --filesystem source=/tmp/dir1/,target=dir1")
        b.wait_visible("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir1/-dir1']")
        b.click("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir1/-dir1'] button:contains(Remove)")
        b.wait_in_text(".pf-v6-c-modal-box__body .pf-v6-c-description-list", "removed from subVmTest1")
        b.wait_in_text("#delete-resource-modal-source-path", "/tmp/dir1/")
        b.wait_in_text("#delete-resource-modal-mount-tag", "dir1")

        b.click("#delete-resource-modal button:contains(Remove)")
        b.wait_not_present("tr[data-row-id='vm-subVmTest1-filesystem-/tmp/dir1/-dir1']")


if __name__ == '__main__':
    testlib.test_main()
