diff --git a/cinder/tests/unit/volume/drivers/nec/test_volume.py b/cinder/tests/unit/volume/drivers/nec/test_volume.py index 73405165d8e..d2890265755 100644 --- a/cinder/tests/unit/volume/drivers/nec/test_volume.py +++ b/cinder/tests/unit/volume/drivers/nec/test_volume.py @@ -1048,25 +1048,54 @@ class NonDisruptiveBackup_test(volume_helper.MStorageDSVDriver, self._validate_ld_exist( self.lds, self.vol.id, self._properties['ld_name_format']) + @mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute', + patch_execute) + @mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI.' + 'view_all', new=mock.Mock()) def test_validate_iscsildset_exist(self): connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"} ldset = self._validate_iscsildset_exist(self.ldsets, connector) self.assertEqual('LX:OpenStack0', ldset['ldsetname']) - connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255XX"} - with self.assertRaisesRegexp(exception.NotFound, - 'Appropriate Logical Disk Set' - ' could not be found.'): - self._validate_iscsildset_exist(self.ldsets, connector) + connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f232XX"} + mock_data = {'ldsetname': 'LX:redhatd1d8e8f23', + 'protocol': 'iSCSI', + 'portal_list': ['1.1.1.1:3260', '2.2.2.2:3260'], + 'lds': {}, + 'initiator_list': + ['iqn.1994-05.com.redhat:d1d8e8f232XX']} + mock_ldset = {} + mock_ldset['LX:redhatd1d8e8f23'] = mock_data + mock_configs = mock.Mock() + self.configs = mock_configs + self.configs.return_value = None, None, mock_ldset, None, None, None + ldset = self._validate_iscsildset_exist(self.ldsets, connector) + self.assertEqual('LX:redhatd1d8e8f23', ldset['ldsetname']) + self.assertEqual('iqn.1994-05.com.redhat:d1d8e8f232XX', + ldset['initiator_list'][0]) + @mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI._execute', + patch_execute) + @mock.patch('cinder.volume.drivers.nec.cli.MStorageISMCLI.' + 'view_all', new=mock.Mock()) def test_validate_fcldset_exist(self): connector = {'wwpns': ["10000090FAA0786A", "10000090FAA0786B"]} ldset = self._validate_fcldset_exist(self.ldsets, connector) self.assertEqual('LX:OpenStack1', ldset['ldsetname']) connector = {'wwpns': ["10000090FAA0786X", "10000090FAA0786Y"]} - with self.assertRaisesRegexp(exception.NotFound, - 'Appropriate Logical Disk Set' - ' could not be found.'): - self._validate_fcldset_exist(self.ldsets, connector) + mock_data = {'ldsetname': 'LX:10000090FAA0786X', + 'lds': {}, + 'protocol': 'FC', + 'wwpn': ["1000-0090-FAA0-786X", "1000-0090-FAA0-786Y"], + 'port': []} + mock_ldset = {} + mock_ldset['LX:10000090FAA0786X'] = mock_data + mock_configs = mock.Mock() + self.configs = mock_configs + self.configs.return_value = None, None, mock_ldset, None, None, None + ldset = self._validate_fcldset_exist(self.ldsets, connector) + self.assertEqual('LX:10000090FAA0786X', ldset['ldsetname']) + self.assertEqual('1000-0090-FAA0-786X', ldset['wwpn'][0]) + self.assertEqual('1000-0090-FAA0-786Y', ldset['wwpn'][1]) def test_enumerate_iscsi_portals(self): connector = {'initiator': "iqn.1994-05.com.redhat:d1d8e8f23255"} diff --git a/cinder/volume/drivers/nec/cli.py b/cinder/volume/drivers/nec/cli.py index 00810401644..6201c4ff4d2 100644 --- a/cinder/volume/drivers/nec/cli.py +++ b/cinder/volume/drivers/nec/cli.py @@ -31,7 +31,7 @@ from cinder import ssh_utils LOG = logging.getLogger(__name__) -retry_msgids = ['iSM31005', 'iSM31015', 'iSM42408', 'iSM42412'] +retry_msgids = ['iSM31005', 'iSM31015', 'iSM42408', 'iSM42412', 'iSM19411'] class MStorageISMCLI(object): @@ -227,6 +227,41 @@ class MStorageISMCLI(object): % {'ldn': ldn, 'capacity': capacity}) self._execute(cmd) + def addldset_fc(self, ldsetname, connector): + """Create new FC LD Set.""" + cmd = 'iSMcfg addldset -ldset LX:%s -type fc' % ldsetname + out, err, status = self._execute(cmd, [0], False) + if status != 0: + return False + for wwpn in connector['wwpns']: + length = len(wwpn) + setwwpn = '-'.join([wwpn[i:i + 4] + for i in range(0, length, 4)]) + setwwpn = setwwpn.upper() + cmd = ('iSMcfg addldsetpath -ldset LX:%(name)s -path %(path)s' + % {'name': ldsetname, 'path': setwwpn}) + out, err, status = self._execute(cmd, [0], False) + if status != 0: + return False + + return True + + def addldset_iscsi(self, ldsetname, connector): + """Create new iSCSI LD Set.""" + cmd = ('iSMcfg addldset -ldset LX:%s -multitarget on' + ' -type iscsi' % ldsetname) + out, err, status = self._execute(cmd, [0], False) + if status != 0: + return False + cmd = ('iSMcfg addldsetinitiator' + ' -ldset LX:%(name)s -initiatorname %(initiator)s' + % {'name': ldsetname, 'initiator': connector['initiator']}) + out, err, status = self._execute(cmd, [0], False) + if status != 0: + return False + + return True + def addldsetld(self, ldset, ldname, lun=None): """Add an LD to specified LD Set.""" if lun is None: @@ -611,6 +646,14 @@ class MStorageISMCLI(object): % {'lvname': lvname}) self._execute(cmd) + def cvbind(self, poolnumber, cvnumber): + """Create Control Volume.""" + cmd = ('iSMcfg ldbind -poolnumber %(poolnumber)d ' + '-ldattr cv -ldn %(cvnumber)d' + % {'poolnumber': poolnumber, + 'cvnumber': cvnumber}) + self._execute(cmd) + class UnpairWait(object): diff --git a/cinder/volume/drivers/nec/volume_common.py b/cinder/volume/drivers/nec/volume_common.py index 0723ae9e77b..fa9b20f73b9 100644 --- a/cinder/volume/drivers/nec/volume_common.py +++ b/cinder/volume/drivers/nec/volume_common.py @@ -96,6 +96,12 @@ mstorage_opts = [ cfg.IntOpt('nec_iscsi_portals_per_cont', default=1, help='Number of iSCSI portals.'), + cfg.BoolOpt('nec_auto_accesscontrol', + default=True, + help='Configure access control automatically.'), + cfg.StrOpt('nec_cv_ldname_format', + default='LX:__ControlVolume_%xh', + help='M-Series Storage Control Volume name format.'), ] FLAGS.register_opts(mstorage_opts, group=configuration.SHARED_CONF_GROUP) @@ -147,7 +153,7 @@ def convert_to_id(value62): class MStorageVolumeCommon(object): """M-Series Storage volume common class.""" - VERSION = '1.9.2' + VERSION = '1.10.1' WIKI_NAME = 'NEC_Cinder_CI' def do_setup(self, context): @@ -250,7 +256,9 @@ class MStorageVolumeCommon(object): confobj.safe_get('nec_ssh_pool_port_number'), 'diskarray_name': confobj.safe_get('nec_diskarray_name'), 'queryconfig_view': confobj.safe_get('nec_queryconfig_view'), - 'portal_number': confobj.safe_get('nec_iscsi_portals_per_cont') + 'portal_number': confobj.safe_get('nec_iscsi_portals_per_cont'), + 'auto_accesscontrol': confobj.safe_get('nec_auto_accesscontrol'), + 'cv_name_format': confobj.safe_get('nec_cv_ldname_format') } def _set_properties(self): diff --git a/cinder/volume/drivers/nec/volume_helper.py b/cinder/volume/drivers/nec/volume_helper.py index ac352b9a761..c9bab703fb1 100644 --- a/cinder/volume/drivers/nec/volume_helper.py +++ b/cinder/volume/drivers/nec/volume_helper.py @@ -217,8 +217,24 @@ class MStorageDriver(volume_common.MStorageVolumeCommon): ldset = tldset break if ldset is None: - msg = _('Appropriate Logical Disk Set could not be found.') - raise exception.NotFound(msg) + if self._properties['auto_accesscontrol']: + authname = connector['initiator'].strip() + authname = authname.replace((":"), "") + authname = authname.replace(("."), "") + new_ldsetname = authname[-16:] + ret = self._cli.addldset_iscsi(new_ldsetname, connector) + if ret is False: + msg = _('Appropriate Logical Disk Set' + ' could not be found.') + raise exception.NotFound(msg) + xml = self._cli.view_all(self._properties['ismview_path']) + pools, lds, ldsets, used_ldns, hostports, max_ld_count = ( + self.configs(xml)) + ldset = self._validate_iscsildset_exist(ldsets, connector) + else: + msg = _('Appropriate Logical Disk Set could not be found.') + raise exception.NotFound(msg) + if len(ldset['portal_list']) < 1: msg = (_('Logical Disk Set `%s` has no portal.') % ldset['ldsetname']) @@ -240,8 +256,21 @@ class MStorageDriver(volume_common.MStorageVolumeCommon): if ldset is not None: break if ldset is None: - msg = _('Appropriate Logical Disk Set could not be found.') - raise exception.NotFound(msg) + if self._properties['auto_accesscontrol']: + new_ldsetname = connector['wwpns'][0][:16] + ret = self._cli.addldset_fc(new_ldsetname, connector) + if ret is False: + msg = _('Appropriate Logical Disk Set' + ' could not be found.') + raise exception.NotFound(msg) + xml = self._cli.view_all(self._properties['ismview_path']) + pools, lds, ldsets, used_ldns, hostports, max_ld_count = ( + self.configs(xml)) + ldset = self._validate_fcldset_exist(ldsets, connector) + else: + msg = _('Appropriate Logical Disk Set could not be found.') + raise exception.NotFound(msg) + return ldset def _enumerate_iscsi_portals(self, hostports, ldset, prefered_director=0): @@ -819,6 +848,16 @@ class MStorageDriver(volume_common.MStorageVolumeCommon): lvldn = self._select_ldnumber(used_ldns, max_ld_count) LOG.debug('configure backend.') + if not ldset['lds']: + LOG.debug('create and attach control volume.') + used_ldns.append(lvldn) + cvldn = self._select_ldnumber(used_ldns, max_ld_count) + self._cli.cvbind(lds[bvname]['pool_num'], cvldn) + self._cli.changeldname(cvldn, + self._properties['cv_name_format'] % cvldn) + self._cli.addldsetld(ldset['ldsetname'], + self._properties['cv_name_format'] % cvldn) + self._cli.lvbind(bvname, lvname[3:], lvldn) self._cli.lvlink(svname[3:], lvname[3:]) self._cli.addldsetld(ldset['ldsetname'], lvname) @@ -882,6 +921,17 @@ class MStorageDriver(volume_common.MStorageVolumeCommon): lvldn = self._select_ldnumber(used_ldns, max_ld_count) LOG.debug('configure backend.') + lun0 = [ld for (ldn, ld) in ldset['lds'].items() if ld['lun'] == 0] + if not lun0: + LOG.debug('create and attach control volume.') + used_ldns.append(lvldn) + cvldn = self._select_ldnumber(used_ldns, max_ld_count) + self._cli.cvbind(lds[bvname]['pool_num'], cvldn) + self._cli.changeldname(cvldn, + self._properties['cv_name_format'] % cvldn) + self._cli.addldsetld(ldset['ldsetname'], + self._properties['cv_name_format'] % cvldn, 0) + self._cli.lvbind(bvname, lvname[3:], lvldn) self._cli.lvlink(svname[3:], lvname[3:]) @@ -889,6 +939,8 @@ class MStorageDriver(volume_common.MStorageVolumeCommon): ldsetlds = ldset['lds'] for ld in ldsetlds.values(): luns.append(ld['lun']) + if 0 not in luns: + luns.append(0) target_lun = 0 for lun in sorted(luns): if target_lun < lun: diff --git a/releasenotes/notes/nec-auto-accesscontrol-55f4b090e8128f5e.yaml b/releasenotes/notes/nec-auto-accesscontrol-55f4b090e8128f5e.yaml new file mode 100644 index 00000000000..040aaaf7183 --- /dev/null +++ b/releasenotes/notes/nec-auto-accesscontrol-55f4b090e8128f5e.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + Added automatic configuration of SAN access control for the NEC + volume driver.