summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Grover <andy@groveronline.com>2017-01-04 10:30:12 -0800
committerGitHub <noreply@github.com>2017-01-04 10:30:12 -0800
commitba18560bee026dc9b70068465a92eb006f90cfd0 (patch)
tree995a043317e5a9491a78e66f106b73847a7fb0dc
parent7acc1e73f2100bf9116d271a08ba5fe1fcdcdaca (diff)
parent163ee011246971ea3f6a00acd98df5a2df9a8166 (diff)
downloadrtslib-fb-ba18560bee026dc9b70068465a92eb006f90cfd0.tar.gz
Merge pull request #84 from mikechristie/alua
ALUA support for rtslib-fb
-rw-r--r--rtslib/__init__.py2
-rw-r--r--rtslib/alua.py385
-rw-r--r--rtslib/root.py14
-rw-r--r--rtslib/target.py27
-rw-r--r--rtslib/tcm.py16
5 files changed, 442 insertions, 2 deletions
diff --git a/rtslib/__init__.py b/rtslib/__init__.py
index c4b3cb6..48aab2a 100644
--- a/rtslib/__init__.py
+++ b/rtslib/__init__.py
@@ -33,6 +33,8 @@ from .tcm import FileIOStorageObject, BlockStorageObject
from .tcm import PSCSIStorageObject, RDMCPStorageObject, UserBackedStorageObject
from .tcm import StorageObjectFactory
+from .alua import ALUATargetPortGroup
+
__version__ = 'GIT_VERSION'
__author__ = "Jerome Martin <jxm@risingtidesystems.com>"
__url__ = "http://www.risingtidesystems.com"
diff --git a/rtslib/alua.py b/rtslib/alua.py
new file mode 100644
index 0000000..86a4dd3
--- /dev/null
+++ b/rtslib/alua.py
@@ -0,0 +1,385 @@
+'''
+Implements the RTS ALUA Target Port Group class.
+
+This file is part of RTSLib.
+Copyright (c) 2016 by Red Hat, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may
+not use this file except in compliance with the License. You may obtain
+a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations
+ under the License.
+'''
+
+from .node import CFSNode
+from .utils import RTSLibError, fread, fwrite
+
+alua_rw_params = ['alua_access_state', 'alua_access_status',
+ 'alua_write_metadata', 'alua_access_type', 'preferred',
+ 'nonop_delay_msecs', 'trans_delay_msecs',
+ 'implicit_trans_secs', 'alua_support_offline',
+ 'alua_support_standby', 'alua_support_transitioning',
+ 'alua_support_active_nonoptimized',
+ 'alua_support_unavailable', 'alua_support_active_optimized']
+alua_ro_params = ['tg_pt_gp_id', 'members', 'alua_support_lba_dependent']
+alua_types = ['None', 'Implicit', 'Explicit', 'Implicit and Explicit']
+alua_statuses = ['None', 'Altered by Explicit STPG', 'Altered by Implicit ALUA']
+
+class ALUATargetPortGroup(CFSNode):
+ """
+ ALUA Target Port Group interface
+ """
+
+ def __repr__(self):
+ return "<ALUA TPG %s>" % self.name
+
+ def __init__(self, storage_object, name, tag=None):
+ """
+ @param storage_object: backstore storage object to create ALUA group for
+ @param name: name of ALUA group
+ @param tag: target port group id. If not passed in, try to look
+ up existing ALUA TPG with the same name
+ """
+
+ # default_tg_pt_gp takes tag 1
+ if tag is not None and (tag > 65535 or tag < 1):
+ raise RTSLibError("The TPG Tag must be between 1 and 65535")
+
+ super(ALUATargetPortGroup, self).__init__()
+ self.name = name
+ self.storage_object = storage_object
+
+ self._path = "%s/alua/%s" % (storage_object.path, name)
+
+ if tag is not None:
+ try:
+ self._create_in_cfs_ine('create')
+ except OSError as msg:
+ raise RTSLibError(msg)
+
+ try:
+ fwrite("%s/tg_pt_gp_id" % self._path, tag)
+ except IOError as msg:
+ self.delete()
+ raise RTSLibError("Cannot set id to %d: %s" % (tag, str(msg)))
+ else:
+ try:
+ self._create_in_cfs_ine('lookup')
+ except OSError as msg:
+ raise RTSLibError(msg)
+
+ # Public
+
+ def delete(self):
+ """
+ Delete ALUA TPG and unmap from LUNs
+ """
+ self._check_self()
+
+ # default_tg_pt_gp created by the kernel and cannot be deleted
+ if self.name == "default_tg_pt_gp":
+ raise RTSLibError("Can not delete default_tg_pt_gp")
+
+ # This will reset the ALUA tpg to default_tg_pt_gp
+ super(ALUATargetPortGroup, self).delete()
+
+ def _get_alua_access_state(self):
+ self._check_self()
+ path = "%s/alua_access_state" % self.path
+ return int(fread(path))
+
+ def _set_alua_access_state(self, newstate):
+ self._check_self()
+ path = "%s/alua_access_state" % self.path
+ try:
+ fwrite(path, str(int(newstate)))
+ except IOError as e:
+ raise RTSLibError("Cannot change ALUA state: %s" % e)
+
+ def _get_alua_access_status(self):
+ self._check_self()
+ path = "%s/alua_access_status" % self.path
+ status = fread(path)
+ return alua_statuses.index(status)
+
+ def _set_alua_access_status(self, newstatus):
+ self._check_self()
+ path = "%s/alua_access_status" % self.path
+ try:
+ fwrite(path, str(int(newstatus)))
+ except IOError as e:
+ raise RTSLibError("Cannot change ALUA status: %s" % e)
+
+ def _get_alua_access_type(self):
+ self._check_self()
+ path = "%s/alua_access_type" % self.path
+ alua_type = fread(path)
+ return alua_types.index(alua_type)
+
+ def _set_alua_access_type(self, access_type):
+ self._check_self()
+ path = "%s/alua_access_type" % self.path
+ try:
+ fwrite(path, str(int(access_type)))
+ except IOError as e:
+ raise RTSLibError("Cannot change ALUA access type: %s" % e)
+
+ def _get_preferred(self):
+ self._check_self()
+ path = "%s/preferred" % self.path
+ return int(fread(path))
+
+ def _set_preferred(self, pref):
+ self._check_self()
+ path = "%s/preferred" % self.path
+ try:
+ fwrite(path, str(int(pref)))
+ except IOError as e:
+ raise RTSLibError("Cannot set preferred: %s" % e)
+
+ def _get_alua_write_metadata(self):
+ self._check_self()
+ path = "%s/alua_write_metadata" % self.path
+ return int(fread(path))
+
+ def _set_alua_write_metadata(self, pref):
+ self._check_self()
+ path = "%s/alua_write_metadata" % self.path
+ try:
+ fwrite(path, str(int(pref)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_write_metadata: %s" % e)
+
+ def _get_alua_support_active_nonoptimized(self):
+ self._check_self()
+ path = "%s/alua_support_active_nonoptimized" % self.path
+ return int(fread(path))
+
+ def _set_alua_support_active_nonoptimized(self, enabled):
+ self._check_self()
+ path = "%s/alua_support_active_nonoptimized" % self.path
+ try:
+ fwrite(path, str(int(enabled)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_support_active_nonoptimized: %s" % e)
+
+ def _get_alua_support_active_optimized(self):
+ self._check_self()
+ path = "%s/alua_support_active_optimized" % self.path
+ return int(fread(path))
+
+ def _set_alua_support_active_optimized(self, enabled):
+ self._check_self()
+ path = "%s/alua_support_active_optimized" % self.path
+ try:
+ fwrite(path, str(int(enabled)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_support_active_optimized: %s" % e)
+
+ def _get_alua_support_offline(self):
+ self._check_self()
+ path = "%s/alua_support_offline" % self.path
+ return int(fread(path))
+
+ def _set_alua_support_offline(self, enabled):
+ self._check_self()
+ path = "%s/alua_support_offline" % self.path
+ try:
+ fwrite(path, str(int(enabled)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_support_offline: %s" % e)
+
+ def _get_alua_support_unavailable(self):
+ self._check_self()
+ path = "%s/alua_support_unavailable" % self.path
+ return int(fread(path))
+
+ def _set_alua_support_unavailable(self, enabled):
+ self._check_self()
+ path = "%s/alua_support_unavailable" % self.path
+ try:
+ fwrite(path, str(int(enabled)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_support_unavailable: %s" % e)
+
+ def _get_alua_support_standby(self):
+ self._check_self()
+ path = "%s/alua_support_standby" % self.path
+ return int(fread(path))
+
+ def _set_alua_support_standby(self, enabled):
+ self._check_self()
+ path = "%s/alua_support_standby" % self.path
+ try:
+ fwrite(path, str(int(enabled)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_support_standby: %s" % e)
+
+ def _get_alua_support_transitioning(self):
+ self._check_self()
+ path = "%s/alua_support_transitioning" % self.path
+ return int(fread(path))
+
+ def _set_alua_support_transitioning(self, enabled):
+ self._check_self()
+ path = "%s/alua_support_transitioning" % self.path
+ try:
+ fwrite(path, str(int(enabled)))
+ except IOError as e:
+ raise RTSLibError("Cannot set alua_support_transitioning: %s" % e)
+
+ def _get_alua_support_lba_dependent(self):
+ self._check_self()
+ path = "%s/alua_support_lba_dependent" % self.path
+ return int(fread(path))
+
+ def _get_members(self):
+ self._check_self()
+ path = "%s/members" % self.path
+ return fread(path)
+
+ def _get_tg_pt_gp_id(self):
+ self._check_self()
+ path = "%s/tg_pt_gp_id" % self.path
+ return int(fread(path))
+
+ def _get_trans_delay_msecs(self):
+ self._check_self()
+ path = "%s/trans_delay_msecs" % self.path
+ return int(fread(path))
+
+ def _set_trans_delay_msecs(self, secs):
+ self._check_self()
+ path = "%s/trans_delay_msecs" % self.path
+ try:
+ fwrite(path, str(int(secs)))
+ except IOError as e:
+ raise RTSLibError("Cannot set trans_delay_msecs: %s" % e)
+
+ def _get_implicit_trans_secs(self):
+ self._check_self()
+ path = "%s/implicit_trans_secs" % self.path
+ return int(fread(path))
+
+ def _set_implicit_trans_secs(self, secs):
+ self._check_self()
+ path = "%s/implicit_trans_secs" % self.path
+ try:
+ fwrite(path, str(int(secs)))
+ except IOError as e:
+ raise RTSLibError("Cannot set implicit_trans_secs: %s" % e)
+
+ def _get_nonop_delay_msecs(self):
+ self._check_self()
+ path = "%s/nonop_delay_msecs" % self.path
+ return int(fread(path))
+
+ def _set_nonop_delay_msecs(self, delay):
+ self._check_self()
+ path = "%s/nonop_delay_msecs" % self.path
+ try:
+ fwrite(path, str(int(delay)))
+ except IOError as e:
+ raise RTSLibError("Cannot set nonop_delay_msecs: %s" % e)
+
+ def dump(self):
+ d = super(ALUATargetPortGroup, self).dump()
+ d['name'] = self.name
+ d['tg_pt_gp_id'] = self.tg_pt_gp_id
+ for param in alua_rw_params:
+ d[param] = getattr(self, param, None)
+ return d
+
+ alua_access_state = property(_get_alua_access_state, _set_alua_access_state,
+ doc="Get or set ALUA state. "
+ "0 = Active/optimized, "
+ "1 = Active/non-optimized, "
+ "2 = Standby, "
+ "3 = Unavailable, "
+ "4 = LBA Dependent, "
+ "14 = Offline, "
+ "15 = Transitioning")
+
+ alua_access_type = property(_get_alua_access_type, _set_alua_access_type,
+ doc="Get or set ALUA access type. "
+ "1 = Implicit, 2 = Explicit, 3 = Both")
+
+ alua_access_status = property(_get_alua_access_status,
+ _set_alua_access_status,
+ doc="Get or set ALUA access status. "
+ "0 = None, "
+ "1 = Altered by Explicit STPG, "
+ "2 = Altered by Implicit ALUA")
+
+ preferred = property(_get_preferred, _set_preferred,
+ doc="Get or set preferred bit. 1 = Pref, 0 Not-Pre")
+
+ alua_write_metadata = property(_get_alua_write_metadata,
+ _set_alua_write_metadata,
+ doc="Get or set alua_write_metadata flag. "
+ "enable (1) or disable (0)")
+
+ tg_pt_gp_id = property(_get_tg_pt_gp_id, doc="Get ALUA Target Port Group ID")
+
+ members = property(_get_members, doc="Get LUNs in Target Port Group")
+
+ alua_support_active_nonoptimized = property(_get_alua_support_active_nonoptimized,
+ _set_alua_support_active_nonoptimized,
+ doc="Enable (1) or disable (0) "
+ "Active/non-optimized support")
+
+ alua_support_active_optimized = property(_get_alua_support_active_optimized,
+ _set_alua_support_active_optimized,
+ doc="Enable (1) or disable (0) "
+ "Active/optimized support")
+
+ alua_support_offline = property(_get_alua_support_offline,
+ _set_alua_support_offline,
+ doc="Enable (1) or disable (0) "
+ "offline support")
+
+ alua_support_unavailable = property(_get_alua_support_unavailable,
+ _set_alua_support_unavailable,
+ doc="enable (1) or disable (0) "
+ "unavailable support")
+
+ alua_support_standby = property(_get_alua_support_standby,
+ _set_alua_support_standby,
+ doc="enable (1) or disable (0) "
+ "standby support")
+
+ alua_support_lba_dependent = property(_get_alua_support_lba_dependent,
+ doc="show lba_dependent support "
+ "enabled (1) or disabled (0)")
+
+ alua_support_transitioning = property(_get_alua_support_transitioning,
+ _set_alua_support_transitioning,
+ doc="enable (1) or disable (0) "
+ "transitioning support")
+
+ trans_delay_msecs = property(_get_trans_delay_msecs,
+ _set_trans_delay_msecs,
+ doc="msecs to delay state transition")
+
+ implicit_trans_secs = property(_get_implicit_trans_secs,
+ _set_implicit_trans_secs,
+ doc="implicit transition time limit")
+
+ nonop_delay_msecs = property(_get_nonop_delay_msecs, _set_nonop_delay_msecs,
+ doc="msecs to delay IO when non-optimized")
+
+ @classmethod
+ def setup(cls, storage_obj, alua_tpg, err_func):
+ name = alua_tpg['name']
+ if name == 'default_tg_pt_gp':
+ return
+
+ alua_tpg_obj = cls(storage_obj, name, alua_tpg['tg_pt_gp_id'])
+ for param in alua_rw_params:
+ setattr(alua_tpg_obj, param, alua_tpg[param])
diff --git a/rtslib/root.py b/rtslib/root.py
index b65825f..3cc6fbf 100644
--- a/rtslib/root.py
+++ b/rtslib/root.py
@@ -28,6 +28,7 @@ from .fabric import FabricModule
from .tcm import so_mapping, StorageObject
from .utils import RTSLibError, modprobe, mount_configfs
from .utils import dict_remove, set_attributes
+from .alua import ALUATargetPortGroup
default_save_file = "/etc/target/saveconfig.json"
@@ -85,6 +86,12 @@ class RTSRoot(CFSNode):
for so in StorageObject.all():
yield so
+ def _list_alua_tpgs(self):
+ self._check_self()
+ for so in self.storage_objects:
+ for a in so.alua_tpgs:
+ yield a
+
def _list_tpgs(self):
self._check_self()
for t in self.targets:
@@ -203,7 +210,7 @@ class RTSRoot(CFSNode):
err_func("'plugin' not defined or invalid in storageobject %s" % so['name'])
continue
kwargs = so.copy()
- dict_remove(kwargs, ('exists', 'attributes', 'plugin', 'buffered_mode'))
+ dict_remove(kwargs, ('exists', 'attributes', 'plugin', 'buffered_mode', 'alua_tpgs'))
try:
so_obj = so_cls(**kwargs)
except Exception as e:
@@ -216,6 +223,9 @@ class RTSRoot(CFSNode):
set_attributes(so_obj, so.get('attributes', {}), so_err_func)
+ for alua_tpg in so.get('alua_tpgs', {}):
+ ALUATargetPortGroup.setup(so_obj, alua_tpg, err_func)
+
# Don't need to create fabric modules
for index, fm in enumerate(config.get('fabric_modules', [])):
if 'name' not in fm:
@@ -299,6 +309,8 @@ class RTSRoot(CFSNode):
doc="Get the list of all existing LUN objects.")
fabric_modules = property(_list_fabric_modules,
doc="Get the list of all FabricModule objects.")
+ alua_tpgs = property(_list_alua_tpgs,
+ doc="Get the list of all ALUA TPG objects.")
def _test():
'''Run the doctests.'''
diff --git a/rtslib/target.py b/rtslib/target.py
index c2836db..4c06cf4 100644
--- a/rtslib/target.py
+++ b/rtslib/target.py
@@ -582,6 +582,22 @@ class LUN(CFSNode):
if os.path.realpath("%s/%s" % (mlun.path, mlun.alias)) == self.path:
yield mlun
+ def _get_alua_tg_pt_gp_name(self):
+ self._check_self()
+
+ path = "%s/alua_tg_pt_gp" % self.path
+ group_name = fread(path).splitlines()[0]
+ return group_name.split(':')[1].strip()
+
+ def _set_alua_tg_pt_gp_name(self, group_name):
+ self._check_self()
+
+ path = "%s/alua_tg_pt_gp" % self.path
+ try:
+ fwrite(path, group_name)
+ except IOError as e:
+ raise RTSLibError("Cannot set ALUA Target Port Group: %s" % e)
+
# LUN public stuff
def delete(self):
@@ -615,6 +631,8 @@ class LUN(CFSNode):
doc="Get the LUN alias.")
mapped_luns = property(_list_mapped_luns,
doc="List all MappedLUN objects referencing this LUN.")
+ alua_tg_pt_gp_name = property(_get_alua_tg_pt_gp_name, _set_alua_tg_pt_gp_name,
+ doc="Get and Set the LUN's ALUA Target Port Group")
@classmethod
def setup(cls, tpg_obj, lun, err_func):
@@ -637,17 +655,24 @@ class LUN(CFSNode):
return
try:
- cls(tpg_obj, lun['index'], storage_object=match_so, alias=lun.get('alias'))
+ lun_obj = cls(tpg_obj, lun['index'], storage_object=match_so, alias=lun.get('alias'))
except (RTSLibError, KeyError):
err_func("Creating TPG %d LUN index %d failed" %
(tpg_obj.tag, lun['index']))
+ try:
+ lun_obj.alua_tg_pt_gp_name = lun['alua_tg_pt_gp_name']
+ except KeyError:
+ # alua_tg_pt_gp support not present in older versions
+ pass
+
def dump(self):
d = super(LUN, self).dump()
d['storage_object'] = "/backstores/%s/%s" % \
(self.storage_object.plugin, self.storage_object.name)
d['index'] = self.lun
d['alias'] = self.alias
+ d['alua_tg_pt_gp_name'] = self.alua_tg_pt_gp_name
return d
diff --git a/rtslib/tcm.py b/rtslib/tcm.py
index 2ea5d0f..5452f92 100644
--- a/rtslib/tcm.py
+++ b/rtslib/tcm.py
@@ -25,6 +25,7 @@ import glob
import resource
from six.moves import range
+from .alua import ALUATargetPortGroup
from .node import CFSNode
from .utils import fread, fwrite, generate_wwn, RTSLibError, RTSLibNotInCFS
from .utils import convert_scsi_path_to_hctl, convert_scsi_hctl_to_path
@@ -215,6 +216,14 @@ class StorageObject(CFSNode):
for lun in self._gen_attached_luns():
yield lun
+ def _list_alua_tpgs(self):
+ '''
+ Generate all ALUA groups attach to a storage object.
+ '''
+ self._check_self()
+ for tpg in os.listdir("%s/alua" % self.path):
+ yield ALUATargetPortGroup(self, tpg)
+
# StorageObject public stuff
def delete(self):
@@ -226,6 +235,10 @@ class StorageObject(CFSNode):
'''
self._check_self()
+ for alua_tpg in self._list_alua_tpgs():
+ if alua_tpg.name != 'default_tg_pt_gp':
+ alua_tpg.delete()
+
# If we are called after a configure error, we can skip this
if self.is_configured():
for lun in self._gen_attached_luns():
@@ -264,11 +277,14 @@ class StorageObject(CFSNode):
+ "is used by any LUN")
attached_luns = property(_list_attached_luns,
doc="Get the list of all LUN objects attached.")
+ alua_tpgs = property(_list_alua_tpgs,
+ doc="Get list of ALUA Target Port Groups attached.")
def dump(self):
d = super(StorageObject, self).dump()
d['name'] = self.name
d['plugin'] = self.plugin
+ d['alua_tpgs'] = [tpg.dump() for tpg in self.alua_tpgs]
return d