From 5503c0f4d204efb141cf39a183d0bcadf7892505 Mon Sep 17 00:00:00 2001 From: John Cates Date: Wed, 11 Oct 2017 17:07:00 -0500 Subject: [PATCH] FlashSystem: Add CMMVC6045E CLI error for multi-host mapping Add CMMVC6045E to list of CLI error codes indicating a multi-host map was attempted without the '-force' parameter. Re-raise on match failure to avert spurious success when an unexcepted CLI error is emitted. Add good and bad-path testing for multi-host map to test_flashsystem_map_vdisk_to_host Change-Id: I4b8d99966f387f51c1125ba72c1ce149229a8bc0 Closes-Bug: #1723188 (cherry picked from commit 421837ab4a3244022ad8292ad1ef0de37e9de532) --- .../drivers/ibm/test_ibm_flashsystem.py | 45 ++++++++++++++++--- .../volume/drivers/ibm/flashsystem_common.py | 40 ++++++++++++++--- 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/ibm/test_ibm_flashsystem.py b/cinder/tests/unit/volume/drivers/ibm/test_ibm_flashsystem.py index c27727a83e5..a9eccc0ab9c 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_ibm_flashsystem.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_ibm_flashsystem.py @@ -51,8 +51,15 @@ class FlashSystemManagementSimulator(object): # CMMVC50000 is a fake error which indicates that command has not # got expected results. This error represents kinds of CLI errors. 'CMMVC50000': ('', 'CMMVC50000 The command can not be executed ' - 'successfully.') + 'successfully.'), + 'CMMVC6045E': ('', 'CMMVC6045E The action failed, as the ' + '-force flag was not entered.'), + 'CMMVC6071E': ('', 'CMMVC6071E The VDisk-to-host mapping ' + 'was not created because the VDisk is ' + 'already mapped to a host.') } + self._multi_host_map_error = None + self._multi_host_map_errors = ['CMMVC6045E', 'CMMVC6071E'] @staticmethod def _find_unused_id(d): @@ -94,10 +101,9 @@ class FlashSystemManagementSimulator(object): if arg_list[0] not in ('svcinfo', 'svctask') or len(arg_list) < 2: raise exception.InvalidInput(reason=six.text_type(arg_list)) ret = {'cmd': arg_list[1]} - arg_list.pop(0) skip = False - for i in range(1, len(arg_list)): + for i in range(2, len(arg_list)): if skip: skip = False continue @@ -612,15 +618,14 @@ class FlashSystemManagementSimulator(object): if mapping_info['host'] not in self._hosts_list: return self._errors['CMMVC50000'] - if mapping_info['vol'] in self._mappings_list: - return self._errors['CMMVC50000'] - for v in self._mappings_list.values(): + if (v['vol'] == mapping_info['vol']) and ('force' not in kwargs): + return self._errors[self._multi_host_map_error or 'CMMVC50000'] + if ((v['host'] == mapping_info['host']) and (v['lun'] == mapping_info['lun'])): return self._errors['CMMVC50000'] - for v in self._mappings_list.values(): if (v['lun'] == mapping_info['lun']) and ('force' not in kwargs): return self._errors['CMMVC50000'] @@ -748,6 +753,13 @@ class FlashSystemDriverTestCase(test.TestCase): 'wwpns': ['abcd000000000001', 'abcd000000000002'], 'initiator': 'iqn.123456'} + self.alt_connector = { + 'host': 'other', + 'wwnns': ['0123456789fedcba', '0123456789badcfe'], + 'wwpns': ['dcba000000000001', 'dcba000000000002'], + 'initiator': 'iqn.654321' + } + self.sim = FlashSystemManagementSimulator() self.driver = FlashSystemFakeDriver( configuration=conf.Configuration(None)) @@ -1282,6 +1294,25 @@ class FlashSystemDriverTestCase(test.TestCase): 1, self.driver._map_vdisk_to_host(vol1['name'], self.connector)) + # case 4: multi-host mapping, good path + for error in self.sim._multi_host_map_errors: + self.sim._multi_host_map_error = error + self.assertEqual( + 1, + self.driver._map_vdisk_to_host( + vol1['name'], self.alt_connector + ) + ) + self.driver._unmap_vdisk_from_host( + vol1['name'], self.alt_connector + ) + self.sim._multi_host_map_error = None + + # case 5: multi-host mapping, bad path + self.assertRaises( + exception.VolumeBackendAPIException, + self.driver._map_vdisk_to_host, vol1['name'], self.alt_connector) + # clean environment self.driver._unmap_vdisk_from_host(vol1['name'], self.connector) self.driver._unmap_vdisk_from_host(vol2['name'], self.connector) diff --git a/cinder/volume/drivers/ibm/flashsystem_common.py b/cinder/volume/drivers/ibm/flashsystem_common.py index 45dc55901fb..ea46da46804 100644 --- a/cinder/volume/drivers/ibm/flashsystem_common.py +++ b/cinder/volume/drivers/ibm/flashsystem_common.py @@ -96,6 +96,8 @@ class FlashSystemDriver(san.SanDriver, VERSION = "1.0.12" + MULTI_HOST_MAP_ERRORS = ['CMMVC6045E', 'CMMVC6071E'] + def __init__(self, *args, **kwargs): super(FlashSystemDriver, self).__init__(*args, **kwargs) self.configuration.append_config_values(flashsystem_opts) @@ -726,6 +728,27 @@ class FlashSystemDriver(san.SanDriver, existing_ref=existing_ref, reason=reason) return vdisk + def _cli_except(self, fun, cmd, out, err, exc_list): + """Raise if stderr contains an unexpected error code""" + if not err: + return None + if not isinstance(exc_list, (tuple, list)): + exc_list = [exc_list] + + try: + err_type = [e for e in exc_list + if err.startswith(e)].pop() + except IndexError: + msg = _( + '%(fun)s: encountered unexpected CLI error, ' + 'expected one of: %(errors)s' + ) % {'fun': fun, + 'errors': ', '.join(exc_list)} + LOG.error(msg) + raise exception.VolumeBackendAPIException(data=msg) + + return {'code': err_type, 'message': err.strip(err_type).strip()} + @utils.synchronized('flashsystem-map', external=True) def _map_vdisk_to_host(self, vdisk_name, connector): """Create a mapping between a vdisk to a host.""" @@ -752,13 +775,18 @@ class FlashSystemDriver(san.SanDriver, ssh_cmd = ['svctask', 'mkvdiskhostmap', '-host', host_name, '-scsi', six.text_type(result_lun), vdisk_name] out, err = self._ssh(ssh_cmd, check_exit_code=False) - if err and err.startswith('CMMVC6071E'): + map_error = self._cli_except('_map_vdisk_to_host', + ssh_cmd, + out, + err, + self.MULTI_HOST_MAP_ERRORS) + if map_error: if not self.configuration.flashsystem_multihostmap_enabled: - msg = _('flashsystem_multihostmap_enabled is set ' - 'to False, not allow multi host mapping. ' - 'CMMVC6071E The VDisk-to-host mapping ' - 'was not created because the VDisk is ' - 'already mapped to a host.') + msg = _( + 'flashsystem_multihostmap_enabled is set ' + 'to False, failing requested multi-host map. ' + '(%(code)s %(message)s)' + ) % map_error LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg)