From 891b5976069d178dedf41bf6c85a7398bc7460b6 Mon Sep 17 00:00:00 2001 From: Ashutosh K Date: Tue, 10 Feb 2015 12:05:03 +0530 Subject: [PATCH] CLOUDSTACK-8236: Automation for storage migration test path Signed-off-by: SrikanteswaraRao Talluri --- .../testpaths/testpath_storage_migration.py | 3392 +++++++++++++++++ tools/marvin/marvin/codes.py | 7 + tools/marvin/marvin/config/test_data.py | 27 +- tools/marvin/marvin/lib/base.py | 20 +- 4 files changed, 3437 insertions(+), 9 deletions(-) create mode 100644 test/integration/testpaths/testpath_storage_migration.py diff --git a/test/integration/testpaths/testpath_storage_migration.py b/test/integration/testpaths/testpath_storage_migration.py new file mode 100644 index 00000000000..ee77b030afc --- /dev/null +++ b/test/integration/testpaths/testpath_storage_migration.py @@ -0,0 +1,3392 @@ +#_volumes 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. +""" Test cases for Test Paths Storage Migration +""" +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.lib.utils import (cleanup_resources, + random_gen, + format_volume_to_ext3) +from marvin.lib.base import (Account, + ServiceOffering, + DiskOffering, + Volume, + Template, + VirtualMachine, + StoragePool, + Snapshot + ) +from marvin.lib.common import (get_domain, + get_zone, + get_template, + list_volumes, + list_virtual_machines + ) +from marvin.codes import (ZONETAG1, + CLUSTERTAG1) + +from marvin.cloudstackAPI import (deleteVolume) + +import hashlib +from marvin.sshClient import SshClient +import time +from threading import Thread + + +def GetDestinationPool(self, + poolsToavoid, + migrateto + ): + """ Get destination pool which has scope same as migrateto + and which is not in avoid set + """ + + destinationPool = None + + # Get Storage Pool Id to migrate to + for storagePool in self.pools: + if storagePool.scope == migrateto: + if storagePool.name not in poolsToavoid: + destinationPool = storagePool + break + + return destinationPool + + +def MigrateRootVolume(self, + vm, + destinationPool, + expectexception=False): + """ Migrate given volume to type of storage pool mentioned in migrateto: + + Inputs: + 1. volume: Volume to be migrated + 2. migrate_to: Scope of desired Storage pool to which volume + is to be migrated + 3. expectexception: If exception is expected while migration + """ + + if expectexception: + with self.assertRaises(Exception): + VirtualMachine.migrate( + vm, + self.apiclient, + # virtualmachineid=vm.id, + storageid=destinationPool.id, + ) + else: + VirtualMachine.migrate( + vm, + self.apiclient, + # virtualmachineid=vm.id, + storageid=destinationPool.id, + ) + + migrated_vm_response = list_virtual_machines( + self.apiclient, + id=vm.id + ) + + self.assertEqual( + isinstance(migrated_vm_response, list), + True, + "Check list virtual machines response for valid list" + ) + + self.assertNotEqual( + migrated_vm_response, + None, + "Check if virtual machine exists in ListVirtualMachines" + ) + + migrated_vm = migrated_vm_response[0] + + root_volumes_cluster_list = Volume.list( + self.apiclient, + virtualmachineid=migrated_vm.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + self.assertEqual( + root_volume_cluster.storage, + destinationPool.name, + "Check volume is on migrated pool" + ) + return + + +def MigrateDataVolume(self, + volume, + destinationPool, + islive=False, + expectexception=False + ): + """ Migrate given volume to type of storage pool mentioned in migrateto: + + Inputs: + 1. volume: Volume to be migrated + 2. migrate_to: Scope of desired Storage pool to which volume + is to be migrated + 3. expectexception: If exception is expected while migration + """ + + if expectexception: + with self.assertRaises(Exception): + Volume.migrate( + self.apiclient, + volumeid=volume.id, + storageid=destinationPool.id, + livemigrate=islive + ) + else: + Volume.migrate( + self.apiclient, + volumeid=volume.id, + storageid=destinationPool.id, + livemigrate=islive + ) + + migrated_volume_response = list_volumes( + self.apiclient, + id=volume.id + ) + + self.assertEqual( + isinstance(migrated_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + self.assertNotEqual( + migrated_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + + migrated_volume = migrated_volume_response[0] + + self.assertEqual( + str(migrated_volume.state).lower(), + 'ready', + "Check migrated volume is in Ready state" + ) + + self.assertEqual( + migrated_volume.storage, + destinationPool.name, + "Check volume is on migrated pool" + ) + return + + +def createChecksum(self, virtual_machine, disk, disk_type): + """ Write data on the disk and return the md5 checksum""" + + random_data_0 = random_gen(size=100) + # creating checksum(MD5) + m = hashlib.md5() + m.update(random_data_0) + checksum_random_data_0 = m.hexdigest() + try: + ssh_client = SshClient( + virtual_machine.ssh_ip, + virtual_machine.ssh_port, + virtual_machine.username, + virtual_machine.password + ) + except Exception as e: + self.fail("SSH failed for VM: %s" % + e) + + self.debug("Formatting volume: %s to ext3" % disk.id) + # Format partition using ext3 + # Note that this is the second data disk partition of virtual machine + # as it was already containing data disk before attaching the new volume, + # Hence datadiskdevice_2 + + format_volume_to_ext3( + ssh_client, + self.testdata["volume_write_path"][ + virtual_machine.hypervisor][disk_type] + ) + cmds = ["fdisk -l", + "mkdir -p %s" % self.testdata["data_write_paths"]["mount_dir"], + "mount -t ext3 %s1 %s" % ( + self.testdata["volume_write_path"][ + virtual_machine.hypervisor][disk_type], + self.testdata["data_write_paths"]["mount_dir"] + ), + "mkdir -p %s/%s/%s " % ( + self.testdata["data_write_paths"]["mount_dir"], + self.testdata["data_write_paths"]["sub_dir"], + self.testdata["data_write_paths"]["sub_lvl_dir1"], + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_0, + self.testdata["data_write_paths"]["mount_dir"], + self.testdata["data_write_paths"]["sub_dir"], + self.testdata["data_write_paths"]["sub_lvl_dir1"], + self.testdata["data_write_paths"]["random_data"] + ), + "cat %s/%s/%s/%s" % ( + self.testdata["data_write_paths"]["mount_dir"], + self.testdata["data_write_paths"]["sub_dir"], + self.testdata["data_write_paths"]["sub_lvl_dir1"], + self.testdata["data_write_paths"]["random_data"] + ) + ] + + for c in cmds: + self.debug("Command: %s" % c) + result = ssh_client.execute(c) + self.debug(result) + + # Unmount the storage + cmds = [ + "umount %s" % (self.testdata["data_write_paths"]["mount_dir"]), + ] + + for c in cmds: + self.debug("Command: %s" % c) + ssh_client.execute(c) + + return checksum_random_data_0 + + +def compareChecksum( + self, + original_checksum, + disk_type, + virt_machine=None, + disk=None, + new_vm=False): + """ + Create md5 checksum of the data present on the disk and compare + it with the given checksum + """ + + if disk_type == "datadiskdevice_1" and new_vm: + new_virtual_machine = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + new_virtual_machine.start(self.userapiclient) + + self.debug("Attaching volume: %s to VM: %s" % ( + disk.id, + new_virtual_machine.id + )) + + new_virtual_machine.attach_volume( + self.apiclient, + disk + ) + + # Rebooting is required so that newly attached disks are detected + self.debug("Rebooting : %s" % new_virtual_machine.id) + new_virtual_machine.reboot(self.apiclient) + + else: + # If the disk is root disk then no need to create new VM + # Just start the original machine on which root disk is + new_virtual_machine = virt_machine + if new_virtual_machine.state != "Running": + new_virtual_machine.start(self.userapiclient) + + try: + # Login to VM to verify test directories and files + + self.debug( + "SSH into (Public IP: ) %s " % new_virtual_machine.ssh_ip) + ssh = SshClient( + new_virtual_machine.ssh_ip, + new_virtual_machine.ssh_port, + new_virtual_machine.username, + new_virtual_machine.password + ) + except Exception as e: + self.fail("SSH access failed for VM: %s, Exception: %s" % + (new_virtual_machine.ipaddress, e)) + + # Mount datadiskdevice_1 because this is the first data disk of the new + # virtual machine + cmds = ["blkid", + "fdisk -l", + "mkdir -p %s" % self.testdata["data_write_paths"]["mount_dir"], + "mount -t ext3 %s1 %s" % ( + self.testdata["volume_write_path"][ + new_virtual_machine.hypervisor][disk_type], + self.testdata["data_write_paths"]["mount_dir"] + ), + ] + + for c in cmds: + self.debug("Command: %s" % c) + result = ssh.execute(c) + self.debug(result) + + returned_data_0 = ssh.execute( + "cat %s/%s/%s/%s" % ( + self.testdata["data_write_paths"]["mount_dir"], + self.testdata["data_write_paths"]["sub_dir"], + self.testdata["data_write_paths"]["sub_lvl_dir1"], + self.testdata["data_write_paths"]["random_data"] + )) + + n = hashlib.md5() + n.update(returned_data_0[0]) + checksum_returned_data_0 = n.hexdigest() + + self.debug("returned_data_0: %s" % returned_data_0[0]) + + # Verify returned data + self.assertEqual( + original_checksum, + checksum_returned_data_0, + "Cheskum does not match with checksum of original data" + ) + + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.testdata["data_write_paths"]["mount_dir"]), + ] + + for c in cmds: + self.debug("Command: %s" % c) + ssh.execute(c) + + if new_vm: + new_virtual_machine.detach_volume( + self.apiclient, + disk + ) + + new_virtual_machine.delete(self.apiclient) + + return + + +class TestStorageMigration(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestStorageMigration, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.testdata = testClient.getParsedTestDataConfig() + cls.hypervisor = cls.testClient.getHypervisorInfo() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.testdata["ostype"]) + + cls._cleanup = [] + + if cls.hypervisor.lower() not in [ + "vmware", + "kvm", + "xenserver", + "hyper-v"]: + raise unittest.SkipTest( + "Storage migration not supported on %s" % + cls.hypervisor) + + try: + cls.pools = StoragePool.list(cls.apiclient, zoneid=cls.zone.id) + except Exception as e: + raise unittest.SkipTest(e) + try: + + # Create an account + cls.account = Account.create( + cls.apiclient, + cls.testdata["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + + # Create user api client of the account + cls.userapiclient = testClient.getUserApiClient( + UserName=cls.account.name, + DomainName=cls.account.domain + ) + # Create Service offering + cls.service_offering_zone1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"], + tags=ZONETAG1 + ) + cls._cleanup.append(cls.service_offering_zone1) + + cls.service_offering_cluster1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"], + tags=CLUSTERTAG1 + ) + cls._cleanup.append(cls.service_offering_cluster1) + + # If local storage is enabled, alter the offerings to use + # localstorage + if cls.zone.localstorageenabled: + cls.testdata["service_offering"]["storagetype"] = 'local' + + cls.service_offering_local1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"] + ) + cls._cleanup.append(cls.service_offering_local1) + + # Create Disk offering + cls.disk_offering_zone1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"], + tags=ZONETAG1 + ) + cls._cleanup.append(cls.disk_offering_zone1) + + cls.disk_offering_cluster1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"], + tags=CLUSTERTAG1 + ) + cls._cleanup.append(cls.disk_offering_cluster1) + + # If local storage is enabled, alter the offerings to use + # localstorage + if cls.zone.localstorageenabled: + cls.testdata["disk_offering"]["storagetype"] = 'local' + + cls.disk_offering_local1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"] + ) + cls._cleanup.append(cls.disk_offering_local1) + except Exception as e: + cls.tearDownClass() + raise 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.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: + for storagePool in self.pools: + StoragePool.update(self.apiclient, id=storagePool.id, tags="") + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced", "basic"]) + def test_01_migrate_root_and_data_disk_nonlive(self): + """ Test migrate Volume (root and data disk) + + # 1. Deploy a VM on cluster wide primary storage. + # 2. Migrate root and data volume from cluster-to-cluster, + # cluster-to-zone, cluster-to-local. + # 3. Deploy a VM on zone wide primary storage.. + # 4. Migrate root and data volume from zone-to-zone, zone-to-local. + # 5. Deploy a VM on local storage. + # 6. Migrate root and data volume from local-to-local. + + Each Migration has following steps: + + a. Write data to disk, create checksum + b. Migrate the volume to suitable pool + c. Attach disk to VM and compare checksum with + checksum of data on disk, they should match + + In addition to this, + Create snapshot of root and data disk after migration. + For root disk, create template from snapshot, deploy Vm and compare checksum + For data disk, Create volume from snapshot, attach to VM and compare checksum + + """ + + # Skipping test case for XenSever because ZWPS scenarios present in the test case + # Skipping for Vmware because at a time either ZWPS or CWPS can exists in vmware + # so the test case for Vmware is written separately + + if self.hypervisor in ["xenserver", "vmware"]: + self.skipTest("Skip test case for %s" % self.hypervisor) + + try: + self.pools = StoragePool.list(self.apiclient, zoneid=self.zone.id) + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "ZONE")) >= 2,\ + "There must be at least two zone wide\ + storage pools available in the setup" + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "CLUSTER")) >= 2,\ + "There must be at least two cluster wide\ + storage pools available in the setup" + except Exception as e: + self.skipTest(e) + + # Adding tags to Storage Pools + cluster_no = 1 + zone_no = 1 + self.debug("Storage Pools: %s" % self.pools) + for storagePool in self.pools: + if storagePool.scope == "ZONE": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['zwps' + repr(zone_no)]) + zone_no += 1 + elif storagePool.scope == "CLUSTER": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['cwps' + repr(cluster_no)]) + cluster_no += 1 + + # Step 1 + # Create VM on CWPS + vm_cluster = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume + root_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + # Create DATA Volume on Cluster Wide Storage + data_volume_clust = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust + ) + + data_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust.id + ) + + root_vol_snap = Snapshot.create( + self.apiclient, + root_volume_cluster.id) + + data_vol_snap = Snapshot.create( + self.apiclient, + data_volume_clust.id) + + vm_cluster.detach_volume( + self.apiclient, + data_volume_clust) + + # Step 2 + # Migrate ROOT Volume from CWPS to other CWPS, this consists of below 3 steps + # 1. Create Checksum + # 2. Migrate Volume + # 3. Compare checksum with data on volume on new pool + checksum_random_root_cluster = createChecksum( + self, + vm_cluster, + root_volume_cluster, + "rootdiskdevice") + + vm_cluster.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "CLUSTER") + MigrateRootVolume(self, vm_cluster, destinationPool) + + vm_cluster.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_cluster, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + self.debug("Done with compare checksum") + + vm_cluster.stop(self.userapiclient) + # Try to Migrate ROOT Volume from CWPS to ZWPS + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "ZONE") + + MigrateRootVolume( + self, + vm_cluster, + destinationPool) + + vm_cluster.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_cluster, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + + vm_cluster.stop(self.userapiclient) + # Try to Migrate ROOT Volume from CWPS to Local + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "HOST") + + MigrateRootVolume( + self, + vm_cluster, + destinationPool, + expectexception=True) + + vm_cluster.start(self.userapiclient) + + checksum_random_data_cluster = createChecksum( + self, + vm_cluster, + data_volumes_cluster_list[0], + "datadiskdevice_1") + + vm_cluster.detach_volume( + self.apiclient, + data_volumes_cluster_list[0] + ) + + vm_cluster.stop(self.userapiclient) + + # Migrate DATA Volume from CWPS to other CWPS - Create checksum, + # migrate volume, compare checksum + + destinationPool = GetDestinationPool( + self, + [data_volumes_cluster_list[0].storage], + "CLUSTER") + + MigrateDataVolume( + self, + data_volumes_cluster_list[0], + destinationPool) + + vm_cluster.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_cluster_list[0], + new_vm=True) + + # Add more data to disks + data_volume_clust_2 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust_2.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust_2 + ) + + data_disk_2_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust_2.id + ) + + # Ensure we can add data to newly added disks + createChecksum( + self, + vm_cluster, + data_disk_2_volumes_cluster_list[0], + "datadiskdevice_2") + + vm_cluster.detach_volume( + self.apiclient, + data_volume_clust_2 + ) + + templateFromSnapshot = Template.create_from_snapshot( + self.apiclient, + root_vol_snap, + self.testdata["template_2"]) + + vm_from_temp = VirtualMachine.create( + self.apiclient, + self.testdata["small"], + templateid=templateFromSnapshot.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + root_volumes_snap_list = Volume.list( + self.apiclient, + virtualmachineid=vm_from_temp.id, + type='ROOT', + listall=True + ) + + root_volume_snap = root_volumes_snap_list[0] + + createChecksum( + self, + vm_from_temp, + root_volume_snap, + "rootdiskdevice") + + templateFromSnapshot.delete(self.apiclient) + + volumeFormSnap = Volume.create_from_snapshot( + self.apiclient, + data_vol_snap.id, + self.testdata["volume"], + account=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id + ) + + vm_from_temp.attach_volume( + self.userapiclient, + volumeFormSnap + ) + + data_from_snap = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=volumeFormSnap.id + ) + + vm_from_temp.reboot(self.userapiclient) + + createChecksum( + self, + vm_from_temp, + data_from_snap[0], + "datadiskdevice_1") + + vm_from_temp.detach_volume( + self.userapiclient, + volumeFormSnap + ) + + volumeFormSnap.delete(self.apiclient) + + vm_from_temp.delete(self.apiclient) + + vm_cluster.stop(self.userapiclient) + # Try to Migrate DATA Volume from CWPS to ZWPS + destinationPool = GetDestinationPool( + self, + [data_volumes_cluster_list[0].storage], + "ZONE") + MigrateDataVolume( + self, + data_volumes_cluster_list[0], + destinationPool) + + vm_cluster.start(self.userapiclient) + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_cluster_list[0], + new_vm=True) + + vm_cluster.stop(self.userapiclient) + # Try to Migrate DATA Volume from CWPS to Local Storage + destinationPool = GetDestinationPool( + self, + [data_volumes_cluster_list[0].storage], + "HOST") + MigrateDataVolume( + self, + data_volumes_cluster_list[0], + destinationPool) + + # Delete ROOT and DATA Volume from CWPS + self.debug("Deleting Volume %s" % data_volume_clust.id) + + vm_cluster.detach_volume(self.apiclient, data_volume_clust) + + # Delete volume: + data_volume_clust.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_clust.id, + ), None, "Volume list should be empty") + + # Destroy and expunge VM + vm_cluster.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_cluster.id + ), None, "VM list should be empty") + + # Step 3 + # Create VM on ZWPS + vm_zone = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_zone1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + vm_zone.start(self.userapiclient) + + # Get ROOT Volume Id + root_volumes_zone_list = list_volumes( + self.apiclient, + virtualmachineid=vm_zone.id, + type='ROOT', + listall=True + ) + root_volume_zone = root_volumes_zone_list[0] + + # Create DATA Volume on ZWPS + data_volume_zone = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_zone1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_zone.id) + + data_volumes_zone_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_zone.id + ) + + vm_zone.attach_volume( + self.userapiclient, + data_volume_zone + ) + + # Step 4 + # Migrate ROOT Volume from ZWPS to other ZWPS + checksum_random_root_zone = createChecksum( + self, + vm_zone, + data_volumes_zone_list[0], + "rootdiskdevice") + + vm_zone.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, [ + root_volume_zone.storage], "ZONE") + MigrateRootVolume(self, vm_zone, destinationPool) + + vm_zone.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_zone, + "rootdiskdevice", + virt_machine=vm_zone, + disk=None, + ) + + vm_zone.stop(self.userapiclient) + # Try to Migrate ROOT Volume from ZWPS to Local Storage + destinationPool = GetDestinationPool( + self, [ + root_volume_zone.storage], "HOST") + MigrateRootVolume(self, vm_zone, destinationPool, expectexception=True) + + # Try to Migrate ROOT Volume from ZWPS to Cluster wide Storage + + destinationPool = GetDestinationPool( + self, [ + root_volume_zone.storage], "CLUSTER") + MigrateRootVolume( + self, + vm_zone, + destinationPool) # , expectexception=True) + + vm_zone.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_zone, + "rootdiskdevice", + virt_machine=vm_zone, + disk=None, + ) + + checksum_random_data_zone = createChecksum( + self, + vm_zone, + data_volumes_zone_list[0], + "datadiskdevice_1") + + vm_zone.stop(self.userapiclient) + + # Migrate DATA Volume from ZWPS to other ZWPS + destinationPool = GetDestinationPool( + self, + [data_volumes_zone_list[0].storage], + "ZONE") + MigrateDataVolume(self, data_volumes_zone_list[0], destinationPool) + + compareChecksum( + self, + checksum_random_data_zone, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_zone_list[0], + new_vm=True) + + vm_zone.stop(self.userapiclient) + # Try to Migrate DATA Volume from ZWPS to Local Storage + destinationPool = GetDestinationPool( + self, [ + data_volume_zone.storage], "HOST") + MigrateDataVolume( + self, + data_volume_zone, + destinationPool, + expectexception=True) + + # Try to Migrate DATA Volume from ZWPS to Cluster wide Storage + destinationPool = GetDestinationPool( + self, [ + data_volume_zone.storage], "CLUSTER") + MigrateDataVolume( + self, + data_volume_zone, + destinationPool) + + vm_zone.start(self.userapiclient) + compareChecksum( + self, + checksum_random_data_zone, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_zone_list[0], + new_vm=True) + + # Delete ROOT and DATA Volume from ZWPS + + self.debug("Deleting Volume %s" % data_volume_zone.id) + + vm_zone.detach_volume(self.apiclient, data_volume_zone) + # Delete volume + data_volume_zone.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_zone.id, + ), None, "Volume list should be empty") + + # Destroy and expunge VM + vm_zone.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_zone.id + ), None, "VM list should be empty") + + # Step 5 + localStoragePoolsPresent = True + try: + self.assertEqual( + len(list(storagePool for storagePool in self.pools + if storagePool.scope == "HOST")), 2) + except Exception as e: + localStoragePoolsPresent = False + + if localStoragePoolsPresent: + # Create VM on local storage + vm_local = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_local1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume id + root_volumes_local_list = list_volumes( + self.apiclient, + virtualmachineid=vm_local.id, + type='ROOT', + listall=True + ) + + root_volume_local = root_volumes_local_list[0] + + # Create DATA Volume on local storage + data_volume_local = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_local1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_local.id) + + data_volumes_local_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_local.id + ) + + vm_local.attach_volume( + self.userapiclient, + data_volume_local + ) + + # Step 6 + # Migrate root and data volume from Local to another Local storage + checksum_random_root_local = createChecksum( + self, + vm_local, + data_volumes_local_list[0], + "rootdiskdevice") + + vm_local.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, [ + root_volume_local.storage], "HOST") + MigrateRootVolume(self, vm_local, destinationPool) + + vm_local.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_local, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + + checksum_random_data_local = createChecksum( + self, + vm_local, + data_volumes_local_list[0], + "datadiskdevice_1") + + vm_local.stop(self.userapiclient) + destinationPool = GetDestinationPool( + self, + [data_volumes_local_list[0].storage], + "HOST") + MigrateDataVolume( + self, + data_volumes_local_list[0], + destinationPool) + + vm_local.start(self.userapiclient) + compareChecksum( + self, + checksum_random_data_local, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_local_list[0], + new_vm=True) + + # Delete ROOT and DATA Volume from Local Storage + + self.debug("Deleting Volume %s" % data_volume_local.id) + + vm_local.detach_volume(self.apiclient, data_volume_local) + # Delete volume + data_volume_local.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_local.id, + ), None, "Volumes list should be empty") + + # Destroy and expunge VM + vm_local.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_local.id + ), None, "VM list should be empty") + return + + @attr(tags=["advanced", "basic"]) + def test_02_migration_nonlive_xenserver_supported(self): + """ Test migrate Volume (root and data disk) for Hypervisor Xenserver + + # 1. Deploy a VM on cluster wide primary storage. + # 2. Migrate root and data volume from + cluster-to-cluster,cluster-to-local. + # 3. Deploy a VM on local storage. + # 4. Migrate root and data volume from local-to-local. + + Each Migration has following steps: + + a. Write data to disk, create checksum + b. Migrate the volume to suitable pool + c. Attach disk to VM and compare checksum with + checksum of data on disk, they should match + + In addition to this, + Create snapshot of root and data disk after migration. + For root disk, create template from snapshot, deploy Vm and compare checksum + For data disk, Create volume from snapshot, attach to VM and compare checksum + """ + + # Check if Hypervisor is Xenserver + if self.hypervisor != "xenserver": + self.skipTest("This test case is written specifically for xenserver,\ + it does not include ZWPS scenarios") + + try: + self.pools = StoragePool.list(self.apiclient, zoneid=self.zone.id) + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "CLUSTER")) >= 2,\ + "There must be at least two cluster wide\ + storage pools available in the setup" + except Exception as e: + self.skipTest(e) + + # Adding tags to Storage Pools + cluster_no = 1 + self.debug("Storage Pools: %s" % self.pools) + for storagePool in self.pools: + if storagePool.scope == "CLUSTER": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['cwps' + repr(cluster_no)]) + cluster_no += 1 + # Create VM on CWPS + vm_cluster = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume Id + root_volumes_cluster_list = Volume.list( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + data_volume_clust = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust + ) + + data_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust.id + ) + + root_vol_snap = Snapshot.create( + self.apiclient, + root_volume_cluster.id) + + data_vol_snap = Snapshot.create( + self.apiclient, + data_volume_clust.id) + + vm_cluster.detach_volume( + self.apiclient, + data_volume_clust) + + # Migrate ROOT Volume from CWPS to other CWPS + checksum_random_root_cluster = createChecksum( + self, + vm_cluster, + root_volume_cluster, + "rootdiskdevice") + + vm_cluster.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "CLUSTER") + MigrateRootVolume(self, vm_cluster, destinationPool) + + vm_cluster.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_cluster, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + self.debug("Done with compare checksum after first checksum") + + vm_cluster.start(self.userapiclient) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust + ) + + vm_cluster.reboot(self.userapiclient) + + checksum_random_data_cluster = createChecksum( + self, + vm_cluster, + data_volumes_cluster_list[0], + "datadiskdevice_1") + + vm_cluster.stop(self.userapiclient) + + # Migrate DATA Volume from CWPS to other CWPS + destinationPool = GetDestinationPool( + self, + [data_volumes_cluster_list[0].storage], + "CLUSTER") + + MigrateDataVolume(self, data_volumes_cluster_list[0], destinationPool) + + vm_cluster.start(self.userapiclient) + + vm_cluster.detach_volume(self.apiclient, data_volumes_cluster_list[0]) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_cluster_list[0], + new_vm=True) + + # snapshot test case t14 compare checksum for same VM + vm_cluster.attach_volume( + self.apiclient, + data_volumes_cluster_list[0] + ) + + vm_cluster.reboot(self.apiclient) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=vm_cluster, + disk=data_volumes_cluster_list[0] + ) + + # Add more data to disks + data_volume_clust_2 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust_2.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust_2 + ) + + data_disk_2_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust_2.id + ) + + createChecksum( + self, + vm_cluster, + data_disk_2_volumes_cluster_list[0], + "datadiskdevice_2") + + vm_cluster.detach_volume( + self.apiclient, + data_volume_clust_2 + ) + + vm_cluster.stop(self.userapiclient) + # Try to Migrate ROOT Volume from CWPS to Local + destinationPool = GetDestinationPool( + self, [ + root_volume_cluster.storage], "HOST") + MigrateRootVolume( + self, + vm_cluster, + destinationPool, + expectexception=True) + + # Try to Migrate DATA Volume from CWPS to Local Storage + destinationPool = GetDestinationPool( + self, + [data_volumes_cluster_list[0].storage], + "HOST") + MigrateDataVolume( + self, + data_volumes_cluster_list[0], + destinationPool, + expectexception=True) + + # Restore snapshot to preveous stage + # Convert root snap to template and boot vm from that n then check data + # Convert data snap to volume and attach it toa VM and then check data + + templateFromSnapshot = Template.create_from_snapshot( + self.apiclient, + root_vol_snap, + self.testdata["template_2"]) + + vm_from_temp = VirtualMachine.create( + self.apiclient, + self.testdata["small"], + templateid=templateFromSnapshot.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + root_volumes_snap_list = Volume.list( + self.apiclient, + virtualmachineid=vm_from_temp.id, + type='ROOT', + listall=True + ) + + root_volume_snap = root_volumes_snap_list[0] + + createChecksum( + self, + vm_from_temp, + root_volume_snap, + "rootdiskdevice") + + templateFromSnapshot.delete(self.apiclient) + + volumeFormSnap = Volume.create_from_snapshot( + self.apiclient, + data_vol_snap.id, + self.testdata["volume"], + account=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id + ) + + vm_from_temp.attach_volume( + self.userapiclient, + volumeFormSnap + ) + + data_from_snap = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=volumeFormSnap.id + ) + + vm_from_temp.reboot(self.userapiclient) + + createChecksum( + self, + vm_from_temp, + data_from_snap[0], + "datadiskdevice_1") + + vm_from_temp.detach_volume( + self.userapiclient, + volumeFormSnap + ) + + volumeFormSnap.delete(self.apiclient) + + vm_from_temp.delete(self.apiclient) + + # Delete ROOT and DATA Volume from CWPS + self.debug("Deleting Volume %s" % data_volume_clust.id) + + vm_cluster.detach_volume(self.apiclient, data_volume_clust) + + # Delete volume: + data_volume_clust.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_clust.id, + ), None, "Volume list should be empty") + + # Destroy and expunge VM + vm_cluster.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_cluster.id + ), None, "VM list should be empty") + + localStoragePoolsAvailable = True + try: + self.assertEqual( + len(list(storagePool for storagePool in self.pools + if storagePool.scope == "HOST")), 2) + except Exception as e: + localStoragePoolsAvailable = False + + if localStoragePoolsAvailable: + # Create VM on local storage + vm_local = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_local1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume id + root_volumes_local_list = Volume.list( + self.apiclient, + virtualmachineid=vm_local.id, + type='ROOT', + listall=True + ) + + root_volume_local = root_volumes_local_list[0] + + # Create DATA Volume on local storage + data_volume_local = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_local1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_local.id) + + data_volumes_local_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_local.id + ) + + vm_local.attach_volume( + self.userapiclient, + data_volume_local + ) + + vm_local.reboot(self.userapiclient) + + createChecksum( + self, + vm_local, + root_volume_local, + "rootdiskdevice") + + vm_local.stop(self.userapiclient) + + # Migrate root and data volume from Local to another Local storage + destinationPool = GetDestinationPool( + self, [ + root_volume_local.storage], "HOST") + MigrateRootVolume( + self, + vm_local, + destinationPool, + expectexception=True) + + vm_local.detach_volume(self.apiclient, data_volume_local) + + destinationPool = GetDestinationPool( + self, + [data_volumes_local_list[0].storage], + "HOST") + MigrateDataVolume( + self, + data_volumes_local_list[0], + destinationPool, + expectexception=True) + + # Delete ROOT and DATA Volume from Local Storage + + self.debug("Deleting Volume %s" % data_volume_local.id) + + # Delete volume + data_volume_local.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_local.id, + ), None, "Volumes list should be empty") + + # Destroy and expunge VM + vm_local.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_local.id + ), None, "VM list should be empty") + + return + + @attr(tags=["advanced", "basic"]) + def test_03_migrate_root_and_data_disk_nonlive_cwps_vmware(self): + """ Test migrate Volume (root and data disk) + + # 1. Deploy a VM on cluster wide primary storage. + # 2. Migrate root and data volume from cluster-to-cluster + # 3. Deploy a VM on local storage. + # 4. Migrate root and data volume from local-to-local. + + Each Migration has following steps: + + a. Write data to disk, create checksum + b. Migrate the volume to suitable pool + c. Attach disk to VM and compare checksum with + checksum of data on disk, they should match + + In addition to this, + Create snapshot of root and data disk after migration. + For root disk, create template from snapshot, deploy Vm and compare checksum + For data disk, Create volume from snapshot, attach to VM and compare checksum + + """ + # Test case only written for Vmware hypervisor as it + # does not run CWPS and ZWPS scenatios together + if self.hypervisor is not "vmware": + self.skipTest("Skip test case for %s" % self.hypervisor) + + try: + self.pools = StoragePool.list(self.apiclient, zoneid=self.zone.id) + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "CLUSTER")) >= 2,\ + "There must be at least two cluster wide\ + storage pools available in the setup" + except Exception as e: + self.skipTest(e) + + # Adding tags to Storage Pools + cluster_no = 1 + self.debug("Storage Pools: %s" % self.pools) + for storagePool in self.pools: + if storagePool.scope == "CLUSTER": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['cwps' + repr(cluster_no)]) + cluster_no += 1 + + # Step 1 + # Create VM on CWPS + vm_cluster = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume Id + root_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + # Create DATA Volume on Cluster Wide Storage + data_volume_clust = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust + ) + + data_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust.id + ) + + root_vol_snap = Snapshot.create( + self.apiclient, + root_volume_cluster.id) + + data_vol_snap = Snapshot.create( + self.apiclient, + data_volume_clust.id) + + vm_cluster.detach_volume( + self.apiclient, + data_volume_clust) + + # Step 2 + # Migrate ROOT Volume from CWPS to other CWPS + checksum_random_root_cluster = createChecksum( + self, + vm_cluster, + root_volume_cluster, + "rootdiskdevice") + + vm_cluster.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "CLUSTER") + MigrateRootVolume(self, vm_cluster, destinationPool) + + vm_cluster.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_cluster, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + self.debug("Done with compare checksum") + + vm_cluster.start(self.userapiclient) + checksum_random_data_cluster = createChecksum( + self, + vm_cluster, + data_volumes_cluster_list[0], + "datadiskdevice_1") + + vm_cluster.detach_volume( + self.apiclient, + data_volumes_cluster_list[0] + ) + + vm_cluster.stop(self.userapiclient) + + # Migrate DATA Volume from CWPS to other CWPS + destinationPool = GetDestinationPool( + self, + [data_volumes_cluster_list[0].storage], + "CLUSTER") + MigrateDataVolume( + self, + data_volumes_cluster_list[0], + destinationPool) + + vm_cluster.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_cluster_list[0], + new_vm=True) + + # snapshot test case t14 compare checksum for same VM + vm_cluster.attach_volume( + self.apiclient, + data_volumes_cluster_list[0] + ) + + vm_cluster.reboot(self.apiclient) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=vm_cluster, + disk=data_volumes_cluster_list[0] + ) + + # Add more data to disks + data_volume_clust_2 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust_2.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust_2 + ) + + data_disk_2_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust_2.id + ) + + createChecksum( + self, + vm_cluster, + data_disk_2_volumes_cluster_list[0], + "datadiskdevice_2") + + vm_cluster.detach_volume( + self.apiclient, + data_volume_clust_2 + ) + + templateFromSnapshot = Template.create_from_snapshot( + self.apiclient, + root_vol_snap, + self.testdata["template_2"]) + + vm_from_temp = VirtualMachine.create( + self.apiclient, + self.testdata["small"], + templateid=templateFromSnapshot.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + root_volumes_snap_list = Volume.list( + self.apiclient, + virtualmachineid=vm_from_temp.id, + type='ROOT', + listall=True + ) + + root_volume_snap = root_volumes_snap_list[0] + + createChecksum( + self, + vm_from_temp, + root_volume_snap, + "rootdiskdevice") + + templateFromSnapshot.delete(self.apiclient) + + volumeFormSnap = Volume.create_from_snapshot( + self.apiclient, + data_vol_snap.id, + self.testdata["volume"], + account=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id + ) + + vm_from_temp.attach_volume( + self.userapiclient, + volumeFormSnap + ) + + data_from_snap = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=volumeFormSnap.id + ) + + vm_from_temp.reboot(self.userapiclient) + + createChecksum( + self, + vm_from_temp, + data_from_snap[0], + "datadiskdevice_1") + + vm_from_temp.detach_volume( + self.userapiclient, + volumeFormSnap + ) + + volumeFormSnap.delete(self.apiclient) + + vm_from_temp.delete(self.apiclient) + + # Delete ROOT and DATA Volume from CWPS + self.debug("Deleting Volume %s" % data_volume_clust.id) + + vm_cluster.detach_volume(self.apiclient, data_volume_clust) + + # Delete volume: + data_volume_clust.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_clust.id, + ), None, "Volume list should be empty") + + # Destroy and expunge VM + vm_cluster.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_cluster.id + ), None, "VM list should be empty") + + localStoragePoolsPresent = True + # Step 5 + try: + self.assertEqual( + len(list(storagePool for storagePool in self.pools + if storagePool.scope == "HOST")), 2) + except Exception as e: + localStoragePoolsPresent = False + + if localStoragePoolsPresent: + # Create VM on local storage + vm_local = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_local1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume id + root_volumes_local_list = list_volumes( + self.apiclient, + virtualmachineid=vm_local.id, + type='ROOT', + listall=True + ) + + root_volume_local = root_volumes_local_list[0] + + # Create DATA Volume on local storage + data_volume_local = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_local1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_local.id) + + data_volumes_local_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_local.id + ) + + vm_local.attach_volume( + self.userapiclient, + data_volume_local + ) + + # Step 6 + # Migrate root and data volume from Local to another Local storage + checksum_random_root_local = createChecksum( + self, + vm_local, + data_volumes_local_list[0], + "rootdiskdevice") + + vm_local.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, [ + root_volume_local.storage], "HOST") + MigrateRootVolume(self, vm_local, destinationPool) + + vm_local.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_local, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + + checksum_random_data_local = createChecksum( + self, + vm_local, + data_volumes_local_list[0], + "datadiskdevice_1") + + vm_local.stop(self.userapiclient) + destinationPool = GetDestinationPool( + self, + [data_volumes_local_list[0].storage], + "HOST") + MigrateDataVolume( + self, + data_volumes_local_list[0], + destinationPool) + + vm_local.start(self.userapiclient) + compareChecksum( + self, + checksum_random_data_local, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_local_list[0], + new_vm=True) + + # Delete ROOT and DATA Volume from Local Storage + + self.debug("Deleting Volume %s" % data_volume_local.id) + + vm_local.detach_volume(self.apiclient, data_volume_local) + # Delete volume + data_volume_local.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_local.id, + ), None, "Volumes list should be empty") + + # Destroy and expunge VM + vm_local.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_local.id + ), None, "VM list should be empty") + return + + @attr(tags=["advanced", "basic"]) + def test_04_migrate_root_and_data_disk_nonlive_zwps_vmware(self): + """ Test migrate Volume (root and data disk) + + # 1. Deploy a VM on zone wide primary storage.. + # 2. Migrate root and data volume from zone-to-zone, zone-to-local. + + Each Migration has following steps: + + a. Write data to disk, create checksum + b. Migrate the volume to suitable pool + c. Attach disk to VM and compare checksum with + checksum of data on disk, they should match + + """ + # Test case only written for Vmware hypervisor as it + # does not run CWPS and ZWPS scenatios together + if self.hypervisor is not "vmware": + self.skipTest("Skip test case for %s" % self.hypervisor) + + try: + self.pools = StoragePool.list(self.apiclient, zoneid=self.zone.id) + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "ZONE")) >= 2,\ + "There must be at least two zone wide\ + storage pools available in the setup" + except Exception as e: + self.skipTest(e) + + # Adding tags to Storage Pools + zone_no = 1 + self.debug("Storage Pools: %s" % self.pools) + for storagePool in self.pools: + if storagePool.scope == "ZONE": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['zwps' + repr(zone_no)]) + zone_no += 1 + + # Step 3 + # Create VM on ZWPS + vm_zone = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_zone1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + vm_zone.start(self.userapiclient) + + # Get ROOT Volume Id + root_volumes_zone_list = list_volumes( + self.apiclient, + virtualmachineid=vm_zone.id, + type='ROOT', + listall=True + ) + root_volume_zone = root_volumes_zone_list[0] + + # Create DATA Volume on ZWPS + data_volume_zone = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_zone1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_zone.id) + + data_volumes_zone_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_zone.id + ) + + vm_zone.attach_volume( + self.userapiclient, + data_volume_zone + ) + + # Step 4 + # Migrate ROOT Volume from ZWPS to other ZWPS + checksum_random_root_zone = createChecksum( + self, + vm_zone, + data_volumes_zone_list[0], + "rootdiskdevice") + + vm_zone.stop(self.userapiclient) + + destinationPool = GetDestinationPool( + self, [ + root_volume_zone.storage], "ZONE") + MigrateRootVolume(self, vm_zone, destinationPool) + + vm_zone.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_zone, + "rootdiskdevice", + virt_machine=vm_zone, + disk=None, + ) + vm_zone.start(self.userapiclient) + + compareChecksum( + self, + checksum_random_root_zone, + "rootdiskdevice", + virt_machine=vm_zone, + disk=None, + ) + + checksum_random_data_zone = createChecksum( + self, + vm_zone, + data_volumes_zone_list[0], + "datadiskdevice_1") + + vm_zone.stop(self.userapiclient) + + # Migrate DATA Volume from ZWPS to other ZWPS + destinationPool = GetDestinationPool( + self, + [data_volumes_zone_list[0].storage], + "ZONE") + MigrateDataVolume(self, data_volumes_zone_list[0], destinationPool) + + compareChecksum( + self, + checksum_random_data_zone, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_zone_list[0], + new_vm=True) + + # Delete ROOT and DATA Volume from ZWPS + self.debug("Deleting Volume %s" % data_volume_zone.id) + + vm_zone.detach_volume(self.apiclient, data_volume_zone) + # Delete volume + data_volume_zone.delete(self.apiclient) + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_zone.id, + ), None, "Volume list should be empty") + + # Destroy and expunge VM + vm_zone.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_zone.id + ), None, "VM list should be empty") + return + + +class NegativeTestStorageMigration(cloudstackTestCase): + + exceptionList = [] + + @classmethod + def setUpClass(cls): + testClient = super( + NegativeTestStorageMigration, + cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.testdata = testClient.getParsedTestDataConfig() + cls.hypervisor = cls.testClient.getHypervisorInfo() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.testdata["ostype"]) + + cls._cleanup = [] + + if cls.hypervisor.lower() not in [ + "vmware", + "kvm", + "xenserver", + "hyper-v"]: + raise unittest.SkipTest( + "Storage migration not supported on %s" % + cls.hypervisor) + + try: + cls.pools = StoragePool.list(cls.apiclient, zoneid=cls.zone.id) + assert len(list(storagePool for storagePool in cls.pools + if storagePool.scope == "ZONE")) >= 2,\ + "There must be at least two zone wide\ + storage pools available in the setup" + assert len(list(storagePool for storagePool in cls.pools + if storagePool.scope == "CLUSTER")) >= 2,\ + "There must be at least two cluster wide\ + storage pools available in the setup" + except Exception as e: + raise unittest.SkipTest(e) + try: + + # Create an account + cls.account = Account.create( + cls.apiclient, + cls.testdata["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + + # Create user api client of the account + cls.userapiclient = testClient.getUserApiClient( + UserName=cls.account.name, + DomainName=cls.account.domain + ) + # Create Service offering + cls.service_offering_zone1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"], + tags=ZONETAG1 + ) + cls._cleanup.append(cls.service_offering_zone1) + + cls.service_offering_cluster1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"], + tags=CLUSTERTAG1 + ) + cls._cleanup.append(cls.service_offering_cluster1) + + # If local storage is enabled, alter the offerings to use + # localstorage + if cls.zone.localstorageenabled: + cls.testdata["service_offering"]["storagetype"] = 'local' + + cls.service_offering_local1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"] + ) + cls._cleanup.append(cls.service_offering_local1) + + # Create Disk offering + cls.disk_offering_zone1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"], + tags=ZONETAG1 + ) + cls._cleanup.append(cls.disk_offering_zone1) + + cls.disk_offering_cluster1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"], + tags=CLUSTERTAG1 + ) + cls._cleanup.append(cls.disk_offering_cluster1) + + # If local storage is enabled, alter the offerings to use + # localstorage + if cls.zone.localstorageenabled: + cls.testdata["disk_offering"]["storagetype"] = 'local' + + cls.disk_offering_local1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"] + ) + cls._cleanup.append(cls.disk_offering_local1) + except Exception as e: + cls.tearDownClass() + raise 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.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: + for storagePool in self.pools: + StoragePool.update(self.apiclient, id=storagePool.id, tags="") + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def createMigrationJob(self, volume): + try: + destinationPool = None + for storagePool in self.pools: + if storagePool.scope == "CLUSTER": + if storagePool.name != volume.storage: + destinationPool = storagePool + break + + with self.assertRaises(Exception): + Volume.migrate( + self.apiclient, + volumeid=volume.id, + storageid=destinationPool.id, + livemigrate='false' + ) + except Exception as e: + self.exceptionList.append(e) + + def createTemplateJob(self, virtual_machine, root_volume): + try: + services = {"displaytext": "templ", + "name": "vol_template", + "ostypeid": virtual_machine.ostypeid} + with self.assertRaises(Exception): + Template.create(self.apiclient, + services, + volumeid=root_volume.id + ) + + except Exception as e: + self.exceptionList.append(e) + + def createDestroyVmJob(self, virtual_machine): + try: + with self.assertRaises(Exception): + virtual_machine.delete(self.apiclient) + + except Exception as e: + self.exceptionList.append(e) + + @attr(tags=["advanced", "basic"]) + def test_01_migrate_data_disk_negative_test(self): + """ Negative test cases + + # 1. Deploy a VM on cluster wide primary storage. + # 2. Add some data to disks and create checksum + # 3. Migrate root and data volume from cluster-to-cluster wide storage pool + # 4. While migration(ROOT disk) is in progress try following scenarios, they should fail: + I. Take snapshot of the disk + II. Create Template from the volume + III. Destroy the instance + # 5. Compare checksum after migration + + """ + # Adding tags to Storage Pools + cluster_no = 1 + self.debug("Storage Pools: %s" % self.pools) + for storagePool in self.pools: + if storagePool.scope == "CLUSTER": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['cwps' + repr(cluster_no)]) + cluster_no += 1 + + # Step 1 + # Create VM on CWPS + vm_cluster = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + diskofferingid=self.disk_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume Id + disk_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='DATADISK', + listall=True + ) + + data_disk = disk_volumes_cluster_list[0] + + root_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + # Step 2 + # Calculate checksum of ROOT and DATA Disks + + checksum_root_disk = self.createChecksum( + self, + vm_cluster, + root_volume_cluster, + "rootdiskdevice") + + checksum_data_disk = self.createChecksum( + self, + vm_cluster, + data_disk, + "datadiskdevice_1") + + volumes = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_disk.id + ) + + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + + volume = volumes[0] + + vm_cluster.detach_volume( + self.apiclient, + volume + ) + + vm_cluster.stop(self.userapiclient) + + try: + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "CLUSTER") + thread_1 = Thread( + target=MigrateRootVolume, + args=( + self, + vm_cluster, + destinationPool, + )) + thread_2 = Thread( + target=self.createTemplateJob, + args=( + vm_cluster, + root_volume_cluster, + )) + thread_1.start() + thread_2.start() + thread_1.join() + thread_2.join() + except: + self.debug("Error: unable to start thread") + + try: + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "CLUSTER") + thread_3 = Thread( + target=MigrateRootVolume, + args=( + self, + vm_cluster, + destinationPool, + )) + thread_4 = Thread( + target=self.createMigrationJob, + args=( + root_volume_cluster, + )) + thread_3.start() + timeout = 60 + while timeout >= 0: + if volume.state == "Migrating": + break + timeout -= 5 + time.sleep(5) + thread_4.start() + thread_3.join() + thread_4.join() + except: + self.debug("Error: unable to start thread") + + vm_cluster.start(self.userapiclient) + + vm_cluster.attach_volume( + self.apiclient, + volume + ) + + vm_cluster.reboot(self.userapiclient) + + disk_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='DATADISK', + listall=True + ) + + data_disk = disk_volumes_cluster_list[0] + + root_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + self.debug("Done with create checksum") + + compareChecksum( + self, + checksum_root_disk, + "rootdiskdevice", + virt_machine=vm_cluster + ) + + compareChecksum( + self, + checksum_data_disk, + "datadiskdevice_1", + virt_machine=vm_cluster + ) + + vm_cluster.detach_volume( + self.userapiclient, + volume + ) + + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = volume.id + self.apiclient.deleteVolume(cmd) + + vm_cluster.stop(self.userapiclient) + + try: + destinationPool = GetDestinationPool( + self, + [root_volume_cluster.storage], + "CLUSTER") + thread_5 = Thread( + target=MigrateRootVolume, + args=( + self, + vm_cluster, + destinationPool, + )) + thread_6 = Thread( + target=self.createDestroyVmJob, + args=( + vm_cluster, + )) + thread_5.start() + thread_6.start() + thread_5.join() + thread_6.join() + except: + self.debug("Error: unable to start thread") + + # Raise exception if there was any exception raised from the threads + if self.exceptionList: + for i in self.exceptionList: + raise(i) + + vm_cluster.delete(self.apiclient) + return + + +class TestLiveStorageMigration(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestLiveStorageMigration, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.testdata = testClient.getParsedTestDataConfig() + cls.hypervisor = cls.testClient.getHypervisorInfo() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.testdata["ostype"]) + + cls._cleanup = [] + + if cls.hypervisor.lower() in ["kvm", "lxc"]: + raise unittest.SkipTest( + "Live Storage migration not supported on %s" % + cls.hypervisor) + + try: + cls.pools = StoragePool.list(cls.apiclient, zoneid=cls.zone.id) + except Exception as e: + raise unittest.SkipTest(e) + try: + + # Create an account + cls.account = Account.create( + cls.apiclient, + cls.testdata["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + + # Create user api client of the account + cls.userapiclient = testClient.getUserApiClient( + UserName=cls.account.name, + DomainName=cls.account.domain + ) + # Create Service offering + cls.service_offering_zone1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"], + tags=ZONETAG1 + ) + cls._cleanup.append(cls.service_offering_zone1) + + cls.service_offering_cluster1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"], + tags=CLUSTERTAG1 + ) + cls._cleanup.append(cls.service_offering_cluster1) + + # If local storage is enabled, alter the offerings to use + # localstorage + if cls.zone.localstorageenabled: + cls.testdata["service_offering"]["storagetype"] = 'local' + + cls.service_offering_local1 = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"] + ) + cls._cleanup.append(cls.service_offering_local1) + + # Create Disk offering + cls.disk_offering_zone1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"], + tags=ZONETAG1 + ) + cls._cleanup.append(cls.disk_offering_zone1) + + cls.disk_offering_cluster1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"], + tags=CLUSTERTAG1 + ) + cls._cleanup.append(cls.disk_offering_cluster1) + + # If local storage is enabled, alter the offerings to use + # localstorage + if cls.zone.localstorageenabled: + cls.testdata["disk_offering"]["storagetype"] = 'local' + + cls.disk_offering_local1 = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"] + ) + cls._cleanup.append(cls.disk_offering_local1) + except Exception as e: + cls.tearDownClass() + raise 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.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: + for storagePool in self.pools: + StoragePool.update(self.apiclient, id=storagePool.id, tags="") + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced", "basic"]) + def test_01_migrate_live(self): + """ Test migrate Volume (root and data disk) + + # 1. Deploy a VM on cluster wide primary storage. + # 2. Migrate root and data volume to two different storage pools\ + in same cluster. + + """ + + try: + self.pools = StoragePool.list(self.apiclient, zoneid=self.zone.id) + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "CLUSTER")) >= 3,\ + "There must be at least three cluster wide\ + storage pools available in the setup" + except Exception as e: + self.skipTest(e) + + storagePools_to_avoid = [] + + # Adding tags to Storage Pools + cluster_no = 1 + + for storagePool in self.pools: + if storagePool.scope == "CLUSTER": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['cwps' + repr(cluster_no)]) + cluster_no += 1 + + # Step 1 + # Create VM on CWPS + vm_cluster = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume + root_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + # Create DATA Volume on Cluster Wide Storage + data_volume_clust_1 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust_1.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust_1 + ) + + data_disk_1_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust_1.id + ) + + data_volume_1_cluster = data_disk_1_volumes_cluster_list[0] + + # Step 2 + # Migrate ROOT Volume from CWPS to other CWPS + checksum_random_root_cluster = createChecksum( + self, + vm_cluster, + root_volume_cluster, + "rootdiskdevice") + + # Get Destnation Pool + # Avoid storage Pool on which ROOT disk exists + storagePools_to_avoid = [root_volume_cluster.storage] + + destinationPool_for_root_disk = GetDestinationPool( + self, + storagePools_to_avoid, + "CLUSTER") + + # Migrate + MigrateDataVolume( + self, + root_volume_cluster, + destinationPool_for_root_disk, + islive=True) + + compareChecksum( + self, + checksum_random_root_cluster, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + + # Migrate DATA Volume from CWPS to other CWPS + checksum_random_data_cluster = createChecksum( + self, + vm_cluster, + data_volume_1_cluster, + "datadiskdevice_1") + + # Get Destnation Pool + # Avoid storage Pool allocated for ROOT disk, and Pool on which DATA + # disk1 exists + storagePools_to_avoid = [ + data_volume_1_cluster.storage, + destinationPool_for_root_disk.name] + + destinationPool_for_data_disk1 = GetDestinationPool( + self, + storagePools_to_avoid, + "CLUSTER") + + # Migrate + MigrateDataVolume( + self, + data_volume_1_cluster, + destinationPool_for_data_disk1, + islive=True) + + # Check VM State is Running + self.assertEqual( + vm_cluster.state, + "Running", + "Check VM State is running or not.") + + vm_cluster.detach_volume( + self.userapiclient, + data_volume_clust_1 + ) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=None, + disk=data_volume_1_cluster, + new_vm=True) + + # Destroy and expunge VM and data disk + vm_cluster.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_cluster.id + ), None, "VM list should be empty") + + # Delete volume: + data_volume_clust_1.delete(self.apiclient) + + # Verify associated disks are deleted + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_clust_1.id, + ), None, "Volume list should be empty") + + self.assertEqual( + Volume.list( + self.apiclient, + id=root_volume_cluster.id, + ), None, "Volume list should be empty") + + return + + @unittest.skip( + "Requires setup with 2 pods - Each pod having 2 clusters. \ + Yet to be tested") + @attr(tags=["advanced", "basic"]) + def test_02_migration_live_different_pods(self): + """ Test migrate Volume (root and data disk) + + # 1. Deploy a VM on cluster wide primary storage. + # 2. Migrate root and data volume to two different storage pools\ + in same cluster. + + """ + + try: + self.pools = StoragePool.list(self.apiclient, zoneid=self.zone.id) + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "ZONE")) >= 2,\ + "There must be at least two zone wide\ + storage pools available in the setup" + + assert len(list(storagePool for storagePool in self.pools + if storagePool.scope == "CLUSTER")) >= 3,\ + "There must be at least two cluster wide\ + storage pools available in the setup" + except Exception as e: + self.skipTest(e) + + storagePools_to_avoid = [] + + # Adding tags to Storage Pools + cluster_no = 1 + zone_no = 1 + self.debug("Storage Pools: %s" % self.pools) + for storagePool in self.pools: + if storagePool.scope == "ZONE": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['zwps' + repr(zone_no)]) + zone_no += 1 + elif storagePool.scope == "CLUSTER": + StoragePool.update( + self.apiclient, + id=storagePool.id, + tags=['cwps' + repr(cluster_no)]) + cluster_no += 1 + + # Step 1 + # Create VM on CWPS + vm_cluster = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_cluster1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + # Get ROOT Volume + root_volumes_cluster_list = list_volumes( + self.apiclient, + virtualmachineid=vm_cluster.id, + type='ROOT', + listall=True + ) + + root_volume_cluster = root_volumes_cluster_list[0] + + # Create DATA Volume on Cluster Wide Storage + data_volume_clust_1 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust_1.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust_1 + ) + + data_disk_1_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust_1.id + ) + + data_volume_1_cluster = data_disk_1_volumes_cluster_list[0] + + # Step 2 + # Migrate ROOT Volume from CWPS to other CWPS + checksum_random_root_cluster = createChecksum( + self, + vm_cluster, + root_volume_cluster, + "rootdiskdevice") + + # Get Destnation Pool + # Avoid storage Pool on which ROOT disk exists + storagePools_to_avoid = [root_volume_cluster.storage] + + destinationPool_for_root_disk = GetDestinationPool( + self, + storagePools_to_avoid, + "CLUSTER") + + # Migrate + MigrateDataVolume( + self, + root_volume_cluster, + destinationPool_for_root_disk, + islive=True) + + compareChecksum( + self, + checksum_random_root_cluster, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + + # Migrate DATA Volume from CWPS to other CWPS + checksum_random_data_cluster = createChecksum( + self, + vm_cluster, + data_volume_1_cluster, + "datadiskdevice_1") + + # Get Destnation Pool + # Avoid storage Pool allocated for ROOT disk, and Pool on which DATA + # disk1 exists + + storagePools_to_avoid = [ + data_volume_1_cluster.storage, + destinationPool_for_root_disk.name] + + destinationPool_for_data_disk1 = GetDestinationPool( + self, + storagePools_to_avoid, + "CLUSTER") + + # Migrate + MigrateDataVolume( + self, + data_volume_1_cluster, + destinationPool_for_data_disk1, + islive=True) + + # Check VM State is Running + self.assertEqual( + vm_cluster.state, + "Running", + "Check VM State is running or not.") + + vm_cluster.detach_volume( + self.userapiclient, + data_volume_clust_1 + ) + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_1", + virt_machine=None, + disk=data_volume_1_cluster, + new_vm=True) + + # Add disk 2 + + data_volume_clust_2 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_cluster1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_clust_2.id) + + vm_cluster.attach_volume( + self.userapiclient, + data_volume_clust_2 + ) + + data_disk_2_volumes_cluster_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_clust_2.id + ) + + data_volume_2_cluster = data_disk_2_volumes_cluster_list[0] + + # Add data to second data disk + + checksum_random_data_cluster = createChecksum( + self, + vm_cluster, + data_volume_2_cluster, + "datadiskdevice_2") + + # TO-DO Migration + + compareChecksum( + self, + checksum_random_data_cluster, + "datadiskdevice_2", + virt_machine=None, + disk=data_volume_2_cluster, + new_vm=True) + + # TO-DO: Create Snapshot, Migrate and Restore Snapshot + # But Restore snapshot to previous stage + # is currently not working, need to investigate + + # Step 3 + # Create VM on ZWPS + vm_zone = VirtualMachine.create( + self.userapiclient, + self.testdata["small"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_zone1.id, + zoneid=self.zone.id, + mode=self.zone.networktype + ) + + vm_zone.start(self.userapiclient) + + # Get ROOT Volume Id + root_volumes_zone_list = list_volumes( + self.apiclient, + virtualmachineid=vm_zone.id, + type='ROOT', + listall=True + ) + root_volume_zone = root_volumes_zone_list[0] + + # Create DATA Volume on ZWPS + data_volume_zone = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_zone1.id + ) + + self.debug("Created volume with ID: %s" % data_volume_zone.id) + + data_volumes_zone_list = Volume.list( + self.userapiclient, + listall=self.testdata["listall"], + id=data_volume_zone.id + ) + + vm_zone.attach_volume( + self.userapiclient, + data_volumes_zone_list[0] + ) + + # Step 4 + # Migrate ROOT Volume from ZWPS to other ZWPS + checksum_random_root_zone = createChecksum( + self, + vm_zone, + root_volume_zone, + "rootdiskdevice") + + destinationPool = GetDestinationPool(self, root_volume_zone, "ZONE") + MigrateRootVolume(self, vm_zone, destinationPool) + + compareChecksum( + self, + checksum_random_root_zone, + "rootdiskdevice", + virt_machine=vm_zone, + disk=None, + ) + + # Try to Migrate ROOT Volume from ZWPS to Cluster wide Storage + + destinationPool = GetDestinationPool(self, root_volume_zone, "CLUSTER") + MigrateRootVolume( + self, + vm_zone, + destinationPool, + expectexception=True) + + compareChecksum( + self, + checksum_random_root_zone, + "rootdiskdevice", + virt_machine=vm_cluster, + disk=None, + ) + + # DATA Disk + + checksum_random_data_zone = createChecksum( + self, + vm_zone, + data_volumes_zone_list[0], + "datadiskdevice_1") + + # Migrate DATA Volume from ZWPS to other ZWPS + destinationPool = GetDestinationPool( + self, + data_volumes_zone_list[0], + "ZONE") + MigrateDataVolume(self, data_volumes_zone_list[0], destinationPool) + + compareChecksum( + self, + checksum_random_data_zone, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_zone_list[0], + new_vm=True) + + # Try to Migrate DATA Volume from ZWPS to Cluster wide Storage + destinationPool = GetDestinationPool(self, data_volume_zone, "CLUSTER") + MigrateDataVolume( + self, + data_volume_zone, + destinationPool, + expectexception=True) + + compareChecksum( + self, + checksum_random_data_zone, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_zone_list[0], + new_vm=True) + + # Try to Migrate DATA Volume from ZWPS to Cluster wide Storage + destinationPool = GetDestinationPool(self, data_volume_zone, "CLUSTER") + MigrateDataVolume( + self, + data_volume_zone, + destinationPool, + expectexception=True) + + compareChecksum( + self, + checksum_random_data_zone, + "datadiskdevice_1", + virt_machine=None, + disk=data_volumes_zone_list[0], + new_vm=True) + + # Destroy and expunge VM and data disk + vm_zone.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_cluster.id + ), None, "VM list should be empty") + + # Delete volume: + data_volume_zone.delete(self.apiclient) + + # Verify associated disks are deleted + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_zone.id, + ), None, "Volume list should be empty") + + self.assertEqual( + Volume.list( + self.apiclient, + id=root_volume_zone.id, + ), None, "Volume list should be empty") + + # TO-DO: Local Storage + + # Destroy and expunge VM and data disk + vm_cluster.delete(self.apiclient) + + self.assertEqual( + VirtualMachine.list( + self.apiclient, + id=vm_cluster.id + ), None, "VM list should be empty") + + # Delete volume: + data_volume_clust_1.delete(self.apiclient) + data_volume_clust_2.delete(self.apiclient) + + # Verify associated disks are deleted + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_clust_1.id, + ), None, "Volume list should be empty") + + self.assertEqual( + Volume.list( + self.apiclient, + id=data_volume_clust_2.id, + ), None, "Volume list should be empty") + + self.assertEqual( + Volume.list( + self.apiclient, + id=root_volume_cluster.id, + ), None, "Volume list should be empty") + + return diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py index a7e8ec8a675..31ff537a207 100644 --- a/tools/marvin/marvin/codes.py +++ b/tools/marvin/marvin/codes.py @@ -110,3 +110,10 @@ CHILD_DOMAIN_ADMIN="child domain admin" Network states ''' ALLOCATED = "Allocated" +''' +Storage Tags +''' +ZONETAG1 = "zwps1" +ZONETAG2 = "zwps2" +CLUSTERTAG1 = "cwps1" +CLUSTERTAG2 = "cwps2" diff --git a/tools/marvin/marvin/config/test_data.py b/tools/marvin/marvin/config/test_data.py index 34ccec25497..47eb4533e61 100644 --- a/tools/marvin/marvin/config/test_data.py +++ b/tools/marvin/marvin/config/test_data.py @@ -872,7 +872,32 @@ test_data = { "iscsi://192.168.100.21/iqn.2012-01.localdomain.clo-cstack-cos6:iser/1", "name": "Primary iSCSI" }, - "volume": {"diskname": "Test Volume"}, + "volume": {"diskname": "Test Volume", + "size": 1 + }, + "volume_write_path": { + "diskname": "APP Data Volume", + "size": 1, # in GBs + "xenserver": {"rootdiskdevice":"/dev/xvda", + "datadiskdevice_1": '/dev/xvdb', + "datadiskdevice_2": '/dev/xvdc', # Data Disk + }, + "KVM": {"rootdiskdevice": "/dev/vda", + "datadiskdevice_1": "/dev/vdb", + "datadiskdevice_2": "/dev/vdc" + }, + "vmware": {"rootdiskdevice": "/dev/hda", + "datadiskdevice_1": "/dev/hdb", + "datadiskdevice_2": "/dev/hdc" + } + }, + "data_write_paths": { + "mount_dir": "/mnt/tmp", + "sub_dir": "test", + "sub_lvl_dir1": "test1", + "sub_lvl_dir2": "test2", + "random_data": "random.data", + }, "custom_volume": { "customdisksize": 1, "diskname": "Custom disk", diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 74433731cae..db2f3a80a92 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -1881,7 +1881,7 @@ class ServiceOffering: self.__dict__.update(items) @classmethod - def create(cls, apiclient, services, domainid=None, **kwargs): + def create(cls, apiclient, services, tags=None, domainid=None, **kwargs): """Create Service offering""" cmd = createServiceOffering.createServiceOfferingCmd() cmd.cpunumber = services["cpunumber"] @@ -1898,9 +1898,6 @@ class ServiceOffering: if "issystem" in services: cmd.issystem = services['issystem'] - if "tags" in services: - cmd.tags = services["tags"] - if "hosttags" in services: cmd.hosttags = services["hosttags"] @@ -1934,6 +1931,11 @@ class ServiceOffering: if domainid: cmd.domainid = domainid + if tags: + cmd.tags = tags + elif "tags" in services: + cmd.tags = services["tags"] + [setattr(cmd, k, v) for k, v in kwargs.items()] return ServiceOffering(apiclient.createServiceOffering(cmd).__dict__) @@ -1962,7 +1964,7 @@ class DiskOffering: self.__dict__.update(items) @classmethod - def create(cls, apiclient, services, custom=False, domainid=None): + def create(cls, apiclient, services, tags=None, custom=False, domainid=None): """Create Disk offering""" cmd = createDiskOffering.createDiskOfferingCmd() cmd.displaytext = services["displaytext"] @@ -1975,6 +1977,11 @@ class DiskOffering: if domainid: cmd.domainid = domainid + if tags: + cmd.tags = tags + elif "tags" in services: + cmd.tags = services["tags"] + if "storagetype" in services: cmd.storagetype = services["storagetype"] @@ -1987,9 +1994,6 @@ class DiskOffering: if "miniops" in services: cmd.miniops = services["miniops"] - if "tags" in services: - cmd.tags = services["tags"] - if "provisioningtype" in services: cmd.provisioningtype = services["provisioningtype"]