| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| """ Deploy VM from ISO with UEFI |
| """ |
| # Import Local Modules |
| from nose.plugins.attrib import attr |
| from marvin.cloudstackTestCase import cloudstackTestCase, unittest |
| from marvin.lib.utils import cleanup_resources |
| from marvin.lib.base import (Account, |
| VirtualMachine, |
| ServiceOffering, |
| Iso, |
| DiskOffering, |
| Host, |
| ) |
| from marvin.lib.common import (get_zone, |
| get_domain, |
| list_hosts, |
| ) |
| from marvin.codes import PASS |
| from marvin.sshClient import SshClient |
| import xml.etree.ElementTree as ET |
| from lxml import etree |
| |
| class TestDeployVMFromISOWithUefi(cloudstackTestCase): |
| |
| @classmethod |
| def setUpClass(cls): |
| cls.testClient = super(TestDeployVMFromISOWithUefi, cls).getClsTestClient() |
| cls.apiclient = cls.testClient.getApiClient() |
| |
| cls.hypervisor = cls.testClient.getHypervisorInfo() |
| |
| if cls.hypervisor != 'kvm': |
| raise unittest.SkipTest("Those tests can be run only for KVM hypervisor") |
| |
| cls.testdata = cls.testClient.getParsedTestDataConfig() |
| # Get Zone, Domain and templates |
| cls.domain = get_domain(cls.apiclient) |
| cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests()) |
| |
| hosts = list_hosts(cls.apiclient, zoneid = cls.zone.id, type="Routing") |
| |
| if not cls.isUefiEnabledOnAtLeastOnHost(hosts): |
| raise unittest.SkipTest("At least one host should support UEFI") |
| |
| cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__ |
| |
| # Create service, disk offerings etc |
| cls.service_offering = ServiceOffering.create( |
| cls.apiclient, |
| cls.testdata["service_offering"] |
| ) |
| |
| cls.disk_offering = DiskOffering.create( |
| cls.apiclient, |
| cls.testdata["disk_offering"] |
| ) |
| |
| cls.testdata["virtual_machine"]["zoneid"] = cls.zone.id |
| cls.testdata["iso1"]["zoneid"] = cls.zone.id |
| cls.testdata["iso3"]["zoneid"] = cls.zone.id |
| cls.account = Account.create( |
| cls.apiclient, |
| cls.testdata["account"], |
| domainid=cls.domain.id |
| ) |
| cls._cleanup = [cls.account, cls.service_offering, cls.disk_offering] |
| |
| cls.centos_iso = Iso.create( |
| cls.apiclient, |
| cls.testdata["iso1"], |
| account=cls.account.name, |
| domainid=cls.account.domainid, |
| zoneid=cls.zone.id |
| ) |
| try: |
| # Download the ISO |
| cls.centos_iso.download(cls.apiclient) |
| except Exception as e: |
| raise Exception("Exception while downloading ISO %s: %s" |
| % (cls.centos_iso.id, e)) |
| |
| cls.windows_iso = Iso.create( |
| cls.apiclient, |
| cls.testdata["iso3"], |
| account=cls.account.name, |
| domainid=cls.account.domainid, |
| zoneid=cls.zone.id |
| ) |
| try: |
| # Download the ISO |
| cls.windows_iso.download(cls.apiclient) |
| except Exception as e: |
| raise Exception("Exception while downloading ISO %s: %s" |
| % (cls.windows_iso.id, e)) |
| |
| return |
| |
| @classmethod |
| def tearDownClass(cls): |
| try: |
| cleanup_resources(cls.apiclient, cls._cleanup) |
| except Exception as e: |
| raise Exception("Warning: Exception during cleanup : %s" % e) |
| |
| def setUp(self): |
| self.hypervisor = self.testClient.getHypervisorInfo() |
| if self.hypervisor != 'kvm': |
| raise self.skipTest("Skipping test case for non-kvm hypervisor") |
| return |
| |
| |
| @attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true") |
| def test_01_deploy_vm_from_iso_uefi_secure(self): |
| """Test Deploy CentOS Virtual Machine from ISO with UEFI Secure |
| """ |
| |
| self.deployVmAndTestUefi(self.centos_iso, 'Secure', 'yes') |
| |
| return |
| |
| @attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true") |
| def test_02_deploy_vm_from_iso_uefi_legacy(self): |
| """Test Deploy CentOS Virtual Machine from ISO with UEFI Legacy mode |
| """ |
| self.deployVmAndTestUefi(self.centos_iso, 'Legacy', 'no') |
| |
| return |
| |
| @attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true") |
| def test_03_deploy_windows_vm_from_iso_uefi_legacy(self): |
| """Test Deploy Windows Virtual Machine from ISO with UEFI Legacy mode |
| """ |
| self.deployVmAndTestUefi(self.windows_iso, 'Legacy', 'no') |
| |
| return |
| |
| @attr(tags=["advanced", "eip", "advancedns", "basic", "sg"], required_hardware="true") |
| def test_04_deploy_windows_vm_from_iso_uefi_secure(self): |
| """Test Deploy Windows Virtual Machine from ISO with UEFI Secure mode |
| """ |
| self.deployVmAndTestUefi(self.windows_iso, 'Secure', 'yes') |
| |
| return |
| |
| def getVirshXML(self, host, instancename): |
| self.assertIsNotNone(host, "Host should not be None") |
| |
| self.assertIsNotNone(instancename, "Instance name should not be None") |
| |
| sshc = SshClient( |
| host=host.ipaddress, |
| port=22, |
| user=self.hostConfig['username'], |
| passwd=self.hostConfig['password']) |
| |
| virsh_cmd = 'virsh dumpxml %s' % instancename |
| xml_res = sshc.execute(virsh_cmd) |
| xml_as_str = ''.join(xml_res) |
| parser = etree.XMLParser(remove_blank_text=True) |
| return ET.fromstring(xml_as_str, parser=parser) |
| |
| def checkBootTypeAndMode(self, root, bootmodesecure, isWindowsIso): |
| |
| machine = root.find(".os/type").get("machine") |
| self.assertEqual(("q35" in machine), True, "The virtual machine is not with UEFI boot type") |
| |
| if root.find(".os/loader") is not None : |
| bootmode = root.find(".os/loader").get("secure") |
| if bootmode is not None : |
| self.assertEqual((bootmode == bootmodesecure), True, "The VM is not in the right boot mode") |
| |
| if isWindowsIso: |
| disks = root.findall("devices/disk") |
| for disk in disks: |
| bus = disk.find("target").get("bus") |
| self.debug("bus is %s" % bus) |
| self.assertEqual(bus == 'sata', True, "All disks of the VM should be with bus SATA") |
| |
| def deployVmAndTestUefi(self, iso, bootmode, bootmodesecure): |
| self.virtual_machine = VirtualMachine.create( |
| self.apiclient, |
| self.testdata["virtual_machine"], |
| accountid=self.account.name, |
| domainid=self.account.domainid, |
| templateid=iso.id, |
| serviceofferingid=self.service_offering.id, |
| diskofferingid=self.disk_offering.id, |
| hypervisor=self.hypervisor, |
| boottype='UEFI', |
| bootmode=bootmode, |
| zoneid=self.zone.id |
| ) |
| |
| response = self.virtual_machine.getState( |
| self.apiclient, |
| VirtualMachine.RUNNING) |
| self.assertEqual(response[0], PASS, response[1]) |
| |
| hosts = Host.list(self.apiclient, id=self.virtual_machine.hostid) |
| if len(hosts) != 1: |
| assert False, "Could not find host with id " + self.virtual_machine.hostid |
| |
| host = hosts[0] |
| |
| isWindowsIso = False |
| if "Windows" in iso.ostypename: |
| isWindowsIso = True |
| |
| instancename = self.virtual_machine.instancename |
| virshxml = self.getVirshXML(host, instancename) |
| self.checkBootTypeAndMode(virshxml, bootmodesecure, isWindowsIso) |
| |
| @classmethod |
| def isUefiEnabledOnAtLeastOnHost(cls, hosts): |
| for h in hosts: |
| if h.ueficapability: |
| return True |
| return False |