#!/usr/bin/python
# -*- coding: utf-8 -*-

# This file is part of Cockpit.
#
# Copyright (C) 2013 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 testlib import *
import os
import sys

def readFile(name):
    content = ''
    if os.path.exists(name):
        with open(name, 'r') as f:
            content = f.read().replace('\n', '')
    return content

# If this test fails to run, the host machine needs:
# echo "options kvm-intel nested=1" > /etc/modprobe.d/kvm-intel.conf
# rmmod kvm-intel && modprobe kvm-intel || true

@skipImage("Atomic cannot run virtual machines", "fedora-atomic", "rhel-atomic", "continuous-atomic")
class TestMachines(MachineCase):
    vm1_name = "subVmTest1"
    vm1_id = "#vm-{0}".format(vm1_name)
    vm1_img = "/var/lib/libvirt/images/{0}.img".format(vm1_name)
    vm1_img2 = "/var/lib/libvirt/images/{0}_2.img".format(vm1_name)
    vm1_img3 = "/var/lib/libvirt/images/{0}_3.img".format(vm1_name)

    providerVm1_name = "vm1"
    providerVm1_id = "#vm-{0}".format(providerVm1_name)

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

        # Ensure everything has started correctly
        m.execute("systemctl start libvirtd")
        # Wait until we can get a list of domains
        wait(lambda: m.execute("virsh list"))
        # Wait for the network 'default' to become active
        wait(lambda: m.execute(command="virsh net-info default | grep Active"))

        self.login_and_go("/machines")
        b.wait_in_text("body", "No VM is running or defined on this host")

        m.execute("qemu-img create -f qcow2 {0} 1G".format(self.vm1_img))
        m.execute("qemu-img create -f qcow2 {0} 2G".format(self.vm1_img2))
        m.execute("virt-install --cpu host -r 128 --pxe --force --nographics --noautoconsole "
                  "--disk path={0},size=1,format=qcow2 --disk path={1},size=2,format=qcow2 "
                  "-n {2} || true"
                  .format(self.vm1_img, self.vm1_img2, self.vm1_name))

        # state == 'running' is expected, but can be 'paused' if the VM creation fails. Anyway, the VM shall be listed at least
        state = m.execute("virsh domstate {0}".format(self.vm1_name)).strip()
        # debug info: machine state
        m.message("state: '{0}'".format(state))
        self.assertIn(state, ["running", "paused"], "Test environment failed to set up - testing VM failed to start")

        b.wait_in_text("body", "Virtual Machines")
        b.wait_in_text("tbody tr th", self.vm1_name) # is VM name listed?

        return state

    def testBasic(self):
        state = self.envSetup()

        b = self.browser

        b.click("tbody tr th") # click on the row header
        b.wait_present("{0}-state".format(self.vm1_id))
        b.wait_in_text("{0}-state".format(self.vm1_id), state) # running or paused
        b.wait_present("{0}-vcpus".format(self.vm1_id))
        b.wait_in_text("{0}-vcpus".format(self.vm1_id), "1")

        if state == "running":
            b.click("{0}-off-caret".format(self.vm1_id))
            b.wait_visible("{0}-forceOff".format(self.vm1_id))
            b.click("{0}-forceOff".format(self.vm1_id))
            b.wait_in_text("{0}-state".format(self.vm1_id), "shut off")
        else:
            print "WARNING: Test VM creation failed, virt-install finished in 'paused' state. Skipping the 'shut down' scenario"

    def wait_for_disk_stats(self, vm_id, target):
        b = self.browser
        try:
            with b.wait_timeout(10):
                b.wait_present("{0}-disks-{1}-used".format(vm_id, target)) # wait for disk statistics to show up
        except Error, ex:
            if not ex.msg.startswith('timeout'):
                exc_info = sys.exc_info()
                raise exc_info[0], exc_info[1], exc_info[2]
            # stats did not show up, check if user message showed up
            print "Libvirt version does not support disk statistics"
            b.wait_present("{0}-disksstats-unsupported".format(vm_id))

    def testDisks(self):
        state = self.envSetup()

        b = self.browser
        m = self.machine

        b.click("tbody tr th") # click on the row header
        b.wait_present("{0}-state".format(self.vm1_id))
        b.wait_in_text("{0}-state".format(self.vm1_id), state) # running or paused

        b.wait_present("{0}-disks".format(self.vm1_id)) # wait for the tab
        b.click("{0}-disks".format(self.vm1_id)) # open the "Disks" subtab

        # Test basic disk properties
        b.wait_present("{0}-disks-total-value".format(self.vm1_id)) # wait for the "Count" value
        b.wait_in_text("{0}-disks-total-value".format(self.vm1_id), "2")

        b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "hda"), "hda")
        b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "hdb"), "hdb")

        b.wait_in_text("{0}-disks-{1}-bus".format(self.vm1_id, "hda"), "ide")
        b.wait_in_text("{0}-disks-{1}-bus".format(self.vm1_id, "hdb"), "ide")

        b.wait_in_text("{0}-disks-{1}-device".format(self.vm1_id, "hda"), "disk")
        b.wait_in_text("{0}-disks-{1}-device".format(self.vm1_id, "hdb"), "disk")

        b.wait_in_text("{0}-disks-{1}-source".format(self.vm1_id, "hda"), self.vm1_img)
        b.wait_in_text("{0}-disks-{1}-source".format(self.vm1_id, "hdb"), self.vm1_img2)

        if state == "running": # if test env setup passed ok
            # Test domstats
            self.wait_for_disk_stats(self.vm1_id, "hda")
            if b.is_present("{0}-disks-{1}-used".format(self.vm1_id, "hda")):
                b.wait_in_text("{0}-disks-{1}-used".format(self.vm1_id, "hda"), "0.00")
                b.wait_present("{0}-disks-{1}-used".format(self.vm1_id, "hdb"))
                b.wait_in_text("{0}-disks-{1}-used".format(self.vm1_id, "hdb"), "0.00")

                b.wait_in_text("{0}-disks-{1}-capacity".format(self.vm1_id, "hda"), "1")
                b.wait_in_text("{0}-disks-{1}-capacity".format(self.vm1_id, "hdb"), "2")
            else:
                print "Disk statistics checks skipped"

            # Test add disk
            m.execute("qemu-img create -f raw {0} 128M".format(self.vm1_img3))
            m.execute("virsh attach-disk subVmTest1 {0} vdc".format(self.vm1_img3)) # attach to the virtio bus instead of ide

            b.wait_in_text("{0}-disks-total-value".format(self.vm1_id), "3")
            b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "hda"), "hda")
            b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "hdb"), "hdb")
            b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "vdc"), "vdc")

            b.wait_in_text("{0}-disks-{1}-bus".format(self.vm1_id, "hda"), "ide")

            b.wait_in_text("{0}-disks-{1}-bus".format(self.vm1_id, "vdc"), "virtio")
            b.wait_in_text("{0}-disks-{1}-device".format(self.vm1_id, "vdc"), "disk")
            b.wait_in_text("{0}-disks-{1}-source".format(self.vm1_id, "vdc"), self.vm1_img3)

            self.wait_for_disk_stats(self.vm1_id, "vdc")
            if b.is_present("{0}-disks-{1}-used".format(self.vm1_id, "hda")):
                b.wait_in_text("{0}-disks-{1}-used".format(self.vm1_id, "vdc"), "0.00")
                b.wait_in_text("{0}-disks-{1}-capacity".format(self.vm1_id, "vdc"), "0.13") # 128 MB

            # Test remove disk
            m.execute("virsh detach-disk subVmTest1 vdc")
            print "Restarting {0}, might take a while".format(self.vm1_id)
            b.click("{0}-reboot-caret".format(self.vm1_id))
            b.wait_visible("{0}-forceReboot".format(self.vm1_id))
            b.click("{0}-forceReboot".format(self.vm1_id))

            b.wait_in_text("{0}-disks-total-value".format(self.vm1_id), "2")

            b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "hda"), "hda")
            b.wait_in_text("{0}-disks-{1}-target".format(self.vm1_id, "hdb"), "hdb")
        else:
            print "WARNING: Test VM creation failed, virt-install finished in 'paused' state. Skipping the 'add/remove disk' scenario"

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

        # install the provider
        m.execute("mkdir /usr/share/cockpit/machines/provider")
        m.upload(["verify/files/cockpitMachinesTestExternalProvider.js"], "/usr/share/cockpit/machines/provider") # do not rename
        m.execute("mv /usr/share/cockpit/machines/provider/cockpitMachinesTestExternalProvider.js /usr/share/cockpit/machines/provider/index.js")

        self.login_and_go("/machines")
        b.wait_in_text("body", "Virtual Machines")
        b.wait_present("{0}-row".format(self.providerVm1_id)) # is VM listed so the provider is correctly loaded, initialized and GET_ALL_VMS() passed??

        b.click("{0}-row".format(self.providerVm1_id)) # click on the row header
        b.wait_present("{0}-state".format(self.providerVm1_id))
        b.wait_in_text("{0}-state".format(self.providerVm1_id), "running")

        b.click("{0}-off".format(self.providerVm1_id)) # click the Shut Down button
        b.wait_in_text("{0}-state".format(self.providerVm1_id), "shut off")

        b.click("a:contains('Test Subtab')") # Click on the subtab injected by the Provider
        b.wait_visible("#test-subtab-body-vm1") # is subtab rendered?
        b.wait_in_text("#test-subtab-body-vm1", "Content of subtab")

        b.click("{0}-run".format(self.providerVm1_id)) # click the Run button which intentionally leads to an error, so check it
        b.click("a:contains('Overview')") # where the error message is rendered
        b.wait_visible("{0}-state".format(self.providerVm1_id))
        b.wait_present("{0}-last-message".format(self.providerVm1_id))
        b.wait_in_text("{0}-last-message".format(self.providerVm1_id), "VM failed to start")

if __name__ == '__main__':
    test_main()
