# 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. """ Component tests for Base Image Updation functionality Feature Specifications: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Base+Image+Update+facility Test Plan: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Base+Image+Updation+facility+TestPlan Issue Link: https://issues.apache.org/jira/browse/CLOUDSTACK-2243 """ #Import Local Modules from marvin.codes import (PASS, RECURRING) from nose.plugins.attrib import attr from marvin.cloudstackTestCase import cloudstackTestCase from marvin.lib.base import (ServiceOffering, Account, VirtualMachine, Volume, Host, Snapshot, SnapshotPolicy, Template ) from marvin.lib.common import (get_domain, get_zone, get_template, list_templates ) from marvin.lib.utils import (validateList, cleanup_resources) import time class Services: """Test Base Image Updation """ def __init__(self): self.services = { "account": { "email": "test@test.com", "firstname": "Test", "lastname": "User", "username": "test", # Random characters are appended for unique # username "password": "password", }, "service_offering_with_reset": { "name": "Tiny Instance With Reset", "displaytext": "Tiny Instance With Reset", "cpunumber": 1, "cpuspeed": 100, "memory": 128, "isvolatile": True, }, "service_offering_without_reset": { "name": "Tiny Instance Without Reset", "displaytext": "Tiny Instance Without Reset", "cpunumber": 1, "cpuspeed": 100, "memory": 128, "isvolatile": False, }, "recurring_snapshot": { "intervaltype": 'HOURLY', # Frequency of snapshots "maxsnaps": 2, # Should be min 2 "schedule": 1, "timezone": 'America/New_York', # Timezone Formats - http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.0.0-incubating/html-single/API_Developers_Guide/#time-zones }, "templates": { # Configs for different Template formats # For Eg. raw image, zip etc "XenServer": { "displaytext": "Public Template - Xen", "name": "Public template - Xen", "ostype": "CentOS 5.3 (64-bit)", "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.vhd.bz2", "hypervisor": "xenserver", "format": "VHD", "isfeatured": True, "ispublic": True, "isextractable": True, }, "KVM": { "displaytext": "Public Template - KVM", "name": "Public template -KVM", "ostype": "CentOS 5.3 (64-bit)", "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.qcow2.bz2", "hypervisor": "kvm", "format": "qcow2", "isfeatured": True, "ispublic": True, "isextractable": True, } }, "template": { "displaytext": "Cent OS Template 2", "name": "Cent OS Template 2", "ostype": "CentOS 5.3 (64-bit)", "templatefilter": "self", }, "virtual_machine": { "displayname": "Test VM", "username": "root", "password": "password", "ssh_port": 22, "hypervisor": "XenServer", # Hypervisor type should be same as # hypervisor type of cluster "privateport": 22, "publicport": 22, "protocol": "TCP", "userdata": "This is sample data", }, "ostype": "CentOS 5.3 (64-bit)", # Cent OS 5.3 (64 bit) "sleep": 60, "retriesCount": 10, "mode": "advanced" } class TestBaseImageUpdate(cloudstackTestCase): @classmethod def setUpClass(cls): cls.testClient = super(TestBaseImageUpdate, cls).getClsTestClient() 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_with_reset = ServiceOffering.create( cls.api_client, cls.services["service_offering_with_reset"] ) cls.service_offering_without_reset = ServiceOffering.create( cls.api_client, cls.services["service_offering_without_reset"] ) cls.account = Account.create( cls.api_client, cls.services["account"], admin=True, domainid=cls.domain.id ) cls.vm_with_reset = VirtualMachine.create( cls.api_client, cls.services["virtual_machine"], accountid=cls.account.name, domainid=cls.account.domainid, serviceofferingid=cls.service_offering_with_reset.id, ) cls.vm_with_reset_root_disk_id = cls.get_root_device_uuid_for_vm(cls.vm_with_reset.id, cls.vm_with_reset.rootdeviceid) cls.vm_without_reset = VirtualMachine.create( cls.api_client, cls.services["virtual_machine"], accountid=cls.account.name, domainid=cls.account.domainid, serviceofferingid=cls.service_offering_without_reset.id, ) cls.vm_without_reset_root_disk_id = cls.get_root_device_uuid_for_vm(cls.vm_without_reset.id, cls.vm_without_reset.rootdeviceid) cls._cleanup = [ cls.account, cls.service_offering_with_reset, cls.service_offering_without_reset, ] 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 @classmethod def get_root_device_uuid_for_vm(cls, vm_id, root_device_id): volumes = Volume.list(cls.api_client, virtualmachineid=vm_id, listall=True) return volumes[root_device_id].id def setUp(self): self.apiclient = self.testClient.getApiClient() self.hypervisor = self.testClient.getHypervisorInfo() self.dbclient = self.testClient.getDbConnection() self.cleanup = [] return def tearDown(self): try: #Clean up, terminate the created network offerings cleanup_resources(self.apiclient, self.cleanup) except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return def verify_template_listing(self, template): retriesCount = int(self.services["retriesCount"]) template_list_validation_result = None while True: list_template_response = list_templates( self.apiclient, templatefilter=\ self.services["template"]["templatefilter"], id=template.id, zoneid=self.zone.id, account=self.account.name, domainid=self.account.domainid ) template_list_validation_result = validateList(list_template_response) if template_list_validation_result[0] == PASS: break elif retriesCount == 0: self.fail("Failed to get the template list") # Sleep for 5 seconds and again continue the loop if retriesCount has not reached zero time.sleep(5) #Reduce the retriesCount until it becomes zero, when it reaches zero, exception is raised retriesCount = retriesCount - 1 template_response = template_list_validation_result[1] self.assertEqual( template_response.isready, True, "Check isready of newly created template Expected :True Got:%s" %template_response.isready ) return @attr(tags=["advanced", "basic"]) def test_01_deploy_instance_with_is_volatile_offering(self): """ Test deploy an instance with service offerings with IsVolatile set. """ # Validate the following # 1. Service offerings were created successfully # 2. Vms were successfully deployed with the service offerings. self.debug("Checking if deployed VMs are in running state...") vms = VirtualMachine.list( self.apiclient, account=self.account.name, domainid=self.account.domainid, 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]) for vm in vms: self.debug("VM name: %s, VM state: %s" % (vm.name, vm.state)) self.debug("%s" %vm) self.assertEqual( vm.state, "Running", "Vm state should be running for each VM deployed" ) return @attr(tags=["advanced", "basic"]) def test_02_reboot_instance_with_is_volatile_offering(self): """ Test rebooting instances created with isVolatile service offerings """ # Validate the following # 1. Reboot the virtual machines. # 2. Validate the following # a. VM with created with isVolatile=True should have new Root disk but same IP # b. VM with created with isVolatile=False should have same Root disk and IP as before reboot self.debug("Rebooting the virtual machines in account: %s" % self.account.name) try: self.vm_with_reset.reboot(self.apiclient) self.vm_without_reset.reboot(self.apiclient) except Exception as e: self.fail("Failed to reboot the virtual machines, %s" % e) # Check if the the root disk was destroyed and recreated for isVolatile=True self.debug("Checking root disk of VM with isVolatile=True") vms = VirtualMachine.list( self.apiclient, id=self.vm_with_reset.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_with_reset = vm_list_validation_result[1] vm_with_reset_root_disk_id = self.get_root_device_uuid_for_vm(vm_with_reset.id, vm_with_reset.rootdeviceid) self.assertNotEqual(self.vm_with_reset_root_disk_id, vm_with_reset_root_disk_id, "VM created with IsVolatile=True has same rootdeviceid : %s after reboot" %vm_with_reset_root_disk_id ) # Make sure it has the same IP after reboot self.assertEqual(self.vm_with_reset.nic[0].ipaddress, vm_with_reset.nic[0].ipaddress, "VM created with IsVolatile=True doesn't have same ip after reboot. Got : %s Expected : %s" %(vm_with_reset.nic[0].ipaddress, self.vm_with_reset.nic[0].ipaddress) ) # Check if the the root disk was not destroyed for isVolatile=False self.debug("Checking root disk of VM with isVolatile=False") vms = VirtualMachine.list( self.apiclient, id=self.vm_without_reset.id, listall=True ) vm_list_validation_result = validateList(vms) self.assertEqual(vm_list_validation_result[0], PASS, "list validation failed due to %s" % vm_list_validation_result[2]) vm_without_reset = vm_list_validation_result[1] vm_without_reset_root_disk_id = self.get_root_device_uuid_for_vm(vm_without_reset.id, vm_without_reset.rootdeviceid) self.assertEqual(self.vm_without_reset_root_disk_id, vm_without_reset_root_disk_id, "VM created with IsVolatile=False has different rootdeviceid after reboot Got: %s Expected : %s" %(vm_without_reset_root_disk_id, self.vm_without_reset_root_disk_id) ) # Make sure it has the same IP after reboot self.assertEqual(self.vm_without_reset.nic[0].ipaddress, vm_without_reset.nic[0].ipaddress, "VM created with IsVolatile=True doesn't have same ip after reboot. Got : %s Expected : %s" %(vm_without_reset.nic[0].ipaddress, self.vm_without_reset.nic[0].ipaddress) ) return @attr(tags=["advanced", "basic"]) def test_03_restore_vm_with_new_template(self): """ Test restoring a vm with different template than the one it was created with """ hosts = Host.list( self.apiclient, type="Routing", listall=True ) 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]) hypervisor = host_list_validation_result[1].hypervisor for k, v in self.services["templates"].items(): if k.lower() == hypervisor.lower(): # Register new template template = Template.register( self.apiclient, v, zoneid=self.zone.id, account=self.account.name, domainid=self.account.domainid, hypervisor=self.hypervisor ) self.debug( "Registered a template of format: %s with ID: %s" % ( v["format"], template.id )) self.debug( "Downloading template with ID: %s" % ( template.id )) template.download(self.apiclient) self.cleanup.append(template) # Wait for template status to be changed across time.sleep(self.services["sleep"]) self.verify_template_listing(template) # Restore a vm with the new template. self.vm_with_reset.restore(self.apiclient, templateid=template.id) self.vm_without_reset.restore(self.apiclient, templateid=template.id) # Make sure the VMs now have the new template ID # Make sure the Ip address of the VMs haven't changed self.debug("Checking template id of VM with isVolatile=True") vms = VirtualMachine.list( self.apiclient, id=self.vm_with_reset.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_with_reset = vm_list_validation_result[1] self.assertNotEqual(self.vm_with_reset.templateid, vm_with_reset.templateid, "VM created with IsVolatile=True has same templateid : %s after restore" %vm_with_reset.templateid ) self.assertNotEqual(self.vm_with_reset.templateid, template.id, "VM created with IsVolatile=True has wrong templateid after restore Got:%s Expected: %s" %(self.vm_with_reset.templateid, template.id) ) # Make sure it has the same IP after reboot self.assertEqual(self.vm_with_reset.nic[0].ipaddress, vm_with_reset.nic[0].ipaddress, "VM created with IsVolatile=True doesn't have same ip after restore. Got : %s Expected : %s" %(vm_with_reset.nic[0].ipaddress, self.vm_with_reset.nic[0].ipaddress) ) # Check if the the root disk was not destroyed for isVolatile=False self.debug("Checking template id of VM with isVolatile=False") vms = VirtualMachine.list( self.apiclient, id=self.vm_without_reset.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_without_reset = vm_list_validation_result[1] self.assertNotEqual(self.vm_without_reset.templateid, vm_without_reset.templateid, "VM created with IsVolatile=False has same templateid : %s after restore" %vm_with_reset.templateid ) self.assertNotEqual(self.vm_without_reset.templateid, template.id, "VM created with IsVolatile=False has wrong templateid after restore Got:%s Expected: %s" %(self.vm_without_reset.templateid, template.id) ) # Make sure it has the same IP after reboot self.assertEqual(self.vm_without_reset.nic[0].ipaddress, vm_without_reset.nic[0].ipaddress, "VM created with IsVolatile=False doesn't have same ip after restore. Got : %s Expected : %s" %(vm_without_reset.nic[0].ipaddress, self.vm_without_reset.nic[0].ipaddress) ) return @attr(tags=["advanced", "basic"]) def test_04_reoccuring_snapshot_rules(self): """ 1) Create a VM using the Service offering IsVolatile enabled 2) Apply a recurring snapshot rule on the Volume. 3) After a couple of snapshots are taken reboot the VM. Verify the following conditions 1) New root disk should be formed 2) The recurring snapshot rule should be deleted """ vms = VirtualMachine.list( self.apiclient, id=self.vm_with_reset.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_with_reset = vm_list_validation_result[1] vm_with_reset_root_disk_id = self.get_root_device_uuid_for_vm( vm_with_reset.id, vm_with_reset.rootdeviceid ) self.debug("Creating recurring snapshot policy for root disk on vm created with IsVolatile=True") self.debug("Snapshot Policy - Type : %s Scheduled Hours : %s" %( self.services["recurring_snapshot"]["intervaltype"], self.services["recurring_snapshot"]["schedule"])) recurring_snapshot = SnapshotPolicy.create( self.apiclient, vm_with_reset_root_disk_id, self.services["recurring_snapshot"] ) #ListSnapshotPolicy should return newly created policy list_snapshots_policy = SnapshotPolicy.list( self.apiclient, id=recurring_snapshot.id, volumeid=vm_with_reset_root_disk_id ) snapshot_list_validation_result = validateList(list_snapshots_policy) self.assertEqual(snapshot_list_validation_result[0], PASS, "snapshot list validation failed due to %s" % snapshot_list_validation_result[2]) snapshots_policy = snapshot_list_validation_result[1] self.assertEqual( snapshots_policy.id, recurring_snapshot.id, "Check recurring snapshot id in list resources call" ) self.assertEqual( snapshots_policy.maxsnaps, self.services["recurring_snapshot"]["maxsnaps"], "Check interval type in list resources call" ) sleep_seconds = (self.services["recurring_snapshot"]["schedule"]) * 3600 + 600 sleep_minutes = sleep_seconds/60 self.debug("Sleeping for %s minutes till the volume is snapshoted" %sleep_minutes) time.sleep(sleep_seconds) retriesCount = self.services["retriesCount"] while True: snapshots = Snapshot.list( self.apiclient, volumeid=vm_with_reset_root_disk_id, intervaltype=\ self.services["recurring_snapshot"]["intervaltype"], snapshottype=RECURRING, listall=True ) snapshot_list_validation_result = validateList(snapshots) if snapshot_list_validation_result[0] == PASS: break elif retriesCount == 0: self.fail("Failed to get snapshots list") time.sleep(60) retriesCount = retriesCount - 1 # rebooting the vm with isVolatile = True try: self.vm_with_reset.reboot(self.apiclient) except Exception as e: self.fail("Failed to reboot the virtual machine. Error: %s" % e) # Check if the the root disk was destroyed and recreated for isVolatile=True self.debug("Checking whether root disk of VM with isVolatile=True was destroyed") vms = VirtualMachine.list( self.apiclient, id=self.vm_with_reset.id, listall=True ) vm_list_validation_result = validateList(vms) self.assertEqual(vm_list_validation_result[0], PASS, "list validation failed due to %s" % vm_list_validation_result[2]) vm_with_reset_after_reboot = vm_list_validation_result[1] vm_with_reset_root_disk_id_after_reboot = self.get_root_device_uuid_for_vm( vm_with_reset_after_reboot.id, vm_with_reset_after_reboot.rootdeviceid ) self.assertNotEqual(vm_with_reset_root_disk_id, vm_with_reset_root_disk_id_after_reboot, "VM created with IsVolatile=True has same rootdeviceid : %s after reboot" %vm_with_reset_root_disk_id_after_reboot ) # Make sure it has the same IP after reboot self.assertEqual(vm_with_reset.nic[0].ipaddress, vm_with_reset_after_reboot.nic[0].ipaddress, "VM created with IsVolatile=True doesn't have same ip after reboot. Got : %s Expected : %s" %(vm_with_reset_after_reboot.nic[0].ipaddress, vm_with_reset.nic[0].ipaddress) ) # Check whether the recurring policy has been deleted from the database self.debug("Checking whether snapshot rule for VM with isVolatile=True was destroyed \ Here we are passing root disk id of vm before reboot which does not exist hence\ listing should fail") with self.assertRaises(Exception): list_snapshots_policy = SnapshotPolicy.list( self.apiclient, volumeid=vm_with_reset_root_disk_id ) return