From 30baa03ff4c7a8a94788901645019c8fca8ae4b8 Mon Sep 17 00:00:00 2001 From: Tom Swanson Date: Tue, 25 Apr 2017 15:36:05 -0500 Subject: [PATCH] Dell EMC SC: Terminate_connection chokes on None. Terminate_connection called with a connector set to None should break all connections to a volume. Change-Id: Ib3906c96ba2c4b76d4a879376208d9907d211f55 --- .../volume/drivers/dell_emc/sc/test_sc.py | 19 ++--- .../drivers/dell_emc/sc/storagecenter_api.py | 6 ++ .../drivers/dell_emc/sc/storagecenter_fc.py | 79 ++++++++++++------- .../dell_emc/sc/storagecenter_iscsi.py | 33 +++++--- 4 files changed, 87 insertions(+), 50 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/dell_emc/sc/test_sc.py b/cinder/tests/unit/volume/drivers/dell_emc/sc/test_sc.py index 1bf3c54898c..4854dea99ae 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/sc/test_sc.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/sc/test_sc.py @@ -1386,28 +1386,25 @@ class DellSCSanISCSIDriverTestCase(test.TestCase): self.assertIsNone(res, 'None expected') self.assertTrue(mock_terminate_secondary.called) - @mock.patch.object(storagecenter_api.SCApi, - 'find_server', - return_value=None) @mock.patch.object(storagecenter_api.SCApi, 'find_volume', return_value=VOLUME) @mock.patch.object(storagecenter_api.SCApi, - 'unmap_volume', + 'unmap_all', return_value=True) def test_terminate_connection_no_server(self, - mock_unmap_volume, + mock_unmap_all, mock_find_volume, - mock_find_server, mock_close_connection, mock_open_connection, mock_init): - volume = {'id': fake.VOLUME_ID} + volume = {'id': fake.VOLUME_ID, 'provider_id': '101.101'} connector = {'initiator': ''} - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.terminate_connection, - volume, - connector) + res = self.driver.terminate_connection(volume, connector) + mock_find_volume.assert_called_once_with(fake.VOLUME_ID, '101.101', + False) + mock_unmap_all.assert_called_once_with(self.VOLUME) + self.assertIsNone(res) @mock.patch.object(storagecenter_api.SCApi, 'find_server', diff --git a/cinder/volume/drivers/dell_emc/sc/storagecenter_api.py b/cinder/volume/drivers/dell_emc/sc/storagecenter_api.py index 21813ac3705..903c5cf0ead 100644 --- a/cinder/volume/drivers/dell_emc/sc/storagecenter_api.py +++ b/cinder/volume/drivers/dell_emc/sc/storagecenter_api.py @@ -1972,6 +1972,12 @@ class SCApi(object): # return true/false. return rtn + def unmap_all(self, scvolume): + volumeid = self._get_id(scvolume) + r = self.client.post('StorageCenter/ScVolume/%s/Unmap' % volumeid, + {}, True) + return self._check_result(r) + def get_storage_usage(self): """Gets the storage usage object from the Dell backend. diff --git a/cinder/volume/drivers/dell_emc/sc/storagecenter_fc.py b/cinder/volume/drivers/dell_emc/sc/storagecenter_fc.py index 1105ac1c9c7..191c40b0da3 100644 --- a/cinder/volume/drivers/dell_emc/sc/storagecenter_fc.py +++ b/cinder/volume/drivers/dell_emc/sc/storagecenter_fc.py @@ -75,6 +75,14 @@ class SCFCDriver(storagecenter_common.SCCommonDriver, self.configuration.safe_get('volume_backend_name') or 'Dell-FC' self.storage_protocol = 'FC' + def validate_connector(self, connector): + """Fail if connector doesn't contain all the data needed by driver. + + Do a check on the connector and ensure that it has wwnns, wwpns. + """ + self.validate_connector_has_setting(connector, 'wwpns') + self.validate_connector_has_setting(connector, 'wwnns') + @fczm_utils.add_fc_zone def initialize_connection(self, volume, connector): """Initializes the connection and returns connection info. @@ -155,7 +163,8 @@ class SCFCDriver(storagecenter_common.SCCommonDriver, LOG.error('Failed to initialize connection.') # We get here because our mapping is none so blow up. - raise exception.VolumeBackendAPIException(_('Unable to map volume.')) + raise exception.VolumeBackendAPIException( + data=_('Unable to map volume.')) def _find_server(self, api, wwns, ssn=-1): for wwn in wwns: @@ -196,20 +205,22 @@ class SCFCDriver(storagecenter_common.SCCommonDriver, @fczm_utils.remove_fc_zone def terminate_connection(self, volume, connector, force=False, **kwargs): - # Get our volume name + # Grab some quick info. volume_name = volume.get('id') provider_id = volume.get('provider_id') - islivevol = self._is_live_vol(volume) LOG.debug('Terminate connection: %s', volume_name) + with self._client.open_connection() as api: try: - wwpns = connector.get('wwpns') + wwpns = [] if not connector else connector.get('wwpns', []) # Find the volume on the storage center. + islivevol = self._is_live_vol(volume) scvolume = api.find_volume(volume_name, provider_id, islivevol) if scvolume: # Get the SSN it is on. ssn = scvolume['instanceId'].split('.')[0] + # Will be None if we have no wwpns. scserver = self._find_server(api, wwpns, ssn) # Get our target map so we can return it to free up a zone. @@ -232,14 +243,14 @@ class SCFCDriver(storagecenter_common.SCCommonDriver, targets += lvtargets init_targ_map.update(lvinit_targ_map) - # If we have a server and a volume lets unmap them. - if (scserver is not None and - scvolume is not None and + if (wwpns and scserver and api.unmap_volume(scvolume, scserver) is True): LOG.debug('Connection terminated') + elif not wwpns and api.unmap_all(scvolume): + LOG.debug('All connections terminated') else: raise exception.VolumeBackendAPIException( - _('Terminate connection failed')) + data=_('Terminate connection failed')) # basic return info... info = {'driver_volume_type': 'fibre_channel', @@ -247,7 +258,7 @@ class SCFCDriver(storagecenter_common.SCCommonDriver, # if not then we return the target map so that # the zone can be freed up. - if api.get_volume_count(scserver) == 0: + if scserver and api.get_volume_count(scserver) == 0: info['data'] = {'target_wwn': targets, 'initiator_target_map': init_targ_map} return info @@ -256,25 +267,39 @@ class SCFCDriver(storagecenter_common.SCCommonDriver, with excutils.save_and_reraise_exception(): LOG.error('Failed to terminate connection') raise exception.VolumeBackendAPIException( - _('Terminate connection unable to connect to backend.')) + data=_('Terminate connection unable to connect to backend.')) def terminate_secondary(self, api, sclivevolume, wwns): - # Find our server. - secondary = self._find_server( - api, wwns, sclivevolume['secondaryScSerialNumber']) + lun = None + targets = [] + init_targ_map = {} + # Get our volume. secondaryvol = api.get_volume( sclivevolume['secondaryVolume']['instanceId']) - if secondary and secondaryvol: - # Get our map. - lun, targets, init_targ_map = api.find_wwns(secondaryvol, - secondary) - # If we have a server and a volume lets unmap them. - ret = api.unmap_volume(secondaryvol, secondary) - LOG.debug('terminate_secondary: secondary volume %(name)s unmap ' - 'to secondary server %(server)s result: %(result)r', - {'name': secondaryvol['name'], - 'server': secondary['name'], - 'result': ret}) - # return info for - return lun, targets, init_targ_map - return None, [], {} + # We have one so let's get to work. + if secondaryvol: + # Are we unmapping a specific server? + if wwns: + # Find our server. + secondary = self._find_server( + api, wwns, sclivevolume['secondaryScSerialNumber']) + # Get our map. + lun, targets, init_targ_map = api.find_wwns(secondaryvol, + secondary) + # If we have a server and a volume lets unmap them. + ret = api.unmap_volume(secondaryvol, secondary) + LOG.debug('terminate_secondary: ' + 'secondary volume %(name)s unmap ' + 'to secondary server %(server)s result: %(result)r', + {'name': secondaryvol['name'], + 'server': secondary['name'], 'result': ret}) + else: + # Just unmap all. + ret = api.unmap_all(secondaryvol) + LOG.debug('terminate_secondary: secondary volume %(name)s ' + 'unmap all result: %(result)r', + {'name': secondaryvol['name'], 'result': ret}) + else: + LOG.debug('terminate_secondary: secondary volume not found.') + # return info if any + return lun, targets, init_targ_map diff --git a/cinder/volume/drivers/dell_emc/sc/storagecenter_iscsi.py b/cinder/volume/drivers/dell_emc/sc/storagecenter_iscsi.py index 5d99bba1ab0..994bd523542 100644 --- a/cinder/volume/drivers/dell_emc/sc/storagecenter_iscsi.py +++ b/cinder/volume/drivers/dell_emc/sc/storagecenter_iscsi.py @@ -220,25 +220,24 @@ class SCISCSIDriver(storagecenter_common.SCCommonDriver, return data def terminate_connection(self, volume, connector, force=False, **kwargs): - # Grab some initial info. - initiator_name = connector.get('initiator') + # Grab some quick info. volume_name = volume.get('id') provider_id = volume.get('provider_id') - islivevol = self._is_live_vol(volume) + initiator_name = None if not connector else connector.get('initiator') LOG.debug('Terminate connection: %(vol)s:%(initiator)s', {'vol': volume_name, 'initiator': initiator_name}) + with self._client.open_connection() as api: try: # Find the volume on the storage center. Note that if this # is live volume and we are swapped this will be the back # half of the live volume. + islivevol = self._is_live_vol(volume) scvolume = api.find_volume(volume_name, provider_id, islivevol) if scvolume: # Get the SSN it is on. ssn = scvolume['instanceId'].split('.')[0] - # Find our server. - scserver = api.find_server(initiator_name, ssn) # Unmap our secondary if not failed over.. if islivevol: @@ -249,10 +248,14 @@ class SCISCSIDriver(storagecenter_common.SCCommonDriver, self.terminate_secondary(api, sclivevolume, initiator_name) + # Find our server. + scserver = (None if not initiator_name else + api.find_server(initiator_name, ssn)) + # If we have a server and a volume lets pull them apart. - if (scserver is not None and - scvolume is not None and - api.unmap_volume(scvolume, scserver) is True): + if ((scserver and + api.unmap_volume(scvolume, scserver) is True) or + (not scserver and api.unmap_all(scvolume))): LOG.debug('Connection terminated') return except Exception: @@ -265,9 +268,15 @@ class SCISCSIDriver(storagecenter_common.SCCommonDriver, _('Terminate connection failed')) def terminate_secondary(self, api, sclivevolume, initiatorname): - # Find our server. - secondary = api.find_server(initiatorname, - sclivevolume['secondaryScSerialNumber']) secondaryvol = api.get_volume( sclivevolume['secondaryVolume']['instanceId']) - return api.unmap_volume(secondaryvol, secondary) + if secondaryvol: + if initiatorname: + # Find our server. + secondary = api.find_server( + initiatorname, sclivevolume['secondaryScSerialNumber']) + return api.unmap_volume(secondaryvol, secondary) + else: + return api.unmap_all(secondaryvol) + else: + LOG.debug('terminate_secondary: secondary volume not found.')