| # 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. | |
| """ P1 for VMware DRS testing | |
| """ | |
| #Import Local Modules | |
| import marvin | |
| from nose.plugins.attrib import attr | |
| from marvin.cloudstackTestCase import cloudstackTestCase | |
| import unittest | |
| from marvin.lib.base import (Account, | |
| AffinityGroup, | |
| Host, | |
| VirtualMachine, | |
| ServiceOffering) | |
| from marvin.lib.common import (get_zone, | |
| get_template, | |
| get_domain, | |
| get_pod | |
| ) | |
| from marvin.lib.utils import (validateList, | |
| cleanup_resources, | |
| random_gen) | |
| from marvin.cloudstackAPI import (prepareHostForMaintenance, | |
| cancelHostMaintenance, | |
| migrateVirtualMachine) | |
| from marvin.codes import PASS | |
| #Import System modules | |
| import time | |
| class Services: | |
| """Test vmware DRS services | |
| """ | |
| def __init__(self): | |
| self.services = { | |
| "account": { | |
| "email": "test@test.com", | |
| "firstname": "Test", | |
| "lastname": "User", | |
| "username": "test", | |
| # Random characters are appended in create account to | |
| # ensure unique username generated each time | |
| "password": "password", | |
| }, | |
| "virtual_machine": | |
| { | |
| "displayname": "testserver", | |
| "username": "root", # VM creds for SSH | |
| "password": "password", | |
| "ssh_port": 22, | |
| "hypervisor": 'XenServer', | |
| "privateport": 22, | |
| "publicport": 22, | |
| "protocol": 'TCP', | |
| }, | |
| "service_offering": | |
| { | |
| "name": "Tiny Instance", | |
| "displaytext": "Tiny Instance", | |
| "cpunumber": 1, | |
| "cpuspeed": 100, # in MHz | |
| "memory": 2048, # In MBs | |
| }, | |
| "service_offering_max_memory": | |
| { | |
| "name": "Tiny Instance", | |
| "displaytext": "Tiny Instance", | |
| "cpunumber": 1, | |
| "cpuspeed": 100, # in MHz | |
| "memory": 128, # In MBs | |
| }, | |
| "host_anti_affinity": { | |
| "name": "", | |
| "type": "host anti-affinity", | |
| }, | |
| "host_affinity": { | |
| "name": "", | |
| "type": "host affinity", | |
| }, | |
| "sleep": 60, | |
| "timeout": 10, | |
| "ostype": 'CentOS 5.3 (64-bit)', | |
| # CentOS 5.3 (64-bit) | |
| } | |
| class TestVMPlacement(cloudstackTestCase): | |
| @classmethod | |
| def setUpClass(cls): | |
| cls.testClient = super(TestVMPlacement, cls).getClsTestClient() | |
| if cls.testClient.getHypervisorInfo().lower() != "vmware": | |
| raise unittest.SkipTest("VMWare tests only valid on VMWare hypervisor") | |
| cls.api_client = cls.testClient.getApiClient() | |
| cls.services = Services().services | |
| # Get Zone, Domain and templates | |
| cls.domain = get_domain(cls.api_client) | |
| cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) | |
| cls.pod = get_pod( | |
| cls.api_client, | |
| zone_id=cls.zone.id) | |
| cls.template = get_template( | |
| cls.api_client, | |
| cls.zone.id, | |
| cls.services["ostype"] | |
| ) | |
| cls.services["virtual_machine"]["zoneid"] = cls.zone.id | |
| cls.services["virtual_machine"]["template"] = cls.template.id | |
| cls.service_offering = ServiceOffering.create( | |
| cls.api_client, | |
| cls.services["service_offering"], | |
| offerha=True | |
| ) | |
| cls._cleanup = [ | |
| cls.service_offering, | |
| ] | |
| return | |
| @classmethod | |
| def tearDownClass(cls): | |
| try: | |
| #Cleanup resources used | |
| cleanup_resources(cls.api_client, cls._cleanup) | |
| except Exception as e: | |
| raise Exception("Warning: Exception during cleanup : %s" % e) | |
| return | |
| def setUp(self): | |
| self.apiclient = self.testClient.getApiClient() | |
| self.dbclient = self.testClient.getDbConnection() | |
| self.account = Account.create( | |
| self.apiclient, | |
| self.services["account"], | |
| admin=True, | |
| domainid=self.domain.id | |
| ) | |
| self.cleanup = [self.account] | |
| return | |
| def tearDown(self): | |
| try: | |
| #Clean up, terminate the created accounts, domains etc | |
| cleanup_resources(self.apiclient, self.cleanup) | |
| #self.testClient.close() | |
| except Exception as e: | |
| raise Exception("Warning: Exception during cleanup : %s" % e) | |
| return | |
| @unittest.skip("Test case is not testable as the VM is being deployed with\ | |
| too high memory - above account capacity") | |
| @attr(tags=["invalid"], required_hardware="true") | |
| def test_vm_creation_in_fully_automated_mode(self): | |
| """ Test VM Creation in automation mode = Fully automated | |
| This test requires following preconditions: | |
| - DRS Cluster is configured in "Fully automated" mode | |
| """ | |
| # Validate the following | |
| # 1. Create a new VM in a host which is almost fully utilized | |
| # 2 Automatically places VM on the other host | |
| # 3. VM state is running after deployment | |
| hosts = Host.list( | |
| self.apiclient, | |
| zoneid=self.zone.id, | |
| resourcestate='Enabled', | |
| type='Routing' | |
| ) | |
| self.assertEqual( | |
| isinstance(hosts, list), | |
| True, | |
| "List hosts should return valid host response" | |
| ) | |
| self.assertGreaterEqual( | |
| len(hosts), | |
| 2, | |
| "There must be two hosts present in a cluster" | |
| ) | |
| host_1 = hosts[0] | |
| #Convert available memory( Keep some margin) into MBs and assign to service offering | |
| self.services["service_offering_max_memory"]["memory"] = int((int(hosts[0].memorytotal) - int(hosts[0].memoryused))/1048576 - 1024) | |
| self.debug("max memory: %s" % self.services["service_offering_max_memory"]["memory"]) | |
| service_offering_max_memory = ServiceOffering.create( | |
| self.apiclient, | |
| self.services["service_offering_max_memory"] | |
| ) | |
| VirtualMachine.create( | |
| self.apiclient, | |
| self.services["virtual_machine"], | |
| accountid=self.account.name, | |
| domainid=self.account.domainid, | |
| serviceofferingid=service_offering_max_memory.id, | |
| hostid = host_1.id | |
| ) | |
| # Host 1 has only 1024 MB memory available now after deploying the instance | |
| # We are trying to deploy an instance with 2048 MB memory, this should automatically | |
| # get deployed on other host which has the enough capacity | |
| self.debug("Trying to deploy instance with memory requirement more than that is available on\ | |
| the first host") | |
| self.debug("Deploying VM in account: %s" % self.account.name) | |
| # Spawn an instance in that network | |
| virtual_machine = VirtualMachine.create( | |
| self.apiclient, | |
| self.services["virtual_machine"], | |
| accountid=self.account.name, | |
| domainid=self.account.domainid, | |
| serviceofferingid=self.service_offering.id | |
| ) | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| id=virtual_machine.id, | |
| listall=True | |
| ) | |
| self.assertEqual( | |
| isinstance(vms, list), | |
| True, | |
| "List VMs should return valid response for deployed VM" | |
| ) | |
| self.assertNotEqual( | |
| len(vms), | |
| 0, | |
| "List VMs should return valid response for deployed VM" | |
| ) | |
| vm = vms[0] | |
| self.assertEqual( | |
| vm.state, | |
| "Running", | |
| "Deployed VM should be in RUnning state" | |
| ) | |
| self.assertNotEqual( | |
| vm.hostid, | |
| host_1.id, | |
| "Host Ids of two should not match as one host is full" | |
| ) | |
| self.debug("The host ids of two virtual machines are different as expected\ | |
| they are %s and %s" % (vm.hostid, host_1.id)) | |
| return | |
| @unittest.skip("Skipping... Not tested due to unavailibility of multihosts setup - 3 hosts in a cluster") | |
| class TestAntiAffinityRules(cloudstackTestCase): | |
| @classmethod | |
| def setUpClass(cls): | |
| cls.testClient = super(TestAntiAffinityRules, cls).getClsTestClient() | |
| if cls.testClient.getHypervisorInfo().lower() != "vmware": | |
| raise unittest.SkipTest("VMWare tests only valid on VMWare hypervisor") | |
| cls.api_client = cls.testClient.getApiClient() | |
| cls.services = Services().services | |
| # Get Zone, Domain and templates | |
| cls.domain = get_domain(cls.api_client) | |
| cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) | |
| cls.template = get_template( | |
| cls.api_client, | |
| cls.zone.id, | |
| cls.services["ostype"] | |
| ) | |
| cls.services["virtual_machine"]["zoneid"] = cls.zone.id | |
| cls.services["virtual_machine"]["template"] = cls.template.id | |
| cls.service_offering = ServiceOffering.create( | |
| cls.api_client, | |
| cls.services["service_offering"], | |
| offerha=True | |
| ) | |
| cls.account = Account.create( | |
| cls.api_client, | |
| cls.services["account"], | |
| domainid=cls.domain.id | |
| ) | |
| cls._cleanup = [cls.account] | |
| return | |
| @classmethod | |
| def tearDownClass(cls): | |
| try: | |
| #Cleanup resources used | |
| cleanup_resources(cls.api_client, cls._cleanup) | |
| except Exception as e: | |
| raise Exception("Warning: Exception during cleanup : %s" % e) | |
| return | |
| def setUp(self): | |
| self.apiclient = self.testClient.getApiClient() | |
| self.dbclient = self.testClient.getDbConnection() | |
| self.cleanup = [] | |
| return | |
| def tearDown(self): | |
| try: | |
| #Clean up, terminate the created accounts, domains etc | |
| cleanup_resources(self.apiclient, self.cleanup) | |
| #self.testClient.close() | |
| except Exception as e: | |
| raise Exception("Warning: Exception during cleanup : %s" % e) | |
| return | |
| def create_aff_grp(self, aff_grp=None, | |
| acc=None, domainid=None): | |
| aff_grp["name"] = "aff_grp_" + random_gen(size=6) | |
| try: | |
| aff_grp = AffinityGroup.create(self.apiclient, | |
| aff_grp, acc, domainid) | |
| return aff_grp | |
| except Exception as e: | |
| raise Exception("Error: Creation of Affinity Group failed : %s" %e) | |
| @attr(tags=["advanced", "vmware", "multihost"], required_hardware="true") | |
| def test_vmware_anti_affinity(self): | |
| """ Test Set up anti-affinity rules | |
| The test requires following pre-requisites | |
| - VMWare cluster configured in fully automated mode | |
| """ | |
| # Validate the following | |
| # 1. Deploy VMs on host 1 and 2 | |
| # 2. Enable maintenance mode for host 1 | |
| # 3. VM should be migrated to 3rd host | |
| hosts = Host.list( | |
| self.apiclient, | |
| zoneid=self.zone.id, | |
| resourcestate='Enabled', | |
| type='Routing' | |
| ) | |
| self.assertEqual( | |
| isinstance(hosts, list), | |
| True, | |
| "List hosts should return valid host response" | |
| ) | |
| self.debug(len(hosts)) | |
| self.assertGreaterEqual( | |
| len(hosts), | |
| 3, | |
| "There must be at least 3 hosts present in a cluster" | |
| ) | |
| aff_grp = self.create_aff_grp(aff_grp=self.services["host_anti_affinity"], acc=self.account.name, domainid=self.domain.id) | |
| vm_1 = VirtualMachine.create( | |
| self.apiclient, | |
| self.services["virtual_machine"], | |
| accountid=self.account.name, | |
| domainid=self.domain.id, | |
| serviceofferingid=self.service_offering.id, | |
| affinitygroupnames=[aff_grp.name] | |
| ) | |
| vm_2 = VirtualMachine.create( | |
| self.apiclient, | |
| self.services["virtual_machine"], | |
| accountid=self.account.name, | |
| domainid=self.domain.id, | |
| serviceofferingid=self.service_offering.id, | |
| affinitygroupnames=[aff_grp.name] | |
| ) | |
| host_1 = vm_1.hostid | |
| host_2 = vm_2.hostid | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| id=vm_1.id, | |
| listall=True | |
| ) | |
| vm_list_validation_result = validateList(vms) | |
| self.assertEqual(vm_list_validation_result[0], PASS, "vm list validation failed due to %s" % | |
| vm_list_validation_result[1]) | |
| virtual_machine_1 = vm_list_validation_result[1] | |
| self.debug("VM State: %s" % virtual_machine_1.state) | |
| self.assertEqual( | |
| virtual_machine_1.state, | |
| "Running", | |
| "Deployed VM should be in RUnning state" | |
| ) | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| id=vm_2.id, | |
| listall=True | |
| ) | |
| vm_list_validation_result = validateList(vms) | |
| self.assertEqual(vm_list_validation_result[0], PASS, "vm list validation failed due to %s" % | |
| vm_list_validation_result[1]) | |
| virtual_machine_2 = vm_list_validation_result[1] | |
| self.debug("VM %s State: %s" % ( | |
| virtual_machine_2.name, | |
| virtual_machine_2.state | |
| )) | |
| self.assertEqual( | |
| virtual_machine_2.state, | |
| "Running", | |
| "Deployed VM should be in RUnning state" | |
| ) | |
| self.debug("Enabling maintenance mode on host_1: %s" % host_1) | |
| cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() | |
| cmd.id = host_1 | |
| self.apiclient.prepareHostForMaintenance(cmd) | |
| timeout = self.services["timeout"] | |
| while True: | |
| hosts = Host.list( | |
| self.apiclient, | |
| zoneid=self.zone.id, | |
| type='Routing', | |
| id=host_1 | |
| ) | |
| host_list_validation_result = validateList(hosts) | |
| self.assertEqual(host_list_validation_result[0], PASS, "host list validation failed due to %s" | |
| % host_list_validation_result[2]) | |
| host = host_list_validation_result[1] | |
| if host.resourcestate == 'Maintenance': | |
| break | |
| elif timeout == 0: | |
| self.fail("Failed to put host: %s in maintenance mode" % host.name) | |
| time.sleep(self.services["sleep"]) | |
| timeout = timeout - 1 | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| id=virtual_machine_1.id, | |
| listall=True | |
| ) | |
| vm_list_validation_result = validateList(vms) | |
| self.assertEqual(vm_list_validation_result[0], PASS, "vm list validation failed due to %s" % | |
| vm_list_validation_result[2]) | |
| vm = vm_list_validation_result[0] | |
| self.assertEqual( | |
| vm.state, | |
| "Running", | |
| "Deployed VM should be in RUnning state" | |
| ) | |
| self.assertNotEqual( | |
| vm.hostid, | |
| host_2, | |
| "The host name should not match with second host name" | |
| ) | |
| self.debug("Canceling host maintenance for ID: %s" % host_1.id) | |
| cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() | |
| cmd.id = host_1.id | |
| self.apiclient.cancelHostMaintenance(cmd) | |
| self.debug("Maintenance mode canceled for host: %s" % host_1.id) | |
| return | |
| @unittest.skip("Skipping...Host Affinity feature not available yet in cloudstack") | |
| class TestAffinityRules(cloudstackTestCase): | |
| @classmethod | |
| def setUpClass(cls): | |
| cls.testClient = super(TestAffinityRules, cls).getClsTestClient() | |
| if cls.testClient.getHypervisorInfo().lower() != "vmware": | |
| raise unittest.SkipTest("VMWare tests only valid on VMWare hypervisor") | |
| cls.api_client = cls.testClient.getApiClient() | |
| cls.services = Services().services | |
| # Get Zone, Domain and templates | |
| cls.domain = get_domain(cls.api_client) | |
| cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) | |
| cls.template = get_template( | |
| cls.api_client, | |
| cls.zone.id, | |
| cls.services["ostype"] | |
| ) | |
| cls.services["virtual_machine"]["zoneid"] = cls.zone.id | |
| cls.services["virtual_machine"]["template"] = cls.template.id | |
| cls.service_offering = ServiceOffering.create( | |
| cls.api_client, | |
| cls.services["service_offering"], | |
| offerha=True | |
| ) | |
| cls.account = Account.create( | |
| cls.api_client, | |
| cls.services["account"], | |
| domainid=cls.domain.id | |
| ) | |
| cls._cleanup = [cls.account] | |
| return | |
| @classmethod | |
| def tearDownClass(cls): | |
| try: | |
| #Cleanup resources used | |
| cleanup_resources(cls.api_client, cls._cleanup) | |
| except Exception as e: | |
| raise Exception("Warning: Exception during cleanup : %s" % e) | |
| return | |
| def setUp(self): | |
| self.apiclient = self.testClient.getApiClient() | |
| self.dbclient = self.testClient.getDbConnection() | |
| self.cleanup = [] | |
| return | |
| def tearDown(self): | |
| try: | |
| # Clean up, terminate the created accounts, domains etc | |
| cleanup_resources(self.apiclient, self.cleanup) | |
| except Exception as e: | |
| raise Exception("Warning: Exception during cleanup : %s" % e) | |
| return | |
| def create_aff_grp(self, aff_grp=None, | |
| acc=None, domainid=None): | |
| aff_grp["name"] = "aff_grp_" + random_gen(size=6) | |
| try: | |
| aff_grp = AffinityGroup.create(self.apiclient, | |
| aff_grp, acc, domainid) | |
| return aff_grp | |
| except Exception as e: | |
| raise Exception("Error: Creation of Affinity Group failed : %s" %e) | |
| @attr(tags=["advanced", "vmware", "multihost"], required_hardware="true") | |
| def test_vmware_affinity(self): | |
| """ Test Set up affinity rules | |
| The test requires following pre-requisites | |
| - VMWare cluster configured in fully automated mode | |
| """ | |
| # Validate the following | |
| # 1. Deploy 2 VMs on same hosts | |
| # 2. Migrate one VM from one host to another | |
| # 3. The second VM should also get migrated | |
| hosts = Host.list( | |
| self.apiclient, | |
| zoneid=self.zone.id, | |
| resourcestate='Enabled', | |
| type='Routing' | |
| ) | |
| self.assertEqual( | |
| isinstance(hosts, list), | |
| True, | |
| "List hosts should return valid host response" | |
| ) | |
| self.assertGreaterEqual( | |
| len(hosts), | |
| 2, | |
| "There must be two hosts present in a cluster" | |
| ) | |
| host_1 = hosts[0].id | |
| host_2 = hosts[1].id | |
| aff_grp = self.create_aff_grp(aff_grp=self.services["host_affinity"], acc=self.account.name, domainid=self.domain.id) | |
| vm_1 = VirtualMachine.create( | |
| self.apiclient, | |
| self.services["virtual_machine"], | |
| accountid=self.account.name, | |
| domainid=self.domain.id, | |
| serviceofferingid=self.service_offering.id, | |
| affinitygroupnames=[aff_grp.name], | |
| hostid = host_1 | |
| ) | |
| vm_2 = VirtualMachine.create( | |
| self.apiclient, | |
| self.services["virtual_machine"], | |
| accountid=self.account.name, | |
| domainid=self.domain.id, | |
| serviceofferingid=self.service_offering.id, | |
| affinitygroupnames=[aff_grp.name] | |
| ) | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| id= vm_1.id, | |
| listall=True | |
| ) | |
| vm_list_validation_result = validateList(vms) | |
| self.assertEqual(vm_list_validation_result[0], PASS, "vm list validation failed due to %s" % | |
| vm_list_validation_result[2]) | |
| virtual_machine_1 = vm_list_validation_result[1] | |
| self.assertEqual( | |
| virtual_machine_1.state, | |
| "Running", | |
| "Deployed VM should be in RUnning state" | |
| ) | |
| self.debug("Deploying VM on account: %s" % self.account.name) | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| id=vm_2.id, | |
| listall=True | |
| ) | |
| vm_list_validation_result = validateList(vms) | |
| self.assertEqual(vm_list_validation_result[0], PASS, "vm list validation failed due to %s" % | |
| vm_list_validation_result[2]) | |
| virtual_machine_2 = vm_list_validation_result[1] | |
| self.assertEqual( | |
| virtual_machine_2.state, | |
| "Running", | |
| "Deployed VM should be in RUnning state" | |
| ) | |
| self.debug("Migrate VM from host_1 to host_2") | |
| cmd = migrateVirtualMachine.migrateVirtualMachineCmd() | |
| cmd.virtualmachineid = virtual_machine_2.id | |
| cmd.hostid = host_2 | |
| self.apiclient.migrateVirtualMachine(cmd) | |
| self.debug("Migrated VM from host_1 to host_2") | |
| vms = VirtualMachine.list( | |
| self.apiclient, | |
| hostid=host_2, | |
| listall=True | |
| ) | |
| vm_list_validation_result = validateList(vms) | |
| self.assertEqual(vm_list_validation_result[0], PASS, "vm list validation failed due to %s" % | |
| vm_list_validation_result[2]) | |
| vmids = [vm.id for vm in vms] | |
| self.assertIn( | |
| virtual_machine_1.id, | |
| vmids, | |
| "VM 1 should be successfully migrated to host 2" | |
| ) | |
| self.assertIn( | |
| virtual_machine_2.id, | |
| vmids, | |
| "VM 2 should be automatically migrated to host 2" | |
| ) | |
| return |