NEC driver: add automatic configuration of SAN access control.

NEC driver user has to register WWPNs or an initiator name of every
node to storage. This is inconvenient for the user.
This patch enables NEC driver to configure SAN access control
automatically.

Change-Id: I92b42600b71fbdfe1b84046c59ab485f333be889
Closes-Bug: #1737452
This commit is contained in:
Shunei Shiono 2017-12-20 18:21:06 +09:00
parent 3b86eb7a82
commit d4dd162bcd
5 changed files with 152 additions and 16 deletions

View File

@ -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"}

View File

@ -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):

View File

@ -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):

View File

@ -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:

View File

@ -0,0 +1,4 @@
---
upgrade:
Added automatic configuration of SAN access control for the NEC
volume driver.