#!/usr/bin/python3

# This file is part of Cockpit.
#
# Copyright (C) 2015 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 parent
from packagelib import *
from storagelib import *
from testlib import *


# vdo only exists on RHEL
SUPPORTED_OS = ["centos-8-stream", "rhel-8-4", "rhel-8-4-distropkg", "rhel-8-5", "rhel-8-5-distropkg"]


@skipImage("no vdo package yet on RHEL 9, https://bugzilla.redhat.com/show_bug.cgi?id=1916216", "rhel-9-0", "rhel-9-0-distropkg")
class TestStorageVDO(StorageCase):

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

        self.login_and_go("/storage")

        if m.image not in SUPPORTED_OS:
            b.click("#devices .pf-c-dropdown button.pf-c-dropdown__toggle")
            b.wait_not_present("#devices .pf-c-dropdown a:contains('VDO')")
            return

        b.wait_visible("#devices")

        # Make a logical volume for use as the backing device.
        m.add_disk("10G", serial="DISK1")
        b.wait_in_text("#drives", "DISK1")
        m.execute("vgcreate vdo_vgroup /dev/sda && lvcreate -n lvol -L 5G vdo_vgroup")
        b.wait_in_text("#devices", "vdo_vgroup")

        # Create VDO

        self.devices_dropdown("Create VDO device")
        self.dialog_wait_open()
        self.dialog_wait_val("name", "vdo0")
        self.dialog_set_val("space", {"/dev/vdo_vgroup/lvol": True})
        self.dialog_apply()
        self.dialog_wait_close()

        b.wait_in_text("#devices", "vdo0")
        b.click("#devices .sidepanel-row:contains(vdo0)")
        b.wait_visible("#storage-detail")

        def detail(index):
            return '#detail-header .pf-c-description-list__group:nth-of-type(%s) > dd' % index

        b.wait_text(detail(1), "/dev/mapper/vdo0")
        b.wait_in_text(detail(2), "vdo_vgroup")
        b.wait_in_text(detail(3), "used of 5 GiB")
        b.wait_in_text(detail(4), "used of 5 GiB")
        b.wait_text(detail(5), "256 MiB")
        b.wait_visible(detail(6) + " input:checked")
        b.wait_visible(detail(7) + " input:checked")

        # Make a filesystem on it

        self.content_row_wait_in_col(1, 1, "Unrecognized data")
        self.content_head_action(1, "Format")
        self.dialog({"type": "xfs",
                     "name": "FILESYSTEM",
                     "mount_point": "/run/data"})
        self.content_row_wait_in_col(1, 1, "xfs file system")
        # _netdev etc should have been prefilled
        self.content_tab_wait_in_info(1, 1, "Mount point", "_netdev")
        self.content_tab_wait_in_info(1, 1, "Mount point", "x-systemd.device-timeout=0")
        self.content_tab_wait_in_info(1, 1, "Mount point", "x-systemd.requires=vdo.service")
        self.content_tab_wait_in_info(1, 1, "Used", "of 4.99 GiB", "of 5.0 GiB")

        # Grow physical

        m.execute("lvresize vdo_vgroup/lvol -L 9G")
        b.wait_in_text(".pf-c-alert__description", '5 GiB of 9 GiB')
        b.click("button:contains('Grow to take all space')")
        b.wait_not_present(".pf-c-alert")
        b.wait_in_text(detail(3), "used of 9 GiB")

        # Grow logical

        b.click(detail(4) + " button:contains(Grow)")
        self.dialog({"lsize": 10 * 1024})
        b.wait_in_text(detail(4), "used of 10 GiB")
        self.content_tab_wait_in_info(1, 1, "Used", "of 9.99 GiB", "of 10.0 GiB")

        # Stop

        b.wait_visible('#detail-content table')
        b.click('.pf-c-card__header:contains("VDO") button:contains("Stop")')
        self.dialog_wait_open()
        b.wait_in_text("#dialog", "Proceeding will unmount all filesystems on it.")
        self.dialog_apply()
        self.dialog_wait_close()
        b.wait_not_present('#detail-content table')

        # Delete

        b.click('.pf-c-card__header:contains("VDO") button:contains("Delete")')
        self.dialog_wait_open()
        self.dialog_apply_with_retry(expected_errors=["Device or resource busy"])
        b.wait_visible("#storage")
        b.wait_not_in_text("#devices", "vdo0")

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

        self.login_and_go("/storage")

        if m.image not in SUPPORTED_OS:
            b.click("#devices .pf-c-dropdown button.pf-c-dropdown__toggle")
            b.wait_not_present("#devices .pf-c-dropdown a:contains('VDO')")
            return

        b.wait_visible("#devices")

        m.add_disk("10G", serial="DISK1")
        b.wait_in_text("#drives", "DISK1")

        # Install a valid configuration file that describes a broken VDO
        m.write("/etc/vdoconf.yml", """
config: !Configuration
  vdos:
    vdo0: !VDOService
      _operationState: beginCreate
      ackThreads: 1
      activated: enabled
      bioRotationInterval: 64
      bioThreads: 4
      blockMapCacheSize: 128M
      blockMapPeriod: 16380
      compression: enabled
      cpuThreads: 2
      deduplication: enabled
      device: /dev/sda
      hashZoneThreads: 1
      indexCfreq: 0
      indexMemory: 0.25
      indexSparse: disabled
      indexThreads: 0
      logicalBlockSize: 4096
      logicalSize: 10G
      logicalThreads: 1
      name: vdo0
      physicalSize: 10G
      physicalThreads: 1
      readCache: disabled
      readCacheSize: 0M
      slabSize: 2G
      writePolicy: sync
  version: 538380551
""")

        b.wait_in_text("#devices", "vdo0")
        b.click("#devices .sidepanel-row:contains(vdo0)")

        b.click("#storage-detail .pf-m-danger button:contains('Remove device')")
        b.wait_visible("#storage")
        b.wait_not_in_text("#devices", "vdo0")

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

        self.login_and_go("/storage")

        if m.image not in SUPPORTED_OS:
            b.click("#devices .pf-c-dropdown button.pf-c-dropdown__toggle")
            b.wait_not_present("#devices .pf-c-dropdown a:contains('VDO')")
            return

        b.wait_visible("#devices")

        # Install a valid configuration file
        m.write("/etc/vdoconf.yml", """
config: !Configuration
  vdos:
    vdo0: !VDOService
      _operationState: finished
      ackThreads: 1
      activated: enabled
      bioRotationInterval: 64
      bioThreads: 4
      blockMapCacheSize: 128M
      blockMapPeriod: 16380
      compression: enabled
      cpuThreads: 2
      deduplication: enabled
      device: /dev/sda
      hashZoneThreads: 1
      indexCfreq: 0
      indexMemory: 0.25
      indexSparse: disabled
      indexThreads: 0
      logicalBlockSize: 4096
      logicalSize: 10G
      logicalThreads: 1
      name: vdo0
      physicalSize: 10G
      physicalThreads: 1
      readCache: disabled
      readCacheSize: 0M
      slabSize: 2G
      writePolicy: sync
  version: 538380551
""")

        b.wait_in_text("#devices", "vdo0")

        # Install a broken configuration file
        m.write("/etc/vdoconf.yml", """
config: !Configuration
  vdos:
    vdo0: !VDOService
      blah: 12
""")

        b.wait_not_in_text("#devices", "vdo0")

        # Install a valid configuration file again
        m.write("/etc/vdoconf.yml", """
config: !Configuration
  vdos:
    vdo1: !VDOService
      _operationState: finished
      ackThreads: 1
      activated: enabled
      bioRotationInterval: 64
      bioThreads: 4
      blockMapCacheSize: 128M
      blockMapPeriod: 16380
      compression: enabled
      cpuThreads: 2
      deduplication: enabled
      device: /dev/sda
      hashZoneThreads: 1
      indexCfreq: 0
      indexMemory: 0.25
      indexSparse: disabled
      indexThreads: 0
      logicalBlockSize: 4096
      logicalSize: 10G
      logicalThreads: 1
      name: vdo1
      physicalSize: 10G
      physicalThreads: 1
      readCache: disabled
      readCacheSize: 0M
      slabSize: 2G
      writePolicy: sync
  version: 538380551
""")

        b.wait_in_text("#devices", "vdo1")


class TestStoragePackagesVDO(PackageCase, StorageHelpers):

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

        if m.image not in SUPPORTED_OS:
            self.skipTest("No vdo available")

        m.execute("pkcon remove -y vdo")

        self.createPackage("vdo", "999", "1")
        self.enableRepo()
        m.execute("pkcon refresh")

        self.login_and_go("/storage")

        self.devices_dropdown("Create VDO device")
        self.dialog_wait_open()
        b.wait_in_text("#dialog", "The vdo package must be installed")
        b.wait_in_text("#dialog", "Total size:")
        self.dialog_apply()
        # A new dialog opens immediately
        b.wait_in_text("#dialog", "Create VDO device")


if __name__ == '__main__':
    test_main()
