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.
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/.pre-commit-config.yaml +15 -3
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/PKG-INFO +5 -5
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/README.md +4 -4
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/pyproject.toml +1 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/targetcli_shell.py +6 -6
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/targetclid.py +2 -2
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/ui_backstore.py +15 -10
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/ui_node.py +1 -1
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/ui_root.py +6 -8
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/ui_target.py +22 -14
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/targetcli.8 +5 -4
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/.gitignore +0 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/COPYING +0 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/THANKS +0 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/src/targetcli/__init__.py +0 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/systemd/targetclid.service +0 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/systemd/targetclid.socket +0 -0
- {targetcli-3.0.0.dev0 → targetcli-3.0.2}/targetclid.8 +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
repos:
|
|
2
|
-
- repo: https://github.com/
|
|
3
|
-
rev: v0.
|
|
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:
|
|
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.
|
|
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
|
|
30
|
-
from
|
|
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
|
-
|
|
312
|
-
|
|
311
|
+
while not shell._exit:
|
|
312
|
+
try:
|
|
313
313
|
shell.run_interactive()
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
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
|
|
29
|
+
from configshell import ExecutionError
|
|
30
30
|
from gi.repository import Gio
|
|
31
|
-
from
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
30
|
-
from
|
|
31
|
-
from
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
28
|
-
from
|
|
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, "
|
|
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
|
-
" (
|
|
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
|
|
942
|
+
completions.extend(storage_object.path
|
|
943
|
+
for storage_object in backstore.children)
|
|
942
944
|
|
|
943
|
-
completions.extend(lun.name
|
|
945
|
+
completions.extend(lun.name
|
|
946
|
+
for lun in self.parent.parent.get_node("luns").children)
|
|
944
947
|
|
|
945
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1291
|
+
IN6ADDR_ANY (::0) will be used.
|
|
1284
1292
|
|
|
1285
|
-
|
|
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',
|
|
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 ==
|
|
1306
|
-
self.shell.log.info("Binding to INADDR_ANY (
|
|
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
|
|
163
|
-
If
|
|
164
|
-
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|