diff --git a/cinder/tests/unit/test_vmware_vmdk.py b/cinder/tests/unit/test_vmware_vmdk.py index 2c3a10f3307..a9cbd29dc77 100644 --- a/cinder/tests/unit/test_vmware_vmdk.py +++ b/cinder/tests/unit/test_vmware_vmdk.py @@ -294,90 +294,6 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): self.assertRaises(cinder_exceptions.InvalidVolume, self._driver.delete_snapshot, snapshot) - @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume') - @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk') - @mock.patch.object(VMDK_DRIVER, 'volumeops') - def test_extend_volume(self, volume_ops, _extend_virtual_disk, - _select_ds_for_volume): - """Test extend_volume.""" - self._test_extend_volume(volume_ops, _extend_virtual_disk, - _select_ds_for_volume) - - def _test_extend_volume(self, volume_ops, _extend_virtual_disk, - _select_ds_for_volume): - fake_name = u'volume-00000001' - new_size = '21' - fake_size = '20' - fake_vol = {'project_id': 'testprjid', 'name': fake_name, - 'size': fake_size, - 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} - fake_host = mock.sentinel.host - fake_rp = mock.sentinel.rp - fake_folder = mock.sentinel.folder - fake_summary = mock.Mock(spec=object) - fake_summary.datastore = mock.sentinel.datastore - fake_summary.name = 'fake_name' - fake_backing = mock.sentinel.backing - volume_ops.get_backing.return_value = fake_backing - - # If there is enough space in the datastore, where the volume is - # located, then the rest of this method will not be called. - self._driver.extend_volume(fake_vol, new_size) - _extend_virtual_disk.assert_called_with(fake_name, new_size) - self.assertFalse(_select_ds_for_volume.called) - self.assertFalse(volume_ops.get_backing.called) - self.assertFalse(volume_ops.relocate_backing.called) - self.assertFalse(volume_ops.move_backing_to_folder.called) - - # If there is not enough space in the datastore, where the volume is - # located, then the rest of this method will be called. The first time - # _extend_virtual_disk is called, VimFaultException is raised. The - # second time it is called, there is no exception. - _extend_virtual_disk.reset_mock() - _extend_virtual_disk.side_effect = [exceptions. - VimFaultException([], - 'Error'), None] - # When _select_ds_for_volume raises no exception. - _select_ds_for_volume.return_value = (fake_host, fake_rp, - fake_folder, fake_summary) - self._driver.extend_volume(fake_vol, new_size) - _select_ds_for_volume.assert_called_with(new_size) - volume_ops.get_backing.assert_called_with(fake_name) - volume_ops.relocate_backing.assert_called_with(fake_backing, - fake_summary.datastore, - fake_rp, - fake_host) - _extend_virtual_disk.assert_called_with(fake_name, new_size) - volume_ops.move_backing_to_folder.assert_called_with(fake_backing, - fake_folder) - - # If get_backing raises error_util.VimException, - # this exception will be caught for volume extend. - _extend_virtual_disk.reset_mock() - _extend_virtual_disk.side_effect = [exceptions. - VimFaultException([], - 'Error'), None] - volume_ops.get_backing.side_effect = exceptions.VimException('Error') - self.assertRaises(exceptions.VimException, self._driver.extend_volume, - fake_vol, new_size) - - # If _select_ds_for_volume raised an exception, the rest code will - # not be called. - _extend_virtual_disk.reset_mock() - volume_ops.get_backing.reset_mock() - volume_ops.relocate_backing.reset_mock() - volume_ops.move_backing_to_folder.reset_mock() - _extend_virtual_disk.side_effect = [exceptions. - VimFaultException([], - 'Error'), None] - _select_ds_for_volume.side_effect = exceptions.VimException('Error') - self.assertRaises(exceptions.VimException, self._driver.extend_volume, - fake_vol, new_size) - _extend_virtual_disk.assert_called_once_with(fake_name, new_size) - self.assertFalse(volume_ops.get_backing.called) - self.assertFalse(volume_ops.relocate_backing.called) - self.assertFalse(volume_ops.move_backing_to_folder.called) - def test_copy_image_to_volume_non_vmdk(self): """Test copy_image_to_volume for a non-vmdk disk format.""" fake_context = mock.sentinel.context @@ -391,7 +307,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): fake_context, fake_volume, image_service, fake_image_id) - @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') @mock.patch('oslo_utils.uuidutils.generate_uuid') @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume') @mock.patch.object(VMDK_DRIVER, 'volumeops') @@ -405,7 +321,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): def test_copy_image_to_volume_non_stream_optimized( self, create_backing, get_ds_name_folder_path, get_disk_type, create_disk_from_sparse_image, create_disk_from_preallocated_image, - vops, select_ds_for_volume, generate_uuid, extend_disk): + vops, select_ds_for_volume, generate_uuid, extend_backing): self._test_copy_image_to_volume_non_stream_optimized( create_backing, get_ds_name_folder_path, @@ -415,12 +331,12 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): vops, select_ds_for_volume, generate_uuid, - extend_disk) + extend_backing) def _test_copy_image_to_volume_non_stream_optimized( self, create_backing, get_ds_name_folder_path, get_disk_type, create_disk_from_sparse_image, create_disk_from_preallocated_image, - vops, select_ds_for_volume, generate_uuid, extend_disk): + vops, select_ds_for_volume, generate_uuid, extend_backing): image_size_in_bytes = 2 * units.Gi adapter_type = 'lsiLogic' image_meta = {'disk_format': 'vmdk', @@ -494,8 +410,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): vops.delete_backing.assert_called_once_with(backing) vops.update_backing_disk_uuid.assert_called_once_with(clone, volume['id']) - self.assertFalse(extend_disk.called) + self.assertFalse(extend_backing.called) + vops.get_backing.return_value = backing vops.get_disk_size.return_value = 1 * units.Gi create_backing.reset_mock() vops.attach_disk_to_backing.reset_mock() @@ -517,9 +434,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): adapter_type, path.get_descriptor_ds_file_path()) vops.update_backing_disk_uuid.assert_called_once_with(backing, volume['id']) - extend_disk.assert_called_once_with(volume['name'], volume['size']) + extend_backing.assert_called_once_with(backing, volume['size']) - extend_disk.reset_mock() + extend_backing.reset_mock() create_disk_from_preallocated_image.side_effect = ( exceptions.VimException("Error")) @@ -527,7 +444,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): self._driver.copy_image_to_volume, context, volume, image_service, image_id) vops.delete_backing.assert_called_once_with(backing) - self.assertFalse(extend_disk.called) + self.assertFalse(extend_backing.called) @mock.patch.object(VMDK_DRIVER, '_copy_temp_virtual_disk') @mock.patch.object(VMDK_DRIVER, '_get_temp_image_folder') @@ -724,7 +641,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): self.assertEqual(dest_path, ret) @mock.patch.object(image_transfer, 'download_stream_optimized_image') - @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume') @mock.patch.object(VMDK_DRIVER, '_get_storage_profile_id') @mock.patch.object(VMDK_DRIVER, 'session') @@ -734,7 +651,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): session, get_profile_id, _select_ds_for_volume, - _extend_virtual_disk, + extend_backing, download_image): """Test copy_image_to_volume. @@ -744,14 +661,14 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): session, get_profile_id, _select_ds_for_volume, - _extend_virtual_disk, + extend_backing, download_image) def _test_copy_image_to_volume_stream_optimized(self, volumeops, session, get_profile_id, _select_ds_for_volume, - _extend_virtual_disk, + extend_backing, download_image): fake_context = mock.Mock() fake_backing = mock.sentinel.backing @@ -797,7 +714,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): self.assertFalse(volumeops.get_create_spec.called) # If the volume size is greater then than the backing's disk size, - # _extend_vmdk_virtual_disk will be called. + # _extend_backing will be called. _select_ds_for_volume.side_effect = None _select_ds_for_volume.return_value = (fake_host, fake_rp, fake_folder, fake_summary) @@ -836,18 +753,17 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): image_size=size) volumeops.update_backing_disk_uuid.assert_called_once_with( backing, fake_volume['id']) - _extend_virtual_disk.assert_called_once_with(fake_volume['name'], - fake_volume_size) + extend_backing.assert_called_once_with(backing, fake_volume_size) # If the volume size is not greater then than backing's disk size, - # _extend_vmdk_virtual_disk will not be called. + # _extend_backing will not be called. volumeops.get_disk_size.return_value = fake_volume_size * units.Gi - _extend_virtual_disk.reset_mock() + extend_backing.reset_mock() self._driver.copy_image_to_volume(fake_context, fake_volume, image_service, fake_image_id) - self.assertFalse(_extend_virtual_disk.called) + self.assertFalse(extend_backing.called) # If fetch_stream_optimized_image raises an exception, # get_backing and delete_backing will be called. @@ -859,7 +775,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): image_service, fake_image_id) volumeops.get_backing.assert_called_with(fake_volume['name']) volumeops.delete_backing.assert_called_with(fake_backing) - self.assertFalse(_extend_virtual_disk.called) + self.assertFalse(extend_backing.called) def test_copy_volume_to_image_non_vmdk(self): """Test copy_volume_to_image for a non-vmdk disk format.""" @@ -1152,45 +1068,21 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): self.assertFalse(vops.change_backing_profile.called) @mock.patch.object(VMDK_DRIVER, 'volumeops') - def test_extend_vmdk_virtual_disk(self, volume_ops): - """Test vmdk._extend_vmdk_virtual_disk.""" - self._test_extend_vmdk_virtual_disk(volume_ops) + def test_extend_backing(self, vops): + vmdk_path = mock.sentinel.vmdk_path + vops.get_vmdk_path.return_value = vmdk_path + dc = mock.sentinel.datacenter + vops.get_dc.return_value = dc - def _test_extend_vmdk_virtual_disk(self, volume_ops): - fake_backing = mock.sentinel.backing - fake_vmdk_path = "[datastore] dest_vm/dest_vm.vmdk" - fake_dc = mock.sentinel.datacenter - fake_name = 'fake_name' - fake_size = 7 + backing = mock.sentinel.backing + new_size = 1 + self._driver._extend_backing(backing, new_size) - # If the backing is None, get_vmdk_path and get_dc - # will not be called - volume_ops.get_backing.return_value = None - volume_ops.get_vmdk_path.return_value = fake_vmdk_path - volume_ops.get_dc.return_value = fake_dc - self._driver._extend_vmdk_virtual_disk(fake_name, fake_size) - volume_ops.get_backing.assert_called_once_with(fake_name) - self.assertFalse(volume_ops.get_vmdk_path.called) - self.assertFalse(volume_ops.get_dc.called) - self.assertFalse(volume_ops.extend_virtual_disk.called) - - # Reset the mock and set the backing with a fake, - # all the mocks should be called. - volume_ops.get_backing.reset_mock() - volume_ops.get_backing.return_value = fake_backing - self._driver._extend_vmdk_virtual_disk(fake_name, fake_size) - volume_ops.get_vmdk_path.assert_called_once_with(fake_backing) - volume_ops.get_dc.assert_called_once_with(fake_backing) - volume_ops.extend_virtual_disk.assert_called_once_with(fake_size, - fake_vmdk_path, - fake_dc) - - # Test the exceptional case for extend_virtual_disk - volume_ops.extend_virtual_disk.side_effect = exceptions.VimException( - 'VimException raised.') - self.assertRaises(exceptions.VimException, - self._driver._extend_vmdk_virtual_disk, - fake_name, fake_size) + vops.get_vmdk_path.assert_called_once_with(backing) + vops.get_dc.assert_called_once_with(backing) + vops.extend_virtual_disk.assert_called_once_with(new_size, + vmdk_path, + dc) @mock.patch.object(image_transfer, 'copy_stream_optimized_disk') @mock.patch('cinder.volume.drivers.vmware.vmdk.open', create=True) @@ -1790,9 +1682,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): vops.create_vm_inventory_folder.assert_called_once_with( datacenter, ['OpenStack', project_folder_name, self.VOLUME_FOLDER]) - @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') @mock.patch.object(VMDK_DRIVER, 'volumeops') - def test_clone_backing_linked(self, volume_ops, _extend_vmdk_virtual_disk): + def test_clone_backing_linked(self, volume_ops, extend_backing): """Test _clone_backing with clone type - linked.""" clone = mock.sentinel.clone volume_ops.clone_backing.return_value = clone @@ -1823,25 +1715,24 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): clone, fake_volume['id']) # If the volume size is greater than the original snapshot size, - # _extend_vmdk_virtual_disk will be called. - _extend_vmdk_virtual_disk.assert_called_with(fake_volume['name'], - fake_volume['size']) + # _extend_backing will be called. + extend_backing.assert_called_with(clone, fake_volume['size']) # If the volume size is not greater than the original snapshot size, - # _extend_vmdk_virtual_disk will not be called. + # _extend_backing will not be called. fake_size = 2 fake_volume['size'] = fake_size - _extend_vmdk_virtual_disk.reset_mock() + extend_backing.reset_mock() self._driver._clone_backing(fake_volume, fake_backing, fake_snapshot, volumeops.LINKED_CLONE_TYPE, fake_snapshot['volume_size']) - self.assertFalse(_extend_vmdk_virtual_disk.called) + self.assertFalse(extend_backing.called) - @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume') @mock.patch.object(VMDK_DRIVER, 'volumeops') def test_clone_backing_full(self, volume_ops, _select_ds_for_volume, - _extend_vmdk_virtual_disk): + extend_backing): """Test _clone_backing with clone type - full.""" fake_host = mock.sentinel.host fake_folder = mock.sentinel.folder @@ -1882,19 +1773,18 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): clone, fake_volume['id']) # If the volume size is greater than the original snapshot size, - # _extend_vmdk_virtual_disk will be called. - _extend_vmdk_virtual_disk.assert_called_with(fake_volume['name'], - fake_volume['size']) + # _extend_backing will be called. + extend_backing.assert_called_with(clone, fake_volume['size']) # If the volume size is not greater than the original snapshot size, - # _extend_vmdk_virtual_disk will not be called. + # _extend_backing will not be called. fake_size = 2 fake_volume['size'] = fake_size - _extend_vmdk_virtual_disk.reset_mock() + extend_backing.reset_mock() self._driver._clone_backing(fake_volume, fake_backing, fake_snapshot, volumeops.FULL_CLONE_TYPE, fake_snapshot['volume_size']) - self.assertFalse(_extend_vmdk_virtual_disk.called) + self.assertFalse(extend_backing.called) @mock.patch('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver.' 'volumeops', new_callable=mock.PropertyMock) @@ -2492,6 +2382,88 @@ class VMwareVcVmdkDriverTestCase(test.TestCase): cacert=self._config.vmware_ca_file, insecure=self._config.vmware_insecure) + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') + def test_extend_volume_with_no_backing(self, extend_backing, vops): + vops.get_backing.return_value = None + + volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'volume_type_id': None, 'size': 1, + 'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'display_name': 'foo'} + self._driver.extend_volume(volume, 2) + + self.assertFalse(extend_backing.called) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') + def test_extend_volume(self, extend_backing, vops): + backing = mock.sentinel.backing + vops.get_backing.return_value = backing + + volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'volume_type_id': None, 'size': 1, + 'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'display_name': 'foo'} + new_size = 2 + self._driver.extend_volume(volume, new_size) + + extend_backing.assert_called_once_with(backing, new_size) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') + @mock.patch.object(VMDK_DRIVER, '_select_ds_for_volume') + def test_extend_volume_with_no_disk_space(self, select_ds_for_volume, + extend_backing, vops): + backing = mock.sentinel.backing + vops.get_backing.return_value = backing + + extend_backing.side_effect = [exceptions.NoDiskSpaceException, None] + + host = mock.sentinel.host + rp = mock.sentinel.rp + folder = mock.sentinel.folder + datastore = mock.sentinel.datastore + summary = mock.Mock(datastore=datastore) + select_ds_for_volume.return_value = (host, rp, folder, summary) + + volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'volume_type_id': None, 'size': 1, + 'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'display_name': 'foo'} + new_size = 2 + self._driver.extend_volume(volume, new_size) + + create_params = {vmdk.CREATE_PARAM_DISK_SIZE: new_size} + select_ds_for_volume.assert_called_once_with( + volume, create_params=create_params) + + vops.relocate_backing.assert_called_once_with(backing, datastore, rp, + host) + vops.move_backing_to_folder(backing, folder) + + extend_backing_calls = [mock.call(backing, new_size), + mock.call(backing, new_size)] + self.assertEqual(extend_backing_calls, extend_backing.call_args_list) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_extend_backing') + def test_extend_volume_with_extend_backing_error( + self, extend_backing, vops): + backing = mock.sentinel.backing + vops.get_backing.return_value = backing + + extend_backing.side_effect = exceptions.VimException("Error") + + volume = {'name': 'volume-51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'volume_type_id': None, 'size': 1, + 'id': '51e47214-8e3c-475d-b44b-aea6cd3eef53', + 'display_name': 'foo'} + new_size = 2 + self.assertRaises(exceptions.VimException, self._driver.extend_volume, + volume, new_size) + extend_backing.assert_called_once_with(backing, new_size) + class ImageDiskTypeTest(test.TestCase): """Unit tests for ImageDiskType.""" diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index b8cefbbb1b7..7c65e805b75 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -57,6 +57,7 @@ EAGER_ZEROED_THICK_VMDK_TYPE = 'eagerZeroedThick' CREATE_PARAM_ADAPTER_TYPE = 'adapter_type' CREATE_PARAM_DISK_LESS = 'disk_less' CREATE_PARAM_BACKING_NAME = 'name' +CREATE_PARAM_DISK_SIZE = 'disk_size' TMP_IMAGES_DATASTORE_FOLDER_PATH = "cinder_temp/" @@ -474,8 +475,11 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): :return: (host, resource_pool, folder, summary) """ # Form requirements for datastore selection. + create_params = create_params or {} + size = create_params.get(CREATE_PARAM_DISK_SIZE, volume['size']) + req = {} - req[hub.DatastoreSelector.SIZE_BYTES] = (volume['size'] * units.Gi) + req[hub.DatastoreSelector.SIZE_BYTES] = size * units.Gi req[hub.DatastoreSelector.PROFILE_NAME] = self._get_storage_profile( volume) @@ -1037,39 +1041,16 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): LOG.info(_LI("Done copying image: %(id)s to volume: %(vol)s."), {'id': image_id, 'vol': volume['name']}) - def _extend_vmdk_virtual_disk(self, name, new_size_in_gb): - """Extend the size of the vmdk virtual disk to the new size. + def _extend_backing(self, backing, new_size_in_gb): + """Extend volume backing's virtual disk. - :param name: the name of the volume - :param new_size_in_gb: the new size the vmdk virtual disk extends to + :param backing: volume backing + :param new_size_in_gb: new size of virtual disk """ - backing = self.volumeops.get_backing(name) - if not backing: - LOG.info(_LI("The backing is not found, so there is no need " - "to extend the vmdk virtual disk for the volume " - "%s."), name) - else: - root_vmdk_path = self.volumeops.get_vmdk_path(backing) - datacenter = self.volumeops.get_dc(backing) - self._extend_volumeops_virtual_disk(new_size_in_gb, root_vmdk_path, - datacenter) - - def _extend_volumeops_virtual_disk(self, new_size_in_gb, root_vmdk_path, - datacenter): - """Call the ExtendVirtualDisk_Task. - - :param new_size_in_gb: the new size the vmdk virtual disk extends to - :param root_vmdk_path: the path for the vmdk file - :param datacenter: reference to the datacenter - """ - try: - self.volumeops.extend_virtual_disk(new_size_in_gb, - root_vmdk_path, datacenter) - except exceptions.VimException: - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Unable to extend the size of the " - "vmdk virtual disk at the path %s."), - root_vmdk_path) + root_vmdk_path = self.volumeops.get_vmdk_path(backing) + datacenter = self.volumeops.get_dc(backing) + self.volumeops.extend_virtual_disk(new_size_in_gb, root_vmdk_path, + datacenter) def copy_image_to_volume(self, context, volume, image_service, image_id): """Creates volume from image. @@ -1146,7 +1127,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): {'name': volume['name'], 'vol_size': volume_size, 'disk_size': disk_size}) - self._extend_vmdk_virtual_disk(volume['name'], volume['size']) + self._extend_backing(backing, volume['size']) # TODO(vbala): handle volume_size < disk_size case. def copy_volume_to_image(self, context, volume, image_service, image_meta): @@ -1377,50 +1358,58 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): return True def extend_volume(self, volume, new_size): - """Extend vmdk to new_size. + """Extend volume to new size. - Extends the vmdk backing to new volume size. First try to extend in - place on the same datastore. If that fails, try to relocate the volume - to a different datastore that can accommodate the new_size'd volume. + Extends the volume backing's virtual disk to new size. First, try to + extend in place on the same datastore. If that fails due to + insufficient disk space, then try to relocate the volume to a different + datastore that can accommodate the backing with new size and retry + extend. :param volume: dictionary describing the existing 'available' volume :param new_size: new size in GB to extend this volume to """ vol_name = volume['name'] + backing = self.volumeops.get_backing(vol_name) + if not backing: + LOG.info(_LI("There is no backing for volume: %s; no need to " + "extend the virtual disk."), vol_name) + return + # try extending vmdk in place try: - self._extend_vmdk_virtual_disk(vol_name, new_size) - LOG.info(_LI("Done extending volume %(vol)s " - "to size %(size)s GB."), + self._extend_backing(backing, new_size) + LOG.info(_LI("Successfully extended volume: %(vol)s to size: " + "%(size)s GB."), {'vol': vol_name, 'size': new_size}) return - except exceptions.VimFaultException: - LOG.info(_LI("Relocating volume %s vmdk to a different " - "datastore since trying to extend vmdk file " - "in place failed."), vol_name) - # If in place extend fails, then try to relocate the volume - try: - (host, rp, folder, summary) = self._select_ds_for_volume(new_size) - except exceptions.VimException: - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Not able to find a different datastore to " - "place the extended volume %s."), vol_name) - - LOG.info(_LI("Selected datastore %(ds)s to place extended volume of " - "size %(size)s GB."), {'ds': summary.name, - 'size': new_size}) + except exceptions.NoDiskSpaceException: + LOG.warning(_LW("Unable to extend volume: %(vol)s to size: " + "%(size)s on current datastore due to insufficient" + " space."), + {'vol': vol_name, 'size': new_size}) + # Insufficient disk space; relocate the volume to a different datastore + # and retry extend. + LOG.info(_LI("Relocating volume: %s to a different datastore due to " + "insufficient disk space on current datastore."), + vol_name) try: - backing = self.volumeops.get_backing(vol_name) + create_params = {CREATE_PARAM_DISK_SIZE: new_size} + (host, rp, folder, summary) = self._select_ds_for_volume( + volume, create_params=create_params) self.volumeops.relocate_backing(backing, summary.datastore, rp, host) - self._extend_vmdk_virtual_disk(vol_name, new_size) self.volumeops.move_backing_to_folder(backing, folder) - except exceptions.VimException: + self._extend_backing(backing, new_size) + except exceptions.VMwareDriverException: with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Not able to relocate volume %s for " - "extending."), vol_name) - LOG.info(_LI("Done extending volume %(vol)s to size %(size)s GB."), + LOG.error(_LE("Failed to extend volume: %(vol)s to size: " + "%(size)s GB."), + {'vol': vol_name, 'size': new_size}) + + LOG.info(_LI("Successfully extended volume: %(vol)s to size: " + "%(size)s GB."), {'vol': vol_name, 'size': new_size}) @contextlib.contextmanager @@ -1860,7 +1849,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver): # we need to extend/resize the capacity of the vmdk virtual disk from # the size of the source volume to the volume size. if volume['size'] > src_vsize: - self._extend_vmdk_virtual_disk(volume['name'], volume['size']) + self._extend_backing(clone, volume['size']) LOG.info(_LI("Successfully created clone: %s."), clone) def _create_volume_from_snapshot(self, volume, snapshot): diff --git a/cinder/volume/drivers/vmware/volumeops.py b/cinder/volume/drivers/vmware/volumeops.py index 9e8fd80dd9b..c238e32c62f 100644 --- a/cinder/volume/drivers/vmware/volumeops.py +++ b/cinder/volume/drivers/vmware/volumeops.py @@ -617,18 +617,18 @@ class VMwareVolumeOps(object): 'folder': folder}) return folder - def extend_virtual_disk(self, requested_size_in_gb, name, dc_ref, + def extend_virtual_disk(self, requested_size_in_gb, path, dc_ref, eager_zero=False): """Extend the virtual disk to the requested size. :param requested_size_in_gb: Size of the volume in GB - :param name: Name of the backing + :param path: Datastore path of the virtual disk to extend :param dc_ref: Reference to datacenter :param eager_zero: Boolean determining if the free space is zeroed out """ - LOG.debug("Extending the volume %(name)s to %(size)s GB.", - {'name': name, 'size': requested_size_in_gb}) + LOG.debug("Extending virtual disk: %(path)s to %(size)s GB.", + {'path': path, 'size': requested_size_in_gb}) diskMgr = self._session.vim.service_content.virtualDiskManager # VMWare API needs the capacity unit to be in KB, so convert the @@ -637,14 +637,14 @@ class VMwareVolumeOps(object): task = self._session.invoke_api(self._session.vim, "ExtendVirtualDisk_Task", diskMgr, - name=name, + name=path, datacenter=dc_ref, newCapacityKb=size_in_kb, eagerZero=eager_zero) self._session.wait_for_task(task) - LOG.info(_LI("Successfully extended the volume %(name)s to " + LOG.info(_LI("Successfully extended virtual disk: %(path)s to " "%(size)s GB."), - {'name': name, 'size': requested_size_in_gb}) + {'path': path, 'size': requested_size_in_gb}) def _create_controller_config_spec(self, adapter_type): """Returns config spec for adding a disk controller."""