diff --git a/rtslib/__init__.py b/rtslib/__init__.py index 48aab2a..568619e 100644 --- a/rtslib/__init__.py +++ b/rtslib/__init__.py @@ -23,6 +23,7 @@ from .root import RTSRoot from .utils import RTSLibError, RTSLibBrokenLink, RTSLibNotInCFS +from .utils import RTSLibALUANotSupported from .target import LUN, MappedLUN from .target import NodeACL, NetworkPortal, TPG, Target diff --git a/rtslib/alua.py b/rtslib/alua.py index 86a4dd3..390e74a 100644 --- a/rtslib/alua.py +++ b/rtslib/alua.py @@ -18,7 +18,7 @@ ''' from .node import CFSNode -from .utils import RTSLibError, fread, fwrite +from .utils import RTSLibError, RTSLibALUANotSupported, fread, fwrite alua_rw_params = ['alua_access_state', 'alua_access_status', 'alua_write_metadata', 'alua_access_type', 'preferred', @@ -46,6 +46,12 @@ def __init__(self, storage_object, name, tag=None): @param tag: target port group id. If not passed in, try to look up existing ALUA TPG with the same name """ + # kernel partially sets up default_tg_pt_gp and will let you partially + # setup ALUA groups for pscsi and user, but writing to some of the + # files will crash the kernel. Just fail to even create groups until + # the kernel is fixed. + if storage_object.alua_supported is False: + raise RTSLibALUANotSupported("Backend does not support ALUA setup") # default_tg_pt_gp takes tag 1 if tag is not None and (tag > 65535 or tag < 1): diff --git a/rtslib/root.py b/rtslib/root.py index 3cc6fbf..6d6b5ee 100644 --- a/rtslib/root.py +++ b/rtslib/root.py @@ -26,7 +26,7 @@ from .target import Target from .fabric import FabricModule from .tcm import so_mapping, StorageObject -from .utils import RTSLibError, modprobe, mount_configfs +from .utils import RTSLibError, RTSLibALUANotSupported, modprobe, mount_configfs from .utils import dict_remove, set_attributes from .alua import ALUATargetPortGroup @@ -224,7 +224,10 @@ def so_err_func(x): 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) + try: + ALUATargetPortGroup.setup(so_obj, alua_tpg, err_func) + except RTSLibALUANotSupported: + pass # Don't need to create fabric modules for index, fm in enumerate(config.get('fabric_modules', [])): diff --git a/rtslib/target.py b/rtslib/target.py index 816be74..d3cdab7 100644 --- a/rtslib/target.py +++ b/rtslib/target.py @@ -582,31 +582,43 @@ def _list_mapped_luns(self): if os.path.realpath("%s/%s" % (mlun.path, mlun.alias)) == self.path: yield mlun + + # pass through backends will not have setup all the default + # ALUA structs in the kernel. If the kernel has been setup, + # a user created group or default_tg_pt_gp will be returned. + # If the kernel was not properly setup an empty string is + # return in alua_tg_pt_gp. Writing to alua_tg_pt_gp will crash + # older kernels and will return a -Exyz code in newer ones. def _get_alua_tg_pt_gp_name(self): self._check_self() + storage_object = self._get_storage_object() + if storage_object.alua_supported is False: + return None + path = "%s/alua_tg_pt_gp" % self.path - info = fread(path) - if info: + try: + info = fread(path) + if not info: + return None group_line = info.splitlines()[0] return group_line.split(':')[1].strip() - return None + except IOError as e: + return None def _set_alua_tg_pt_gp_name(self, group_name): self._check_self() - path = "%s/alua_tg_pt_gp" % self.path - - info = fread(path) - if not info: - # pass through backends will not have setup the default - # ALUA structs in the kernel. - raise RTSLibError("This LUN does not support setting the ALUA Target Port Group") + if not self._get_alua_tg_pt_gp_name(): + return -1 + 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) + return -1 + + return 0 # LUN public stuff diff --git a/rtslib/tcm.py b/rtslib/tcm.py index 5452f92..aa3530a 100644 --- a/rtslib/tcm.py +++ b/rtslib/tcm.py @@ -222,7 +222,15 @@ def _list_alua_tpgs(self): ''' self._check_self() for tpg in os.listdir("%s/alua" % self.path): - yield ALUATargetPortGroup(self, tpg) + if self.alua_supported: + yield ALUATargetPortGroup(self, tpg) + + def _get_alua_supported(self): + ''' + Children should override and return false if ALUA setup is not supported. + ''' + self._check_self() + return True # StorageObject public stuff @@ -279,6 +287,8 @@ def is_configured(self): doc="Get the list of all LUN objects attached.") alua_tpgs = property(_list_alua_tpgs, doc="Get list of ALUA Target Port Groups attached.") + alua_supported = property(_get_alua_supported, + doc="Returns true if ALUA can be setup. False if not supported.") def dump(self): d = super(StorageObject, self).dump() @@ -408,6 +418,10 @@ def _get_host_id(self): self._check_self() return int(self._parse_info('Host ID')) + def _get_alua_supported(self): + self._check_self() + return False + # PSCSIStorageObject public stuff wwn = property(StorageObject._get_wwn, _set_wwn, @@ -427,6 +441,8 @@ def _get_host_id(self): doc="Get the SCSI device target id") lun = property(_get_lun, doc="Get the SCSI device LUN") + alua_supported = property(_get_alua_supported, + doc="ALUA cannot be setup with rtslib, so False is returned."); def dump(self): d = super(PSCSIStorageObject, self).dump() @@ -808,10 +824,16 @@ def _get_config(self): return None return val + def _get_alua_supported(self): + self._check_self() + return False + size = property(_get_size, doc="Get the size in bytes.") config = property(_get_config, doc="Get the TCMU config.") + alua_supported = property(_get_alua_supported, + doc="ALUA cannot be setup with rtslib, so False is returned."); def dump(self): d = super(UserBackedStorageObject, self).dump() diff --git a/rtslib/utils.py b/rtslib/utils.py index 969d2d7..1a5315e 100644 --- a/rtslib/utils.py +++ b/rtslib/utils.py @@ -37,6 +37,12 @@ class RTSLibError(Exception): ''' pass +class RTSLibALUANotSupported(RTSLibError): + ''' + Backend does not support ALUA. + ''' + pass + class RTSLibBrokenLink(RTSLibError): ''' Broken link in configfs, i.e. missing LUN storage object.