* Plugin synced, notable changes:
* The access_groups() of SMI-S plugin now support interop.
* IBM v7k plugin only support access_groups() since we have not hardware
to test on. The access_groups() methods is not tested.
* The targetd plugin are using initiator to simulator a access_group.
Supporting volume_mask(), volume_unmask(), access_group_create() and
access_group_delete().
* The nstor plugin was not tested.
Changes in V2:
* Remove the remaining_new_access_group().
* Allowing call _cim_ag_to_lsm() without system_id.
# This will be removed after access_groups_granted_to_volume() redesigned.
Changes in V3:
* Fix problems in ontap, nstor, targetd plugins.
Signed-off-by: Gris Ge <***@redhat.com>
---
plugin/nstor/nstor.py | 65 +++++-----
plugin/ontap/ontap.py | 20 ++-
plugin/sim/simarray.py | 7 +-
plugin/sim/simulator.py | 29 ++---
plugin/smispy/smis.py | 312 ++++++++++++++++++++++++++++------------------
plugin/targetd/targetd.py | 100 ++++++++++++++-
plugin/v7k/ibmv7k.py | 41 ++++++
7 files changed, 403 insertions(+), 171 deletions(-)
diff --git a/plugin/nstor/nstor.py b/plugin/nstor/nstor.py
index e06ac95..35c1def 100644
--- a/plugin/nstor/nstor.py
+++ b/plugin/nstor/nstor.py
@@ -17,6 +17,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Author: legkodymov
+# Gris Ge <***@redhat.com>
import urllib2
import urlparse
@@ -252,7 +253,7 @@ class NexentaStor(INfs, IStorageAreaNetwork):
# c.set(Capabilities.VOLUME_OFFLINE)
c.set(Capabilities.VOLUME_MASK)
c.set(Capabilities.VOLUME_UNMASK)
- c.set(Capabilities.ACCESS_GROUP_LIST)
+ c.set(Capabilities.ACCESS_GROUPS)
c.set(Capabilities.ACCESS_GROUP_CREATE)
c.set(Capabilities.ACCESS_GROUP_DELETE)
c.set(Capabilities.ACCESS_GROUP_ADD_INITIATOR)
@@ -668,14 +669,14 @@ class NexentaStor(INfs, IStorageAreaNetwork):
[volume_name, {'host_group': group_name}])
return
- def volume_mask(self, group, volume, flags=0):
+ def volume_mask(self, access_group, volume, flags=0):
"""
Allows an access group to access a volume.
"""
- self._volume_mask(group.name, volume.name)
+ self._volume_mask(access_group.name, volume.name)
return
- def volume_unmask(self, group, volume, flags=0):
+ def volume_unmask(self, access_group, volume, flags=0):
"""
Revokes access for an access group for a volume
"""
@@ -701,34 +702,40 @@ class NexentaStor(INfs, IStorageAreaNetwork):
ag_list = []
for hg in hg_list:
- initiators = self._request("list_hostgroup_members", "stmf",
- [hg])
-
- ag_list.append(AccessGroup(hg, hg, initiators, self.system.id))
+ init_ids = self._request("list_hostgroup_members", "stmf", [hg])
+ ag_list.append(
+ AccessGroup(hg, hg, init_ids,
+ AccessGroup.INIT_TYPE_ISCSI_IQN,
+ self.system.id))
return search_property(ag_list, search_key, search_value)
- def access_group_create(self, name, initiator_id, id_type, system_id,
+ def access_group_create(self, name, init_id, init_type, system_id,
flags=0):
"""
Creates of access group
"""
- # Check that initiator_id is not a part of another hostgroup
+ if system_id != self.system_id:
+ raise LsmError(ErrorNumber.NOT_FOUND_SYSTEM,
+ "System %s not found" % system_id)
+ if init_type != AccessGroup.INIT_TYPE_ISCSI_IQN:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "Nstor only support iSCSI Access Group")
+ # Check that init_id is not a part of another hostgroup
for ag in self.access_groups():
- if initiator_id in ag.initiators:
+ if init_id in ag.init_ids:
raise LsmError(ErrorNumber.EXISTS_INITIATOR,
- "%s is already part of %s access group" % (
- initiator_id,
- ag.name))
+ "%s is already part of %s access group" %
+ (init_id, ag.name))
self._request("create_hostgroup", "stmf", [name])
- self._add_initiator(name, initiator_id)
+ self._add_initiator(name, init_id)
- return AccessGroup(name, name, [initiator_id], self.system.id)
+ return AccessGroup(name, name, [init_id], init_type, system_id)
- def access_group_delete(self, group, flags=0):
+ def access_group_delete(self, access_group, flags=0):
"""
Deletes an access group
"""
- self._request("destroy_hostgroup", "stmf", [group.name])
+ self._request("destroy_hostgroup", "stmf", [access_group.name])
return
def _add_initiator(self, group_name, initiator_id, remove=False):
@@ -739,34 +746,34 @@ class NexentaStor(INfs, IStorageAreaNetwork):
self._request(command, "stmf", [group_name, initiator_id])
return
- def access_group_initiator_add(self, group, initiator_id, id_type,
+ def access_group_initiator_add(self, access_group, init_id, init_type,
flags=0):
"""
Adds an initiator to an access group
"""
- if id_type != Initiator.TYPE_ISCSI:
- raise LsmError(ErrorNumber.INVALID_ARGUMENT,
- "ISCSI only initator type supported")
+ if init_type != Initiator.TYPE_ISCSI:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "Nstor only support iSCSI Access Group")
- self._add_initiator(group.name, initiator_id)
+ self._add_initiator(access_group.name, init_id)
return None
- def access_group_initiator_delete(self, group, initiator_id, flags=0):
+ def access_group_initiator_delete(self, access_group, init_id, flags=0):
"""
Deletes an initiator from an access group
"""
- self._add_initiator(group.name, initiator_id, True)
+ self._add_initiator(access_group.name, init_id, True)
return None
- def volumes_accessible_by_access_group(self, group, flags=0):
+ def volumes_accessible_by_access_group(self, access_group, flags=0):
"""
Returns the list of volumes that access group has access to.
"""
volumes = []
- all_volumes_list = self.volumes()
+ all_volumes_list = self.volumes(flags=flags)
for vol in all_volumes_list:
for view in self._get_views(vol.name):
- if view['host_group'] == group.name:
+ if view['host_group'] == access_group.name:
volumes.append(vol)
return volumes
@@ -774,7 +781,7 @@ class NexentaStor(INfs, IStorageAreaNetwork):
"""
Returns the list of access groups that have access to the specified
"""
- ag_list = self.access_groups()
+ ag_list = self.access_groups(flags=flags)
hg = []
for view in self._get_views(volume.name):
diff --git a/plugin/ontap/ontap.py b/plugin/ontap/ontap.py
index 2536e61..d97bfe7 100644
--- a/plugin/ontap/ontap.py
+++ b/plugin/ontap/ontap.py
@@ -74,6 +74,22 @@ def handle_ontap_errors(method):
return na_wrapper
+_INIT_TYPE_CONV = {
+ 'iscsi': AccessGroup.INIT_TYPE_ISCSI_IQN,
+ 'fcp': AccessGroup.INIT_TYPE_WWPN,
+ 'mixed': AccessGroup.INIT_TYPE_ISCSI_WWPN_MIXED,
+}
+
+
+def _na_init_type_to_lsm(na_ag):
+ if 'initiator-group-type' in na_ag:
+ if na_ag['initiator-group-type'] in _INIT_TYPE_CONV.keys():
+ return _INIT_TYPE_CONV[na_ag['initiator-group-type']]
+ else:
+ return AccessGroup.INIT_TYPE_OTHER
+ return AccessGroup.INIT_TYPE_UNKNOWN
+
+
class Ontap(IStorageAreaNetwork, INfs):
TMO_CONV = 1000.0
@@ -441,7 +457,7 @@ class Ontap(IStorageAreaNetwork, INfs):
cap.set(Capabilities.VOLUME_ISCSI_CHAP_AUTHENTICATION)
cap.set(Capabilities.VOLUME_MASK)
cap.set(Capabilities.VOLUME_UNMASK)
- cap.set(Capabilities.ACCESS_GROUP_LIST)
+ cap.set(Capabilities.ACCESS_GROUPS)
cap.set(Capabilities.ACCESS_GROUP_CREATE)
cap.set(Capabilities.ACCESS_GROUP_DELETE)
cap.set(Capabilities.ACCESS_GROUP_ADD_INITIATOR)
@@ -743,7 +759,7 @@ class Ontap(IStorageAreaNetwork, INfs):
ag_id = md5(name)
return AccessGroup(ag_id, name, Ontap._initiators_in_group(g),
- self.sys_info.id)
+ _na_init_type_to_lsm(g), self.sys_info.id)
@handle_ontap_errors
def access_groups(self, search_key=None, search_value=None, flags=0):
diff --git a/plugin/sim/simarray.py b/plugin/sim/simarray.py
index 0d639f6..f530763 100644
--- a/plugin/sim/simarray.py
+++ b/plugin/sim/simarray.py
@@ -351,7 +351,8 @@ class SimArray(object):
@staticmethod
def _sim_ag_2_lsm(sim_ag):
return AccessGroup(sim_ag['ag_id'], sim_ag['name'],
- sim_ag['init_ids'], sim_ag['sys_id'])
+ sim_ag['init_ids'], sim_ag['init_type'],
+ sim_ag['sys_id'])
def ags(self):
sim_ags = self.data.ags()
@@ -467,6 +468,7 @@ class SimData(object):
}
sim_ag = {
'init_ids': [init_id,],
+ 'init_type': AccessGroup.init_type,
'sys_id': SimData.SIM_DATA_SYS_ID,
'name': name,
'ag_id': self._next_ag_id()
@@ -534,7 +536,7 @@ class SimData(object):
}
"""
SIM_DATA_BLK_SIZE = 512
- SIM_DATA_VERSION = "2.2"
+ SIM_DATA_VERSION = "2.3"
SIM_DATA_SYS_ID = 'sim-01'
SIM_DATA_INIT_NAME = 'NULL'
SIM_DATA_TMO = 30000 # ms
@@ -1042,6 +1044,7 @@ class SimData(object):
self.init_dict[init_id] = sim_init
sim_ag['init_ids'] = [init_id]
+ sim_ag['init_type'] = init_type
sim_ag['sys_id'] = SimData.SIM_DATA_SYS_ID
sim_ag['name'] = name
sim_ag['ag_id'] = self._next_ag_id()
diff --git a/plugin/sim/simulator.py b/plugin/sim/simulator.py
index a28d519..cb3f08a 100644
--- a/plugin/sim/simulator.py
+++ b/plugin/sim/simulator.py
@@ -180,36 +180,37 @@ class SimPlugin(INfs, IStorageAreaNetwork):
[SimPlugin._sim_data_2_lsm(a) for a in sim_ags],
search_key, search_value)
- def access_group_create(self, name, initiator_id, id_type, system_id,
+ def access_group_create(self, name, init_id, init_type, system_id,
flags=0):
sim_ag = self.sim_array.access_group_create(
- name, initiator_id, id_type, system_id, flags)
+ name, init_id, init_type, system_id, flags)
return SimPlugin._sim_data_2_lsm(sim_ag)
- def access_group_delete(self, group, flags=0):
- return self.sim_array.access_group_delete(group.id, flags)
+ def access_group_delete(self, access_group, flags=0):
+ return self.sim_array.access_group_delete(access_group.id, flags)
- def access_group_initiator_add(self, group, initiator_id, id_type,
+ def access_group_initiator_add(self, access_group, init_id, init_type,
flags=0):
sim_ag = self.sim_array.access_group_initiator_add(
- group.id, initiator_id, id_type, flags)
+ access_group.id, init_id, init_type, flags)
return SimPlugin._sim_data_2_lsm(sim_ag)
- def access_group_initiator_delete(self, group, initiator_id, flags=0):
+ def access_group_initiator_delete(self, access_group, init_id,
+ flags=0):
return self.sim_array.access_group_initiator_delete(
- group.id, initiator_id, flags)
+ access_group.id, init_id, flags)
- def volume_mask(self, group, volume, flags=0):
+ def volume_mask(self, access_group, volume, flags=0):
return self.sim_array.volume_mask(
- group.id, volume.id, flags)
+ access_group.id, volume.id, flags)
- def volume_unmask(self, group, volume, flags=0):
+ def volume_unmask(self, access_group, volume, flags=0):
return self.sim_array.volume_unmask(
- group.id, volume.id, flags)
+ access_group.id, volume.id, flags)
- def volumes_accessible_by_access_group(self, group, flags=0):
+ def volumes_accessible_by_access_group(self, access_group, flags=0):
sim_vols = self.sim_array.volumes_accessible_by_access_group(
- group.id, flags)
+ access_group.id, flags)
return [SimPlugin._sim_data_2_lsm(v) for v in sim_vols]
def access_groups_granted_to_volume(self, volume, flags=0):
diff --git a/plugin/smispy/smis.py b/plugin/smispy/smis.py
index 0e5ceb5..b2da486 100644
--- a/plugin/smispy/smis.py
+++ b/plugin/smispy/smis.py
@@ -37,6 +37,8 @@ from lsm import (IStorageAreaNetwork, Error, uri_parse, LsmError, ErrorNumber,
# cim_scs CIM_StorageConfigurationService
# cim_vol CIM_StorageVolume
# cim_rp CIM_RegisteredProfile
+# cim_init CIM_StorageHardwareID
+# cim_ag CIM_SCSIProtocolController
#
# sys Object of LSM System
# pool Object of LSM Pool
@@ -98,11 +100,53 @@ def _spec_ver_str_to_num(spec_ver_str):
return None
+class DMTF(object):
+ ID_TYPE_OTHER = pywbem.Uint16(1)
+ ID_TYPE_WWPN = pywbem.Uint16(2)
+ ID_TYPE_WWNN = pywbem.Uint16(3)
+ ID_TYPE_HOSTNAME = pywbem.Uint16(4)
+ ID_TYPE_ISCSI = pywbem.Uint16(5)
+ ID_TYPE_SW_WWN = pywbem.Uint16(6)
+
+
+_INIT_TYPE_CONV = {
+ DMTF.ID_TYPE_OTHER: AccessGroup.INIT_TYPE_OTHER,
+ DMTF.ID_TYPE_WWPN: AccessGroup.INIT_TYPE_WWPN,
+ DMTF.ID_TYPE_WWNN: AccessGroup.INIT_TYPE_WWNN,
+ DMTF.ID_TYPE_HOSTNAME: AccessGroup.INIT_TYPE_HOSTNAME,
+ DMTF.ID_TYPE_ISCSI: AccessGroup.INIT_TYPE_ISCSI_IQN,
+ DMTF.ID_TYPE_SW_WWN: AccessGroup.INIT_TYPE_OTHER,
+}
+
+
+def _dmtf_init_type_to_lsm(cim_init):
+ if 'IDType' in cim_init and cim_init['IDType'] in _INIT_TYPE_CONV.keys():
+ return _INIT_TYPE_CONV[cim_init['IDType']]
+ return AccessGroup.INIT_TYPE_UNKNOWN
+
+
+def _get_key(dictionary, value):
+ keys = [k for k, v in dictionary.items() if v == value]
+ if len(keys) > 0:
+ return keys[0]
+ return None
+
+
+def _lsm_init_type_to_dmtf(init_type):
+ key = _get_key(_INIT_TYPE_CONV, init_type)
+ if key is None:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "Does not support provided init_type: %d" % init_type)
+ else:
+ return key
+
+
class SNIA(object):
BLK_ROOT_PROFILE = 'Array'
BLK_SRVS_PROFILE = 'Block Services'
DISK_LITE_PROFILE = 'Disk Drive Lite'
MULTI_SYS_PROFILE = 'Multiple Computer System'
+ MASK_PROFILE = 'Masking and Mapping'
SMIS_SPEC_VER_1_4 = '1.4'
SMIS_SPEC_VER_1_5 = '1.5'
SMIS_SPEC_VER_1_6 = '1.6'
@@ -772,7 +816,7 @@ class Smis(IStorageAreaNetwork):
e[0] == pywbem.CIM_ERR_INVALID_CLASS:
return
if cim_pcms is not None and len(cim_pcms) == 1:
- cap.set(Capabilities.ACCESS_GROUP_LIST)
+ cap.set(Capabilities.ACCESS_GROUPS)
cap.set(Capabilities.ACCESS_GROUPS_GRANTED_TO_VOLUME)
cap.set(Capabilities.VOLUMES_ACCESSIBLE_BY_ACCESS_GROUP)
@@ -786,7 +830,7 @@ class Smis(IStorageAreaNetwork):
# Since SNIA SMI-S 1.4rev6:
# CIM_ControllerConfigurationService is mandatory
# and it's ExposePaths() and HidePaths() are mandatory
- cap.set(Capabilities.ACCESS_GROUP_LIST)
+ cap.set(Capabilities.ACCESS_GROUPS)
cap.set(Capabilities.VOLUME_MASK)
cap.set(Capabilities.VOLUME_UNMASK)
cap.set(Capabilities.ACCESS_GROUP_ADD_INITIATOR)
@@ -962,17 +1006,17 @@ class Smis(IStorageAreaNetwork):
"""
return "%s@%d" % (self._id('Job', cim_job), int(retrieve_data))
- def _access_group_id(self, cim_spc):
+ def _access_group_id(self, cim_ag):
"""
Retrive Access Group ID from CIM_SCSIProtocolController['DeviceID']
"""
- return self._id('AccessGroup', cim_spc)
+ return self._id('AccessGroup', cim_ag)
- def _init_id(self, cim_st_hwid):
+ def _init_id(self, cim_init):
"""
Retrive Initiator ID from CIM_StorageHardwareID
"""
- return self._id('Initiator', cim_st_hwid)
+ return self._id('Initiator', cim_init)
def _id(self, class_type, cim_xxx):
"""
@@ -1245,26 +1289,44 @@ class Smis(IStorageAreaNetwork):
"Got not new Pool from out of InvokeMethod" +
"when CreateOrModifyElementFromStoragePool")
- def _new_access_group_cim_spc_pros(self):
+ def _cim_ag_pros(self):
"""
Return a list of properties required to build new AccessGroup.
"""
- cim_spc_pros = self._property_list_of_id('AccessGroup')
- cim_spc_pros.extend(self._property_list_of_id('SystemChild'))
- cim_spc_pros.extend(['ElementName', 'StorageID'])
- cim_spc_pros.extend(['EMCAdapterRole']) # EMC specific, used to
+ cim_ag_pros = self._property_list_of_id('AccessGroup')
+ cim_ag_pros.extend(self._property_list_of_id('SystemChild'))
+ cim_ag_pros.extend(['ElementName', 'StorageID'])
+ cim_ag_pros.extend(['EMCAdapterRole']) # EMC specific, used to
# filter out the mapping SPC.
- return cim_spc_pros
+ return cim_ag_pros
- def _new_access_group(self, cim_spc):
- ag_id = self._access_group_id(cim_spc)
- ag_name = cim_spc['ElementName']
+ def _cim_ag_to_lsm(self, cim_ag, system_id=None):
+ if system_id is None:
+ system_id = self._sys_id_child(cim_ag)
+ ag_id = self._access_group_id(cim_ag)
+ ag_name = cim_ag['ElementName']
ag_init_ids = []
- cim_st_hwid_pros = self._property_list_of_id('Initiator')
- cim_st_hwids = self._get_cim_st_hwid_in_spc(cim_spc, cim_st_hwid_pros)
- ag_init_ids = [self._init_id(i) for i in cim_st_hwids]
- sys_id = self._sys_id_child(cim_spc)
- return AccessGroup(ag_id, ag_name, ag_init_ids, sys_id)
+ cim_init_pros = self._property_list_of_id('Initiator')
+ cim_init_pros.extend(['IDType'])
+ cim_inits = self._cim_init_of(cim_ag, cim_init_pros)
+ ag_init_ids = [self._init_id(i) for i in cim_inits]
+ ag_init_types = [_dmtf_init_type_to_lsm(i) for i in cim_inits]
+ init_type = AccessGroup.INIT_TYPE_UNKNOWN
+ ag_init_type_dict = {}
+ for ag_init_type in ag_init_types:
+ ag_init_type_dict[ag_init_type] = 1
+ if len(ag_init_type_dict) == 1:
+ init_type = ag_init_types[0]
+ elif (len(ag_init_type_dict) == 2 and
+ AccessGroup.INIT_TYPE_ISCSI_IQN in ag_init_type_dict.keys() and
+ AccessGroup.INIT_TYPE_WWPN in ag_init_type_dict.keys()):
+ init_type = AccessGroup.INIT_TYPE_ISCSI_WWPN_MIXED
+ else:
+ # We have unknown mixed initiator type
+ init_type = AccessGroup.INIT_TYPE_OTHER
+
+ sys_id = self._sys_id_child(cim_ag)
+ return AccessGroup(ag_id, ag_name, ag_init_ids, init_type, sys_id)
def _new_vol_from_job(self, job):
"""
@@ -1561,13 +1623,13 @@ class Smis(IStorageAreaNetwork):
return [Smis._cim_sys_2_lsm_sys(s) for s in cim_syss]
- def _new_init(self, cim_st_hwid):
+ def _new_init(self, cim_init):
"""
Generate Initiator object from CIM_StorageHardwareID
"""
- init_id = self._init_id(cim_st_hwid)
- init_type = cim_st_hwid['IDType']
- init_name = cim_st_hwid['ElementName']
+ init_id = self._init_id(cim_init)
+ init_type = cim_init['IDType']
+ init_name = cim_init['ElementName']
return Initiator(init_id, init_type, init_name)
def _new_init_pros(self):
@@ -1575,9 +1637,9 @@ class Smis(IStorageAreaNetwork):
Return a list of properties needed to created Initiator from
CIM_StorageHardwareID
"""
- cim_st_hwid_pros = self._property_list_of_id('Initiator')
- cim_st_hwid_pros.extend(['IDType', 'ElementName'])
- return cim_st_hwid_pros
+ cim_init_pros = self._property_list_of_id('Initiator')
+ cim_init_pros.extend(['IDType', 'ElementName'])
+ return cim_init_pros
@handle_cim_errors
def initiators(self, flags=0):
@@ -1594,20 +1656,20 @@ class Smis(IStorageAreaNetwork):
This is supported from SNIA SMI-S 1.3rev6 to latest(1.6rev4).
"""
rc_inits = []
- cim_st_hwid_pros = self._new_init_pros()
+ cim_init_pros = self._new_init_pros()
try:
for cim_sys in self._systems():
- cim_st_hwid_mss_path = self._c.AssociatorNames(
+ cim_init_mss_path = self._c.AssociatorNames(
cim_sys.path,
AssocClass='CIM_HostedService',
ResultClass='CIM_StorageHardwareIDManagementService')
- for cim_st_hwid_ms_path in cim_st_hwid_mss_path:
- cim_st_hwids = self._c.Associators(
- cim_st_hwid_ms_path,
+ for cim_init_ms_path in cim_init_mss_path:
+ cim_inits = self._c.Associators(
+ cim_init_ms_path,
AssocClass='CIM_ConcreteDependency',
ResultClass='CIM_StorageHardwareID',
- PropertyList=cim_st_hwid_pros)
- rc_inits.extend([self._new_init(i) for i in cim_st_hwids])
+ PropertyList=cim_init_pros)
+ rc_inits.extend([self._new_init(i) for i in cim_inits])
except CIMError as ce:
error_code = tuple(ce)[0]
if error_code == pywbem.CIM_ERR_INVALID_CLASS or \
@@ -1915,14 +1977,14 @@ class Smis(IStorageAreaNetwork):
' on initiator_create!')
@handle_cim_errors
- def volume_mask(self, group, volume, flags=0):
+ def volume_mask(self, access_group, volume, flags=0):
"""
Grant access to a volume to an group
"""
ccs = self._get_class_instance('CIM_ControllerConfigurationService',
- 'SystemName', group.system_id)
+ 'SystemName', access_group.system_id)
lun = self._get_cim_instance_by_id('Volume', volume.id)
- spc = self._get_cim_instance_by_id('AccessGroup', group.id)
+ spc = self._get_cim_instance_by_id('AccessGroup', access_group.id)
if not lun:
raise LsmError(ErrorNumber.NOT_FOUND_VOLUME, "Volume not present")
@@ -1955,11 +2017,11 @@ class Smis(IStorageAreaNetwork):
"Expected no errors %s %s" % (job, str(status)))
@handle_cim_errors
- def volume_unmask(self, group, volume, flags=0):
+ def volume_unmask(self, access_group, volume, flags=0):
ccs = self._get_class_instance('CIM_ControllerConfigurationService',
'SystemName', volume.system_id)
lun = self._get_cim_instance_by_id('Volume', volume.id)
- spc = self._get_cim_instance_by_id('AccessGroup', group.id)
+ spc = self._get_cim_instance_by_id('AccessGroup', access_group.id)
if not lun:
raise LsmError(ErrorNumber.NOT_FOUND_VOLUME, "Volume not present")
@@ -1974,21 +2036,21 @@ class Smis(IStorageAreaNetwork):
*(self._c.InvokeMethod('HidePaths', ccs.path,
**hide_params)))[0]
- def _is_access_group(self, cim_spc):
+ def _is_access_group(self, cim_ag):
rc = True
_SMIS_EMC_ADAPTER_ROLE_MASKING = 'MASK_VIEW'
- if 'EMCAdapterRole' in cim_spc:
+ if 'EMCAdapterRole' in cim_ag:
# Currently SNIA does not define LUN mapping.
# EMC is using their specific way for LUN mapping which
# expose their frontend ports as a SPC(SCSIProtocolController).
# which we shall filter out.
- emc_adp_roles = cim_spc['EMCAdapterRole'].split(' ')
+ emc_adp_roles = cim_ag['EMCAdapterRole'].split(' ')
if _SMIS_EMC_ADAPTER_ROLE_MASKING not in emc_adp_roles:
rc = False
return rc
- def _get_access_groups(self, property_list=None):
+ def _cim_ags_of(self, cim_sys, property_list=None):
"""
Return a list of CIM_SCSIProtocolController.
Following SNIA SMIS 'Masking and Mapping Profile':
@@ -2002,46 +2064,45 @@ class Smis(IStorageAreaNetwork):
v
CIM_SCSIProtocolController
"""
- rc_cim_spcs = []
+ rc_cim_ags = []
if property_list is None:
property_list = []
- for cim_sys in self._systems():
- try:
- cim_ccss_path = self._c.AssociatorNames(
- cim_sys.path,
- AssocClass='CIM_HostedService',
- ResultClass='CIM_ControllerConfigurationService')
- except CIMError as ce:
- error_code = tuple(ce)[0]
- if error_code == pywbem.CIM_ERR_INVALID_CLASS or \
- error_code == pywbem.CIM_ERR_INVALID_PARAMETER:
- raise LsmError(ErrorNumber.NO_SUPPORT,
- 'AccessGroup is not supported ' +
- 'by this array')
- cim_ccs_path = None
- if len(cim_ccss_path) == 1:
- cim_ccs_path = cim_ccss_path[0]
- elif len(cim_ccss_path) == 0:
+ try:
+ cim_ccss_path = self._c.AssociatorNames(
+ cim_sys.path,
+ AssocClass='CIM_HostedService',
+ ResultClass='CIM_ControllerConfigurationService')
+ except CIMError as ce:
+ error_code = tuple(ce)[0]
+ if error_code == pywbem.CIM_ERR_INVALID_CLASS or \
+ error_code == pywbem.CIM_ERR_INVALID_PARAMETER:
raise LsmError(ErrorNumber.NO_SUPPORT,
- 'AccessGroup is not supported by this array')
- else:
- raise LsmError(ErrorNumber.INTERNAL_ERROR,
- "Got %d instance of " % len(cim_ccss_path) +
- "ControllerConfigurationService from %s" %
- cim_sys.path + " in _get_access_groups()")
- cim_spcs = self._c.Associators(
- cim_ccs_path,
- AssocClass='CIM_ConcreteDependency',
- ResultClass='CIM_SCSIProtocolController',
- PropertyList=property_list)
- for cim_spc in cim_spcs:
- if self._is_access_group(cim_spc):
- rc_cim_spcs.append(cim_spc)
- return rc_cim_spcs
+ 'AccessGroup is not supported ' +
+ 'by this array')
+ cim_ccs_path = None
+ if len(cim_ccss_path) == 1:
+ cim_ccs_path = cim_ccss_path[0]
+ elif len(cim_ccss_path) == 0:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ 'AccessGroup is not supported by this array')
+ else:
+ raise LsmError(ErrorNumber.INTERNAL_ERROR,
+ "Got %d instance of " % len(cim_ccss_path) +
+ "ControllerConfigurationService from %s" %
+ cim_sys.path + " in _cim_ags_of()")
+ cim_ags = self._c.Associators(
+ cim_ccs_path,
+ AssocClass='CIM_ConcreteDependency',
+ ResultClass='CIM_SCSIProtocolController',
+ PropertyList=property_list)
+ for cim_ag in cim_ags:
+ if self._is_access_group(cim_ag):
+ rc_cim_ags.append(cim_ag)
+ return rc_cim_ags
- def _get_cim_st_hwid_in_spc(self, cim_spc, property_list=None):
+ def _cim_init_of(self, cim_ag, property_list=None):
"""
Take CIM_SCSIProtocolController and return a list of
CIM_StorageHardwareID, both are CIMInstance.
@@ -2070,47 +2131,37 @@ class Smis(IStorageAreaNetwork):
Maybe someday, we will stop trying after knowing array's supported
SMIS version.
"""
- cim_st_hwids = []
+ cim_inits = []
if property_list is None:
property_list = []
- try:
- cim_st_hwids = self._c.Associators(
- cim_spc.path,
+
+ if (not self.fallback_mode and
+ self._profile_is_supported(SNIA.MASK_PROFILE,
+ SNIA.SMIS_SPEC_VER_1_6,
+ strict=False,
+ raise_error=False)):
+ return self._c.Associators(
+ cim_ag.path,
AssocClass='CIM_AssociatedPrivilege',
ResultClass='CIM_StorageHardwareID',
PropertyList=property_list)
- except CIMError as ce:
- error_code = tuple(ce)[0]
- if error_code == pywbem.CIM_ERR_INVALID_CLASS or \
- error_code == pywbem.CIM_ERR_INVALID_PARAMETER:
- pass
- else:
- raise ce
-
- # In better world, we should try fallback way only when got CIMError.
- # But on NetApp SMI-S, they neither raise a error nor support it.
- # So we check length of cim_st_hwids, it will cause double check
- # on EMC or any other array support CIM_AuthorizedPrivilege when
- # no initiator assigned to this access group.
- # We will fix this once we support Profile Registration where we
- # check array's supported SNIA SMIS version.
- if len(cim_st_hwids) == 0:
+ else:
cim_aps_path = self._c.AssociatorNames(
- cim_spc.path,
+ cim_ag.path,
AssocClass='CIM_AuthorizedTarget',
ResultClass='CIM_AuthorizedPrivilege')
for cim_ap_path in cim_aps_path:
- cim_st_hwids = self._c.Associators(
+ cim_inits.extend(self._c.Associators(
cim_ap_path,
AssocClass='CIM_AuthorizedSubject',
ResultClass='CIM_StorageHardwareID',
- PropertyList=property_list)
- return cim_st_hwids
+ PropertyList=property_list))
+ return cim_inits
@handle_cim_errors
- def volumes_accessible_by_access_group(self, group, flags=0):
+ def volumes_accessible_by_access_group(self, access_group, flags=0):
g = self._get_class_instance('CIM_SCSIProtocolController', 'DeviceID',
- group.id)
+ access_group.id)
if g:
logical_units = self._c.Associators(
g.path, AssocClass='CIM_ProtocolControllerForUnit')
@@ -2118,22 +2169,22 @@ class Smis(IStorageAreaNetwork):
else:
raise LsmError(
ErrorNumber.PLUGIN_ERROR,
- 'Error: access group %s does not exist!' % group.id)
+ 'Error: access group %s does not exist!' % access_group.id)
@handle_cim_errors
def access_groups_granted_to_volume(self, volume, flags=0):
vol = self._get_cim_instance_by_id('Volume', volume.id)
if vol:
- cim_spcs = self._c.Associators(
+ cim_ags = self._c.Associators(
vol.path,
AssocClass='CIM_ProtocolControllerForUnit',
ResultClass='CIM_SCSIProtocolController')
access_groups = []
- for cim_spc in cim_spcs:
- if self._is_access_group(cim_spc):
- access_groups.extend([self._new_access_group(cim_spc)])
+ for cim_ag in cim_ags:
+ if self._is_access_group(cim_ag):
+ access_groups.extend([self._cim_ag_to_lsm(cim_ag)])
return access_groups
else:
@@ -2143,11 +2194,23 @@ class Smis(IStorageAreaNetwork):
@handle_cim_errors
def access_groups(self, search_key=None, search_value=None, flags=0):
- cim_spc_pros = self._new_access_group_cim_spc_pros()
- cim_spcs = self._get_access_groups(property_list=cim_spc_pros)
- return search_property(
- [self._new_access_group(cim_spc) for cim_spc in cim_spcs],
- search_key, search_value)
+ if not self.fallback_mode:
+ self._profile_is_supported(SNIA.MASK_PROFILE,
+ SNIA.SMIS_SPEC_VER_1_4,
+ strict=False,
+ raise_error=True)
+ rc = []
+ cim_ag_pros = self._cim_ag_pros()
+ cim_sys_pros = self._property_list_of_id('System')
+ cim_syss = self._root_cim_syss(cim_sys_pros)
+ for cim_sys in cim_syss:
+ system_id = self._sys_id(cim_sys)
+ cim_ags = self._cim_ags_of(cim_sys, cim_ag_pros)
+ rc.extend(
+ list(self._cim_ag_to_lsm(cim_ag, system_id)
+ for cim_ag in cim_ags))
+
+ return search_property(rc, search_key, search_value)
def _initiator_lookup(self, initiator_id):
"""
@@ -2162,20 +2225,23 @@ class Smis(IStorageAreaNetwork):
return initiator
@handle_cim_errors
- def access_group_initiator_add(self, group, initiator_id, id_type,
+ def access_group_initiator_add(self, access_group, init_id, init_type,
flags=0):
# Check to see if we have this initiator already, if we don't create
# it and then add to the view.
- spc = self._get_cim_instance_by_id('AccessGroup', group.id)
- initiator = self._initiator_lookup(initiator_id)
+ spc = self._get_cim_instance_by_id('AccessGroup', access_group.id)
+
+ # This need rework when removing lsm.Initiator class
+ initiator = self._initiator_lookup(init_id)
+ lsm_init_type = _lsm_init_type_to_dmtf(init_type)
if not initiator:
- initiator = self._initiator_create(initiator_id, initiator_id,
- id_type)
+ initiator = self._initiator_create(init_id, init_id,
+ lsm_init_type)
ccs = self._get_class_instance('CIM_ControllerConfigurationService',
- 'SystemName', group.system_id)
+ 'SystemName', access_group.system_id)
in_params = {'InitiatorPortIDs': [initiator.id],
'ProtocolControllers': [spc.path]}
@@ -2186,12 +2252,12 @@ class Smis(IStorageAreaNetwork):
**in_params)))[0]
@handle_cim_errors
- def access_group_initiator_delete(self, group, initiator, flags=0):
- spc = self._get_cim_instance_by_id('AccessGroup', group.id)
+ def access_group_initiator_delete(self, access_group, init_id, flags=0):
+ spc = self._get_cim_instance_by_id('AccessGroup', access_group.id)
ccs = self._get_class_instance('CIM_ControllerConfigurationService',
- 'SystemName', group.system_id)
+ 'SystemName', access_group.system_id)
- hide_params = {'InitiatorPortIDs': [initiator],
+ hide_params = {'InitiatorPortIDs': [init_id],
'ProtocolControllers': [spc.path]}
return self._pi("HidePaths", Smis.JOB_RETRIEVE_NONE,
*(self._c.InvokeMethod('HidePaths', ccs.path,
diff --git a/plugin/targetd/targetd.py b/plugin/targetd/targetd.py
index b4f4396..428cb6c 100644
--- a/plugin/targetd/targetd.py
+++ b/plugin/targetd/targetd.py
@@ -23,7 +23,8 @@ import copy
from lsm import (Pool, Volume, System, Capabilities, Initiator,
IStorageAreaNetwork, INfs, FileSystem, FsSnapshot, NfsExport,
LsmError, ErrorNumber, uri_parse, md5, VERSION,
- common_urllib2_error_handler, search_property)
+ common_urllib2_error_handler, search_property,
+ AccessGroup)
import urllib2
import json
@@ -166,6 +167,103 @@ class TargetdStorage(IStorageAreaNetwork, INfs):
return search_property(pools, search_key, search_value)
@handle_errors
+ def access_groups(self, search_key=None, search_value=None, flags=0):
+ rc = []
+ for init_id in set(i['initiator_wwn']
+ for i in self._jsonrequest("export_list")):
+ ag_id = md5(init_id)
+ init_type = AccessGroup.INIT_TYPE_ISCSI_IQN
+ ag_name = 'N/A'
+ init_ids = [init_id]
+ rc.extend(
+ [AccessGroup(
+ ag_id, ag_name, init_ids, init_type,
+ self.system.id)])
+ return search_property(rc, search_key, search_value)
+
+ def _mask_infos(self):
+ """
+ Return a list of tgt_mask:
+ 'vol_id': volume.id
+ 'ag_id': ag.id
+ 'lun_id': lun_id
+ """
+ tgt_masks = []
+ tgt_exps = self._jsonrequest("export_list")
+ for tgt_exp in tgt_exps:
+ tgt_masks.extend([{
+ 'vol_id': tgt_exp['vol_uuid'],
+ 'ag_id': md5(tgt_exp['initiator_wwn']),
+ 'lun_id': tgt_exp['lun'],
+ }])
+ return tgt_masks
+
+ def volume_mask(self, access_group, volume, flags=0):
+ if len(access_group.init_ids) == 0:
+ raise LsmError(ErrorNumber.INVALID_ARGUMENT,
+ "No member belong to defined access group: %s"
+ % access_group.id)
+ if len(access_group.init_ids) != 1:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "Targetd does not allowing masking two or more "
+ "initiators to volume")
+
+ if access_group.init_type != AccessGroup.INIT_TYPE_ISCSI_IQN:
+ raise LsmError(ErrorNumber.NO_SUPPORT,
+ "Targetd does not %s(%d) type access group"
+ % (AccessGroup._init_type_to_str(
+ access_group.init_type),
+ access_group.init_type))
+
+ ag_id = md5(access_group.init_ids[0])
+ vol_id = volume.id
+ # Return when found already masked.
+ tgt_masks = self._mask_infos()
+ if list(x for x in tgt_masks
+ if x['vol_id'] == vol_id and x['ag_id'] == ag_id):
+ return None
+
+ # find lowest unused lun ID
+ used_lun_ids = [x['lun_id'] for x in tgt_masks]
+ lun_id = 0
+ while True:
+ if lun_id in used_lun_ids:
+ lun_id += 1
+ else:
+ break
+
+ self._jsonrequest("export_create",
+ dict(pool=volume.pool_id,
+ vol=volume.name,
+ initiator_wwn=access_group.init_ids[0],
+ lun=lun_id))
+ return None
+
+ @handle_errors
+ def volume_unmask(self, volume, access_group, flags=0):
+ self._jsonrequest("export_destroy",
+ dict(pool=volume.pool_id,
+ vol=volume.name,
+ initiator_wwn=access_group.init_ids[0]))
+ return None
+
+ @handle_errors
+ def volumes_accessible_by_access_group(self, access_group, flags=0):
+ tgt_masks = self._mask_infos()
+ vol_ids = list(x['vol_id'] for x in tgt_masks
+ if x['ag_id'] == access_group.id)
+ lsm_vols = self.volumes(flags=flags)
+ return [x for x in lsm_vols if x.id in vol_ids]
+
+ @handle_errors
+ def access_groups_granted_to_volume(self, volume, flags=0):
+ tgt_masks = self._mask_infos()
+ ag_ids = list(x['ag_id'] for x in tgt_masks
+ if x['vol_id'] == volume.id)
+ lsm_ags = self.access_groups(flags=flags)
+ return [x for x in lsm_ags if x.id in ag_ids]
+
+ @handle_errors
def initiators(self, flags=0):
inits = []
for init in set(i['initiator_wwn']
diff --git a/plugin/v7k/ibmv7k.py b/plugin/v7k/ibmv7k.py
index 077503b..0b4bb38 100644
--- a/plugin/v7k/ibmv7k.py
+++ b/plugin/v7k/ibmv7k.py
@@ -419,6 +419,7 @@ class IbmV7k(IStorageAreaNetwork):
cap.set(Capabilities.INITIATORS_GRANTED_TO_VOLUME)
cap.set(Capabilities.VOLUME_INITIATOR_GRANT)
cap.set(Capabilities.VOLUME_INITIATOR_REVOKE)
+ cap.set(Capabilities.ACCESS_GROUPS)
return cap
def plugin_info(self, flags=0):
@@ -447,6 +448,46 @@ class IbmV7k(IStorageAreaNetwork):
init_list.append(self._initiator(init))
return init_list
+ @staticmethod
+ def _v7k_init_to_lsm_ag(v7k_init, system_id):
+ lsm_init_id = None
+ lsm_init_type = AccessGroup.INIT_TYPE_UNKNOWN
+ if 'WWPN' in v7k_init:
+ lsm_init_type = AccessGroup.INIT_TYPE_WWPN
+ # TODO: Add support for > 1 wwpn case.
+ # v7k cli is not parse friendly for > 1 case.
+ lsm_init_id = v7k_init['WWPN']
+ elif 'iscsi_name' in v7k_init:
+ lsm_init_type = AccessGroup.INIT_TYPE_ISCSI_IQN
+ # TODO: Add support for > 1 iscsiname case.
+ # v7k cli is not parse friendly for > 1 case.
+ lsm_init_id = v7k_init['iscsi_name']
+ elif 'SAS_WWPN' in v7k_init:
+ # TODO: Add support for > 1 SAS_WWPN case.
+ # v7k cli is not parse friendly for > 1 case.
+ lsm_init_type = AccessGroup.INIT_TYPE_SAS
+ lsm_init_id = v7k_init['SAS_WWPN']
+ else:
+ # Since lshost worked, support it as other type.
+ lsm_init_type = AccessGroup.INIT_TYPE_OTHER
+ lsm_init_id = v7k_init['id']
+
+ ag_name = 'N/A'
+ if name in v7k_init:
+ ag_name = v7k_init['name']
+
+ return AccessGroup(lsm_init_id, ag_name, [lsm_init_id], lsm_init_type,
+ system_id)
+
+ def access_groups(self, search_key=None, search_value=None, flags=0):
+ lsm_ags = []
+ v7k_inits_dict = self._get_initiators()
+ for v7k_init_dict in v7k_inits_dict.itervalues():
+ v7k_init = self._get_initiator(v7k_init_dict['id'])
+ lsm_ags.extend(
+ [IbmV7k._v7k_init_to_lsm_ag(v7k_init, self.sys_info.id)])
+ return lsm_ags
+
def volume_create(self, pool, volume_name, size_bytes, provisioning,
flags=0):
self._create_volume(pool.id, volume_name, size_bytes, provisioning)
--
1.8.3.1