diff options
author | Andy Grover <andy@groveronline.com> | 2017-01-04 10:30:12 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-04 10:30:12 -0800 |
commit | ba18560bee026dc9b70068465a92eb006f90cfd0 (patch) | |
tree | 995a043317e5a9491a78e66f106b73847a7fb0dc | |
parent | 7acc1e73f2100bf9116d271a08ba5fe1fcdcdaca (diff) | |
parent | 163ee011246971ea3f6a00acd98df5a2df9a8166 (diff) | |
download | rtslib-fb-ba18560bee026dc9b70068465a92eb006f90cfd0.tar.gz |
Merge pull request #84 from mikechristie/alua
ALUA support for rtslib-fb
-rw-r--r-- | rtslib/__init__.py | 2 | ||||
-rw-r--r-- | rtslib/alua.py | 385 | ||||
-rw-r--r-- | rtslib/root.py | 14 | ||||
-rw-r--r-- | rtslib/target.py | 27 | ||||
-rw-r--r-- | rtslib/tcm.py | 16 |
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 |