The current migration process creates a new volume,
xfr's it's contents, then deletes the original and
modifies the new volume to have the previous ID.
All in all this is kinda troublesome, but regardless
the bigger problem is that the generic impl doesn't
provide any method to tell the backend devices that
their device names/id's have changed.
This patch provides a method to inform backends
that a migration operation has been completed on
their target volume.
It shouldn't be necessary to do anything with the originating
or source volume because it's deleted as part of the process.
Change-Id: Ib5e6a47fe9eedab3e60e77a6c2d987355c0bf167
Closes-Bug: #
1383499
lambda x, y, z, remote='dest': True)
self.stubs.Set(volume_rpcapi.VolumeAPI, 'delete_volume',
fake_delete_volume_rpc)
+ self.stubs.Set(volume_rpcapi.VolumeAPI,
+ 'update_migrated_volume',
+ lambda *args: self.volume.update_migrated_volume(
+ self.context,
+ args[1],
+ args[2]))
error_logs = []
LOG = logging.getLogger('cinder.volume.manager')
self.stubs.Set(LOG, 'error', lambda x: error_logs.append(x))
lambda *args: self.volume.attach_volume(args[1],
args[2]['id'],
*args[3:]))
+
+ self.stubs.Set(volume_rpcapi.VolumeAPI, 'update_migrated_volume',
+ lambda *args: self.volume.update_migrated_volume(
+ elevated,
+ args[1],
+ args[2]))
self.stubs.Set(self.volume.driver, 'attach_volume',
lambda *args, **kwargs: None)
"""
return False
+ def update_migrated_volume(self, ctxt, volume, new_volume):
+ """Return model update for migrated volume.
+
+ :param volume: The original volume that was migrated to this backend
+ :param new_volume: The migration volume object that was created on
+ this backend as part of the migration process
+ :return model_update to update DB with any needed changes
+ """
+ return None
+
class ISCSIDriver(VolumeDriver):
"""Executes commands relating to ISCSI volumes.
class VolumeManager(manager.SchedulerDependentManager):
"""Manages attachable block storage devices."""
- RPC_API_VERSION = '1.18'
+ RPC_API_VERSION = '1.19'
target = messaging.Target(version=RPC_API_VERSION)
return False
def init_host(self):
- """Do any initialization that needs to be run if this is a
- standalone service.
- """
+ """Perform any required initialization."""
ctxt = context.get_admin_context()
LOG.info(_("Starting volume driver %(driver_name)s (%(version)s)") %
remote='dest')
# The above call is synchronous so we complete the migration
self.migrate_volume_completion(ctxt, volume['id'],
- new_volume['id'], error=False)
+ new_volume['id'],
+ error=False)
else:
nova_api = compute.API()
# This is an async call to Nova, which will call the completion
msg = _("Failed to delete migration source vol %(vol)s: %(err)s")
LOG.error(msg % {'vol': volume_id, 'err': ex})
+ # Give driver (new_volume) a chance to update things as needed
+ # Note this needs to go through rpc to the host of the new volume
+ # the current host and driver object is for the "existing" volume
+ rpcapi.update_migrated_volume(ctxt,
+ volume,
+ new_volume)
self.db.finish_volume_migration(ctxt, volume_id, new_volume_id)
self.db.volume_destroy(ctxt, new_volume_id)
if status_update.get('status') == 'in-use':
self.db.volume_update(ctxt, volume_id, updates)
if 'in-use' in (status_update.get('status'), volume['status']):
+ # NOTE(jdg): if we're passing the ref here, why are we
+ # also passing in the various fields from that ref?
rpcapi.attach_volume(ctxt,
volume,
volume['instance_uuid'],
context, cgsnapshot_ref, "delete.end")
return True
+
+ def update_migrated_volume(self, ctxt, volume, new_volume):
+ """Finalize migration process on backend device."""
+
+ model_update = None
+ model_update = self.driver.update_migrated_volume(ctxt,
+ volume,
+ new_volume)
+ if model_update:
+ self.db.volume_update(ctxt.elevated(),
+ volume['id'],
+ model_update)
1.18 - Adds create_consistencygroup, delete_consistencygroup,
create_cgsnapshot, and delete_cgsnapshot. Also adds
the consistencygroup_id parameter in create_volume.
+ 1.19 - Adds update_migrated_volume
'''
BASE_RPC_API_VERSION = '1.0'
super(VolumeAPI, self).__init__()
target = messaging.Target(topic=CONF.volume_topic,
version=self.BASE_RPC_API_VERSION)
- self.client = rpc.get_client(target, '1.18')
+ self.client = rpc.get_client(target, '1.19')
def create_consistencygroup(self, ctxt, group, host):
new_host = utils.extract_host(host)
new_host = utils.extract_host(volume['host'])
cctxt = self.client.prepare(server=new_host, version='1.17')
cctxt.cast(ctxt, 'reenable_replication', volume_id=volume['id'])
+
+ def update_migrated_volume(self, ctxt, volume, new_volume):
+ host = utils.extract_host(new_volume['host'])
+ cctxt = self.client.prepare(server=host, version='1.19')
+ cctxt.call(ctxt,
+ 'update_migrated_volume',
+ volume=volume,
+ new_volume=new_volume)