targetcli 3.0.0.dev0__tar.gz → 3.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
1
  repos:
2
- - repo: https://github.com/charliermarsh/ruff-pre-commit
3
- rev: v0.4.2
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.14.10
4
4
  hooks:
5
5
  - id: ruff
6
6
  args: [--fix]
7
7
 
8
8
  - repo: https://github.com/pre-commit/pre-commit-hooks
9
- rev: v4.6.0
9
+ rev: v6.0.0
10
10
  hooks:
11
11
  - id: check-case-conflict
12
12
  - id: check-ast
@@ -20,3 +20,15 @@ repos:
20
20
  - id: debug-statements
21
21
  - id: end-of-file-fixer
22
22
  - id: trailing-whitespace
23
+ args: [--markdown-linebreak-ext=md]
24
+
25
+ - repo: https://github.com/packit/pre-commit-hooks
26
+ rev: v1.3.0
27
+ hooks:
28
+ - id: validate-config
29
+
30
+ ci:
31
+ autofix_commit_msg: |
32
+ Auto fixes from pre-commit.com hooks
33
+ autofix_prs: false
34
+ autoupdate_schedule: monthly
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: targetcli
3
- Version: 3.0.0.dev0
3
+ Version: 3.0.2
4
4
  Summary: A command shell for managing the Linux LIO kernel target
5
5
  Project-URL: Homepage, http://github.com/open-iscsi/targetcli-fb
6
6
  Author-email: Andy Grover <agrover@redhat.com>
@@ -50,10 +50,10 @@ Contribute
50
50
  ----------
51
51
  targetcli complies with PEP 621 and as such can be built and installed with tools like `build` and `pip`.
52
52
 
53
- For development, consider using [Hatch](https://hatch.pypa.io):
54
- `hatch shell` to create and enter a Python virtualenv with the project installed in editable mode
55
- `pre-commit install` to enable pre-commit hooks
56
- `hatch build` to create tarball and wheel
53
+ For development, consider using [Hatch](https://hatch.pypa.io):
54
+ `hatch shell` to create and enter a Python virtualenv with the project installed in editable mode
55
+ `pre-commit install` to enable pre-commit hooks
56
+ `hatch build` to create tarball and wheel
57
57
 
58
58
  "fb" -- "free branch"
59
59
  ---------------------
@@ -31,10 +31,10 @@ Contribute
31
31
  ----------
32
32
  targetcli complies with PEP 621 and as such can be built and installed with tools like `build` and `pip`.
33
33
 
34
- For development, consider using [Hatch](https://hatch.pypa.io):
35
- `hatch shell` to create and enter a Python virtualenv with the project installed in editable mode
36
- `pre-commit install` to enable pre-commit hooks
37
- `hatch build` to create tarball and wheel
34
+ For development, consider using [Hatch](https://hatch.pypa.io):
35
+ `hatch shell` to create and enter a Python virtualenv with the project installed in editable mode
36
+ `pre-commit install` to enable pre-commit hooks
37
+ `hatch build` to create tarball and wheel
38
38
 
39
39
  "fb" -- "free branch"
40
40
  ---------------------
@@ -96,6 +96,7 @@ ignore = [
96
96
  "ARG002", "PLR6301", # TODO Unused self, parameter in methods definitions
97
97
  "PLR09", # Too many branches/statements/arguments
98
98
  "PLW1514", # TODO `open` in text mode without explicit `encoding` argument
99
+ "UP031", # Use format specifiers instead of percent format
99
100
  ]
100
101
  [tool.ruff.lint.flake8-quotes]
101
102
  # Single quotes are currently prevalent in the codebase. Not being checked.
@@ -26,8 +26,8 @@ import struct
26
26
  import sys
27
27
  from os import getenv, getuid
28
28
 
29
- from configshell_fb import ConfigShell, ExecutionError
30
- from rtslib_fb import RTSLibError
29
+ from configshell import ConfigShell, ExecutionError
30
+ from rtslib import RTSLibError
31
31
 
32
32
  from targetcli import __version__ as targetcli_version
33
33
  from targetcli.ui_root import UIRoot
@@ -308,11 +308,11 @@ def main():
308
308
  if not is_root:
309
309
  shell.con.display("You are not root, disabling privileged commands.\n")
310
310
 
311
- try:
312
- while not shell._exit:
311
+ while not shell._exit:
312
+ try:
313
313
  shell.run_interactive()
314
- except (RTSLibError, ExecutionError) as msg:
315
- shell.log.error(str(msg))
314
+ except (RTSLibError, ExecutionError) as msg: # noqa: PERF203 - would otherwise exit shell
315
+ shell.log.error(str(msg))
316
316
 
317
317
  if shell.prefs['auto_save_on_exit'] and is_root:
318
318
  shell.log.info("Global pref auto_save_on_exit=true")
@@ -31,7 +31,7 @@ from os import getenv, getuid
31
31
  from pathlib import Path
32
32
  from threading import Thread
33
33
 
34
- from configshell_fb import ConfigShell
34
+ from configshell import ConfigShell
35
35
 
36
36
  from targetcli import __version__ as targetcli_version
37
37
  from targetcli.ui_root import UIRoot
@@ -153,7 +153,7 @@ class TargetCLI:
153
153
  connection.close()
154
154
  still_listen = False
155
155
  else:
156
- self.con._stdout = self.con._stderr = f = tempfile.NamedTemporaryFile(mode='w', delete=False)
156
+ self.con._stdout = self.con._stderr = f = tempfile.NamedTemporaryFile(mode='w', delete=False) # noqa: SIM115
157
157
  try:
158
158
  # extract multiple commands delimited with '%'
159
159
  list_data = data.decode().split('%')
@@ -26,9 +26,9 @@ import stat
26
26
  import struct
27
27
  from pathlib import Path
28
28
 
29
- from configshell_fb import ExecutionError
29
+ from configshell import ExecutionError
30
30
  from gi.repository import Gio
31
- from rtslib_fb import (
31
+ from rtslib import (
32
32
  ALUATargetPortGroup,
33
33
  BlockStorageObject,
34
34
  FileIOStorageObject,
@@ -38,7 +38,7 @@ from rtslib_fb import (
38
38
  RTSRoot,
39
39
  UserBackedStorageObject,
40
40
  )
41
- from rtslib_fb.utils import get_block_type
41
+ from rtslib.utils import get_block_type
42
42
 
43
43
  from .ui_node import UINode, UIRTSLibNode
44
44
 
@@ -516,7 +516,9 @@ class UIFileIOBackstore(UIBackstore):
516
516
  '''
517
517
  if current_param != 'file_or_dev':
518
518
  return []
519
- completions = complete_path(text, lambda x: stat.S_ISREG(x) or stat.S_ISBLK(x))
519
+ completions = complete_path(
520
+ text, lambda x: stat.S_ISREG(x) or stat.S_ISBLK(x),
521
+ ) if text else []
520
522
  if len(completions) == 1 and not completions[0].endswith('/'):
521
523
  completions = [completions[0] + ' ']
522
524
  return completions
@@ -545,11 +547,10 @@ class UIBlockBackstore(UIBackstore):
545
547
  return False
546
548
 
547
549
  os.close(f)
548
- if struct.unpack('I', buf)[0] == 0:
549
- return False
550
- return True
550
+ return struct.unpack('I', buf)[0] != 0
551
551
 
552
- def ui_command_create(self, name, dev, readonly=None, wwn=None):
552
+ def ui_command_create(self, name, dev, readonly=None, wwn=None,
553
+ exclusive=None):
553
554
  '''
554
555
  Creates an Block Storage object. "dev" is the path to the TYPE_DISK
555
556
  block device to use.
@@ -561,7 +562,11 @@ class UIBlockBackstore(UIBackstore):
561
562
 
562
563
  wwn = self.ui_eval_param(wwn, 'string', None)
563
564
 
564
- so = BlockStorageObject(name, dev, readonly=readonly, wwn=wwn)
565
+ excl_string = self.ui_eval_param(exclusive, 'string', None)
566
+ exclusive = True if excl_string is None else self.ui_eval_param(exclusive, "bool", True)
567
+
568
+ so = BlockStorageObject(name, dev, readonly=readonly, wwn=wwn,
569
+ exclusive=exclusive)
565
570
  ui_so = UIBlockStorageObject(so, self)
566
571
  self.setup_model_alias(so)
567
572
  self.shell.log.info(f"Created block storage object {name} using {dev}.")
@@ -573,7 +578,7 @@ class UIBlockBackstore(UIBackstore):
573
578
  '''
574
579
  if current_param != 'dev':
575
580
  return []
576
- completions = complete_path(text, stat.S_ISBLK)
581
+ completions = complete_path(text, stat.S_ISBLK) if text else []
577
582
  if len(completions) == 1 and not completions[0].endswith('/'):
578
583
  completions = [completions[0] + ' ']
579
584
  return completions
@@ -18,7 +18,7 @@ under the License.
18
18
  '''
19
19
 
20
20
 
21
- from configshell_fb import ConfigNode, ExecutionError
21
+ from configshell import ConfigNode, ExecutionError
22
22
 
23
23
 
24
24
  class UINode(ConfigNode):
@@ -26,9 +26,9 @@ from datetime import datetime
26
26
  from glob import glob
27
27
  from pathlib import Path, PurePosixPath
28
28
 
29
- from configshell_fb import ExecutionError
30
- from rtslib_fb import RTSRoot
31
- from rtslib_fb.utils import ignored
29
+ from configshell import ExecutionError
30
+ from rtslib import RTSRoot
31
+ from rtslib.utils import ignored
32
32
 
33
33
  from targetcli import __version__
34
34
 
@@ -155,8 +155,7 @@ class UIRoot(UINode):
155
155
  prefs = Path(universal_prefs_file).read_text()
156
156
  backups = [line for line in prefs.splitlines() if re.match(
157
157
  r'^max_backup_files\s*=', line)]
158
- if max_backup_files < int(backups[0].split('=')[1].strip()):
159
- max_backup_files = int(backups[0].split('=')[1].strip())
158
+ max_backup_files = max(max_backup_files, int(backups[0].split('=')[1].strip()))
160
159
  except:
161
160
  self.shell.log.debug(f"No universal prefs file '{universal_prefs_file}'.")
162
161
 
@@ -200,8 +199,7 @@ class UIRoot(UINode):
200
199
  savefile = os.path.expanduser(savefile)
201
200
 
202
201
  if not os.path.isfile(savefile):
203
- self.shell.log.info(f"Restore file {savefile} not found")
204
- return
202
+ raise ExecutionError(f"Restore file {savefile} not found")
205
203
 
206
204
  target = self.ui_eval_param(target, 'string', None)
207
205
  storage_object = self.ui_eval_param(storage_object, 'string', None)
@@ -320,7 +318,7 @@ class UIRoot(UINode):
320
318
  else:
321
319
  printed_sessions = list(self.rtsroot.sessions)
322
320
 
323
- if len(printed_sessions):
321
+ if printed_sessions:
324
322
  for session in printed_sessions:
325
323
  print_session(session)
326
324
  elif sid is None:
@@ -24,8 +24,8 @@ except ImportError:
24
24
  ethtool = None
25
25
  import stat
26
26
 
27
- from configshell_fb import ExecutionError
28
- from rtslib_fb import (
27
+ from configshell import ExecutionError
28
+ from rtslib import (
29
29
  LUN,
30
30
  TPG,
31
31
  MappedLUN,
@@ -43,6 +43,7 @@ from .ui_node import UINode, UIRTSLibNode
43
43
  auth_params = ('userid', 'password', 'mutual_userid', 'mutual_password')
44
44
  int_params = ('enable',)
45
45
  discovery_params = auth_params + int_params
46
+ default_portal_listen = "::0"
46
47
 
47
48
  class UIFabricModule(UIRTSLibNode):
48
49
  '''
@@ -340,10 +341,10 @@ class UIMultiTPGTarget(UIRTSLibNode):
340
341
 
341
342
  if tpg.has_feature("nps") and self.shell.prefs['auto_add_default_portal']:
342
343
  try:
343
- NetworkPortal(tpg, "0.0.0.0")
344
+ NetworkPortal(tpg, f"[{default_portal_listen}]")
344
345
  self.shell.log.info("Global pref auto_add_default_portal=true")
345
346
  self.shell.log.info("Created default portal listening on all IPs"
346
- " (0.0.0.0), port 3260.")
347
+ f" ({default_portal_listen}), port 3260.")
347
348
  except RTSLibError:
348
349
  self.shell.log.info("Default portal not created, TPGs within a target cannot share ip:port.")
349
350
 
@@ -938,11 +939,15 @@ class UINodeACL(UIRTSLibNode):
938
939
  if current_param == 'tpg_lun_or_backstore':
939
940
  completions = []
940
941
  for backstore in self.get_node('/backstores').children:
941
- completions = [storage_object.path for storage_object in backstore.children]
942
+ completions.extend(storage_object.path
943
+ for storage_object in backstore.children)
942
944
 
943
- completions.extend(lun.name for lun in self.parent.parent.get_node("luns").children)
945
+ completions.extend(lun.name
946
+ for lun in self.parent.parent.get_node("luns").children)
944
947
 
945
- completions.extend(complete_path(text, lambda x: stat.S_ISREG(x) or stat.S_ISBLK(x)))
948
+ if text:
949
+ completions.extend(complete_path(text,
950
+ lambda x: stat.S_ISREG(x) or stat.S_ISBLK(x)))
946
951
 
947
952
  completions = [c for c in completions if c.startswith(text)]
948
953
  else:
@@ -1150,10 +1155,13 @@ class UILUNs(UINode):
1150
1155
  if current_param == 'storage_object':
1151
1156
  storage_objects = []
1152
1157
  for backstore in self.get_node('/backstores').children:
1153
- storage_objects = [storage_object.path for storage_object in backstore.children]
1158
+ storage_objects.extend(storage_object.path
1159
+ for storage_object in backstore.children)
1154
1160
  completions = [so for so in storage_objects if so.startswith(text)]
1155
1161
 
1156
- completions.extend(complete_path(text, lambda x: stat.S_ISREG(x) or stat.S_ISBLK(x)))
1162
+ if text:
1163
+ completions.extend(complete_path(text,
1164
+ lambda x: stat.S_ISREG(x) or stat.S_ISBLK(x)))
1157
1165
  else:
1158
1166
  completions = []
1159
1167
 
@@ -1280,9 +1288,9 @@ class UIPortals(UINode):
1280
1288
  Creates a Network Portal with the specified IP address and
1281
1289
  port. If the port is omitted, the default port for
1282
1290
  the target fabric will be used. If the IP address is omitted,
1283
- INADDR_ANY (0.0.0.0) will be used.
1291
+ IN6ADDR_ANY (::0) will be used.
1284
1292
 
1285
- Choosing IN6ADDR_ANY (::0) will listen on all IPv6 interfaces
1293
+ The default IN6ADDR_ANY (::0) will listen on all IPv6 interfaces
1286
1294
  as well as IPv4, assuming IPV6_V6ONLY sockopt has not been
1287
1295
  set.
1288
1296
 
@@ -1298,12 +1306,12 @@ class UIPortals(UINode):
1298
1306
  # FIXME: Add a specfile parameter to determine default port
1299
1307
  default_port = 3260
1300
1308
  ip_port = self.ui_eval_param(ip_port, 'number', default_port)
1301
- ip_address = self.ui_eval_param(ip_address, 'string', "0.0.0.0")
1309
+ ip_address = self.ui_eval_param(ip_address, 'string', default_portal_listen)
1302
1310
 
1303
1311
  if ip_port == default_port:
1304
1312
  self.shell.log.info("Using default IP port %d" % ip_port)
1305
- if ip_address == "0.0.0.0":
1306
- self.shell.log.info("Binding to INADDR_ANY (0.0.0.0)")
1313
+ if ip_address == default_portal_listen:
1314
+ self.shell.log.info(f"Binding to INADDR_ANY ({default_portal_listen})")
1307
1315
 
1308
1316
  portal = NetworkPortal(self.tpg, self._canonicalize_ip(ip_address),
1309
1317
  ip_port, mode='create')
@@ -159,9 +159,10 @@ ports. These addr:port pairs are called
159
159
  Both IPv4 and IPv6 addresses are supported.
160
160
  .P
161
161
  When a target is created, targetcli automatically creates a default
162
- portal listening on all IPv4 addresses (shown as 0.0.0.0) on port 3260.
163
- If a different configuration is needed, the default portal can be
164
- removed and portals configured as desired.
162
+ portal listening on all IPv4 and IPv6 addresses (shown as ::0) on port
163
+ 3260. If IPV6_V6ONLY is set, the default will only listen on all IPv6
164
+ addresses. If a different configuration is needed, the default portal can
165
+ be removed and portals configured as desired.
165
166
  .P
166
167
  If the hardware supports it,
167
168
  .B iSER
@@ -299,7 +300,7 @@ target.)
299
300
  .B portals/ create
300
301
  .br
301
302
  Add a portal, i.e. an IP address and TCP port via which the target can
302
- be contacted by initiators. Only required if the default 0.0.0.0:3260
303
+ be contacted by initiators. Only required if the default [::0]:3260
303
304
  portal is not present.
304
305
  .P
305
306
  .B luns/ create /backstores/fileio/disk1
File without changes
File without changes
File without changes
File without changes