rc = self.driver.get_volume_stats(True)
self.assertEqual({}, rc)
- @mock.patch.object(driver.FibreChannelDriver, 'copy_volume_data')
- def test_copy_volume_data(self, arg1):
- """Test copy_volume_data."""
- volume = fake_volume.fake_db_volume(**self._VOLUME)
- rc_vol = self.driver.create_volume(volume)
- volume['provider_location'] = rc_vol['provider_location']
-
- volume2 = fake_volume.fake_db_volume(**self._VOLUME2)
- rc_vol2 = self.driver.create_volume(volume2)
- volume2['provider_location'] = rc_vol2['provider_location']
-
- self.driver.copy_volume_data(None, volume, volume2, None)
-
- arg1.assert_called_with(None, volume, volume2, None)
-
- @mock.patch.object(driver.FibreChannelDriver, 'copy_volume_data',
- side_effect=exception.CinderException)
- def test_copy_volume_data_error(self, arg1):
- """Test copy_volume_data is error."""
- volume = fake_volume.fake_db_volume(**self._VOLUME)
- rc_vol = self.driver.create_volume(volume)
- volume['provider_location'] = rc_vol['provider_location']
-
- volume2 = fake_volume.fake_db_volume(**self._VOLUME2)
- volume2['provider_location'] = '2'
-
- self.assertRaises(exception.CinderException,
- self.driver.copy_volume_data,
- None, volume, volume2, None)
-
- arg1.assert_called_with(None, volume, volume2, None)
-
@mock.patch.object(driver.FibreChannelDriver, 'copy_image_to_volume')
def test_copy_image_to_volume(self, arg1):
"""Test copy_image_to_volume."""
self.context,
volume)
- @mock.patch.object(utils, 'brick_get_connector_properties')
- @mock.patch.object(cinder.volume.driver.VolumeDriver, '_attach_volume')
- @mock.patch.object(cinder.volume.driver.VolumeDriver, '_detach_volume')
- @mock.patch.object(volutils, 'copy_volume')
- @mock.patch.object(volume_rpcapi.VolumeAPI, 'get_capabilities')
- def test_copy_volume_data(self,
- mock_get_capabilities,
- mock_copy,
- mock_detach,
- mock_attach,
- mock_get_connector):
-
- src_vol = tests_utils.create_volume(self.context, size=1,
- host=CONF.host)
- dest_vol = tests_utils.create_volume(self.context, size=1,
- host=CONF.host)
- mock_get_connector.return_value = {}
- self.volume.driver._throttle = mock.MagicMock()
-
- attach_expected = [
- mock.call(self.context, dest_vol, {}, remote=False),
- mock.call(self.context, src_vol, {}, remote=False)]
-
- detach_expected = [
- mock.call(self.context, {'device': {'path': 'bar'}},
- dest_vol, {}, force=False, remote=False),
- mock.call(self.context, {'device': {'path': 'foo'}},
- src_vol, {}, force=False, remote=False)]
-
- attach_volume_returns = [
- ({'device': {'path': 'bar'}}, dest_vol),
- ({'device': {'path': 'foo'}}, src_vol),
- ]
-
- # Test case for sparse_copy_volume = False
- mock_attach.side_effect = attach_volume_returns
- mock_get_capabilities.return_value = {}
- self.volume.driver.copy_volume_data(self.context,
- src_vol,
- dest_vol)
-
- self.assertEqual(attach_expected, mock_attach.mock_calls)
- mock_copy.assert_called_with(
- 'foo', 'bar', 1024, '1M',
- throttle=self.volume.driver._throttle,
- sparse=False)
- self.assertEqual(detach_expected, mock_detach.mock_calls)
-
- # Test case for sparse_copy_volume = True
- mock_attach.reset_mock()
- mock_detach.reset_mock()
- mock_attach.side_effect = attach_volume_returns
- mock_get_capabilities.return_value = {'sparse_copy_volume': True}
- self.volume.driver.copy_volume_data(self.context,
- src_vol,
- dest_vol)
-
- self.assertEqual(attach_expected, mock_attach.mock_calls)
- mock_copy.assert_called_with(
- 'foo', 'bar', 1024, '1M',
- throttle=self.volume.driver._throttle,
- sparse=True)
- self.assertEqual(detach_expected, mock_detach.mock_calls)
-
- # cleanup resource
- db.volume_destroy(self.context, src_vol['id'])
- db.volume_destroy(self.context, dest_vol['id'])
-
@mock.patch.object(utils, 'brick_get_connector_properties')
@mock.patch.object(cinder.volume.manager.VolumeManager, '_attach_volume')
@mock.patch.object(cinder.volume.manager.VolumeManager, '_detach_volume')
from cinder import utils
from cinder.volume import rpcapi as volume_rpcapi
from cinder.volume import throttling
-from cinder.volume import utils as volume_utils
LOG = logging.getLogger(__name__)
data["pools"].append(single_pool)
self._stats = data
- def copy_volume_data(self, context, src_vol, dest_vol, remote=None):
- """Copy data from src_vol to dest_vol."""
- LOG.debug('copy_data_between_volumes %(src)s -> %(dest)s.', {
- 'src': src_vol['name'], 'dest': dest_vol['name']})
-
- use_multipath = self.configuration.use_multipath_for_image_xfer
- enforce_multipath = self.configuration.enforce_multipath_for_image_xfer
- properties = utils.brick_get_connector_properties(use_multipath,
- enforce_multipath)
- dest_remote = True if remote in ['dest', 'both'] else False
- dest_orig_status = dest_vol['status']
- try:
- dest_attach_info, dest_vol = self._attach_volume(
- context,
- dest_vol,
- properties,
- remote=dest_remote)
- except Exception:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to attach volume %(vol)s"),
- {'vol': dest_vol['id']})
- self.db.volume_update(context, dest_vol['id'],
- {'status': dest_orig_status})
-
- src_remote = True if remote in ['src', 'both'] else False
- src_orig_status = src_vol['status']
- try:
- src_attach_info, src_vol = self._attach_volume(context,
- src_vol,
- properties,
- remote=src_remote)
- except Exception:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to attach volume %(vol)s"),
- {'vol': src_vol['id']})
- self.db.volume_update(context, src_vol['id'],
- {'status': src_orig_status})
- self._detach_volume(context, dest_attach_info, dest_vol,
- properties, force=True, remote=dest_remote)
-
- # Check the backend capabilities of migration destination host.
- rpcapi = volume_rpcapi.VolumeAPI()
- capabilities = rpcapi.get_capabilities(context, dest_vol['host'],
- False)
- sparse_copy_volume = bool(capabilities and
- capabilities.get('sparse_copy_volume',
- False))
-
- copy_error = True
- try:
- size_in_mb = int(src_vol['size']) * 1024 # vol size is in GB
- volume_utils.copy_volume(
- src_attach_info['device']['path'],
- dest_attach_info['device']['path'],
- size_in_mb,
- self.configuration.volume_dd_blocksize,
- throttle=self._throttle,
- sparse=sparse_copy_volume)
- copy_error = False
- except Exception:
- with excutils.save_and_reraise_exception():
- LOG.error(_LE("Failed to copy volume %(src)s to %(dest)s."),
- {'src': src_vol['id'], 'dest': dest_vol['id']})
- finally:
- self._detach_volume(context, dest_attach_info, dest_vol,
- properties, force=copy_error,
- remote=dest_remote)
- self._detach_volume(context, src_attach_info, src_vol,
- properties, force=copy_error,
- remote=src_remote)
-
def copy_image_to_volume(self, context, volume, image_service, image_id):
"""Fetch the image from image_service and write it to the volume."""
LOG.debug('copy_image_to_volume %s.', volume['name'])
finally:
self._detach_volume(context, attach_info, volume, properties)
+ def before_volume_copy(self, context, src_vol, dest_vol, remote=None):
+ """Driver-specific actions before copyvolume data.
+
+ This method will be called before _copy_volume_data during volume
+ migration
+ """
+ pass
+
+ def after_volume_copy(self, context, src_vol, dest_vol, remote=None):
+ """Driver-specific actions after copyvolume data.
+
+ This method will be called after _copy_volume_data during volume
+ migration
+ """
+ pass
+
def get_filter_function(self):
"""Get filter_function string.
def remove_export(self, context, volume):
pass
- def copy_volume_data(self, context, src_vol, dest_vol, remote=None):
- self.do_setup_status.wait()
- super(HBSDFCDriver, self).copy_volume_data(context, src_vol,
- dest_vol, remote)
- self.discard_zero_page(dest_vol)
-
def copy_image_to_volume(self, context, volume, image_service, image_id):
self.do_setup_status.wait()
super(HBSDFCDriver, self).copy_image_to_volume(context, volume,
image_service,
image_meta)
+ def before_volume_copy(self, context, src_vol, dest_vol, remote=None):
+ """Driver-specific actions before copyvolume data.
+
+ This method will be called before _copy_volume_data during volume
+ migration
+ """
+ self.do_setup_status.wait()
+
+ def after_volume_copy(self, context, src_vol, dest_vol, remote=None):
+ """Driver-specific actions after copyvolume data.
+
+ This method will be called after _copy_volume_data during volume
+ migration
+ """
+ self.discard_zero_page(dest_vol)
+
def restore_backup(self, context, backup, volume, backup_service):
self.do_setup_status.wait()
super(HBSDFCDriver, self).restore_backup(context, backup,
"""Get volume stats."""
return self.common.get_volume_stats(refresh)
- def copy_volume_data(self, context, src_vol, dest_vol, remote=None):
- """Copy data from src_vol to dest_vol.
-
- Call copy_volume_data() of super class and
- carry out original postprocessing.
- """
- super(HPEXPFCDriver, self).copy_volume_data(
- context, src_vol, dest_vol, remote)
- self.common.copy_volume_data(context, src_vol, dest_vol, remote)
-
def copy_image_to_volume(self, context, volume, image_service, image_id):
"""Fetch the image from image_service and write it to the volume.
self.common.copy_image_to_volume(
context, volume, image_service, image_id)
+ def after_volume_copy(self, context, src_vol, dest_vol, remote=None):
+ """Driver-specific actions after copyvolume data.
+
+ This method will be called after _copy_volume_data during volume
+ migration
+ """
+ self.common.copy_volume_data(context, src_vol, dest_vol, remote)
+
def restore_backup(self, context, backup, volume, backup_service):
"""Restore an existing backup to a new or existing volume.
try:
attachments = volume.volume_attachment
if not attachments:
+ # Pre- and post-copy driver-specific actions
+ self.driver.before_volume_copy(ctxt, volume, new_volume,
+ remote='dest')
self._copy_volume_data(ctxt, volume, new_volume, remote='dest')
+ self.driver.after_volume_copy(ctxt, volume, new_volume,
+ remote='dest')
+
# The above call is synchronous so we complete the migration
self.migrate_volume_completion(ctxt, volume.id,
new_volume.id,