Discussion:
[Libstoragemgmt-devel] [PATCH 0/4] Update lsm.AccessGroup and related methods
Gris Ge
2014-06-04 17:03:45 UTC
Permalink
* Update lsm.AccessGroup, preparation for the removal of lsm.Initiator.
* Plugin update for this change.
* lsmcli update for this change.
* lsmcli use 'list' command for volume masking query.
* cmdtest.py use 'list' command for volume masking query test.

Gris Ge (4):
Python library: Update lsm.AccessGroup and related methods
Plugins: Sync with updated lsm.AccessGroup
lsmcli: Sync with updated lsm.AccessGroup
cmdtest.py: use list command for volume masking query

doc/man/lsmcli.1.in | 4 +-
plugin/nstor/nstor.py | 46 +++---
plugin/ontap/ontap.py | 2 +-
plugin/sim/simarray.py | 7 +-
plugin/sim/simulator.py | 29 ++--
plugin/smispy/smis.py | 310 +++++++++++++++++++++++++----------------
plugin/targetd/targetd.py | 102 +++++++++++++-
plugin/v7k/ibmv7k.py | 41 ++++++
python_binding/lsm/_client.py | 26 ++--
python_binding/lsm/_data.py | 41 +++++-
python_binding/lsm/_iplugin.py | 17 +--
test/cmdtest.py | 6 +-
tools/lsmcli/cmdline.py | 57 ++++++--
tools/lsmcli/data_display.py | 27 +++-
14 files changed, 512 insertions(+), 203 deletions(-)
--
1.8.3.1
Gris Ge
2014-06-04 17:03:46 UTC
Permalink
* Update lsm.AccessGroup to replace lsm.Initiator class.
* Added properties:
* init_type
* optional_data
# No optional data yet.
* plugin_data
# Reserved for plugin internal use.
* Renamed properties:
* initiators -> init_ids

* Renamed Capability:
* lsm.Capability.ACCESS_GROUP_LIST -> lsm.Capability.ACCESS_GROUPS
# To follow the same naming scheme with others.

* Updated lsm.Client methods:
* Change the 'group' argument to 'access_group' of these methods:
volume_mask()
volume_unmask()
access_group_delete()
access_group_initiator_add()
access_group_initiator_delete()
* Changed the 'initiator_id' to 'init_id' of these methods:
# To fit the lsm.AccessGroup property name
access_group_create()
access_group_initiator_add()
access_group_initiator_delete()
* Changed the 'id_type' to 'init_type of these methods:
# To fit the lsm.AccessGroup property name
access_group_create()
access_group_initiator_add()

Signed-off-by: Gris Ge <***@redhat.com>
---
python_binding/lsm/_client.py | 26 +++++++++++++-------------
python_binding/lsm/_data.py | 41 +++++++++++++++++++++++++++++++++++++----
python_binding/lsm/_iplugin.py | 17 +++++++++--------
3 files changed, 59 insertions(+), 25 deletions(-)

diff --git a/python_binding/lsm/_client.py b/python_binding/lsm/_client.py
index d640ddf..da06453 100644
--- a/python_binding/lsm/_client.py
+++ b/python_binding/lsm/_client.py
@@ -691,7 +691,7 @@ class Client(INetworkAttachedStorage):
# @param flags Reserved for future use, must be zero.
# @returns None on success, throws LsmError on errors.
@_return_requires(None)
- 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.
"""
@@ -704,7 +704,7 @@ class Client(INetworkAttachedStorage):
# @param flags Reserved for future use, must be zero.
# @returns None on success, throws LsmError on errors.
@_return_requires(None)
- 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
"""
@@ -725,17 +725,17 @@ class Client(INetworkAttachedStorage):
## Creates an access a group with the specified initiator in it.
# @param self The this pointer
# @param name The initiator group name
- # @param initiator_id Initiator id
- # @param id_type Type of initiator (Enumeration)
+ # @param init_id Initiator id
+ # @param init_type Type of initiator (Enumeration)
# @param system_id Which system to create this group on
# @param flags Reserved for future use, must be zero.
# @returns AccessGroup on success, else raises LsmError
@_return_requires(AccessGroup)
- 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 an access group and add the specified initiator id, id_type and
- desired access.
+ Creates an access group and add the specified initiator id,
+ init_type and desired access.
"""
return self._tp.rpc('access_group_create', _del_self(locals()))

@@ -745,7 +745,7 @@ class Client(INetworkAttachedStorage):
# @param flags Reserved for future use, must be zero.
# @returns None on success, throws LsmError on errors.
@_return_requires(None)
- def access_group_delete(self, group, flags=0):
+ def access_group_delete(self, access_group, flags=0):
"""
Deletes an access group
"""
@@ -754,12 +754,12 @@ class Client(INetworkAttachedStorage):
## Adds an initiator to an access group
# @param self The this pointer
# @param group Group to add initiator to
- # @param initiator_id Initiators id
- # @param id_type Initiator id type (enumeration)
+ # @param init_id Initiators id
+ # @param init_type Initiator id type (enumeration)
# @param flags Reserved for future use, must be zero.
# @returns None on success, throws LsmError on errors.
@_return_requires(None)
- 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
@@ -773,7 +773,7 @@ class Client(INetworkAttachedStorage):
# @param flags Reserved for future use, must be zero.
# @returns None on success, throws LsmError on errors.
@_return_requires(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
"""
@@ -786,7 +786,7 @@ class Client(INetworkAttachedStorage):
# @param flags Reserved for future use, must be zero.
# @returns list of volumes
@_return_requires([Volume])
- 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.
"""
diff --git a/python_binding/lsm/_data.py b/python_binding/lsm/_data.py
index a490db1..18a22a9 100644
--- a/python_binding/lsm/_data.py
+++ b/python_binding/lsm/_data.py
@@ -42,6 +42,22 @@ def get_key(dictionary, value):
return None


+def _check_opt_data(optional_data, allowed_properties):
+ if optional_data is None:
+ return OptionalData()
+ else:
+ #Make sure the properties only contain ones we permit
+ allowed = set(allowed_properties)
+ actual = set(optional_data.list())
+
+ if actual <= allowed:
+ return optional_data
+ else:
+ raise LsmError(ErrorNumber.INVALID_ARGUMENT,
+ "Property keys are not supported: %s" %
+ "".join(actual - allowed))
+
+
class DataEncoder(json.JSONEncoder):
"""
Custom json encoder for objects derived form ILsmData
@@ -760,16 +776,33 @@ class BlockRange(IData):

@default_property('id', doc="Unique instance identifier")
@default_property('name', doc="Access group name")
-@default_property('initiators', doc="List of initiators")
+@default_property('init_ids', doc="List of initiator IDs")
+@default_property('init_type', doc="Initiator type")
@default_property('system_id', doc="System identifier")
+@default_property('optional_data', doc="Optional data")
+@default_property('plugin_data', doc="Plugin private data")
class AccessGroup(IData):
SUPPORTED_SEARCH_KEYS = ['id', 'system_id']

- def __init__(self, _id, _name, _initiators, _system_id='NA'):
+ INIT_TYPE_UNKNOWN = 0
+ INIT_TYPE_OTHER = 1
+ INIT_TYPE_WWPN = 2
+ INIT_TYPE_WWNN = 3
+ INIT_TYPE_HOSTNAME = 4
+ INIT_TYPE_ISCSI_IQN = 5
+ INIT_TYPE_SAS = 6
+ INIT_TYPE_ISCSI_WWPN_MIXED = 7
+
+ def __init__(self, _id, _name, _init_ids, _init_type,
+ _system_id, _optional_data=None, _plugin_data=None):
self._id = _id
self._name = _name # AccessGroup name
- self._initiators = _initiators # List of initiators
+ self._init_ids = _init_ids # List of initiator IDs
+ self._init_type = _init_type
self._system_id = _system_id # System id this group belongs
+ self._plugin_data = _plugin_data
+ self._optional_data = _check_opt_data(_optional_data,
+ AccessGroup.OPT_PROPERTIES)


class OptionalData(IData):
@@ -829,7 +862,7 @@ class Capabilities(IData):

VOLUME_MASK = 36
VOLUME_UNMASK = 37
- ACCESS_GROUP_LIST = 38
+ ACCESS_GROUPS = 38
ACCESS_GROUP_CREATE = 39
ACCESS_GROUP_DELETE = 40
ACCESS_GROUP_ADD_INITIATOR = 41
diff --git a/python_binding/lsm/_iplugin.py b/python_binding/lsm/_iplugin.py
index e07f8b3..6cb4d03 100644
--- a/python_binding/lsm/_iplugin.py
+++ b/python_binding/lsm/_iplugin.py
@@ -282,7 +282,7 @@ class IStorageAreaNetwork(IPlugin):
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- def initiator_grant(self, initiator_id, initiator_type, volume, access,
+ def initiator_grant(self, init_id, initiator_type, volume, access,
flags=0):
"""
Allows an initiator to access a volume.
@@ -299,7 +299,7 @@ class IStorageAreaNetwork(IPlugin):
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- 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.

@@ -307,7 +307,7 @@ class IStorageAreaNetwork(IPlugin):
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- 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

@@ -321,33 +321,34 @@ class IStorageAreaNetwork(IPlugin):
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- 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):
"""
Returns a list of access groups, raises LsmError on errors.
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- def access_group_delete(self, group, flags=0):
+ def access_group_delete(self, access_group, flags=0):
"""
Deletes an access group, Raises LsmError on error
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- 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, Raises LsmError on error
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- 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, Raises LsmError on error
"""
raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported")

- 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.
Raises LsmError on error
--
1.8.3.1
Gris Ge
2014-06-04 17:03:47 UTC
Permalink
* 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.

Signed-off-by: Gris Ge <***@redhat.com>
---
plugin/nstor/nstor.py | 46 ++++---
plugin/ontap/ontap.py | 2 +-
plugin/sim/simarray.py | 7 +-
plugin/sim/simulator.py | 29 ++---
plugin/smispy/smis.py | 310 ++++++++++++++++++++++++++++------------------
plugin/targetd/targetd.py | 102 ++++++++++++++-
plugin/v7k/ibmv7k.py | 41 ++++++
7 files changed, 377 insertions(+), 160 deletions(-)

diff --git a/plugin/nstor/nstor.py b/plugin/nstor/nstor.py
index e06ac95..b6456ce 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,20 +702,27 @@ 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
"""
+ 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 initiator_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,
@@ -722,7 +730,7 @@ class NexentaStor(INfs, IStorageAreaNetwork):
self._request("create_hostgroup", "stmf", [name])
self._add_initiator(name, initiator_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):
"""
@@ -739,14 +747,14 @@ 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, 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)
return None
@@ -758,15 +766,15 @@ class NexentaStor(INfs, IStorageAreaNetwork):
self._add_initiator(group.name, initiator_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 +782,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..c466651 100644
--- a/plugin/ontap/ontap.py
+++ b/plugin/ontap/ontap.py
@@ -441,7 +441,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)
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..af4d2d1 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,42 @@ 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):
+ 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 +1621,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 +1635,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 +1654,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 +1975,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 +2015,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 +2034,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 +2062,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 +2129,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 +2167,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._new_access_group(cim_ag)])

return access_groups
else:
@@ -2143,11 +2192,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 +2223,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 +2250,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..49ae428 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,105 @@ 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):
+ if search_key:
+ return access_group_search(self, search_key, search_value, flags)
+ 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 rc
+
+ 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
Gris Ge
2014-06-04 17:30:45 UTC
Permalink
* 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.

Signed-off-by: Gris Ge <***@redhat.com>
---
plugin/nstor/nstor.py | 46 ++++---
plugin/ontap/ontap.py | 2 +-
plugin/sim/simarray.py | 7 +-
plugin/sim/simulator.py | 29 ++---
plugin/smispy/smis.py | 312 ++++++++++++++++++++++++++++------------------
plugin/targetd/targetd.py | 102 ++++++++++++++-
plugin/v7k/ibmv7k.py | 41 ++++++
7 files changed, 379 insertions(+), 160 deletions(-)

diff --git a/plugin/nstor/nstor.py b/plugin/nstor/nstor.py
index e06ac95..b6456ce 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,20 +702,27 @@ 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
"""
+ 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 initiator_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,
@@ -722,7 +730,7 @@ class NexentaStor(INfs, IStorageAreaNetwork):
self._request("create_hostgroup", "stmf", [name])
self._add_initiator(name, initiator_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):
"""
@@ -739,14 +747,14 @@ 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, 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)
return None
@@ -758,15 +766,15 @@ class NexentaStor(INfs, IStorageAreaNetwork):
self._add_initiator(group.name, initiator_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 +782,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..c466651 100644
--- a/plugin/ontap/ontap.py
+++ b/plugin/ontap/ontap.py
@@ -441,7 +441,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)
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..49ae428 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,105 @@ 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):
+ if search_key:
+ return access_group_search(self, search_key, search_value, flags)
+ 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 rc
+
+ 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
Gris Ge
2014-06-04 17:53:23 UTC
Permalink
* 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
Gris Ge
2014-06-04 17:03:48 UTC
Permalink
* Update lsmcli, notable changes:
* Use 'list' command to do masking filter:
list --type volumes --ag <AG_ID>
list --type access_groups --vol <VOL_ID>
Old 'access-group-volumes' and 'volume-access-group' is untouched,
but might be removed in the future.
* Manpage updated.

Signed-off-by: Gris Ge <***@redhat.com>
---
doc/man/lsmcli.1.in | 4 ++--
tools/lsmcli/cmdline.py | 57 +++++++++++++++++++++++++++++++++++---------
tools/lsmcli/data_display.py | 27 +++++++++++++++++++--
3 files changed, 73 insertions(+), 15 deletions(-)

diff --git a/doc/man/lsmcli.1.in b/doc/man/lsmcli.1.in
index 20add64..53d8673 100644
--- a/doc/man/lsmcli.1.in
+++ b/doc/man/lsmcli.1.in
@@ -173,7 +173,7 @@ resources: \fBVOLUMES\fR, \fBPOOLS\fR, \fBFS\fR.
.TP
\fB--vol\fR \fI<VOL_ID>\fR
Search resources from volume with VOL_ID. Only supported by these types of
-resources: \fBVOLUMES\fR.
+resources: \fBVOLUMES\fR, \fBACCESS_GROUPS\fR.
.TP
\fB--disk\fR \fI<DISK_ID>\fR
Search resources from disk with DISK_ID. Only supported by these types of
@@ -181,7 +181,7 @@ resources: \fBDISK\fR.
.TP
\fB--ag\fR \fI<AG_ID>\fR
Search resources from access group with AG_ID. Only supported by these types
-of resources: \fBACCESS_GROUPS\fR.
+of resources: \fBACCESS_GROUPS\fR, \fBVOLUMES\fR.
.TP
\fB--fs\fR \fI<FS_ID>\fR
Search resources from file system with FS_ID. Only supported by these types
diff --git a/tools/lsmcli/cmdline.py b/tools/lsmcli/cmdline.py
index a416346..f9d7b49 100644
--- a/tools/lsmcli/cmdline.py
+++ b/tools/lsmcli/cmdline.py
@@ -35,7 +35,7 @@ from lsm.lsmcli.data_display import (
DisplayData, PlugData, out,
pool_raid_type_str_to_type, pool_member_type_str_to_type,
vol_provision_str_to_type, vol_rep_type_str_to_type,
- vol_access_type_str_to_type)
+ vol_access_type_str_to_type, ag_init_type_str_to_lsm)


## Wraps the invocation to the command line
@@ -348,9 +348,18 @@ cmds = (
help='Grants access to an access group to a volume, '
'like LUN Masking',
args=[
- dict(ag_id_opt),
dict(vol_id_opt),
],
+ optional=[
+ dict(ag_id_opt),
+ dict(name='--init', metavar='<INIT_ID>', action='append',
+ help='Initiator ID, only used when access-group-create is '
+ 'not supported'),
+ dict(name="--init-type", type=str.upper,
+ metavar='<INIT_TYPE>', choices=initiator_id_types,
+ help="%s only used when access-group-create is not supported"
+ % initiator_id_help),
+ ],
),

dict(
@@ -941,7 +950,12 @@ class CmdLine:
if args.type == 'VOLUMES':
if search_key == 'volume_id':
search_key = 'id'
- if search_key and search_key not in Volume.SUPPORTED_SEARCH_KEYS:
+ if search_key == 'access_group_id':
+ lsm_ag = _get_item(self.c.access_groups(), args.ag,
+ "Access Group ID")
+ return self.display_data(
+ self.c.volumes_accessible_by_access_group(lsm_ag))
+ elif search_key and search_key not in Volume.SUPPORTED_SEARCH_KEYS:
raise ArgError("Search key '%s' is not supported by "
"volume listing." % search_key)
self.display_data(self.c.volumes(search_key, search_value))
@@ -985,7 +999,12 @@ class CmdLine:
elif args.type == 'ACCESS_GROUPS':
if search_key == 'access_group_id':
search_key = 'id'
- if search_key and \
+ if search_key == 'volume_id':
+ lsm_vol = _get_item(self.c.volumes(), args.vol,
+ "Volume ID")
+ return self.display_data(
+ self.c.access_groups_granted_to_volume(lsm_vol))
+ elif search_key and \
search_key not in AccessGroup.SUPPORTED_SEARCH_KEYS:
raise ArgError("Search key '%s' is not supported by "
"Access Group listing" % search_key)
@@ -1212,8 +1231,8 @@ class CmdLine:
cap.supported(Capabilities.VOLUME_MASK))
self._cp("VOLUME_UNMASK",
cap.supported(Capabilities.VOLUME_UNMASK))
- self._cp("ACCESS_GROUP_LIST",
- cap.supported(Capabilities.ACCESS_GROUP_LIST))
+ self._cp("ACCESS_GROUPS",
+ cap.supported(Capabilities.ACCESS_GROUPS))
self._cp("ACCESS_GROUP_CREATE",
cap.supported(Capabilities.ACCESS_GROUP_CREATE))
self._cp("ACCESS_GROUP_DELETE",
@@ -1466,9 +1485,6 @@ class CmdLine:
return self._access(False, args)

def _volume_masking(self, args, grant=True):
- agl = self.c.access_groups()
- group = _get_item(agl, args.ag, "access group id")
- v = _get_item(self.c.volumes(), args.vol, "volume id")

if grant:
self.c.volume_mask(group, v)
@@ -1476,10 +1492,29 @@ class CmdLine:
self.c.volume_unmask(group, v)

def volume_mask(self, args):
- return self._volume_masking(args, grant=True)
+ if not args.ag and not args.init:
+ raise ArgError('Neither --ag nor --init is defined. '
+ 'Please specify at lease one')
+ if args.init and not args.init_type:
+ raise ArgError('In volume-mask command, --init and '
+ '--init-type should be defined at the same time')
+ if args.ag and args.init:
+ raise ArgError('In volume-mask command, --init is '
+ 'conflicting with --ag')
+ vol = _get_item(self.c.volumes(), args.vol, 'Volume ID')
+ ag = None
+ if args.ag:
+ ag = _get_item(self.c.access_groups(), args.ag, 'Access Group ID')
+
+ if args.init:
+ init_type = ag_init_type_str_to_lsm(args.init_type)
+ ag = AccessGroup(0, '', args.init, init_type, vol.system_id)
+ self.c.volume_mask(ag, vol)

def volume_unmask(self, args):
- return self._volume_masking(args, grant=False)
+ ag = _get_item(self.c.access_groups(), args.ag, "Access Group ID")
+ vol = _get_item(self.c.volumes(), args.vol, "volume id")
+ return self.c.volume_unmask(ag, vol)

## Re-sizes a volume
def volume_resize(self, args):
diff --git a/tools/lsmcli/data_display.py b/tools/lsmcli/data_display.py
index 66be13c..e9b7c19 100644
--- a/tools/lsmcli/data_display.py
+++ b/tools/lsmcli/data_display.py
@@ -307,6 +307,26 @@ def init_type_to_str(init_type):
return _enum_type_to_str(init_type, _INIT_TYPE_CONV)


+_AG_INIT_TYPE_CONV = {
+ AccessGroup.INIT_TYPE_UNKNOWN: 'Unknown',
+ AccessGroup.INIT_TYPE_OTHER: 'Other',
+ AccessGroup.INIT_TYPE_WWPN: 'WWPN',
+ AccessGroup.INIT_TYPE_WWNN: 'WWNN',
+ AccessGroup.INIT_TYPE_HOSTNAME: 'Hostname',
+ AccessGroup.INIT_TYPE_ISCSI_IQN: 'iSCSI',
+ AccessGroup.INIT_TYPE_SAS: 'SAS',
+ AccessGroup.INIT_TYPE_ISCSI_WWPN_MIXED: 'iSCSI/WWPN Mixed',
+}
+
+
+def ag_init_type_to_str(init_type):
+ return _enum_type_to_str(init_type, _AG_INIT_TYPE_CONV)
+
+
+def ag_init_type_str_to_lsm(init_type_str):
+ return _str_to_enum(init_type_str, _AG_INIT_TYPE_CONV)
+
+
class PlugData(object):
def __init__(self, description, plugin_version):
self.desc = description
@@ -490,14 +510,17 @@ class DisplayData(object):
AG_MAN_HEADER = OrderedDict()
AG_MAN_HEADER['id'] = 'ID'
AG_MAN_HEADER['name'] = 'Name'
- AG_MAN_HEADER['initiators'] = 'Initiator IDs'
+ AG_MAN_HEADER['init_ids'] = 'Initiator IDs'
+ AG_MAN_HEADER['init_type'] = 'Type'
AG_MAN_HEADER['system_id'] = 'System ID'

AG_COLUMN_KEYS = AG_MAN_HEADER.keys()

AG_OPT_HEADER = OrderedDict()

- AG_VALUE_CONV_ENUM = {}
+ AG_VALUE_CONV_ENUM = {
+ 'init_type': ag_init_type_to_str,
+ }

AG_VALUE_CONV_HUMAN = []
--
1.8.3.1
Gris Ge
2014-06-04 17:03:49 UTC
Permalink
* Use lsmcli new way to replace volumes_accessible_by_access_group()
and access_groups_granted_to_volume() test.
* Rename Capability ACCESS_GROUP_LIST to ACCESS_GROUPS as lsmcli did.

Signed-off-by: Gris Ge <***@redhat.com>
---
test/cmdtest.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/cmdtest.py b/test/cmdtest.py
index be4270e..78b9ee7 100755
--- a/test/cmdtest.py
+++ b/test/cmdtest.py
@@ -222,7 +222,7 @@ def volume_unmask(group, volume_id):


def volumes_accessible_by_access_group(ag_id):
- call([cmd, 'access-group-volumes', '--ag', ag_id])
+ call([cmd, 'list', '--type', 'volumes', '--ag', ag_id])


def volume_accessible_by_initiator(iqn2):
@@ -234,7 +234,7 @@ def initiators_granted_to_volume(vol):


def access_groups_granted_to_volume(vol_id):
- call([cmd, 'volume-access-group', '--vol', vol_id])
+ call([cmd, 'list', '--type', 'access_groups', '--vol', vol_id])


def resize_vol(vol_id):
@@ -454,7 +454,7 @@ def test_display(cap, system_id):
if cap['EXPORTS']:
to_test.append("EXPORTS")

- if cap['ACCESS_GROUP_LIST']:
+ if cap['ACCESS_GROUPS']:
to_test.append("ACCESS_GROUPS")

if cap['FS_SNAPSHOTS']:
--
1.8.3.1
Tony Asleson
2014-06-05 16:16:17 UTC
Permalink
Patch series committed!

Thanks!
Tony
Post by Gris Ge
* Update lsm.AccessGroup, preparation for the removal of lsm.Initiator.
* Plugin update for this change.
* lsmcli update for this change.
* lsmcli use 'list' command for volume masking query.
* cmdtest.py use 'list' command for volume masking query test.
Python library: Update lsm.AccessGroup and related methods
Plugins: Sync with updated lsm.AccessGroup
lsmcli: Sync with updated lsm.AccessGroup
cmdtest.py: use list command for volume masking query
doc/man/lsmcli.1.in | 4 +-
plugin/nstor/nstor.py | 46 +++---
plugin/ontap/ontap.py | 2 +-
plugin/sim/simarray.py | 7 +-
plugin/sim/simulator.py | 29 ++--
plugin/smispy/smis.py | 310 +++++++++++++++++++++++++----------------
plugin/targetd/targetd.py | 102 +++++++++++++-
plugin/v7k/ibmv7k.py | 41 ++++++
python_binding/lsm/_client.py | 26 ++--
python_binding/lsm/_data.py | 41 +++++-
python_binding/lsm/_iplugin.py | 17 +--
test/cmdtest.py | 6 +-
tools/lsmcli/cmdline.py | 57 ++++++--
tools/lsmcli/data_display.py | 27 +++-
14 files changed, 512 insertions(+), 203 deletions(-)
Loading...