Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 664A117C65 for ; Thu, 2 Apr 2015 14:30:15 +0000 (UTC) Received: (qmail 76437 invoked by uid 500); 2 Apr 2015 14:24:05 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 69843 invoked by uid 500); 2 Apr 2015 14:24:00 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 62430 invoked by uid 99); 2 Apr 2015 13:56:06 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 02 Apr 2015 13:56:06 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 59D97E2F39; Thu, 2 Apr 2015 13:56:06 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ekho@apache.org To: commits@cloudstack.apache.org Date: Thu, 02 Apr 2015 13:56:06 -0000 Message-Id: <537cee2b285945b783ecdc8e98dc6aa3@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] git commit: updated refs/heads/master to bbdec1c Repository: cloudstack Updated Branches: refs/heads/master 20a663a30 -> bbdec1c8d CLOUDSTACK-8357 Add Test cases for vMotion support on VMFS as primary storage Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/dedc860a Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/dedc860a Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/dedc860a Branch: refs/heads/master Commit: dedc860a62bbfc12971b3661fc654f3b8687c1ca Parents: c05e37b Author: Abhinav Roy Authored: Wed Apr 1 23:18:40 2015 +0530 Committer: Abhinav Roy Committed: Wed Apr 1 23:18:40 2015 +0530 ---------------------------------------------------------------------- .../testpaths/testpath_storage_migration.py | 457 ++++++++++++++++++- tools/marvin/marvin/lib/base.py | 20 +- 2 files changed, 474 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/dedc860a/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 index 2a92914..4f8ee65 100644 --- a/test/integration/testpaths/testpath_storage_migration.py +++ b/test/integration/testpaths/testpath_storage_migration.py @@ -34,7 +34,10 @@ from marvin.lib.common import (get_domain, get_zone, get_template, list_volumes, - list_virtual_machines + list_virtual_machines, + list_storage_pools, + list_clusters, + list_hosts ) from marvin.codes import (ZONETAG1, CLUSTERTAG1) @@ -3391,3 +3394,455 @@ class TestLiveStorageMigration(cloudstackTestCase): ), None, "Volume list should be empty") return + +def MigrateVmWithVolume(self,vm,destinationHost,volumes,pools): + """ + This method is used to migrate a vm and its volumes using migrate virtual machine with volume API + INPUTS: + 1. vm -> virtual machine object + 2. destinationHost -> the host to which VM will be migrated + 3. volumes -> list of volumes which are to be migrated + 4. pools -> list of destination pools + """ + vol_pool_map = {} + for vol,pool in zip(volumes,pools): + vol_pool_map.update({vol.id:pool.id}) + + vm.migrate_vm_with_volume( + self.apiclient, + hostid=destinationHost.id, + migrateto = vol_pool_map + ) + vm.getState( + self.apiclient, + "Running" + ) + #check for the VM's host and volume's storage post migration + 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.assertEqual( + migrated_vm_response[0].hostid, + destinationHost.id, + "VM did not migrate to a specified host" + ) + + for vol,pool in zip(volumes,pools): + migrated_volume_response = list_volumes(self.apiclient, virtualmachineid = migrated_vm_response[0].id, name=vol.name, listall = True) + self.assertEqual( + isinstance(migrated_volume_response, list), + True, + "Check list virtual machines response for valid list" + ) + self.assertEqual( + migrated_volume_response[0].storageid, + pool.id, + "Volume did not migrate to a specified pool" + ) + + self.assertEqual( + str(migrated_volume_response[0].state).lower(), + 'ready', + "Check migrated volume is in Ready state" + ) + """ + #Take VM snapshot to check data integrity + try : + vm_snapshot = VmSnapshot.create(self.apiclient, vmid = migrated_vm_response[0].id) + except Exception as e: + raise Exception("Warning: Exception during VM snapshot creation : %s" % e) + + #Delete the snapshot + try : + VmSnapshot.deleteVMSnapshot(self.apiclient, vmsnapshotid = vm_snapshot.id) + except Exception as e: + raise Exception("Warning: Exception during VM snapshot creation : %s" % e) + """ + + return migrated_vm_response[0] + +def MigrateVm(self, vm, destinationHost): + """ + This method is to migrate a VM using migrate virtual machine API + """ + + vm.migrate( + self.apiclient, + hostid=destinationHost.id, + ) + vm.getState( + self.apiclient, + "Running" + ) + #check for the VM's host and volume's storage post migration + 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.assertEqual( + migrated_vm_response[0].hostid, + destinationHost.id, + "VM did not migrate to a specified host" + ) + return migrated_vm_response[0] + +def get_destination_pools_hosts(self, vm): + """ + Get destination Pools for all volumes and destination Host for the VM + This method is use in case we use the API migrate volume with storage + """ + + destinationPools = [] + vol_list = list_volumes(self.apiclient, virtualmachineid=vm.id, listall=True) + # For each volume get destination pool + for vol in vol_list: + pool = GetDestinationPool(self, vol.storage, "CLUSTER") + destinationPools.append(pool) + #Get destination host + destinationHost = self.GetDestinationHost(vm.hostid) + return destinationHost, destinationPools, vol_list + + +def check_files(self, vm, destinationHost): + """ + Check for VMX and VMDK files + INPUTS : + 1. vm -> The Virtual Machine object + 2. destinationHost -> The host to which we want to migrate the VM + """ + # list volumes and their pools + # Here we list all the volumes of the VM , then login to the destination host + # and check for vmx and vmdk files in the storage + + vm_volumes = list_volumes(self.apiclient, virtualmachineid = vm.id, listall=True) + print vm_volumes + for vol in vm_volumes: + spool = list_storage_pools(self.apiclient, id=vol.storageid) + split_path = spool[0].path.split("/") + pool_path = split_path[2] + sshclient = SshClient(host = destinationHost.ipaddress, port = 22, user = "root", passwd = "freebsd") + pool_data_vmdk = sshclient.execute("ls /vmfs/volumes/" + pool_path + "/" + vm.instancename + "| grep vmdk") + pool_data_vmx = sshclient.execute("ls /vmfs/volumes/" + pool_path + "/" + vm.instancename + "| grep vmx") + if(pool_data_vmx): + vmx_file = vm.instancename + ".vmx" + if vol.type == "ROOT": + self.assertIn( + vmx_file, + pool_data_vmx, + "The VMX files are missing" + ) + if(pool_data_vmdk): + vmdk_file1 = vol.path + ".vmdk" + vmdk_file2 = vol.path + "-flat.vmdk" + + self.assertIn( + vmdk_file1, + pool_data_vmdk, + "The VMDK files are missing" + ) + self.assertIn( + vmdk_file2, + pool_data_vmdk, + "The VMDK flat files are missing" + ) + return + + +class TestStorageLiveMigrationVmware(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestStorageLiveMigrationVmware, 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) + # Get Hosts in the cluster and iscsi/vmfs storages for that cluster + iscsi_pools = [] + try : + list_vmware_clusters = list_clusters(cls.apiclient, hypervisor="vmware") + except Exception as e: + raise unittest.SkipTest(e) + + if len(list_vmware_clusters) < 1 : + raise unittest.SkipTest("There is no cluster available in the setup") + else : + for cluster in list_vmware_clusters : + try: + list_esx_hosts = list_hosts(cls.apiclient, clusterid = cluster.id) + except Exception as e: + raise unittest.SkipTest(e) + if len(list_esx_hosts) > 1 : + try: + list_storage = list_storage_pools(cls.apiclient, clusterid = cluster.id) + except Exception as e: + raise unittest.SkipTest(e) + for storage in list_storage : + if storage.type == "VMFS" : + iscsi_pools.append(storage) + if len(iscsi_pools) > 1: + my_cluster_id = cluster.id + break + else : + iscsi_pools = [] + if len(iscsi_pools) < 2 : + raise unittest.SkipTest("Not enough resources available in the setup") + cls.hosts = list_esx_hosts + cls.pools = list_storage + + # Create an account + cls.account = Account.create( + cls.apiclient, + cls.testdata["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + + # Create Service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.testdata["service_offering"] + ) + cls._cleanup.append(cls.service_offering) + + # Create Disk offering + cls.disk_offering = DiskOffering.create( + cls.apiclient, + cls.testdata["disk_offering"] + ) + # Create disk offering for resize + cls.resized_disk_offering = DiskOffering.create( + cls.apiclient, + cls.testdata["resized_disk_offering"] + ) + + cls._cleanup.append(cls.disk_offering) + cls._cleanup.append(cls.resized_disk_offering) + + 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 deploy_virtual_machine(self, service_offering_id, vm): + """ + Function to Deploy VMs + """ + virtual_machine = VirtualMachine.create( + self.apiclient, + self.testdata[vm], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=service_offering_id, + templateid=self.template.id, + ) + virtual_machine.getState( + self.apiclient, + "Running" + ) + + return virtual_machine + + def GetDestinationHost(self, hostsToavoid): + """ + This method gives us the destination host to which VM will be migrated + It takes the souce host i.e. hostsToavoid as input + """ + destinationHost = None + for host in self.hosts: + if host.id not in hostsToavoid: + destinationHost = host + break + return destinationHost + + + @attr(tags=["advanced", "basic", "vmware", "vmfs"]) + def test_01_migrate_root_and_data_disk_live(self): + """ + Migrate VMs/Volumes on VMware with VMFS storage + """ + #List clusters and check if they have multiple hosts + #and multiple storages + #check if they storages are VMFS type + self.debug("---------------This is the test no 1--------------") + """ + Create a VM, live migrate the VM + """ + vm = "virtual_machine2" + virtual_machine_1 = self.deploy_virtual_machine(self.service_offering.id, vm) + + #Get destination host + destinationHost = self.GetDestinationHost(virtual_machine_1.hostid) + #Migrate the VM + vm = MigrateVm(self, virtual_machine_1, destinationHost) + #self.check_files(vm,destinationHost) + + self.debug("---------------This is the test no 2--------------") + """ + Migrate the ROOT Volume + """ + # Get ROOT volume and destination pool + vol_list = list_volumes(self.apiclient, virtualmachineid=vm.id, type="ROOT", listall=True) + root_vol = vol_list[0] + destinationPool = GetDestinationPool(self, root_vol.storage, "CLUSTER") + #Migrate ROOT volume + islive = True + MigrateDataVolume(self, root_vol, destinationPool, islive) + check_files(self, vm ,destinationHost) + + self.debug("---------------This is the test no 3--------------") + """ + Migrate the VM and ROOT volume + """ + #Get all volumes to be migrated + + destinationHost, destinationPools, vol_list = get_destination_pools_hosts(self, vm) + vm = MigrateVmWithVolume(self, virtual_machine_1, destinationHost, vol_list, destinationPools) + + check_files(self, vm,destinationHost) + + self.debug("---------------This is the test no 4--------------") + """ + Add a data disk and migrate vm, data disk and root disk + """ + + data_disk_1 = Volume.create( + self.apiclient, + self.testdata["volume"], + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering.id + ) + + self.debug("Created volume with ID: %s" % data_disk_1.id) + + virtual_machine_1.attach_volume( + self.apiclient, + data_disk_1 + ) + + destinationHost, destinationPools, vol_list = get_destination_pools_hosts(self, vm) + vm = MigrateVmWithVolume(self, virtual_machine_1, destinationHost, vol_list, destinationPools) + + check_files(self, vm,destinationHost) + self.debug("---------------This is the test no 5--------------") + """ + Upload a Volume, Attach it to the VM, Migrate all the volumes and VM. + """ + + #upload a volume + self.testdata["configurableData"]["upload_volume"]["format"] = "OVA" + self.testdata["configurableData"]["upload_volume"]["url"] = "http://nfs1.lab.vmops.com/templates/burbank-systemvm-08012012.ova" + upload_volume = Volume.upload( + self.apiclient, + self.testdata["configurableData"]["upload_volume"], + account= self.account.name, + domainid= self.domain.id, + zoneid= self.zone.id + ) + upload_volume.wait_for_upload(self.apiclient) + virtual_machine_1.attach_volume( + self.apiclient, + upload_volume + ) + + destinationHost, destinationPools, vol_list = get_destination_pools_hosts(self, vm) + vm = MigrateVmWithVolume(self, virtual_machine_1, destinationHost, vol_list, destinationPools) + + check_files(self, vm,destinationHost) + + self.debug("---------------This is the test no 6--------------") + """ + Create snapshots on all the volumes, Migrate all the volumes and VM. + """ + #Get ROOT Volume + vol_for_snap = list_volumes(self.apiclient, virtualmachineid=vm.id, listall=True) + for vol in vol_for_snap: + snapshot = Snapshot.create( + self.apiclient, + volume_id = vol.id + ) + snapshot.validateState( + self.apiclient, + snapshotstate="backedup", + ) + # Migrate all volumes and VMs + + destinationHost, destinationPools, vol_list = get_destination_pools_hosts(self, vm) + vm = MigrateVmWithVolume(self, virtual_machine_1, destinationHost, vol_list, destinationPools) + + check_files(self, vm, destinationHost) + + self.debug("---------------This is the test no 7--------------") + """ + Resize the data volume , Migrate all the volumes and VM. + """ + data_disk_1.resize( + self.apiclient, + diskofferingid = self.resized_disk_offering.id + ) + # Migrate all volumes and VMs + destinationHost, destinationPools, vol_list = get_destination_pools_hosts(self, vm) + vm = MigrateVmWithVolume(self, virtual_machine_1, destinationHost, vol_list, destinationPools) + + check_files(self, vm,destinationHost) + + self.debug("---------------This is the test no 8--------------") + """ + Restore the VM , Migrate all the volumes and VM. + """ + virtual_machine_1.restore(self.apiclient) + virtual_machine_1.getState( + self.apiclient, + "Running" + ) + # Migrate the VM and its volumes + + destinationHost, destinationPools, vol_list = get_destination_pools_hosts(self, vm) + vm = MigrateVmWithVolume(self, virtual_machine_1, destinationHost, vol_list, destinationPools) + + check_files(self, vm,destinationHost) http://git-wip-us.apache.org/repos/asf/cloudstack/blob/dedc860a/tools/marvin/marvin/lib/base.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 3cb04fe..91a4f60 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -651,6 +651,21 @@ class VirtualMachine: cmd.hostid = hostid apiclient.migrateVirtualMachine(cmd) + def migrate_vm_with_volume(self, apiclient, hostid=None, migrateto=None): + """migrate an Instance and its volumes""" + cmd = migrateVirtualMachineWithVolume.migrateVirtualMachineWithVolumeCmd() + cmd.virtualmachineid = self.id + if hostid: + cmd.hostid = hostid + if migrateto: + migrateto = [] + for volume, pool in migrateto.items(): + cmd.migrateto.append({ + 'volume': volume, + 'pool': pool + }) + apiclient.migrateVirtualMachineWithVolume(cmd) + def attach_volume(self, apiclient, volume): """Attach volume to instance""" cmd = attachVolume.attachVolumeCmd() @@ -3636,11 +3651,12 @@ class Configurations: """Manage Configuration""" @classmethod - def update(cls, apiclient, **kwargs): + def update(cls, apiclient, name, value=None): """Updates the specified configuration""" cmd = updateConfiguration.updateConfigurationCmd() - [setattr(cmd, k, v) for k, v in kwargs.items()] + cmd.name = name + cmd.value = value apiclient.updateConfiguration(cmd) @classmethod