summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Grover <andy@groveronline.com>2014-12-16 10:35:44 -0800
committerAndy Grover <andy@groveronline.com>2014-12-16 10:35:44 -0800
commita5efbcfd1378018907ea4b681f193f6fd5b59448 (patch)
treec8364eee89fc9e293d6b400c82f192608e54a83c
parentde5bf11e8ca349e92ed02aa2cacb12b321a140c7 (diff)
parent1a36126a1747ab8f44dc3afebba46e6713385697 (diff)
downloadrtslib-fb-a5efbcfd1378018907ea4b681f193f6fd5b59448.tar.gz
Merge pull request #56 from agrover/dev-groups
Initiator groups
-rw-r--r--rtslib/__init__.py1
-rw-r--r--rtslib/target.py343
2 files changed, 342 insertions, 2 deletions
diff --git a/rtslib/__init__.py b/rtslib/__init__.py
index 5b6dfd6..56a8062 100644
--- a/rtslib/__init__.py
+++ b/rtslib/__init__.py
@@ -26,6 +26,7 @@ from utils import RTSLibError, RTSLibBrokenLink, RTSLibNotInCFS
from target import LUN, MappedLUN
from target import NodeACL, NetworkPortal, TPG, Target
+from target import NodeACLGroup, MappedLUNGroup
from fabric import FabricModule
from tcm import FileIOStorageObject, BlockStorageObject
diff --git a/rtslib/target.py b/rtslib/target.py
index 95b93c4..2787c42 100644
--- a/rtslib/target.py
+++ b/rtslib/target.py
@@ -290,6 +290,20 @@ class TPG(CFSNode):
fm = self.parent_target.fabric_module
yield NodeACL(self, fm.from_fabric_wwn(node_acl_dir), 'lookup')
+ def _list_node_acl_groups(self):
+ self._check_self()
+ if not self.has_feature('acls'):
+ return
+
+ names = set([])
+
+ for na in self.node_acls:
+ tag = na.tag
+ if tag:
+ names.add(tag)
+
+ return (NodeACLGroup(self, n) for n in names)
+
def _list_luns(self):
self._check_self()
lun_dirs = [os.path.basename(path)
@@ -368,6 +382,9 @@ class TPG(CFSNode):
node_acls = property(_list_node_acls,
doc="Get the list of NodeACL objects currently " \
+ "attached to the TPG.")
+ node_acl_groups = property(_list_node_acl_groups,
+ doc="Get the list of NodeACL groups currently " \
+ + "attached to the TPG.")
luns = property(_list_luns,
doc="Get the list of LUN objects currently attached " \
+ "to the TPG.")
@@ -446,7 +463,7 @@ class LUN(CFSNode):
A LUN is identified by its parent TPG and LUN index.
'''
- MAX_LUN = 16383
+ MAX_LUN = 255
# LUN private stuff
@@ -975,7 +992,9 @@ class MappedLUN(CFSNode):
# MappedLUN private stuff
def __repr__(self):
- return "<MappedLUN %d -> %d>" % (self.mapped_lun, self.tpg_lun.lun)
+ return "<MappedLUN %s lun %d -> tpg%d lun %d>" % \
+ (self.parent_nodeacl.node_wwn, self.mapped_lun,
+ self.parent_nodeacl.parent_tpg.tag, self.tpg_lun.lun)
def __init__(self, parent_nodeacl, mapped_lun,
tpg_lun=None, write_protect=None):
@@ -1163,6 +1182,326 @@ class MappedLUN(CFSNode):
return d
+class Group(object):
+ '''
+ An abstract base class akin to CFSNode, but for classes that
+ emulate a higher-level group object across the actual NodeACL
+ configfs structure.
+ '''
+ def __init__(self, members_func):
+ '''
+ members_func is a function that takes a self argument
+ and returns an iterator of the objects that the
+ derived Group class is grouping.
+ '''
+ self._mem_func = members_func
+
+ def _get_first_member(self):
+ try:
+ return self._mem_func(self).next()
+ except StopIteration:
+ raise IndexError("Group is empty")
+
+ def _get_prop(self, prop):
+ '''
+ Helper fn to use with partial() to support getting a
+ property value from the first member of the group.
+ (All members of the group should be identical.)
+ '''
+ return getattr(self._get_first_member(), prop)
+
+ def _set_prop(self, value, prop):
+ '''
+ Helper fn to use with partial() to support setting a
+ property value in all members of the group.
+
+ Caution: Arguments reversed!
+ This is so partial() can be used on property name.
+ '''
+
+ for mem in self._mem_func(self):
+ setattr(mem, prop, value)
+
+ def list_attributes(self, writable=None):
+ return self._get_first_member().list_attributes(writable)
+
+ def list_parameters(self, writable=None):
+ return self._get_first_member().list_parameters(writable)
+
+ def set_attribute(self, attribute, value):
+ for obj in self._mem_func(self):
+ obj.set_attribute(attribute, value)
+
+ def set_parameter(self, parameter, value):
+ for obj in self._mem_func(self):
+ obj.set_parameter(parameter, value)
+
+ def get_attribute(self, attribute):
+ return self._get_first_member().get_attribute(attribute)
+
+ def get_parameter(self, parameter):
+ return self._get_first_member().get_parameter(parameter)
+
+ def delete(self):
+ '''
+ Delete all members of the group.
+ '''
+ for mem in self._mem_func(self):
+ mem.delete()
+
+ @property
+ def exists(self):
+ return any(self._mem_func(self))
+
+
+def _check_group_name(name):
+ # Since all WWNs have a '.' in them, let's avoid confusion.
+ if '.' in name:
+ raise ExecutionError("'.' not permitted in group names.")
+
+
+class NodeACLGroup(Group):
+ '''
+ Allow a group of NodeACLs that share a tag to be managed collectively.
+ '''
+ def __repr__(self):
+ return "<NodeACLGroup %s>" % self.name
+
+ def __init__(self, parent_tpg, name):
+ super(NodeACLGroup, self).__init__(NodeACLGroup._node_acls.fget)
+ _check_group_name(name)
+ self._name = name
+ self._parent_tpg = parent_tpg
+
+ def _get_name(self):
+ return self._name
+
+ def _set_name(self, name):
+ _check_group_name(name)
+ for na in self._node_acls:
+ na.tag = name
+ self._name = name
+
+ @property
+ def parent_tpg(self):
+ '''
+ Get the parent TPG object.
+ '''
+ return self._parent_tpg
+
+ def add_acl(self, node_wwn):
+ '''
+ Add a WWN to the NodeACLGroup. If a NodeACL already exists for this WWN,
+ its configuration will be changed to match the NodeACLGroup, except for its
+ auth parameters, which can vary among group members.
+ @param node_wwn: An initiator WWN
+ @type node_wwn: string
+ '''
+ nacl = NodeACL(self.parent_tpg, node_wwn)
+
+ if nacl in self._node_acls:
+ return
+
+ # if joining a group, take its config
+ try:
+ model = self._node_acls.next()
+ except StopIteration:
+ pass
+ else:
+ for mlun in nacl.mapped_luns:
+ mlun.delete()
+
+ for mlun in model.mapped_luns:
+ MappedLUN(nacl, mlun.mapped_lun, mlun.tpg_lun, mlun.write_protect)
+
+ for item in model.list_attributes(writable=True):
+ nacl.set_attribute(item, model.get_attribute(item))
+ for item in model.list_parameters(writable=True):
+ nacl.set_parameter(item, model.get_parameter(item))
+ finally:
+ nacl.tag = self.name
+
+ def remove_acl(self, node_wwn):
+ '''
+ Remove a WWN from the NodeACLGroup.
+ @param node_wwn: An initiator WWN
+ @type node_wwn: string
+ '''
+ nacl = NodeACL(self.parent_tpg, node_wwn, mode='lookup')
+
+ nacl.delete()
+
+ @property
+ def _node_acls(self):
+ '''
+ Gives access to the underlying NodeACLs within this group.
+ '''
+ for na in self.parent_tpg.node_acls:
+ if na.tag == self.name:
+ yield na
+
+ @property
+ def wwns(self):
+ '''
+ Give the Node WWNs of members of this group.
+ '''
+ return (na.node_wwn for na in self._node_acls)
+
+ def has_feature(self, feature):
+ '''
+ Whether or not this NodeACL has a certain feature.
+ '''
+ return self._parent_tpg.has_feature(feature)
+
+ @property
+ def sessions(self):
+ '''
+ Yields any current sessions.
+ '''
+ for na in self._node_acls:
+ session = na.session
+ if session:
+ yield session
+
+ def mapped_lun_group(self, mapped_lun, tpg_lun=None, write_protect=None):
+ '''
+ Add a mapped lun to all group members.
+ '''
+ return MappedLUNGroup(self, mapped_lun=mapped_lun, tpg_lun=tpg_lun,
+ write_protect=write_protect)
+
+ @property
+ def mapped_lun_groups(self):
+ '''
+ Generates all MappedLUNGroup objects in this NodeACLGroup.
+ '''
+ try:
+ first = self._get_first_member()
+ except IndexError:
+ return
+
+ for mlun in first.mapped_luns:
+ yield MappedLUNGroup(self, mlun.mapped_lun)
+
+ name = property(_get_name, _set_name,
+ doc="Get/set NodeACLGroup name.")
+
+ def _get_chap(self, name):
+ for na in self._node_acls:
+ yield (na.node_wwn, getattr(na, "chap_" + name))
+
+ def _set_chap(self, name, value, wwn):
+ for na in self._node_acls:
+ if not wwn:
+ setattr(na, "chap_" + name, value)
+ elif wwn == na.node_wwn:
+ setattr(na, "chap_" + name, value)
+ break
+
+ def get_userids(self):
+ '''
+ Returns a (wwn, userid) tuple for each member of the group.
+ '''
+ return self._get_chap(name="userid")
+
+ def set_userids(self, value, wwn=None):
+ '''
+ If wwn, set the userid for just that wwn, otherwise set it for
+ all group members.
+ '''
+ return self._set_chap("userid", value, wwn)
+
+ def get_passwords(self):
+ '''
+ Returns a (wwn, password) tuple for each member of the group.
+ '''
+ return self._get_chap(name="password")
+
+ def set_passwords(self, value, wwn=None):
+ '''
+ If wwn, set the password for just that wwn, otherwise set it for
+ all group members.
+ '''
+ return self._set_chap("password", value, wwn)
+
+ def get_mutual_userids(self):
+ '''
+ Returns a (wwn, mutual_userid) tuple for each member of the group.
+ '''
+ return self._get_chap(name="mutual_userid")
+
+ def set_mutual_userids(self, value, wwn=None):
+ '''
+ If wwn, set the mutual_userid for just that wwn, otherwise set it for
+ all group members.
+ '''
+ return self._set_chap("mutual_userid", value, wwn)
+
+ def get_mutual_passwords(self):
+ '''
+ Returns a (wwn, mutual_password) tuple for each member of the group.
+ '''
+ return self._get_chap(name="mutual_password")
+
+ def set_mutual_passwords(self, value, wwn=None):
+ '''
+ If wwn, set the mutual_password for just that wwn, otherwise set it for
+ all group members.
+ '''
+ return self._set_chap("mutual_password", value, wwn)
+
+ tcq_depth = property(partial(Group._get_prop, prop="tcq_depth"),
+ partial(Group._set_prop, prop="tcq_depth"),
+ doc="Set or get the TCQ depth for the initiator "
+ + "sessions matching this NodeACLGroup")
+ authenticate_target = property(partial(Group._get_prop, prop="authenticate_target"),
+ doc="Get the boolean authenticate target flag.")
+
+
+class MappedLUNGroup(Group):
+ '''
+ Used with NodeACLGroup, this aggregates all MappedLUNs with the same LUN
+ so that it can be configured across all members of the NodeACLGroup.
+ '''
+
+ def __repr__(self):
+ return "<MappedLUNGroup %s:lun %d>" % (self._nag.name, self._mapped_lun)
+
+ def __init__(self, nodeaclgroup, mapped_lun, *args, **kwargs):
+ super(MappedLUNGroup, self).__init__(MappedLUNGroup._mapped_luns.fget)
+ self._nag = nodeaclgroup
+ self._mapped_lun = mapped_lun
+ for na in self._nag._node_acls:
+ MappedLUN(na, mapped_lun=mapped_lun, *args, **kwargs)
+
+ @property
+ def _mapped_luns(self):
+ for na in self._nag._node_acls:
+ for mlun in na.mapped_luns:
+ if mlun.mapped_lun == self.mapped_lun:
+ yield mlun
+
+ @property
+ def mapped_lun(self):
+ '''
+ Get the integer MappedLUN mapped_lun index.
+ '''
+ return self._mapped_lun
+
+ @property
+ def parent_nodeaclgroup(self):
+ '''
+ Get the parent NodeACLGroup object.
+ '''
+ return self._nag
+
+ write_protect = property(partial(Group._get_prop, prop="write_protect"),
+ partial(Group._set_prop, prop="write_protect"),
+ doc="Get or set the boolean write protection.")
+ tpg_lun = property(partial(Group._get_prop, prop="tpg_lun"),
+ doc="Get the TPG LUN object the MappedLUN is pointing at.")
+
+
def _test():
from doctest import testmod
testmod()