From: Geraint North Date: Mon, 21 Jul 2014 20:14:45 +0000 (+0100) Subject: Implements new 'bootable' option for manage existing volume. X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=65c10a3329dbf09b0c533a49157242f1a521f691;p=openstack-build%2Fcinder-build.git Implements new 'bootable' option for manage existing volume. A 'bootable' option (defaults to False) can be passed as part of the HTTP POST request to manage an existing volume. This is passed into the database when the volume is created. Also changes all drivers to use the more agnostic keys 'source-name' or 'source-id' when specifying which existing volume to manage. This change is to support the cinderclient change in https://review.openstack.org/#/c/76216/ to make it easier for CLI users to manage existing volumes without having to know the implementation-specifics of the driver. Change-Id: I58eaa75181fd0b606628584308fd83bdeaa8b64f Implements: blueprint add-export-import-volumes --- diff --git a/cinder/api/contrib/volume_manage.py b/cinder/api/contrib/volume_manage.py index b5bc35ed8..c9303c5a7 100755 --- a/cinder/api/contrib/volume_manage.py +++ b/cinder/api/contrib/volume_manage.py @@ -91,6 +91,7 @@ class VolumeManageController(wsgi.Controller): volume. availability_zone The availability zone to associate with the new volume. + bootable If set to True, marks the volume as bootable. """ context = req.environ['cinder.context'] authorize(context) @@ -134,7 +135,7 @@ class VolumeManageController(wsgi.Controller): kwargs['description'] = volume.get('description', None) kwargs['metadata'] = volume.get('metadata', None) kwargs['availability_zone'] = volume.get('availability_zone', None) - + kwargs['bootable'] = volume.get('bootable', False) try: new_volume = self.volume_api.manage_existing(context, volume['host'], diff --git a/cinder/tests/test_hp3par.py b/cinder/tests/test_hp3par.py index 45d81e8f1..e417587db 100644 --- a/cinder/tests/test_hp3par.py +++ b/cinder/tests/test_hp3par.py @@ -974,15 +974,15 @@ class HP3PARBaseDriver(object): unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) osv_matcher = self.driver.common._get_3par_vol_name(volume['id']) - existing_ref = {'name': unm_matcher} + existing_ref = {'source-name': unm_matcher} obj = self.driver.manage_existing(volume, existing_ref) expected_obj = {'display_name': 'Foo Volume'} expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), - mock.call.modifyVolume(existing_ref['name'], + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], {'newName': osv_matcher, 'comment': new_comment}), mock.call.logout() @@ -998,8 +998,8 @@ class HP3PARBaseDriver(object): expected_obj = {'display_name': 'Test Volume'} expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), - mock.call.modifyVolume(existing_ref['name'], + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], {'newName': osv_matcher, 'comment': new_comment}), mock.call.logout() @@ -1026,15 +1026,15 @@ class HP3PARBaseDriver(object): unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) osv_matcher = self.driver.common._get_3par_vol_name(volume['id']) - existing_ref = {'name': unm_matcher} + existing_ref = {'source-name': unm_matcher} obj = self.driver.manage_existing(volume, existing_ref) expected_obj = {'display_name': 'Foo Volume'} expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), - mock.call.modifyVolume(existing_ref['name'], + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], {'newName': osv_matcher, 'comment': new_comment}), mock.call.logout() @@ -1050,8 +1050,8 @@ class HP3PARBaseDriver(object): expected_obj = {'display_name': 'Test Volume'} expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), - mock.call.modifyVolume(existing_ref['name'], + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], {'newName': osv_matcher, 'comment': new_comment}), mock.call.logout() @@ -1068,8 +1068,8 @@ class HP3PARBaseDriver(object): expected_obj = {'display_name': None} expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), - mock.call.modifyVolume(existing_ref['name'], + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], {'newName': osv_matcher, 'comment': new_comment}), mock.call.logout() @@ -1088,7 +1088,7 @@ class HP3PARBaseDriver(object): mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound('fake') unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - existing_ref = {'name': unm_matcher} + existing_ref = {'source-name': unm_matcher} self.assertRaises(exception.InvalidInput, self.driver.manage_existing, @@ -1097,7 +1097,7 @@ class HP3PARBaseDriver(object): expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), + mock.call.getVolume(existing_ref['source-name']), mock.call.logout() ] @@ -1116,7 +1116,7 @@ class HP3PARBaseDriver(object): mock_client.getVolume.return_value = {'comment': comment} unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - existing_ref = {'name': unm_matcher} + existing_ref = {'source-name': unm_matcher} self.assertRaises(exception.ManageExistingVolumeTypeMismatch, self.driver.manage_existing, @@ -1125,7 +1125,7 @@ class HP3PARBaseDriver(object): expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), + mock.call.getVolume(existing_ref['source-name']), mock.call.logout() ] @@ -1137,14 +1137,14 @@ class HP3PARBaseDriver(object): unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) volume = {} - existing_ref = {'name': unm_matcher} + existing_ref = {'source-name': unm_matcher} size = self.driver.manage_existing_get_size(volume, existing_ref) expected_size = 2 expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), + mock.call.getVolume(existing_ref['source-name']), mock.call.logout() ] @@ -1154,7 +1154,7 @@ class HP3PARBaseDriver(object): def test_manage_existing_get_size_invalid_reference(self): mock_client = self.setup_driver() volume = {} - existing_ref = {'name': self.VOLUME_3PAR_NAME} + existing_ref = {'source-name': self.VOLUME_3PAR_NAME} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing_get_size, @@ -1188,7 +1188,7 @@ class HP3PARBaseDriver(object): unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) volume = {} - existing_ref = {'name': unm_matcher} + existing_ref = {'source-name': unm_matcher} self.assertRaises(exception.InvalidInput, self.driver.manage_existing_get_size, @@ -1197,7 +1197,7 @@ class HP3PARBaseDriver(object): expected = [ mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['name']), + mock.call.getVolume(existing_ref['source-name']), mock.call.logout() ] diff --git a/cinder/tests/test_ibm_xiv_ds8k.py b/cinder/tests/test_ibm_xiv_ds8k.py index 26b90112b..e6bd12ab5 100644 --- a/cinder/tests/test_ibm_xiv_ds8k.py +++ b/cinder/tests/test_ibm_xiv_ds8k.py @@ -80,12 +80,12 @@ class XIVDS8KFakeProxyDriver(object): del self.volumes[volume['name']] def manage_volume_get_size(self, volume, existing_ref): - if self.volumes.get(existing_ref['existing_ref'], None) is None: + if self.volumes.get(existing_ref['source-name'], None) is None: raise self.exception.VolumeNotFound(volume_id=volume['id']) - return self.volumes[existing_ref['existing_ref']]['size'] + return self.volumes[existing_ref['source-name']]['size'] def manage_volume(self, volume, existing_ref): - if self.volumes.get(existing_ref['existing_ref'], None) is None: + if self.volumes.get(existing_ref['source-name'], None) is None: raise self.exception.VolumeNotFound(volume_id=volume['id']) volume['size'] = MANAGED_VOLUME['size'] return {} @@ -273,7 +273,7 @@ class XIVDS8KVolumeDriverTest(test.TestCase): self.driver.do_setup(None) self.driver.create_volume(MANAGED_VOLUME) - existing_ref = {'existing_ref': MANAGED_VOLUME['name']} + existing_ref = {'source-name': MANAGED_VOLUME['name']} return_size = self.driver.manage_existing_get_size( VOLUME, existing_ref) @@ -288,7 +288,7 @@ class XIVDS8KVolumeDriverTest(test.TestCase): self.driver.do_setup(None) # on purpose - do NOT create managed volume - existing_ref = {'existing_ref': MANAGED_VOLUME['name']} + existing_ref = {'source-name': MANAGED_VOLUME['name']} self.assertRaises(exception.VolumeNotFound, self.driver.manage_existing_get_size, VOLUME, @@ -299,7 +299,7 @@ class XIVDS8KVolumeDriverTest(test.TestCase): self.driver.do_setup(None) self.driver.create_volume(MANAGED_VOLUME) - existing_ref = {'existing_ref': MANAGED_VOLUME['name']} + existing_ref = {'source-name': MANAGED_VOLUME['name']} has_volume = self.driver.manage_existing( VOLUME, existing_ref) @@ -314,7 +314,7 @@ class XIVDS8KVolumeDriverTest(test.TestCase): self.driver.do_setup(None) # on purpose - do NOT create managed volume - existing_ref = {'existing_ref': MANAGED_VOLUME['name']} + existing_ref = {'source-name': MANAGED_VOLUME['name']} self.assertRaises(exception.VolumeNotFound, self.driver.manage_existing, VOLUME, diff --git a/cinder/tests/test_rbd.py b/cinder/tests/test_rbd.py index 0e572206f..6d8902a1d 100644 --- a/cinder/tests/test_rbd.py +++ b/cinder/tests/test_rbd.py @@ -188,7 +188,7 @@ class RBDTestCase(test.TestCase): with mock.patch.object(self.driver.rbd.Image, 'close') \ as mock_rbd_image_close: mock_rbd_image_size.return_value = 2 * units.Gi - existing_ref = {'rbd_name': self.volume_name} + existing_ref = {'source-name': self.volume_name} return_size = self.driver.manage_existing_get_size( self.volume, existing_ref) @@ -204,7 +204,7 @@ class RBDTestCase(test.TestCase): with mock.patch.object(self.driver.rbd.Image, 'close') \ as mock_rbd_image_close: mock_rbd_image_size.return_value = 'abcd' - existing_ref = {'rbd_name': self.volume_name} + existing_ref = {'source-name': self.volume_name} self.assertRaises(exception.VolumeBackendAPIException, self.driver.manage_existing_get_size, self.volume, existing_ref) @@ -221,7 +221,7 @@ class RBDTestCase(test.TestCase): with mock.patch.object(self.driver.rbd.RBD(), 'rename') as \ mock_rbd_image_rename: exist_volume = 'vol-exist' - existing_ref = {'rbd_name': exist_volume} + existing_ref = {'source-name': exist_volume} mock_rbd_image_rename.return_value = 0 mock_rbd_image_rename(mock_rados_client.ioctx, exist_volume, @@ -242,7 +242,7 @@ class RBDTestCase(test.TestCase): MockImageExistsException exist_volume = 'vol-exist' - existing_ref = {'rbd_name': exist_volume} + existing_ref = {'source-name': exist_volume} self.assertRaises(self.mock_rbd.ImageExists, self.driver.manage_existing, self.volume, existing_ref) diff --git a/cinder/tests/test_storwize_svc.py b/cinder/tests/test_storwize_svc.py index 6ac863ecd..abcc47ec0 100644 --- a/cinder/tests/test_storwize_svc.py +++ b/cinder/tests/test_storwize_svc.py @@ -2610,7 +2610,7 @@ class StorwizeSVCDriverTestCase(test.TestCase): def test_manage_existing_bad_uid(self): """Error when the specified UUID does not exist.""" volume = self._generate_vol_info(None, None) - ref = {'vdisk_UID': 'bad_uid'} + ref = {'source-id': 'bad_uid'} self.assertRaises(exception.ManageExistingInvalidReference, self.driver.manage_existing_get_size, volume, ref) pass @@ -2633,7 +2633,7 @@ class StorwizeSVCDriverTestCase(test.TestCase): new_volume = self._generate_vol_info(None, None) # Submit the request to manage it. - ref = {'vdisk_UID': uid} + ref = {'source-id': uid} size = self.driver.manage_existing_get_size(new_volume, ref) self.assertEqual(size, 10) self.driver.manage_existing(new_volume, ref) @@ -2663,7 +2663,7 @@ class StorwizeSVCDriverTestCase(test.TestCase): # Descriptor of the Cinder volume that we want to own the vdisk # refrerenced by uid. volume = self._generate_vol_info(None, None) - ref = {'vdisk_UID': uid} + ref = {'source-id': uid} # Attempt to manage this disk, and except an exception beause the # volume is already mapped. @@ -2694,7 +2694,7 @@ class StorwizeSVCDriverTestCase(test.TestCase): # Submit the request to manage it, specifying that it is OK to # manage a volume that is already attached. - ref = {'vdisk_UID': uid, 'manage_if_in_use': True} + ref = {'source-id': uid, 'manage_if_in_use': True} size = self.driver.manage_existing_get_size(new_volume, ref) self.assertEqual(size, 10) self.driver.manage_existing(new_volume, ref) diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index de59d1789..57851d4ec 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -3282,11 +3282,11 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase): """ self._setup_stubs_for_manage_existing() - ref = {'lv_name': 'fake_lv'} + ref = {'source-name': 'fake_lv'} vol = {'name': 'test', 'id': 1, 'size': 0} def _rename_volume(old_name, new_name): - self.assertEqual(old_name, ref['lv_name']) + self.assertEqual(old_name, ref['source-name']) self.assertEqual(new_name, vol['name']) self.stubs.Set(self.volume.driver.vg, 'rename_volume', @@ -3306,7 +3306,7 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase): """ self._setup_stubs_for_manage_existing() - ref = {'lv_name': 'fake_lv_bad_size'} + ref = {'source-name': 'fake_lv_bad_size'} vol = {'name': 'test', 'id': 1, 'size': 2} self.assertRaises(exception.VolumeBackendAPIException, @@ -3321,7 +3321,7 @@ class LVMISCSIVolumeDriverTestCase(DriverTestCase): """ self._setup_stubs_for_manage_existing() - ref = {'lv_name': 'fake_nonexistent_lv'} + ref = {'source-name': 'fake_nonexistent_lv'} vol = {'name': 'test', 'id': 1, 'size': 0, 'status': 'available'} self.assertRaises(exception.ManageExistingInvalidReference, diff --git a/cinder/volume/api.py b/cinder/volume/api.py index 17ba2e360..89355772e 100644 --- a/cinder/volume/api.py +++ b/cinder/volume/api.py @@ -994,7 +994,7 @@ class API(base.Base): def manage_existing(self, context, host, ref, name=None, description=None, volume_type=None, metadata=None, - availability_zone=None): + availability_zone=None, bootable=False): if availability_zone is None: elevated = context.elevated() try: @@ -1018,7 +1018,8 @@ class API(base.Base): 'host': host, 'availability_zone': availability_zone, 'volume_type_id': volume_type_id, - 'metadata': metadata + 'metadata': metadata, + 'bootable': bootable } # Call the scheduler to ensure that the host exists and that it can diff --git a/cinder/volume/drivers/ibm/storwize_svc/__init__.py b/cinder/volume/drivers/ibm/storwize_svc/__init__.py index 70659626c..008c77367 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/__init__.py +++ b/cinder/volume/drivers/ibm/storwize_svc/__init__.py @@ -822,18 +822,19 @@ class StorwizeSVCDriver(san.SanDriver): if we got here then we have a vdisk that isn't in use (or we don't care if it is in use. """ - vdisk = self._helpers.vdisk_by_uid(ref['vdisk_UID']) + vdisk = self._helpers.vdisk_by_uid(ref['source-id']) if vdisk is None: - reason = _('No vdisk with the specified vdisk_UID.') + reason = (_('No vdisk with the UID specified by source-id %s.') + % ref['source-id']) raise exception.ManageExistingInvalidReference(existing_ref=ref, reason=reason) self._helpers.rename_vdisk(vdisk['name'], volume['name']) def manage_existing_get_size(self, volume, ref): - """Return size of an existing LV for manage_existing. + """Return size of an existing Vdisk for manage_existing. existing_ref is a dictionary of the form: - {'vdisk_UID': } + {'source-id': } Optional elements are: 'manage_if_in_use': True/False (default is False) @@ -842,15 +843,16 @@ class StorwizeSVCDriver(san.SanDriver): """ # Check that the reference is valid - if 'vdisk_UID' not in ref: - reason = _('Reference must contain vdisk_UID element.') + if 'source-id' not in ref: + reason = _('Reference must contain source-id element.') raise exception.ManageExistingInvalidReference(existing_ref=ref, reason=reason) # Check for existence of the vdisk - vdisk = self._helpers.vdisk_by_uid(ref['vdisk_UID']) + vdisk = self._helpers.vdisk_by_uid(ref['source-id']) if vdisk is None: - reason = _('No vdisk with the specified vdisk_UID.') + reason = (_('No vdisk with the UID specified by source-id %s.') + % (ref['source-id'])) raise exception.ManageExistingInvalidReference(existing_ref=ref, reason=reason) diff --git a/cinder/volume/drivers/lvm.py b/cinder/volume/drivers/lvm.py index a53c93352..326c13ef4 100644 --- a/cinder/volume/drivers/lvm.py +++ b/cinder/volume/drivers/lvm.py @@ -395,7 +395,7 @@ class LVMVolumeDriver(driver.VolumeDriver): Renames the LV to match the expected name for the volume. Error checking done by manage_existing_get_size is not repeated. """ - lv_name = existing_ref['lv_name'] + lv_name = existing_ref['source-name'] self.vg.get_volume(lv_name) # Attempt to rename the LV to match the OpenStack internal name. @@ -413,15 +413,15 @@ class LVMVolumeDriver(driver.VolumeDriver): """Return size of an existing LV for manage_existing. existing_ref is a dictionary of the form: - {'lv_name': } + {'source-name': } """ # Check that the reference is valid - if 'lv_name' not in existing_ref: - reason = _('Reference must contain lv_name element.') + if 'source-name' not in existing_ref: + reason = _('Reference must contain source-name element.') raise exception.ManageExistingInvalidReference( existing_ref=existing_ref, reason=reason) - lv_name = existing_ref['lv_name'] + lv_name = existing_ref['source-name'] lv = self.vg.get_volume(lv_name) # Raise an exception if we didn't find a suitable LV. diff --git a/cinder/volume/drivers/rbd.py b/cinder/volume/drivers/rbd.py index a2d28c525..827675c12 100644 --- a/cinder/volume/drivers/rbd.py +++ b/cinder/volume/drivers/rbd.py @@ -883,11 +883,11 @@ class RBDDriver(driver.VolumeDriver): volume ref info to be set :param existing_ref: existing_ref is a dictionary of the form: - {'rbd_name': } + {'source-name': } """ # Raise an exception if we didn't find a suitable rbd image. with RADOSClient(self) as client: - rbd_name = existing_ref['rbd_name'] + rbd_name = existing_ref['source-name'] self.rbd.RBD().rename(client.ioctx, strutils.safe_encode(rbd_name), strutils.safe_encode(volume['name'])) @@ -898,16 +898,16 @@ class RBDDriver(driver.VolumeDriver): volume ref info to be set :param existing_ref: existing_ref is a dictionary of the form: - {'rbd_name': } + {'source-name': } """ # Check that the reference is valid - if 'rbd_name' not in existing_ref: - reason = _('Reference must contain rbd_name element.') + if 'source-name' not in existing_ref: + reason = _('Reference must contain source-name element.') raise exception.ManageExistingInvalidReference( existing_ref=existing_ref, reason=reason) - rbd_name = strutils.safe_encode(existing_ref['rbd_name']) + rbd_name = strutils.safe_encode(existing_ref['source-name']) with RADOSClient(self) as client: # Raise an exception if we didn't find a suitable rbd image. diff --git a/cinder/volume/drivers/san/hp/hp_3par_common.py b/cinder/volume/drivers/san/hp/hp_3par_common.py index 48cd899af..23d23f7b6 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_common.py +++ b/cinder/volume/drivers/san/hp/hp_3par_common.py @@ -132,10 +132,11 @@ class HP3PARCommon(object): 2.0.11 - Remove hp3parclient requirement from unit tests #1315195 2.0.12 - Volume detach hangs when host is in a host set bug #1317134 2.0.13 - Added support for managing/unmanaging of volumes + 2.0.14 - Modified manage volume to use standard 'source-name' element. """ - VERSION = "2.0.13" + VERSION = "2.0.14" stats = {} @@ -271,13 +272,17 @@ class HP3PARCommon(object): self._extend_volume(volume, volume_name, growth_size_mib) def manage_existing(self, volume, existing_ref): - """Manage an existing 3PAR volume.""" + """Manage an existing 3PAR volume. + + existing_ref is a dictionary of the form: + {'source-name': } + """ # Check for the existence of the virtual volume. try: - vol = self.client.getVolume(existing_ref['name']) + vol = self.client.getVolume(existing_ref['source-name']) except hpexceptions.HTTPNotFound: err = (_("Virtual volume '%s' doesn't exist on array.") % - existing_ref['name']) + existing_ref['source-name']) LOG.error(err) raise exception.InvalidInput(reason=err) @@ -323,12 +328,12 @@ class HP3PARCommon(object): new_comment['qos'] = settings['qos'] # Update the existing volume with the new name and comments. - self.client.modifyVolume(existing_ref['name'], + self.client.modifyVolume(existing_ref['source-name'], {'newName': new_vol_name, 'comment': json.dumps(new_comment)}) LOG.info(_("Virtual volume '%(ref)s' renamed to '%(new)s'.") % - {'ref': existing_ref['name'], 'new': new_vol_name}) + {'ref': existing_ref['source-name'], 'new': new_vol_name}) LOG.info(_("Virtual volume %(disp)s '%(new)s' is now being managed.") % {'disp': display_name, 'new': new_vol_name}) @@ -339,17 +344,17 @@ class HP3PARCommon(object): """Return size of volume to be managed by manage_existing. existing_ref is a dictionary of the form: - {'name': } + {'source-name': } """ # Check that a valid reference was provided. - if 'name' not in existing_ref: - reason = _("Reference must contain name element.") + if 'source-name' not in existing_ref: + reason = _("Reference must contain source-name element.") raise exception.ManageExistingInvalidReference( existing_ref=existing_ref, reason=reason) # Make sure the reference is not in use. - if re.match('osv-*|oss-*|vvs-*', existing_ref['name']): + if re.match('osv-*|oss-*|vvs-*', existing_ref['source-name']): reason = _("Reference must be for an unmanaged virtual volume.") raise exception.ManageExistingInvalidReference( existing_ref=existing_ref, @@ -357,10 +362,10 @@ class HP3PARCommon(object): # Check for the existence of the virtual volume. try: - vol = self.client.getVolume(existing_ref['name']) + vol = self.client.getVolume(existing_ref['source-name']) except hpexceptions.HTTPNotFound: err = (_("Virtual volume '%s' doesn't exist on array.") % - existing_ref['name']) + existing_ref['source-name']) LOG.error(err) raise exception.InvalidInput(reason=err)