From 0b7cc087f81ede19ac9daaaf63aa73f2a2ab789d Mon Sep 17 00:00:00 2001 From: David Mabry Date: Mon, 18 Apr 2016 15:40:36 -0500 Subject: [PATCH 1/4] Let hypervisor type KVM and Simulator detach root volumes. Updated test_volumes.py to include a test for detaching and reattaching a root volume from a vm. I also had to update base.py to allow attach_volume to have the parameter deviceid to be passed as needed. --- .../cloud/storage/VolumeApiServiceImpl.java | 4 +- test/integration/component/test_volumes.py | 129 +++++++++++++++++- tools/marvin/marvin/lib/base.py | 6 +- 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 5a53f9c531d..6a59f548669 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -1744,8 +1744,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } private void validateRootVolumeDetachAttach(VolumeVO volume, UserVmVO vm) { - if (!(vm.getHypervisorType() == HypervisorType.XenServer || vm.getHypervisorType() == HypervisorType.VMware)) { - throw new InvalidParameterValueException("Root volume detach is allowed for hypervisor type " + HypervisorType.XenServer + " only"); + if (!(vm.getHypervisorType() == HypervisorType.XenServer || vm.getHypervisorType() == HypervisorType.VMware || vm.getHypervisorType() == HypervisorType.KVM || vm.getHypervisorType() == HypervisorType.Simulator)) { + throw new InvalidParameterValueException("Root volume detach is not supported for hypervisor type " + vm.getHypervisorType() ); } if (!(vm.getState() == State.Stopped) || (vm.getState() == State.Destroyed)) { throw new InvalidParameterValueException("Root volume detach can happen only when vm is in states: " + State.Stopped.toString() + " or " + State.Destroyed.toString()); diff --git a/test/integration/component/test_volumes.py b/test/integration/component/test_volumes.py index 12c8f9de347..941e225f54f 100644 --- a/test/integration/component/test_volumes.py +++ b/test/integration/component/test_volumes.py @@ -603,7 +603,134 @@ class TestAttachDetachVolume(cloudstackTestCase): "Check the state of VM" ) except Exception as e: - self.fail("Exception occuered: %s" % e) + self.fail("Exception occurred: %s" % e) + return + + @attr(tags=["advanced", "advancedns"]) + def test_02_root_volume_attach_detach(self): + """Test Root Volume attach/detach to VM + """ + + # Validate the following + # 1. Deploy a VM + # 2. Check for root volume + # 3. Stop VM + # 4. Detach root volume + # 5. Verify root volume detached + # 6. Attach root volume + # 7. Start VM + + try: + # Check for root volume + root_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertNotEqual( + root_volume_response, + None, + "Check if root volume exists in ListVolumes" + ) + self.assertEqual( + isinstance(root_volume_response, list), + True, + "Check list volumes response for valid list" + ) + # Grab the root volume for later use + root_volume = root_volume_response[0] + + # Stop VM + self.debug("Stopping the VM: %s" % self.virtual_machine.id) + self.virtual_machine.stop(self.apiclient) + + # Ensure VM is stopped before detaching the root volume + time.sleep(self.services["sleep"]) + + vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Stopped', + "Check the state of VM" + ) + + # Detach root volume from VM + self.virtual_machine.detach_volume( + self.apiclient, + root_volume + ) + + # Verify that root disk is gone + no_root_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + no_root_volume_response, + None, + "Check if root volume exists in ListVolumes" + ) + + # Attach root volume to VM + self.virtual_machine.attach_volume( + self.apiclient, + root_volume, + 0 + ) + + # Check for root volume + new_root_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertNotEqual( + new_root_volume_response, + None, + "Check if root volume exists in ListVolumes" + ) + self.assertEqual( + isinstance(new_root_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + # Start VM + self.virtual_machine.start(self.apiclient) + # Sleep to ensure that VM is in ready state + time.sleep(self.services["sleep"]) + + vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + ) + # Verify VM response to check whether VM deployment was successful + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM" + ) + except Exception as e: + self.fail("Exception occurred: %s" % e) return diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 4e04ba82496..5d81e5fe8e5 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -667,11 +667,15 @@ class VirtualMachine: }) apiclient.migrateVirtualMachineWithVolume(cmd) - def attach_volume(self, apiclient, volume): + def attach_volume(self, apiclient, volume, deviceid=None): """Attach volume to instance""" cmd = attachVolume.attachVolumeCmd() cmd.id = volume.id cmd.virtualmachineid = self.id + + if deviceid is not None: + cmd.deviceid = deviceid + return apiclient.attachVolume(cmd) def detach_volume(self, apiclient, volume): From ec7a86b2de6716f1fa9d7ba2d10fe82d9e83c4f6 Mon Sep 17 00:00:00 2001 From: David Mabry Date: Tue, 19 Apr 2016 08:21:18 -0500 Subject: [PATCH 2/4] Modified test_volumes.py to include a hypervisor test for root attach/detach testing --- test/integration/component/test_volumes.py | 244 +++++++++++---------- 1 file changed, 127 insertions(+), 117 deletions(-) diff --git a/test/integration/component/test_volumes.py b/test/integration/component/test_volumes.py index 941e225f54f..1b1773cea0b 100644 --- a/test/integration/component/test_volumes.py +++ b/test/integration/component/test_volumes.py @@ -613,124 +613,134 @@ class TestAttachDetachVolume(cloudstackTestCase): # Validate the following # 1. Deploy a VM - # 2. Check for root volume - # 3. Stop VM - # 4. Detach root volume - # 5. Verify root volume detached - # 6. Attach root volume - # 7. Start VM + # 2. Verify that we are testing a supported hypervisor + # 3. Check for root volume + # 4. Stop VM + # 5. Detach root volume + # 6. Verify root volume detached + # 7. Attach root volume + # 8. Start VM - try: - # Check for root volume - root_volume_response = Volume.list( - self.apiclient, - virtualmachineid=self.virtual_machine.id, - type='ROOT', - listall=True - ) - self.assertNotEqual( - root_volume_response, - None, - "Check if root volume exists in ListVolumes" - ) - self.assertEqual( - isinstance(root_volume_response, list), - True, - "Check list volumes response for valid list" - ) - # Grab the root volume for later use - root_volume = root_volume_response[0] - - # Stop VM - self.debug("Stopping the VM: %s" % self.virtual_machine.id) - self.virtual_machine.stop(self.apiclient) - - # Ensure VM is stopped before detaching the root volume - time.sleep(self.services["sleep"]) - - vm_response = VirtualMachine.list( - self.apiclient, - id=self.virtual_machine.id, - ) - vm = vm_response[0] - self.assertEqual( - vm.state, - 'Stopped', - "Check the state of VM" - ) - - # Detach root volume from VM - self.virtual_machine.detach_volume( - self.apiclient, - root_volume - ) - - # Verify that root disk is gone - no_root_volume_response = Volume.list( - self.apiclient, - virtualmachineid=self.virtual_machine.id, - type='ROOT', - listall=True - ) - self.assertEqual( - no_root_volume_response, - None, - "Check if root volume exists in ListVolumes" - ) - - # Attach root volume to VM - self.virtual_machine.attach_volume( - self.apiclient, - root_volume, - 0 - ) - - # Check for root volume - new_root_volume_response = Volume.list( - self.apiclient, - virtualmachineid=self.virtual_machine.id, - type='ROOT', - listall=True - ) - self.assertNotEqual( - new_root_volume_response, - None, - "Check if root volume exists in ListVolumes" - ) - self.assertEqual( - isinstance(new_root_volume_response, list), - True, - "Check list volumes response for valid list" - ) - - # Start VM - self.virtual_machine.start(self.apiclient) - # Sleep to ensure that VM is in ready state - time.sleep(self.services["sleep"]) - - vm_response = VirtualMachine.list( - self.apiclient, - id=self.virtual_machine.id, - ) - # Verify VM response to check whether VM deployment was successful - self.assertEqual( - isinstance(vm_response, list), - True, - "Check list VM response for valid list" - ) - self.assertNotEqual( - len(vm_response), - 0, - "Check VMs available in List VMs response" - ) - vm = vm_response[0] - self.assertEqual( - vm.state, - 'Running', - "Check the state of VM" - ) - except Exception as e: - self.fail("Exception occurred: %s" % e) + # Verify we are using a supported hypervisor + if (self.hypervisor.lower() == 'vmware' + or self.hypervisor.lower() == 'kvm' + or self.hypervisor.lower() == 'simulator' + or self.hypervisor.lower() == 'xenserver'): + + try: + # Check for root volume + root_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertNotEqual( + root_volume_response, + None, + "Check if root volume exists in ListVolumes" + ) + self.assertEqual( + isinstance(root_volume_response, list), + True, + "Check list volumes response for valid list" + ) + # Grab the root volume for later use + root_volume = root_volume_response[0] + + # Stop VM + self.debug("Stopping the VM: %s" % self.virtual_machine.id) + self.virtual_machine.stop(self.apiclient) + + # Ensure VM is stopped before detaching the root volume + time.sleep(self.services["sleep"]) + + vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Stopped', + "Check the state of VM" + ) + + # Detach root volume from VM + self.virtual_machine.detach_volume( + self.apiclient, + root_volume + ) + + # Verify that root disk is gone + no_root_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + no_root_volume_response, + None, + "Check if root volume exists in ListVolumes" + ) + + # Attach root volume to VM + self.virtual_machine.attach_volume( + self.apiclient, + root_volume, + 0 + ) + + # Check for root volume + new_root_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertNotEqual( + new_root_volume_response, + None, + "Check if root volume exists in ListVolumes" + ) + self.assertEqual( + isinstance(new_root_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + # Start VM + self.virtual_machine.start(self.apiclient) + # Sleep to ensure that VM is in ready state + time.sleep(self.services["sleep"]) + + vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + ) + # Verify VM response to check whether VM deployment was successful + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM" + ) + except Exception as e: + self.fail("Exception occurred: %s" % e) + + else: + self.skipTest("Root Volume attach/detach is not supported on %s " % self.hypervisor) return From 760ad0e265c99200c5a54c154ec54bd871ef634c Mon Sep 17 00:00:00 2001 From: David Mabry Date: Wed, 20 Apr 2016 08:32:42 -0500 Subject: [PATCH 3/4] Added required_hardware="false" attr above test_02_root_volume_attach_detach --- test/integration/component/test_volumes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/component/test_volumes.py b/test/integration/component/test_volumes.py index 1b1773cea0b..0d18ef3a43b 100644 --- a/test/integration/component/test_volumes.py +++ b/test/integration/component/test_volumes.py @@ -606,7 +606,7 @@ class TestAttachDetachVolume(cloudstackTestCase): self.fail("Exception occurred: %s" % e) return - @attr(tags=["advanced", "advancedns"]) + @attr(tags=["advanced", "advancedns"], required_hardware="false") def test_02_root_volume_attach_detach(self): """Test Root Volume attach/detach to VM """ From 73cd427555ddb1625482082ea1c2d8ba339d2e9a Mon Sep 17 00:00:00 2001 From: David Mabry Date: Sat, 23 Apr 2016 18:47:30 -0500 Subject: [PATCH 4/4] Removed sleeps and used validateList as requested. --- test/integration/component/test_volumes.py | 56 ++++++++++------------ 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/test/integration/component/test_volumes.py b/test/integration/component/test_volumes.py index 0d18ef3a43b..77d8679dd69 100644 --- a/test/integration/component/test_volumes.py +++ b/test/integration/component/test_volumes.py @@ -635,16 +635,13 @@ class TestAttachDetachVolume(cloudstackTestCase): type='ROOT', listall=True ) - self.assertNotEqual( - root_volume_response, - None, - "Check if root volume exists in ListVolumes" - ) + self.assertEqual( - isinstance(root_volume_response, list), - True, - "Check list volumes response for valid list" + validateList(root_volume_response)[0], + PASS, + "Invalid response returned for root volume list" ) + # Grab the root volume for later use root_volume = root_volume_response[0] @@ -652,13 +649,18 @@ class TestAttachDetachVolume(cloudstackTestCase): self.debug("Stopping the VM: %s" % self.virtual_machine.id) self.virtual_machine.stop(self.apiclient) - # Ensure VM is stopped before detaching the root volume - time.sleep(self.services["sleep"]) - vm_response = VirtualMachine.list( self.apiclient, id=self.virtual_machine.id, ) + + # Ensure that vm_response is a valid list + self.assertEqual( + validateList(vm_response)[0], + PASS, + "Invalid response returned for vm_response list" + ) + vm = vm_response[0] self.assertEqual( vm.state, @@ -679,6 +681,7 @@ class TestAttachDetachVolume(cloudstackTestCase): type='ROOT', listall=True ) + self.assertEqual( no_root_volume_response, None, @@ -699,43 +702,36 @@ class TestAttachDetachVolume(cloudstackTestCase): type='ROOT', listall=True ) - self.assertNotEqual( - new_root_volume_response, - None, - "Check if root volume exists in ListVolumes" - ) + + # Ensure that new_root_volume_response is a valid list self.assertEqual( - isinstance(new_root_volume_response, list), - True, - "Check list volumes response for valid list" + validateList(new_root_volume_response)[0], + PASS, + "Invalid response returned for new_root_volume_response list" ) # Start VM self.virtual_machine.start(self.apiclient) - # Sleep to ensure that VM is in ready state - time.sleep(self.services["sleep"]) vm_response = VirtualMachine.list( self.apiclient, id=self.virtual_machine.id, ) + # Verify VM response to check whether VM deployment was successful self.assertEqual( - isinstance(vm_response, list), - True, - "Check list VM response for valid list" - ) - self.assertNotEqual( - len(vm_response), - 0, - "Check VMs available in List VMs response" + validateList(vm_response)[0], + PASS, + "Invalid response returned for vm_response list during VM start up" ) + vm = vm_response[0] self.assertEqual( vm.state, 'Running', - "Check the state of VM" + "Ensure the state of VM is running" ) + except Exception as e: self.fail("Exception occurred: %s" % e)