#!/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 . 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): def startVm(self, name, console='spice'): 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")) img1 = "/var/lib/libvirt/images/{0}.img".format(name) img2 = "/var/lib/libvirt/images/{0}-2.img".format(name) m.execute("qemu-img create -f qcow2 {0} 1G".format(img1)) m.execute("qemu-img create -f qcow2 {0} 2G".format(img2)) m.execute("virt-install --cpu host -r 128 --pxe --force --graphics {3},listen=127.0.0.1 --noautoconsole " "--disk path={0},size=1,format=qcow2 --disk path={1},size=2,format=qcow2 " "--boot hd,network " "-n {2}" .format(img1, img2, name, console)) m.execute('[ "$(virsh domstate {0})" = running ] || ' '{{ virsh dominfo {0} >&2; cat /var/log/libvirt/qemu/{0}.log >&2; exit 1; }}'.format(name)) def testBasic(self): b = self.browser m = self.machine self.startVm("subVmTest1") self.login_and_go("/machines") b.wait_in_text("body", "Virtual Machines") b.wait_in_text("tbody tr th", "subVmTest1") b.click("tbody tr th") # click on the row header b.wait_present("#vm-subVmTest1-state") b.wait_in_text("#vm-subVmTest1-state", "running") b.wait_present("#vm-subVmTest1-vcpus") b.wait_in_text("#vm-subVmTest1-vcpus", "1") b.wait_in_text("#vm-subVmTest1-bootorder", "disk,network") cpu_type = b.eval_js("$('#vm-subVmTest1-cputype').text()") self.assertTrue(cpu_type == 'host' or cpu_type.startswith('custom')) # should be "host" due to "--cpu host", but debian-stable keeps claiming "custom" emulated_machine = b.eval_js("$('#vm-subVmTest1-emulatedmachine').text()") self.assertTrue(len(emulated_machine) > 0) # emulated machine varies across test machines # switch to and check Usage b.wait_present("#vm-subVmTest1-usage") b.click("#vm-subVmTest1-usage") b.wait_present("tbody.open .listing-ct-body td:nth-child(1) .usage-donut-caption") b.wait_in_text("tbody.open .listing-ct-body td:nth-child(1) .usage-donut-caption", "128 MiB") b.wait_present("#chart-donut-0 .donut-title-big-pf") b.wait(lambda: float(b.text("#chart-donut-0 .donut-title-big-pf")) > 0.0) b.wait_present("tbody.open .listing-ct-body td:nth-child(2) .usage-donut-caption") b.wait_in_text("tbody.open .listing-ct-body td:nth-child(2) .usage-donut-caption", "1 vCPU") # CPU usage cannot be nonzero with blank image, so just ensure it's a percentage b.wait_present("#chart-donut-1 .donut-title-big-pf") self.assertLessEqual(float(b.text("#chart-donut-1 .donut-title-big-pf")), 100.0) # suspend/resume m.execute("virsh suspend subVmTest1") b.wait_in_text("#vm-subVmTest1-state", "paused") # resume sometimes fails with "unable to execute QEMU command 'cont': Resetting the Virtual Machine is required" m.execute('virsh resume subVmTest1 || { virsh destroy subVmTest1 && virsh start subVmTest1; }') b.wait_in_text("#vm-subVmTest1-state", "running") # shut off b.click("#vm-subVmTest1-off-caret") b.wait_visible("#vm-subVmTest1-forceOff") b.click("#vm-subVmTest1-forceOff") b.wait_in_text("#vm-subVmTest1-state", "shut off") # usage should drop to zero b.wait_in_text("#chart-donut-0 .donut-title-big-pf", "0.00") b.wait_in_text("#chart-donut-1 .donut-title-big-pf", "0.0") # start another one, should appear automatically self.startVm("subVmTest2") b.wait_present("#app .listing-ct tbody:nth-of-type(2) th") b.wait_in_text("#app .listing-ct tbody:nth-of-type(2) th", "subVmTest2") b.click("#app .listing-ct tbody:nth-of-type(2) th") # click on the row header b.wait_present("#vm-subVmTest2-state") b.wait_in_text("#vm-subVmTest2-state", "running") b.wait_present("#vm-subVmTest2-vcpus") b.wait_in_text("#vm-subVmTest2-vcpus", "1") b.wait_in_text("#vm-subVmTest2-bootorder", "disk,network") # restart libvirtd m.execute("systemctl stop libvirtd") b.wait_present("#app .blank-slate-pf") b.wait_visible("#app .blank-slate-pf") b.wait_in_text("#app .blank-slate-pf", "No VM is running") m.execute("systemctl start libvirtd") b.wait_present("#app .listing-ct tbody:nth-of-type(1) th") b.wait_in_text("#app .listing-ct tbody:nth-of-type(1) th", "subVmTest1") b.wait_present("#app .listing-ct tbody:nth-of-type(2) th") b.wait_in_text("#app .listing-ct tbody:nth-of-type(2) th", "subVmTest2") b.wait_present("#vm-subVmTest1-state") b.wait_in_text("#vm-subVmTest1-state", "shut off") b.wait_present("#vm-subVmTest2-state") b.wait_in_text("#vm-subVmTest2-state", "running") # stop second VM, event handling should still work b.wait_present("#app .listing-ct tbody:nth-of-type(2) th") b.wait_in_text("#app .listing-ct tbody:nth-of-type(2) th", "subVmTest2") b.click("#app .listing-ct tbody:nth-of-type(2) th") # click on the row header b.click("#vm-subVmTest2-off-caret") b.wait_visible("#vm-subVmTest2-forceOff") b.click("#vm-subVmTest2-forceOff") b.wait_in_text("#vm-subVmTest2-state", "shut off") # HACK: restarting libvirtd causes this completely unrelated SELinux issue self.allow_journal_messages('.*denied.*search.*"systemd-machine".*dev="proc".*') def wait_for_disk_stats(self, name, target): b = self.browser try: with b.wait_timeout(10): b.wait_present("#vm-{0}-disks-{1}-used".format(name, 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("#vm-{0}-disksstats-unsupported".format(name)) def testDisks(self): b = self.browser m = self.machine self.startVm("subVmTest1") self.login_and_go("/machines") b.wait_in_text("body", "Virtual Machines") b.wait_in_text("tbody tr th", "subVmTest1") b.click("tbody tr th") # click on the row header b.wait_present("#vm-subVmTest1-state") b.wait_in_text("#vm-subVmTest1-state", "running") b.wait_present("#vm-subVmTest1-disks") # wait for the tab b.click("#vm-subVmTest1-disks") # open the "Disks" subtab # Test basic disk properties b.wait_present("#vm-subVmTest1-disks-total-value") # wait for the "Count" value b.wait_in_text("#vm-subVmTest1-disks-total-value", "2") b.wait_in_text("#vm-subVmTest1-disks-hda-target".format("hda"), "hda") b.wait_in_text("#vm-subVmTest1-disks-hdb-target", "hdb") b.wait_in_text("#vm-subVmTest1-disks-hda-bus", "ide") b.wait_in_text("#vm-subVmTest1-disks-hdb-bus", "ide") b.wait_in_text("#vm-subVmTest1-disks-hda-device", "disk") b.wait_in_text("#vm-subVmTest1-disks-hdb-device", "disk") b.wait_in_text("#vm-subVmTest1-disks-hdb-source", "/var/lib/libvirt/images/subVmTest1-2.img") # Test domstats self.wait_for_disk_stats("subVmTest1", "hda") if b.is_present("#vm-subVmTest1-disks-hda-used"): b.wait_in_text("#vm-subVmTest1-disks-hda-used", "0.00") b.wait_present("#vm-subVmTest1-disks-hdb-used") b.wait_in_text("#vm-subVmTest1-disks-hdb-used", "0.00") b.wait_in_text("#vm-subVmTest1-disks-hda-capacity", "1") b.wait_in_text("#vm-subVmTest1-disks-hdb-capacity", "2") # Test add disk m.execute("qemu-img create -f raw /var/lib/libvirt/images/image3.img 128M") m.execute("virsh attach-disk subVmTest1 /var/lib/libvirt/images/image3.img vdc") # attach to the virtio bus instead of ide b.wait_in_text("#vm-subVmTest1-disks-total-value", "3") b.wait_in_text("#vm-subVmTest1-disks-hda-target", "hda") b.wait_in_text("#vm-subVmTest1-disks-hdb-target", "hdb") b.wait_in_text("#vm-subVmTest1-disks-vdc-target", "vdc") b.wait_in_text("#vm-subVmTest1-disks-hda-bus", "ide") b.wait_in_text("#vm-subVmTest1-disks-vdc-bus", "virtio") b.wait_in_text("#vm-subVmTest1-disks-vdc-device", "disk") b.wait_in_text("#vm-subVmTest1-disks-vdc-source", "/var/lib/libvirt/images/image3.img") self.wait_for_disk_stats("subVmTest1", "vdc") if b.is_present("#vm-subVmTest1-disks-hda-used"): b.wait_in_text("#vm-subVmTest1-disks-vdc-used", "0.00") b.wait_in_text("#vm-subVmTest1-disks-vdc-capacity", "0.13") # 128 MB # Test remove disk m.execute("virsh detach-disk subVmTest1 vdc") print "Restarting vm-subVmTest1, might take a while" b.click("#vm-subVmTest1-reboot-caret") b.wait_visible("#vm-subVmTest1-forceReboot") b.click("#vm-subVmTest1-forceReboot") b.wait_in_text("#vm-subVmTest1-disks-total-value", "2") b.wait_in_text("#vm-subVmTest1-disks-hda-target", "hda") b.wait_in_text("#vm-subVmTest1-disks-hdb-target", "hdb") def testExternalConsole(self): b = self.browser self.startVm("subVmTest1") self.login_and_go("/machines") b.wait_in_text("body", "Virtual Machines") b.wait_in_text("tbody tr th", "subVmTest1") b.click("tbody tr th") # click on the row header b.wait_present("#vm-subVmTest1-state") b.wait_in_text("#vm-subVmTest1-state", "running") # running or paused b.wait_present("#vm-subVmTest1-consoles") # wait for the tab b.click("#vm-subVmTest1-consoles") # open the "Console" subtab # since VNC is not defined for this VM, the view for "Desktop Viewer" is rendered by default b.wait_present("#vm-subVmTest1-consoles-manual-address") # wait for the tab b.wait_in_text("#vm-subVmTest1-consoles-manual-address", "127.0.0.1") b.wait_in_text("#vm-subVmTest1-consoles-manual-port-spice", "5900") b.wait_present("#vm-subVmTest1-consoles-launch") # "Launch Remote Viewer" button b.click("#vm-subVmTest1-consoles-launch") b.wait_present("#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") def testInlineConsole(self): b = self.browser self.startVm("subVmTest1", console='vnc') self.login_and_go("/machines") b.wait_in_text("body", "Virtual Machines") b.wait_in_text("tbody tr th", "subVmTest1") b.click("tbody tr th") # click on the row header b.wait_present("#vm-subVmTest1-state") b.wait_in_text("#vm-subVmTest1-state", "running") # running or paused b.wait_present("#vm-subVmTest1-consoles") # wait for the tab b.click("#vm-subVmTest1-consoles") # open the "Console" subtab # since VNC is defined for this VM, the view for "In-Browser Viewer" is rendered by default b.wait_present("iframe.machines-console-frame-vnc") b.switch_to_frame("vm-subVmTest1-novnc-frame-container") # FIXME: the usual b.wait_present() does not work here with b.wait_timeout(360): b.wait(lambda: b.eval_js("document.documentElement.outerHTML.indexOf('noVNC_status_normal') >= 0")) b.wait(lambda: b.eval_js("document.documentElement.outerHTML.indexOf('Connected (unencrypted) to: QEMU (subVmTest1)') >= 0")) def testDelete(self): b = self.browser m = self.machine name = "subVmTest1" img1 = "/var/lib/libvirt/images/{0}.img".format(name) img2 = "/var/lib/libvirt/images/{0}-2.img".format(name) self.startVm(name, console='vnc') self.login_and_go("/machines") b.wait_in_text("body", "Virtual Machines") b.wait_in_text("tbody tr th", name) m.execute("test -f {0}".format(img1)) m.execute("test -f {0}".format(img2)) b.click("tbody tr th") # click on the row header b.wait_present("#vm-{0}-delete".format(name)) b.click("#vm-{0}-delete".format(name)) b.wait_present("#cockpit_modal_dialog") b.wait_present("#cockpit_modal_dialog div:contains(The VM is running)") b.wait_present("#cockpit_modal_dialog tr:contains({0})".format(img1)) b.wait_present("#cockpit_modal_dialog tr:contains({0})".format(img2)) b.click("#cockpit_modal_dialog button:contains(Delete)") b.wait_not_present("#cockpit_modal_dialog") b.wait_not_present("#vm-{0}-row".format(name)) m.execute("! test -f {0}".format(img1)) m.execute("! test -f {0}".format(img2)) self.assertNotIn(name, m.execute("virsh list --all")) if __name__ == '__main__': test_main()