src_vmdk,
fake_size)
+ @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 = [error_util.
+ VimFaultException(mock.Mock(),
+ '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 = [error_util.
+ VimFaultException(mock.Mock(),
+ 'Error'), None]
+ volume_ops.get_backing.side_effect = error_util.VimException('Error')
+ self.assertRaises(error_util.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 = [error_util.
+ VimFaultException(mock.Mock(),
+ 'Error'), None]
+ _select_ds_for_volume.side_effect = error_util.VimException('Error')
+ self.assertRaises(error_util.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
# If _select_ds_for_volume raises an exception, _get_create_spec
# will not be called.
_select_ds_for_volume.side_effect = error_util.VimException('Error')
- self.assertRaises(error_util.VimException,
+ self.assertRaises(exception.VolumeBackendAPIException,
self._driver.copy_image_to_volume,
fake_context, fake_volume,
image_service, fake_image_id)
_select_ds_for_volume,
_extend_virtual_disk,
fetch_optimized_image)
+
+ @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)
LOG.info(_("Done copying image: %(id)s to volume: %(vol)s.") %
{'id': image_id, 'vol': volume['name']})
except Exception as excep:
- LOG.exception(_("Exception in copy_image_to_volume: %(excep)s. "
- "Deleting the backing: %(back)s.") %
- {'excep': excep, 'back': backing})
+ err_msg = (_("Exception in copy_image_to_volume: "
+ "%(excep)s. Deleting the backing: "
+ "%(back)s.") % {'excep': excep, 'back': backing})
# delete the backing
self.volumeops.delete_backing(backing)
- raise excep
+ raise exception.VolumeBackendAPIException(data=err_msg)
def _fetch_stream_optimized_image(self, context, volume, image_service,
image_id, image_size):
# find host in which to create the volume
(host, rp, folder, summary) = self._select_ds_for_volume(volume)
except error_util.VimException as excep:
- LOG.exception(_("Exception in _select_ds_for_volume: %s.") % excep)
- raise excep
+ err_msg = (_("Exception in _select_ds_for_volume: "
+ "%s."), excep)
+ raise exception.VolumeBackendAPIException(data=err_msg)
size_gb = volume['size']
LOG.debug(_("Selected datastore %(ds)s for new volume of size "
vm_import_spec,
image_size=image_size)
except exception.CinderException as excep:
- LOG.exception(_("Exception in copy_image_to_volume: %s.") % excep)
- backing = self.volumeops.get_backing(volume['name'])
- if backing:
- LOG.exception(_("Deleting the backing: %s") % backing)
- # delete the backing
- self.volumeops.delete_backing(backing)
- raise excep
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("Exception in copy_image_to_volume: %s."),
+ excep)
+ backing = self.volumeops.get_backing(volume['name'])
+ if backing:
+ LOG.exception(_("Deleting the backing: %s") % backing)
+ # delete the backing
+ self.volumeops.delete_backing(backing)
LOG.info(_("Done copying image: %(id)s to volume: %(vol)s.") %
{'id': image_id, 'vol': volume['name']})
if properties and 'vmware_disktype' in properties:
disk_type = properties['vmware_disktype']
- if disk_type == 'streamOptimized':
- self._fetch_stream_optimized_image(context, volume, image_service,
- image_id, image_size_in_bytes)
- else:
- self._fetch_flat_image(context, volume, image_service, image_id,
- image_size_in_bytes)
+ try:
+ if disk_type == 'streamOptimized':
+ self._fetch_stream_optimized_image(context, volume,
+ image_service, image_id,
+ image_size_in_bytes)
+ else:
+ self._fetch_flat_image(context, volume, image_service,
+ image_id, image_size_in_bytes)
+ except exception.CinderException as excep:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("Exception in copying the image to the "
+ "volume: %s."), excep)
+
# image_size_in_bytes is the capacity of the image in Bytes and
# volume_size_in_gb is the size specified by the user, if the
# size is input from the API.
LOG.info(_("Done copying volume %(vol)s to a new image %(img)s") %
{'vol': volume['name'], 'img': image_meta['name']})
+ def extend_volume(self, volume, new_size):
+ """Extend vmdk 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.
+
+ :param volume: dictionary describing the existing 'available' volume
+ :param new_size: new size in GB to extend this volume to
+ """
+ vol_name = volume['name']
+ # try extending vmdk in place
+ try:
+ self._extend_vmdk_virtual_disk(vol_name, new_size)
+ LOG.info(_("Done extending volume %(vol)s to size %(size)s GB.") %
+ {'vol': vol_name, 'size': new_size})
+ return
+ except error_util.VimFaultException:
+ LOG.info(_("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 error_util.VimException:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("Not able to find a different datastore to "
+ "place the extended volume %s."), vol_name)
+
+ LOG.info(_("Selected datastore %(ds)s to place extended volume of "
+ "size %(size)s GB.") % {'ds': summary.name,
+ 'size': new_size})
+
+ try:
+ backing = self.volumeops.get_backing(vol_name)
+ 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 error_util.VimException:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("Not able to relocate volume %s for "
+ "extending."), vol_name)
+ LOG.info(_("Done extending volume %(vol)s to size %(size)s GB.") %
+ {'vol': vol_name, 'size': new_size})
+
class VMwareVcVmdkDriver(VMwareEsxVmdkDriver):
"""Manage volumes on VMware VC server."""