Hello community, here is the log from the commit of package python-rtslib-fb for openSUSE:Factory checked in at 2018-10-22 11:23:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-rtslib-fb (Old) and /work/SRC/openSUSE:Factory/.python-rtslib-fb.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-rtslib-fb" Mon Oct 22 11:23:14 2018 rev:18 rq:643053 version:2.1.69 Changes: -------- --- /work/SRC/openSUSE:Factory/python-rtslib-fb/python-rtslib-fb.changes 2018-04-19 15:29:24.199264772 +0200 +++ /work/SRC/openSUSE:Factory/.python-rtslib-fb.new/python-rtslib-fb.changes 2018-10-22 11:23:17.947158503 +0200 @@ -1,0 +2,23 @@ +Thu Oct 18 23:27:42 UTC 2018 - [email protected] + +- Update to version 2.1.69: + * version 2.1.fb69 + * fix compiler warning + * version 2.1.fb68 + * Fix typo + * - remove underscore in hostname + * tcm: allow to enable asynchronous I/O for file backing stores + * saveconfig: way for block-level save with delete command + * saveconfig: fix missing import + * saveconfig: handle no attr exception in _parse_info() + * saveconfig: fix failure in absence of save file + * saveconfig: dump control string containing control=value tuples + * restoreconfig: fix alua tpg config setup + * tcmu: add control constructor arg + * save_to_file: support saveconfig at storage object level + * Allow creating more than 256 LUNs per target + * Ship a systemd service file + Which replaces python-rtslib-fb-2.1.67.tar.xz with + python-rtslib-fb-2.1.69.tar.xz, and updates the SPEC file as well. + +------------------------------------------------------------------- Old: ---- python-rtslib-fb-2.1.67.tar.xz New: ---- python-rtslib-fb-2.1.69.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-rtslib-fb.spec ++++++ --- /var/tmp/diff_new_pack.EZYHrD/_old 2018-10-22 11:23:18.611157836 +0200 +++ /var/tmp/diff_new_pack.EZYHrD/_new 2018-10-22 11:23:18.611157836 +0200 @@ -19,7 +19,7 @@ %define dbdir %{_sysconfdir}/target %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-rtslib-fb -Version: 2.1.67 +Version: 2.1.69 Release: 0%{?dist} Summary: API for Linux kernel SCSI target (aka LIO) License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.EZYHrD/_old 2018-10-22 11:23:18.635157812 +0200 +++ /var/tmp/diff_new_pack.EZYHrD/_new 2018-10-22 11:23:18.635157812 +0200 @@ -7,7 +7,7 @@ <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(\d*\.\d*\.)fb(\d*)</param> <param name="versionrewrite-replacement">\1\2</param> - <param name="revision">v2.1.fb67</param> + <param name="revision">v2.1.fb69</param> <param name="changesgenerate">enable</param> </service> <service name="recompress" mode="disabled"> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.EZYHrD/_old 2018-10-22 11:23:18.651157796 +0200 +++ /var/tmp/diff_new_pack.EZYHrD/_new 2018-10-22 11:23:18.651157796 +0200 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/open-iscsi/rtslib-fb.git</param> - <param name="changesrevision">a46e6bf9ea0f83acdff1761a11c502ea5863945f</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">b2ec3746fb772aa3ff8b8853965292ca7dc2d7b1</param></service></servicedata> \ No newline at end of file ++++++ python-rtslib-fb-2.1.67.tar.xz -> python-rtslib-fb-2.1.69.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/rtslib/alua.py new/python-rtslib-fb-2.1.69/rtslib/alua.py --- old/python-rtslib-fb-2.1.67/rtslib/alua.py 2018-02-12 18:34:38.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/rtslib/alua.py 2018-09-18 08:47:13.000000000 +0200 @@ -19,6 +19,7 @@ from .node import CFSNode from .utils import RTSLibError, RTSLibALUANotSupported, fread, fwrite +import six alua_rw_params = ['alua_access_state', 'alua_access_status', 'alua_write_metadata', 'alua_access_type', 'preferred', @@ -393,5 +394,10 @@ 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]) + for param, value in six.iteritems(alua_tpg): + if param != 'name' and param != 'tg_pt_gp_id': + try: + setattr(alua_tpg_obj, param, value) + except: + raise RTSLibError("Could not set attribute '%s' for alua tpg '%s'" + % (param, alua_tpg['name'])) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/rtslib/root.py new/python-rtslib-fb-2.1.69/rtslib/root.py --- old/python-rtslib-fb-2.1.67/rtslib/root.py 2018-02-12 18:34:38.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/rtslib/root.py 2018-09-18 08:47:13.000000000 +0200 @@ -22,6 +22,7 @@ import stat import json import glob +import errno from .node import CFSNode from .target import Target @@ -176,6 +177,88 @@ def _get_dbroot(self): return self._dbroot + def _get_saveconf(self, so_path, save_file): + ''' + Fetch the configuration of all the blocks and return conf with + updated storageObject info and its related target configuraion of + given storage object path + ''' + current = self.dump() + + try: + with open(save_file, "r") as f: + saveconf = json.loads(f.read()) + except IOError as e: + if e.errno == errno.ENOENT: + saveconf = {'storage_objects': [], 'targets': []} + else: + raise ExecutionError("Could not open %s" % save_file) + + fetch_cur_so = False + fetch_cur_tg = False + # Get the given block current storageObj configuration + for sidx, sobj in enumerate(current.get('storage_objects', [])): + if '/backstores/' + sobj['plugin'] + '/' + sobj['name'] == so_path: + current_so = current['storage_objects'][sidx] + fetch_cur_so = True + break + + # Get the given block current target configuration + if fetch_cur_so: + for tidx, tobj in enumerate(current.get('targets', [])): + if fetch_cur_tg: + break + for luns in tobj.get('tpgs', []): + if fetch_cur_tg: + break + for lun in luns.get('luns', []): + if lun['storage_object'] == so_path: + current_tg = current['targets'][tidx] + fetch_cur_tg = True + break + + fetch_sav_so = False + fetch_sav_tg = False + # Get the given block storageObj from saved configuration + for sidx, sobj in enumerate(saveconf.get('storage_objects', [])): + if '/backstores/' + sobj['plugin'] + '/' + sobj['name'] == so_path: + # Merge StorageObj + if fetch_cur_so: + saveconf['storage_objects'][sidx] = current_so; + # Remove StorageObj + else: + saveconf['storage_objects'].remove(saveconf['storage_objects'][sidx]) + fetch_sav_so = True + break + + # Get the given block target from saved configuration + if fetch_sav_so: + for tidx, tobj in enumerate(saveconf.get('targets', [])): + if fetch_sav_tg: + break + for luns in tobj.get('tpgs', []): + if fetch_sav_tg: + break + for lun in luns.get('luns', []): + if lun['storage_object'] == so_path: + # Merge target + if fetch_cur_tg: + saveconf['targets'][tidx] = current_tg; + # Remove target + else: + saveconf['targets'].remove(saveconf['targets'][tidx]) + fetch_sav_tg = True + break + + # Insert storageObj + if fetch_cur_so and not fetch_sav_so: + saveconf['storage_objects'].append(current_so) + # Insert target + if fetch_cur_tg and not fetch_sav_tg: + saveconf['targets'].append(current_tg) + + return saveconf + # RTSRoot public stuff def dump(self): @@ -290,7 +373,7 @@ return errors - def save_to_file(self, save_file=None): + def save_to_file(self, save_file=None, so_path=None): ''' Write the configuration in json format to a file. Save file defaults to '/etc/targets/saveconfig.json'. @@ -298,9 +381,14 @@ if not save_file: save_file = default_save_file + if so_path: + saveconf = self._get_saveconf(so_path, save_file) + else: + saveconf = self.dump() + with open(save_file+".temp", "w+") as f: os.fchmod(f.fileno(), stat.S_IRUSR | stat.S_IWUSR) - f.write(json.dumps(self.dump(), sort_keys=True, indent=2)) + f.write(json.dumps(saveconf, sort_keys=True, indent=2)) f.write("\n") f.flush() os.fsync(f.fileno()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/rtslib/target.py new/python-rtslib-fb-2.1.69/rtslib/target.py --- old/python-rtslib-fb-2.1.67/rtslib/target.py 2018-02-12 18:34:38.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/rtslib/target.py 2018-09-18 08:47:13.000000000 +0200 @@ -464,7 +464,7 @@ A LUN is identified by its parent TPG and LUN index. ''' - MAX_LUN = 255 + MAX_TARGET_LUN = 65535 # LUN private stuff @@ -487,7 +487,7 @@ @param parent_tpg: The parent TPG object. @type parent_tpg: TPG @param lun: The LUN index. - @type lun: 0-255 + @type lun: 0-65535 @param storage_object: The storage object to be exported as a LUN. @type storage_object: StorageObject subclass @param alias: An optional parameter to manually specify the LUN alias. @@ -504,16 +504,16 @@ if lun is None: luns = [l.lun for l in self.parent_tpg.luns] - for index in range(self.MAX_LUN+1): + for index in range(self.MAX_TARGET_LUN+1): if index not in luns: lun = index break if lun is None: - raise RTSLibError("All LUNs 0-%d in use" % self.MAX_LUN) + raise RTSLibError("All LUNs 0-%d in use" % self.MAX_TARGET_LUN) else: lun = int(lun) - if lun < 0 or lun > self.MAX_LUN: - raise RTSLibError("LUN must be 0 to %d" % self.MAX_LUN) + if lun < 0 or lun > self.MAX_TARGET_LUN: + raise RTSLibError("LUN must be 0 to %d" % self.MAX_TARGET_LUN) self._lun = lun @@ -1060,6 +1060,8 @@ the initiator node as the MappedLUN. ''' + MAX_LUN = 255 + # MappedLUN private stuff def __repr__(self): @@ -1107,6 +1109,9 @@ raise RTSLibError("The mapped_lun parameter must be an " \ + "integer value") + if self._mapped_lun < 0 or self._mapped_lun > self.MAX_LUN: + raise RTSLibError("Mapped LUN must be 0 to %d" % self.MAX_LUN) + self._path = "%s/lun_%d" % (self.parent_nodeacl.path, self.mapped_lun) if tpg_lun is None and write_protect is not None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/rtslib/tcm.py new/python-rtslib-fb-2.1.69/rtslib/tcm.py --- old/python-rtslib-fb-2.1.67/rtslib/tcm.py 2018-02-12 18:34:38.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/rtslib/tcm.py 2018-09-18 08:47:13.000000000 +0200 @@ -184,8 +184,11 @@ def _parse_info(self, key): self._check_self() info = fread("%s/info" % self.path) - return re.search(".*%s: ([^: ]+).*" \ - % key, ' '.join(info.split())).group(1) + try: + return re.search(".*%s: ([^: ]+).*" \ + % key, ' '.join(info.split())).group(1) + except AttributeError: + return None def _get_status(self): self._check_self() @@ -248,7 +251,7 @@ # StorageObject public stuff - def delete(self): + def delete(self, save=False): ''' Recursively deletes a StorageObject object. This will delete all attached LUNs currently using the StorageObject @@ -271,6 +274,9 @@ super(StorageObject, self).delete() self._backstore.delete() + if save: + from .root import RTSRoot, default_save_file + RTSRoot().save_to_file(default_save_file, '/backstores/' + self.plugin + '/' + self._name) def is_configured(self): ''' @@ -569,7 +575,7 @@ # FileIOStorageObject private stuff def __init__(self, name, dev=None, size=None, - wwn=None, write_back=False): + wwn=None, write_back=False, aio=False): ''' A FileIOStorageObject can be instantiated in two ways: - B{Creation mode}: If I{dev} and I{size} are specified, the @@ -603,14 +609,14 @@ if dev is not None: super(FileIOStorageObject, self).__init__(name, 'create') try: - self._configure(dev, size, wwn, write_back) + self._configure(dev, size, wwn, write_back, aio) except: self.delete() raise else: super(FileIOStorageObject, self).__init__(name, 'lookup') - def _configure(self, dev, size, wwn, write_back): + def _configure(self, dev, size, wwn, write_back, aio): self._check_self() block_type = get_blockdev_type(dev) if block_type is None: # a file @@ -638,6 +644,9 @@ self.set_attribute("emulate_write_cache", 1) self._control("fd_buffered_io=%d" % write_back) + if aio: + self._control("fd_async_io=%d" % aio) + self._set_udev_path(dev) self._enable() @@ -660,6 +669,15 @@ def _is_block(self): return get_blockdev_type(self.udev_path) is not None + def _aio(self): + self._check_self() + info = fread("%s/info" % self.path) + r = re.search(".*Async: ([^: ]+).*", ' '.join(info.split())) + if not r: # for backward compatibility with old kernels + return False + + return bool(int(r.group(1))) + # FileIOStorageObject public stuff write_back = property(_get_wb_enabled, @@ -668,6 +686,8 @@ doc="Get the current FileIOStorage size in bytes") is_block = property(_is_block, doc="True if FileIoStorage is backed by a block device instead of a file") + aio = property(_aio, + doc="True if asynchronous I/O is enabled") def dump(self): d = super(FileIOStorageObject, self).dump() @@ -675,6 +695,7 @@ d['wwn'] = self.wwn d['dev'] = self.udev_path d['size'] = self.size + d['aio'] = self.aio return d @@ -787,7 +808,7 @@ ''' def __init__(self, name, config=None, size=None, wwn=None, - hw_max_sectors=None): + hw_max_sectors=None, control=None): ''' @param name: The name of the UserBackedStorageObject. @type name: string @@ -800,6 +821,9 @@ @type wwn: string @hw_max_sectors: Max sectors per command limit to export to initiators. @type hw_max_sectors: int + @control: String of control=value tuples separate by a ',' that will + passed to the kernel control file. + @type: string @return: A UserBackedStorageObject object. ''' @@ -812,14 +836,14 @@ "from its configuration string") super(UserBackedStorageObject, self).__init__(name, 'create') try: - self._configure(config, size, wwn, hw_max_sectors) + self._configure(config, size, wwn, hw_max_sectors, control) except: self.delete() raise else: super(UserBackedStorageObject, self).__init__(name, 'lookup') - def _configure(self, config, size, wwn, hw_max_sectors): + def _configure(self, config, size, wwn, hw_max_sectors, control): self._check_self() if ':' in config: @@ -828,6 +852,8 @@ self._control("dev_size=%d" % size) if hw_max_sectors is not None: self._control("hw_max_sectors=%s" % hw_max_sectors) + if control is not None: + self._control(control) self._enable() super(UserBackedStorageObject, self)._configure(wwn) @@ -840,6 +866,17 @@ self._check_self() return int(self._parse_info('HwMaxSectors')) + def _get_control_tuples(self): + self._check_self() + tuples = [] + # 1. max_data_area_mb + val = self._parse_info('MaxDataAreaMB') + if val != "NULL": + tuples.append("max_data_area_mb=%s" % val) + # 2. add next ... + + return ",".join(tuples) + def _get_config(self): self._check_self() val = self._parse_info('Config') @@ -853,6 +890,8 @@ hw_max_sectors = property(_get_hw_max_sectors, doc="Get the max sectors per command.") + control_tuples = property(_get_control_tuples, + doc="Get the comma separated string containing control=value tuples.") size = property(_get_size, doc="Get the size in bytes.") config = property(_get_config, @@ -866,6 +905,7 @@ d['size'] = self.size d['config'] = self.config d['hw_max_sectors'] = self.hw_max_sectors + d['control'] = self.control_tuples return d @@ -975,8 +1015,11 @@ def _parse_info(self, key): self._check_self() info = fread("%s/hba_info" % self.path) - return re.search(".*%s: ([^: ]+).*" \ - % key, ' '.join(info.split())).group(1) + try: + return re.search(".*%s: ([^: ]+).*" \ + % key, ' '.join(info.split())).group(1) + except AttributeError: + return None def _get_version(self): self._check_self() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/rtslib/utils.py new/python-rtslib-fb-2.1.69/rtslib/utils.py --- old/python-rtslib-fb-2.1.67/rtslib/utils.py 2018-02-12 18:34:38.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/rtslib/utils.py 2018-09-18 08:47:13.000000000 +0200 @@ -326,7 +326,7 @@ if wwn_type == 'unit_serial': return str(uuid.uuid4()) elif wwn_type == 'iqn': - localname = socket.gethostname().split(".")[0] + localname = socket.gethostname().split(".")[0].replace("_", "") localarch = os.uname()[4].replace("_", "") prefix = "iqn.2003-01.org.linux-iscsi.%s.%s" % (localname, localarch) prefix = prefix.strip().lower() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/setup.py new/python-rtslib-fb-2.1.69/setup.py --- old/python-rtslib-fb-2.1.67/setup.py 2018-02-12 18:34:38.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/setup.py 2018-09-18 08:47:13.000000000 +0200 @@ -20,7 +20,7 @@ setup ( name = 'rtslib-fb', - version = '2.1.67', + version = '2.1.69', description = 'API for Linux kernel SCSI target (aka LIO)', license = 'Apache 2.0', maintainer = 'Andy Grover', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/systemd/README.md new/python-rtslib-fb-2.1.69/systemd/README.md --- old/python-rtslib-fb-2.1.67/systemd/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/systemd/README.md 2018-09-18 08:47:13.000000000 +0200 @@ -0,0 +1,8 @@ +### Service file for rtslib-fb use with systemd + +The systemd developers encourage upstream projects to ship and install +a service file, saving each systemd-based distribution from having to +create one. + +In this directory is the systemd service file for rtslib-fb. However, +it is not currently installed by default. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-rtslib-fb-2.1.67/systemd/target.service new/python-rtslib-fb-2.1.69/systemd/target.service --- old/python-rtslib-fb-2.1.67/systemd/target.service 1970-01-01 01:00:00.000000000 +0100 +++ new/python-rtslib-fb-2.1.69/systemd/target.service 2018-09-18 08:47:13.000000000 +0200 @@ -0,0 +1,15 @@ +[Unit] +Description=Restore LIO kernel target configuration +Requires=sys-kernel-config.mount +After=sys-kernel-config.mount network.target local-fs.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/targetctl restore +ExecStop=/usr/bin/targetctl clear +SyslogIdentifier=target + +[Install] +WantedBy=multi-user.target +
