From fb9ed782f1cac63a16f1f41f4e45c6c1031bff81 Mon Sep 17 00:00:00 2001 From: John Griffith Date: Fri, 20 Jan 2017 18:02:50 +0000 Subject: [PATCH] Add lock around SolidFire clone-image The SolidFire cluster has limits on simultaneous clones per volume, we've dealt with this pretty easily by just waiting if we encounter the limit (clones are pretty fast , so waiting for them isn't a terribly big deal). This process adds complications to the driver though and can be racey, so instead we should make this simple and just use Cinders lock mechanism. This change adds a lock decorator to the clone_image method just like we did for the clone_volume method. This elminates the need for various tracking and retry mechanisms when using image caching with SolidFire. Change-Id: Ia5a034ad578149ddae84ef2fe7e2be95f713072b Closes-Bug: #1658154 --- cinder/volume/drivers/solidfire.py | 57 ++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/cinder/volume/drivers/solidfire.py b/cinder/volume/drivers/solidfire.py index bf1819c0fc6..74f464d795e 100644 --- a/cinder/volume/drivers/solidfire.py +++ b/cinder/volume/drivers/solidfire.py @@ -231,6 +231,42 @@ class SolidFireDriver(san.SanISCSIDriver): if not self.failed_over_id: self._set_cluster_pairs() + def locked_image_id_operation(f, external=False): + def lvo_inner1(inst, *args, **kwargs): + lock_tag = inst.driver_prefix + call_args = inspect.getcallargs(f, inst, *args, **kwargs) + + if call_args.get('image_meta'): + image_id = call_args['image_meta']['id'] + else: + err_msg = _('The decorated method must accept image_meta.') + raise exception.VolumeBackendAPIException(data=err_msg) + + @utils.synchronized('%s-%s' % (lock_tag, image_id), + external=external) + def lvo_inner2(): + return f(inst, *args, **kwargs) + return lvo_inner2() + return lvo_inner1 + + def locked_source_uuid_operation(f, external=False): + def lvo_inner1(inst, *args, **kwargs): + lock_tag = inst.driver_prefix + call_args = inspect.getcallargs(f, inst, *args, **kwargs) + + if call_args.get('src_uuid'): + volume_id = call_args['src_uuid'] + else: + err_msg = _('The decorated method must accept src_uuid.') + raise exception.VolumeBackendAPIException(message=err_msg) + + @utils.synchronized('%s-%s' % (lock_tag, volume_id), + external=external) + def lvo_inner2(): + return f(inst, *args, **kwargs) + return lvo_inner2() + return lvo_inner1 + def __getattr__(self, attr): if hasattr(self.target_driver, attr): return getattr(self.target_driver, attr) @@ -588,10 +624,10 @@ class SolidFireDriver(san.SanISCSIDriver): is_clone = True return params, is_clone, sf_vol + @locked_source_uuid_operation def _do_clone_volume(self, src_uuid, vref, sf_src_snap=None): """Create a clone of an existing volume or snapshot.""" - attributes = {} sf_account = self._get_create_account(vref['project_id']) params = {'name': '%(prefix)s%(id)s' % @@ -799,24 +835,6 @@ class SolidFireDriver(san.SanISCSIDriver): return self._issue_api_request( 'ListSnapshots', params, version='6.0')['result']['snapshots'] - def locked_image_id_operation(f, external=False): - def lvo_inner1(inst, *args, **kwargs): - lock_tag = inst.driver_prefix - call_args = inspect.getcallargs(f, inst, *args, **kwargs) - - if call_args.get('image_meta'): - image_id = call_args['image_meta']['id'] - else: - err_msg = _('The decorated method must accept image_meta.') - raise exception.VolumeBackendAPIException(data=err_msg) - - @utils.synchronized('%s-%s' % (lock_tag, image_id), - external=external) - def lvo_inner2(): - return f(inst, *args, **kwargs) - return lvo_inner2() - return lvo_inner1 - def _create_image_volume(self, context, image_meta, image_service, image_id): @@ -1155,6 +1173,7 @@ class SolidFireDriver(san.SanISCSIDriver): def clone_image(self, context, volume, image_location, image_meta, image_service): + """Clone an existing image volume.""" public = False # Check out pre-requisites: # Is template caching enabled?