Merge ~rafaeldtinoco/ubuntu/+source/targetcli-fb:focal-merge-upstream-2.1.51 into ubuntu/+source/targetcli-fb:ubuntu/focal-devel
- Git
- lp:~rafaeldtinoco/ubuntu/+source/targetcli-fb
- focal-merge-upstream-2.1.51
- Merge into ubuntu/focal-devel
Status: | Merged |
---|---|
Merge reported by: | Rafael David Tinoco |
Merged at revision: | 327f616ba21fff347ec51c57790cd5c28c27256c |
Proposed branch: | ~rafaeldtinoco/ubuntu/+source/targetcli-fb:focal-merge-upstream-2.1.51 |
Merge into: | ubuntu/+source/targetcli-fb:ubuntu/focal-devel |
Diff against target: |
1425 lines (+743/-132) 18 files modified
daemon/targetclid (+276/-0) debian/changelog (+17/-0) debian/control (+2/-1) debian/manpages (+1/-0) debian/patches/0003-Use-etc-rtslib-fb-target-instead-of-etc-target.patch (+5/-5) debian/rules (+11/-0) debian/watch (+1/-1) scripts/targetcli (+162/-12) setup.py (+4/-1) systemd/targetclid.service (+13/-0) systemd/targetclid.socket (+9/-0) targetcli.8 (+7/-1) targetcli/ui_backstore.py (+38/-37) targetcli/ui_node.py (+4/-1) targetcli/ui_root.py (+55/-18) targetcli/ui_target.py (+60/-54) targetcli/version.py (+1/-1) targetclid.8 (+77/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rafael David Tinoco (community) | Approve | ||
Christian Ehrhardt (community) | Approve | ||
Canonical Server | Pending | ||
Review via email: mp+379938@code.launchpad.net |
Commit message
https:/
comment #24 resolution:
@rafaeldtinoco - please update targetcli-fb
- update targetcli-fb to 2.1.51 - done in debian
- fix d/watch to detect the non *fb* versions - done in debian
- not sure but it might even need an epoch :-/ - changed epoch (1:)
- create DEP8
I'm only missing DEP8, I hope I can do that after freeze date.
* Salsa merge proposals:
- https:/
- https:/
- https:/
Description of the change
Rafael David Tinoco (rafaeldtinoco) wrote : | # |
Christian Ehrhardt (paelzer) wrote : | # |
Yes, having and extending dep8 tests on this can be done later.
Also I know that you consider doing even more complex tests outside to autopkgtest - as long as we don't skip the testing I'm fine to not do that now.
Taking a look at the MP now ...
Christian Ehrhardt (paelzer) wrote : | # |
Thanks for submitting to Debian as well, we can get in sync on that later and consider to go ahead for our activities to be in time for 20.04.
The problem of "getting back" is if they don't agree that we need an epoch on this.
IMHO we do as I already outlined on IRC, but you never know ... :-/
At least it is consistent with what we did for the other outdated -fb packages.
So +1 on that aspect.
I'll need to do some builds/tests on my own as no PPA was linked, please bear with me that this takes some time.
Christian Ehrhardt (paelzer) wrote : | # |
d/watch is ok for the new tarballs
- 098b93d... by Christian Ehrhardt
-
changelog: explain epoch bump
Signed-off-by: Christian Ehrhardt <email address hidden>
Christian Ehrhardt (paelzer) wrote : | # |
I was going through the upstream changes again one by one.
at https:/
FYI on reserver/release - new feature https:/
Christian Ehrhardt (paelzer) wrote : | # |
I really think we need to do more for targetclid (https:/
- ./usr/bin/
- try the new socket activation (daemon is still off by default https:/
- changelog needs epoch mentioned (uh I just found there even is a lintian entry for it https:/
- we miss the targetclid.
Summary:
- I'm glad it is still off by default, things will work as before
- We need to package targetclid better
- we need to test and fix targetclid (can be done after FF)
- 30e7bbd... by Christian Ehrhardt
-
d/manpages: install targetclid man page
Signed-off-by: Christian Ehrhardt <email address hidden>
- 59dfa1f... by Christian Ehrhardt
-
d/rules: install and enable targetclid service and socket
Signed-off-by: Christian Ehrhardt <email address hidden>
- 3d2d5fd... by Christian Ehrhardt
-
changelog: targetclid
Signed-off-by: Christian Ehrhardt <email address hidden>
Christian Ehrhardt (paelzer) wrote : | # |
+1 to your work so far, but as outlined it missed some bits.
I have added what IMHO was missing, now the review turns around :-)
- Please review that extra additions at [1]
To do so please pull it there and push it into your branch, then continue the review here.
- I added a review slot for you for that
I'll continue some tests/checks still ... before I give my final +1
Christian Ehrhardt (paelzer) wrote : | # |
Christian Ehrhardt (paelzer) wrote : | # |
+1 for the initial part that was proposed.
Please continue on the daemon packaging to be completed.
I updated the bug and listed TODOs [1] and the most recent state summary [2]:
[1]: https:/
[2]: https:/
Christian Ehrhardt (paelzer) wrote : | # |
I got the service right, this is from an install:
ubuntu@
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
targetcli-fb
0 upgraded, 1 newly installed, 0 to remove and 9 not upgraded.
Need to get 35.1 kB of archives.
After this operation, 170 kB of additional disk space will be used.
Get:1 http://
Fetched 35.1 kB in 0s (306 kB/s)
Selecting previously unselected package targetcli-fb.
(Reading database ... 63672 files and directories currently installed.)
Preparing to unpack .../targetcli-
Unpacking targetcli-fb (1:2.1.
Setting up targetcli-fb (1:2.1.
/usr/lib/
self.stdin = io.open(p2cwrite, 'wb', bufsize)
Created symlink /etc/systemd/
Created symlink /etc/systemd/
Processing triggers for man-db (2.9.0-2) ...
ubuntu@
● targetclid.socket - targetclid socket
Loaded: loaded (/lib/systemd/
Active: active (listening) since Thu 2020-02-27 15:21:06 UTC; 5s ago
Triggers: ● targetclid.service
Docs: man:targetclid(8)
Listen: /run/targetclid
CGroup: /system.
Feb 27 15:21:06 focal-targetcli systemd[1]: Listening on targetclid socket.
● targetclid.service - Targetcli daemon
Loaded: loaded (/lib/systemd/
Active: inactive (dead)
TriggeredBy: ● targetclid.socket
Docs: man:targetclid(8)
Feb 27 11:47:08 focal-targetcli systemd[1]: Started Targetcli daemon.
Feb 27 11:47:09 focal-targetcli targetclid[1651]: Warning: Could not load preferences file /root/.
Feb 27 14:08:20 focal-targetcli targetclid[1651]: Signal received, quiting gracefully!
Feb 27 14:08:20 focal-targetcli systemd[1]: Stopping Targetcli daemon...
Feb 27 14:08:20 focal-targetcli systemd[1]: targetclid.service: Succeeded.
Feb 27 14:08:20 focal-targetcli systemd[1]: Stopped Targetcli daemon.
- 327f616... by Christian Ehrhardt
-
changelog: fixup change attribution
Signed-off-by: Christian Ehrhardt <email address hidden>
Christian Ehrhardt (paelzer) wrote : | # |
@Rafel
All open TODOs that I found are done now.
+1 from me
Once you have checked the content I added (reverse +1) in [1] please tag and sponsor it.
Let me know then so that we can update the MIR request.
Rafael David Tinoco (rafaeldtinoco) wrote : | # |
> I was going through the upstream changes again one by one.
> at https:/
>
> FYI on reserver/release - new feature https:/
> fb/commit/
That's nice! Important "feature" for this need.
Rafael David Tinoco (rafaeldtinoco) wrote : | # |
I have rebased your branch into mine. I am definitely +1 on all your changes and thankful for you being proactive on them when I couldn't deal with them yet.
- stdout:
dpkg-deb: building package 'targetcli-fb' in '../targetcli-
dpkg-genbuildinfo --build=binary
dpkg-genchanges -sa --build=binary >../targetcli-
dpkg-genchanges: info: binary-only upload (no source code included)
dpkg-source --after-build .
dpkg-source: info: unapplying 0003-Use-
dpkg-buildpackage: info: binary-only upload (no source included)
signfile targetcli-
signfile targetcli-
- installation:
(c)rafaeldtinoc
Selecting previously unselected package targetcli-fb.
(Reading database ... 76255 files and directories currently installed.)
Preparing to unpack .../targetcli-
Unpacking targetcli-fb (1:2.1.51-0ubuntu1) ...
Setting up targetcli-fb (1:2.1.51-0ubuntu1) ...
/usr/lib/
self.stdin = io.open(p2cwrite, 'wb', bufsize)
Created symlink /etc/systemd/
Created symlink /etc/systemd/
- service:
(c)rafaeldtinoc
● targetclid.service - Targetcli daemon
Loaded: loaded (/lib/systemd/
Active: inactive (dead)
TriggeredBy: ● targetclid.socket
Docs: man:targetclid(8)
(c)rafaeldtinoc
(c)rafaeldtinoc
● targetclid.service - Targetcli daemon
Loaded: loaded (/lib/systemd/
Active: active (running) since Thu 2020-02-27 15:38:36 UTC; 1s ago
TriggeredBy: ● targetclid.socket
Docs: man:targetclid(8)
Main PID: 15181 (targetclid)
Tasks: 3 (limit: 23186)
Memory: 14.0M
CGroup: /system.
I'm replacing my LIO storage machine with this package to make sure its working... and then I'll upload if you are good.
Rafael David Tinoco (rafaeldtinoco) wrote : | # |
Okay so with previous package I had:
https:/
With the new package after restoring config:
https:/
Client is working:
/> sessions
alias: clufocal01 sid: 1 type: Normal session-state: LOGGED_IN
Will upload this version.
Rafael David Tinoco (rafaeldtinoco) wrote : | # |
$ git log -1 --pretty=oneline --tags
327f616 (HEAD -> focal-merge-
(c)rafaeldtinoc
Enumerating objects: 73, done.
Counting objects: 100% (73/73), done.
Delta compression using up to 8 threads
Compressing objects: 100% (51/51), done.
Writing objects: 100% (54/54), 17.01 KiB | 2.83 MiB/s, done.
Total 54 (delta 30), reused 0 (delta 0)
To ssh://git.
* [new tag] upload/
(c)rafaeldtinoc
Checking signature on .changes
gpg: ../targetcli-
Checking signature on .dsc
gpg: ../targetcli-
Uploading to ubuntu (via ftp to upload.ubuntu.com):
Uploading targetcli-
Uploading targetcli-
Uploading targetcli-
Uploading targetcli-
Uploading targetcli-
Successfully uploaded packages.
Preview Diff
1 | diff --git a/daemon/targetclid b/daemon/targetclid | |||
2 | 0 | new file mode 100644 | 0 | new file mode 100644 |
3 | index 0000000..fb472dc | |||
4 | --- /dev/null | |||
5 | +++ b/daemon/targetclid | |||
6 | @@ -0,0 +1,276 @@ | |||
7 | 1 | #!/usr/bin/python | ||
8 | 2 | |||
9 | 3 | ''' | ||
10 | 4 | targetclid | ||
11 | 5 | |||
12 | 6 | This file is part of targetcli-fb. | ||
13 | 7 | Copyright (c) 2019 by Red Hat, Inc. | ||
14 | 8 | |||
15 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
16 | 10 | not use this file except in compliance with the License. You may obtain | ||
17 | 11 | a copy of the License at | ||
18 | 12 | |||
19 | 13 | http://www.apache.org/licenses/LICENSE-2.0 | ||
20 | 14 | |||
21 | 15 | Unless required by applicable law or agreed to in writing, software | ||
22 | 16 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
23 | 17 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
24 | 18 | License for the specific language governing permissions and limitations | ||
25 | 19 | under the License. | ||
26 | 20 | ''' | ||
27 | 21 | |||
28 | 22 | from __future__ import print_function | ||
29 | 23 | from targetcli import UIRoot | ||
30 | 24 | from targetcli import __version__ as targetcli_version | ||
31 | 25 | from configshell_fb import ConfigShell | ||
32 | 26 | from os import getuid, getenv, unlink | ||
33 | 27 | from threading import Thread | ||
34 | 28 | |||
35 | 29 | import sys | ||
36 | 30 | import socket | ||
37 | 31 | import struct | ||
38 | 32 | import fcntl | ||
39 | 33 | import signal | ||
40 | 34 | import errno | ||
41 | 35 | |||
42 | 36 | |||
43 | 37 | err = sys.stderr | ||
44 | 38 | |||
45 | 39 | class TargetCLI: | ||
46 | 40 | def __init__(self): | ||
47 | 41 | ''' | ||
48 | 42 | initializer | ||
49 | 43 | ''' | ||
50 | 44 | # socket for unix communication | ||
51 | 45 | self.socket_path = '/var/run/targetclid.sock' | ||
52 | 46 | # pid file for defending on multiple daemon runs | ||
53 | 47 | self.pid_file = '/var/run/targetclid.pid' | ||
54 | 48 | |||
55 | 49 | self.NoSignal = True | ||
56 | 50 | self.sock = None | ||
57 | 51 | |||
58 | 52 | # shell console methods | ||
59 | 53 | self.shell = ConfigShell(getenv("TARGETCLI_HOME", '~/.targetcli')) | ||
60 | 54 | self.con = self.shell.con | ||
61 | 55 | self.display = self.shell.con.display | ||
62 | 56 | self.render = self.shell.con.render_text | ||
63 | 57 | |||
64 | 58 | # Handle SIGINT SIGTERM SIGHUP gracefully | ||
65 | 59 | signal.signal(signal.SIGINT, self.signal_handler) | ||
66 | 60 | signal.signal(signal.SIGTERM, self.signal_handler) | ||
67 | 61 | signal.signal(signal.SIGHUP, self.signal_handler) | ||
68 | 62 | |||
69 | 63 | try: | ||
70 | 64 | self.pfd = open(self.pid_file, 'w+') | ||
71 | 65 | except IOError as e: | ||
72 | 66 | self.display( | ||
73 | 67 | self.render( | ||
74 | 68 | "opening pidfile failed: %s" %str(e), | ||
75 | 69 | 'red')) | ||
76 | 70 | sys.exit(1) | ||
77 | 71 | |||
78 | 72 | self.try_pidfile_lock() | ||
79 | 73 | |||
80 | 74 | is_root = False | ||
81 | 75 | if getuid() == 0: | ||
82 | 76 | is_root = True | ||
83 | 77 | |||
84 | 78 | try: | ||
85 | 79 | root_node = UIRoot(self.shell, as_root=is_root) | ||
86 | 80 | root_node.refresh() | ||
87 | 81 | except Exception as error: | ||
88 | 82 | self.display(self.render(str(error), 'red')) | ||
89 | 83 | if not is_root: | ||
90 | 84 | self.display(self.render("Retry as root.", 'red')) | ||
91 | 85 | self.pfd.close() | ||
92 | 86 | sys.exit(1) | ||
93 | 87 | |||
94 | 88 | # Keep track, for later use | ||
95 | 89 | self.con_stdout_ = self.con._stdout | ||
96 | 90 | self.con_stderr_ = self.con._stderr | ||
97 | 91 | |||
98 | 92 | |||
99 | 93 | def __del__(self): | ||
100 | 94 | ''' | ||
101 | 95 | destructor | ||
102 | 96 | ''' | ||
103 | 97 | if hasattr(self, 'pfd'): | ||
104 | 98 | self.pfd.close() | ||
105 | 99 | |||
106 | 100 | |||
107 | 101 | def signal_handler(self, signum, frame): | ||
108 | 102 | ''' | ||
109 | 103 | signal handler | ||
110 | 104 | ''' | ||
111 | 105 | self.NoSignal = False | ||
112 | 106 | if self.sock: | ||
113 | 107 | self.sock.close() | ||
114 | 108 | |||
115 | 109 | |||
116 | 110 | def try_pidfile_lock(self): | ||
117 | 111 | ''' | ||
118 | 112 | get lock on pidfile, which is to check if targetclid is running | ||
119 | 113 | ''' | ||
120 | 114 | # check if targetclid is already running | ||
121 | 115 | lock = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0) | ||
122 | 116 | try: | ||
123 | 117 | fcntl.fcntl(self.pfd, fcntl.F_SETLK, lock) | ||
124 | 118 | except Exception: | ||
125 | 119 | self.display(self.render("targetclid is already running...", 'red')) | ||
126 | 120 | self.pfd.close() | ||
127 | 121 | sys.exit(1) | ||
128 | 122 | |||
129 | 123 | |||
130 | 124 | def release_pidfile_lock(self): | ||
131 | 125 | ''' | ||
132 | 126 | release lock on pidfile | ||
133 | 127 | ''' | ||
134 | 128 | lock = struct.pack('hhllhh', fcntl.F_UNLCK, 0, 0, 0, 0, 0) | ||
135 | 129 | try: | ||
136 | 130 | fcntl.fcntl(self.pfd, fcntl.F_SETLK, lock) | ||
137 | 131 | except Exception as e: | ||
138 | 132 | self.display( | ||
139 | 133 | self.render( | ||
140 | 134 | "fcntl(UNLCK) on pidfile failed: %s" %str(e), | ||
141 | 135 | 'red')) | ||
142 | 136 | self.pfd.close() | ||
143 | 137 | sys.exit(1) | ||
144 | 138 | self.pfd.close() | ||
145 | 139 | |||
146 | 140 | |||
147 | 141 | def client_thread(self, connection): | ||
148 | 142 | ''' | ||
149 | 143 | Handle commands from client | ||
150 | 144 | ''' | ||
151 | 145 | # load the prefs | ||
152 | 146 | self.shell.prefs.load() | ||
153 | 147 | |||
154 | 148 | still_listen = True | ||
155 | 149 | # Receive the data in small chunks and retransmit it | ||
156 | 150 | while still_listen: | ||
157 | 151 | data = connection.recv(65535) | ||
158 | 152 | if b'-END@OF@DATA-' in data: | ||
159 | 153 | connection.close() | ||
160 | 154 | still_listen = False | ||
161 | 155 | else: | ||
162 | 156 | self.con._stdout = self.con._stderr = f = open("/tmp/data.txt", "w") | ||
163 | 157 | try: | ||
164 | 158 | # extract multiple commands delimited with '%' | ||
165 | 159 | list_data = data.decode().split('%') | ||
166 | 160 | for cmd in list_data: | ||
167 | 161 | self.shell.run_cmdline(cmd) | ||
168 | 162 | except Exception as e: | ||
169 | 163 | print(str(e), file=f) # push error to stream | ||
170 | 164 | |||
171 | 165 | # Restore | ||
172 | 166 | self.con._stdout = self.con_stdout_ | ||
173 | 167 | self.con._stderr = self.con_stderr_ | ||
174 | 168 | f.close() | ||
175 | 169 | |||
176 | 170 | with open('/tmp/data.txt', 'r') as f: | ||
177 | 171 | output = f.read() | ||
178 | 172 | var = struct.pack('i', len(output)) | ||
179 | 173 | connection.sendall(var) # length of string | ||
180 | 174 | if len(output): | ||
181 | 175 | connection.sendall(output.encode()) # actual string | ||
182 | 176 | |||
183 | 177 | |||
184 | 178 | def usage(): | ||
185 | 179 | print("Usage: %s [--version|--help]" % sys.argv[0], file=err) | ||
186 | 180 | print(" --version\t\tPrint version", file=err) | ||
187 | 181 | print(" --help\t\tPrint this information", file=err) | ||
188 | 182 | sys.exit(0) | ||
189 | 183 | |||
190 | 184 | |||
191 | 185 | def version(): | ||
192 | 186 | print("%s version %s" % (sys.argv[0], targetcli_version), file=err) | ||
193 | 187 | sys.exit(0) | ||
194 | 188 | |||
195 | 189 | |||
196 | 190 | def usage_version(cmd): | ||
197 | 191 | if cmd in ("help", "--help", "-h"): | ||
198 | 192 | usage() | ||
199 | 193 | |||
200 | 194 | if cmd in ("version", "--version", "-v"): | ||
201 | 195 | version() | ||
202 | 196 | |||
203 | 197 | |||
204 | 198 | def main(): | ||
205 | 199 | ''' | ||
206 | 200 | start targetclid | ||
207 | 201 | ''' | ||
208 | 202 | if len(sys.argv) > 1: | ||
209 | 203 | usage_version(sys.argv[1]) | ||
210 | 204 | print("unrecognized option: %s" % (sys.argv[1])) | ||
211 | 205 | sys.exit(-1) | ||
212 | 206 | |||
213 | 207 | to = TargetCLI() | ||
214 | 208 | |||
215 | 209 | if getenv('LISTEN_PID'): | ||
216 | 210 | # the systemd-activation path, using next available FD | ||
217 | 211 | fn = sys.stderr.fileno() + 1 | ||
218 | 212 | try: | ||
219 | 213 | sock = socket.fromfd(fn, socket.AF_UNIX, socket.SOCK_STREAM) | ||
220 | 214 | except socket.error as err: | ||
221 | 215 | to.display(to.render(err.strerror, 'red')) | ||
222 | 216 | sys.exit(1) | ||
223 | 217 | |||
224 | 218 | # save socket so a signal can clea it up | ||
225 | 219 | to.sock = sock | ||
226 | 220 | else: | ||
227 | 221 | # Make sure file doesn't exist already | ||
228 | 222 | try: | ||
229 | 223 | unlink(to.socket_path) | ||
230 | 224 | except: | ||
231 | 225 | pass | ||
232 | 226 | |||
233 | 227 | # Create a TCP/IP socket | ||
234 | 228 | try: | ||
235 | 229 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
236 | 230 | except socket.error as err: | ||
237 | 231 | to.display(to.render(err.strerror, 'red')) | ||
238 | 232 | sys.exit(1) | ||
239 | 233 | |||
240 | 234 | # save socket so a signal can clea it up | ||
241 | 235 | to.sock = sock | ||
242 | 236 | |||
243 | 237 | # Bind the socket path | ||
244 | 238 | try: | ||
245 | 239 | sock.bind(to.socket_path) | ||
246 | 240 | except socket.error as err: | ||
247 | 241 | to.display(to.render(err.strerror, 'red')) | ||
248 | 242 | sys.exit(1) | ||
249 | 243 | |||
250 | 244 | # Listen for incoming connections | ||
251 | 245 | try: | ||
252 | 246 | sock.listen(1) | ||
253 | 247 | except socket.error as err: | ||
254 | 248 | to.display(to.render(err.strerror, 'red')) | ||
255 | 249 | sys.exit(1) | ||
256 | 250 | |||
257 | 251 | while to.NoSignal: | ||
258 | 252 | try: | ||
259 | 253 | # Wait for a connection | ||
260 | 254 | connection, client_address = sock.accept() | ||
261 | 255 | except socket.error as err: | ||
262 | 256 | if err.errno != errno.EBADF or to.NoSignal: | ||
263 | 257 | to.display(to.render(err.strerror, 'red')) | ||
264 | 258 | break | ||
265 | 259 | |||
266 | 260 | thread = Thread(target=to.client_thread, args=(connection,)) | ||
267 | 261 | thread.start() | ||
268 | 262 | try: | ||
269 | 263 | thread.join() | ||
270 | 264 | except: | ||
271 | 265 | to.display(to.render(str(error), 'red')) | ||
272 | 266 | |||
273 | 267 | to.release_pidfile_lock() | ||
274 | 268 | |||
275 | 269 | if not to.NoSignal: | ||
276 | 270 | to.display(to.render("Signal received, quiting gracefully!", 'green')) | ||
277 | 271 | sys.exit(0) | ||
278 | 272 | sys.exit(1) | ||
279 | 273 | |||
280 | 274 | |||
281 | 275 | if __name__ == "__main__": | ||
282 | 276 | main() | ||
283 | diff --git a/debian/changelog b/debian/changelog | |||
284 | index 6948d6d..493dc40 100644 | |||
285 | --- a/debian/changelog | |||
286 | +++ b/debian/changelog | |||
287 | @@ -1,3 +1,20 @@ | |||
288 | 1 | targetcli-fb (1:2.1.51-0ubuntu1) focal; urgency=medium | ||
289 | 2 | |||
290 | 3 | [ Rafael David Tinoco ] | ||
291 | 4 | * New upstream version 2.1.51 | ||
292 | 5 | - Upstream changed their versioning scheme in a way that makes the | ||
293 | 6 | latest version lower than the previous one therefore we had to add | ||
294 | 7 | an epoch bump. | ||
295 | 8 | * debian/watch: fix the watch file for upstream versions without fb. | ||
296 | 9 | |||
297 | 10 | [ Christian Ehrhardt ] | ||
298 | 11 | * This now provides an optional daemon targetclid to be used for | ||
299 | 12 | operations at scale to retain state between commands in memory. | ||
300 | 13 | - d/manpages: install targetclid man page | ||
301 | 14 | - d/rules: install and enable targetclid service and socket | ||
302 | 15 | |||
303 | 16 | -- Rafael David Tinoco <rafaeldtinoco@ubuntu.com> Thu, 27 Feb 2020 02:30:18 +0000 | ||
304 | 17 | |||
305 | 1 | targetcli-fb (2.1.fb49-1) unstable; urgency=medium | 18 | targetcli-fb (2.1.fb49-1) unstable; urgency=medium |
306 | 2 | 19 | ||
307 | 3 | * New upstream version 2.1.fb49 | 20 | * New upstream version 2.1.fb49 |
308 | diff --git a/debian/control b/debian/control | |||
309 | index a1ce30a..9c717c2 100644 | |||
310 | --- a/debian/control | |||
311 | +++ b/debian/control | |||
312 | @@ -1,7 +1,8 @@ | |||
313 | 1 | Source: targetcli-fb | 1 | Source: targetcli-fb |
314 | 2 | Section: admin | 2 | Section: admin |
315 | 3 | Priority: optional | 3 | Priority: optional |
317 | 4 | Maintainer: Debian LIO Target Packagers <team+linux-blocks@tracker.debian.org> | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
318 | 5 | XSBC-Original-Maintainer: Debian LIO Target Packagers <team+linux-blocks@tracker.debian.org> | ||
319 | 5 | Uploaders: Christophe Vu-Brugier <cvubrugier@fastmail.fm>, | 6 | Uploaders: Christophe Vu-Brugier <cvubrugier@fastmail.fm>, |
320 | 6 | Ritesh Raj Sarraf <rrs@debian.org>, | 7 | Ritesh Raj Sarraf <rrs@debian.org>, |
321 | 7 | Christian Seiler <christian@iwakd.de> | 8 | Christian Seiler <christian@iwakd.de> |
322 | diff --git a/debian/manpages b/debian/manpages | |||
323 | index 07dd87b..78f958a 100644 | |||
324 | --- a/debian/manpages | |||
325 | +++ b/debian/manpages | |||
326 | @@ -1 +1,2 @@ | |||
327 | 1 | targetcli.8 | 1 | targetcli.8 |
328 | 2 | targetclid.8 | ||
329 | diff --git a/debian/patches/0003-Use-etc-rtslib-fb-target-instead-of-etc-target.patch b/debian/patches/0003-Use-etc-rtslib-fb-target-instead-of-etc-target.patch | |||
330 | index 00f6d3d..e7d0324 100644 | |||
331 | --- a/debian/patches/0003-Use-etc-rtslib-fb-target-instead-of-etc-target.patch | |||
332 | +++ b/debian/patches/0003-Use-etc-rtslib-fb-target-instead-of-etc-target.patch | |||
333 | @@ -13,7 +13,7 @@ out of the box. | |||
334 | 13 | 13 | ||
335 | 14 | --- a/targetcli.8 | 14 | --- a/targetcli.8 |
336 | 15 | +++ b/targetcli.8 | 15 | +++ b/targetcli.8 |
338 | 16 | @@ -355,7 +355,7 @@ | 16 | @@ -360,7 +360,7 @@ |
339 | 17 | Save the current configuration settings to a file, from which settings | 17 | Save the current configuration settings to a file, from which settings |
340 | 18 | will be restored if the system is rebooted. By default, this will save | 18 | will be restored if the system is rebooted. By default, this will save |
341 | 19 | the configuration to | 19 | the configuration to |
342 | @@ -22,7 +22,7 @@ out of the box. | |||
343 | 22 | .P | 22 | .P |
344 | 23 | This command is executed from the configuration root node. | 23 | This command is executed from the configuration root node. |
345 | 24 | .P | 24 | .P |
347 | 25 | @@ -415,7 +415,7 @@ | 25 | @@ -420,7 +420,7 @@ |
348 | 26 | to change working path to newly-created nodes. Global settings | 26 | to change working path to newly-created nodes. Global settings |
349 | 27 | are user-specific and are saved to ~/.targetcli/ upon exit, unlike | 27 | are user-specific and are saved to ~/.targetcli/ upon exit, unlike |
350 | 28 | other groups, which are system-wide and kept in | 28 | other groups, which are system-wide and kept in |
351 | @@ -31,7 +31,7 @@ out of the box. | |||
352 | 31 | .SS BACKSTORE-SPECIFIC | 31 | .SS BACKSTORE-SPECIFIC |
353 | 32 | .B attribute | 32 | .B attribute |
354 | 33 | .br | 33 | .br |
356 | 34 | @@ -453,9 +453,9 @@ | 34 | @@ -458,9 +458,9 @@ |
357 | 35 | /iscsi/<target_iqn>/tpgX/acls/<initiator_iqn> configuration node. Set | 35 | /iscsi/<target_iqn>/tpgX/acls/<initiator_iqn> configuration node. Set |
358 | 36 | the userid and password for full-feature phase for this ACL. | 36 | the userid and password for full-feature phase for this ACL. |
359 | 37 | .SH FILES | 37 | .SH FILES |
360 | @@ -42,10 +42,10 @@ out of the box. | |||
361 | 42 | +.B /etc/rtslib-fb-target/backup/* | 42 | +.B /etc/rtslib-fb-target/backup/* |
362 | 43 | .SH ENVIRONMENT | 43 | .SH ENVIRONMENT |
363 | 44 | .SS TARGETCLI_HOME | 44 | .SS TARGETCLI_HOME |
365 | 45 | If set, this variable points to a directory that should be used instead of ~/.targetctl | 45 | If set, this variable points to a directory that should be used instead of ~/.targetcli |
366 | 46 | --- a/targetcli/ui_root.py | 46 | --- a/targetcli/ui_root.py |
367 | 47 | +++ b/targetcli/ui_root.py | 47 | +++ b/targetcli/ui_root.py |
369 | 48 | @@ -33,8 +33,8 @@ | 48 | @@ -34,8 +34,8 @@ |
370 | 49 | from .ui_node import UINode | 49 | from .ui_node import UINode |
371 | 50 | from .ui_target import UIFabricModule | 50 | from .ui_target import UIFabricModule |
372 | 51 | 51 | ||
373 | diff --git a/debian/rules b/debian/rules | |||
374 | index 89af47e..dd11732 100755 | |||
375 | --- a/debian/rules | |||
376 | +++ b/debian/rules | |||
377 | @@ -2,3 +2,14 @@ | |||
378 | 2 | 2 | ||
379 | 3 | %: | 3 | %: |
380 | 4 | dh $@ --with python3 -Spybuild | 4 | dh $@ --with python3 -Spybuild |
381 | 5 | |||
382 | 6 | override_dh_installsystemd: | ||
383 | 7 | # helper for the cli, not an individual entity, keeping it within main pkg | ||
384 | 8 | dh_installsystemd -ptargetcli-fb --name=targetclid --no-restart-on-upgrade --no-start targetclid.service | ||
385 | 9 | dh_installsystemd -ptargetcli-fb --name=targetclid --no-restart-on-upgrade targetclid.socket | ||
386 | 10 | |||
387 | 11 | override_dh_install: | ||
388 | 12 | # not (yet) installed by upstream but required | ||
389 | 13 | cp -v systemd/targetclid.service debian/targetcli-fb.targetclid.service | ||
390 | 14 | cp -v systemd/targetclid.socket debian/targetcli-fb.targetclid.socket | ||
391 | 15 | dh_install | ||
392 | diff --git a/debian/watch b/debian/watch | |||
393 | index c2ed1a0..7a50f79 100644 | |||
394 | --- a/debian/watch | |||
395 | +++ b/debian/watch | |||
396 | @@ -1,3 +1,3 @@ | |||
397 | 1 | version=4 | 1 | version=4 |
399 | 2 | opts=uversionmangle=s/fb//,filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/targetcli-fb-$1\.tar\.gz/ \ | 2 | opts=uversionmangle=s/fb//,dversionmangle=s/fb//,filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/targetcli-fb-$1\.tar\.gz/ \ |
400 | 3 | https://github.com/open-iscsi/targetcli-fb/releases .*/v?(\d\S+)\.tar\.gz | 3 | https://github.com/open-iscsi/targetcli-fb/releases .*/v?(\d\S+)\.tar\.gz |
401 | diff --git a/scripts/targetcli b/scripts/targetcli | |||
402 | index b042ad9..04e5aba 100755 | |||
403 | --- a/scripts/targetcli | |||
404 | +++ b/scripts/targetcli | |||
405 | @@ -24,10 +24,22 @@ from os import getuid, getenv | |||
406 | 24 | from targetcli import UIRoot | 24 | from targetcli import UIRoot |
407 | 25 | from rtslib_fb import RTSLibError | 25 | from rtslib_fb import RTSLibError |
408 | 26 | from configshell_fb import ConfigShell, ExecutionError | 26 | from configshell_fb import ConfigShell, ExecutionError |
409 | 27 | import sys | ||
410 | 28 | from targetcli import __version__ as targetcli_version | 27 | from targetcli import __version__ as targetcli_version |
411 | 29 | 28 | ||
412 | 29 | import sys | ||
413 | 30 | import socket | ||
414 | 31 | import struct | ||
415 | 32 | import readline | ||
416 | 33 | import six | ||
417 | 34 | import fcntl | ||
418 | 35 | |||
419 | 30 | err = sys.stderr | 36 | err = sys.stderr |
420 | 37 | # lockfile for serializing multiple targetcli requests | ||
421 | 38 | lock_file = '/var/run/targetcli.lock' | ||
422 | 39 | socket_path = '/var/run/targetclid.sock' | ||
423 | 40 | hints = ['/', 'backstores/', 'iscsi/', 'loopback/', 'vhost/', 'xen-pvscsi/', | ||
424 | 41 | 'cd', 'pwd', 'ls', 'set', 'get', 'help', 'refresh', 'status', | ||
425 | 42 | 'clearconfig', 'restoreconfig', 'saveconfig', 'exit'] | ||
426 | 31 | 43 | ||
427 | 32 | class TargetCLI(ConfigShell): | 44 | class TargetCLI(ConfigShell): |
428 | 33 | default_prefs = {'color_path': 'magenta', | 45 | default_prefs = {'color_path': 'magenta', |
429 | @@ -51,14 +63,16 @@ class TargetCLI(ConfigShell): | |||
430 | 51 | 'auto_save_on_exit': True, | 63 | 'auto_save_on_exit': True, |
431 | 52 | 'max_backup_files': '10', | 64 | 'max_backup_files': '10', |
432 | 53 | 'auto_add_default_portal': True, | 65 | 'auto_add_default_portal': True, |
433 | 66 | 'auto_use_daemon': False, | ||
434 | 54 | } | 67 | } |
435 | 55 | 68 | ||
436 | 56 | def usage(): | 69 | def usage(): |
438 | 57 | print("Usage: %s [--version|--help|CMD]" % sys.argv[0], file=err) | 70 | print("Usage: %s [--version|--help|CMD|--disable-daemon]" % sys.argv[0], file=err) |
439 | 58 | print(" --version\t\tPrint version", file=err) | 71 | print(" --version\t\tPrint version", file=err) |
440 | 59 | print(" --help\t\tPrint this information", file=err) | 72 | print(" --help\t\tPrint this information", file=err) |
441 | 60 | print(" CMD\t\t\tRun targetcli shell command and exit", file=err) | 73 | print(" CMD\t\t\tRun targetcli shell command and exit", file=err) |
442 | 61 | print(" <nothing>\t\tEnter configuration shell", file=err) | 74 | print(" <nothing>\t\tEnter configuration shell", file=err) |
443 | 75 | print(" --disable-daemon\tTurn-off the global auto use daemon flag", file=err) | ||
444 | 62 | print("See man page for more information.", file=err) | 76 | print("See man page for more information.", file=err) |
445 | 63 | sys.exit(-1) | 77 | sys.exit(-1) |
446 | 64 | 78 | ||
447 | @@ -66,16 +80,153 @@ def version(): | |||
448 | 66 | print("%s version %s" % (sys.argv[0], targetcli_version), file=err) | 80 | print("%s version %s" % (sys.argv[0], targetcli_version), file=err) |
449 | 67 | sys.exit(0) | 81 | sys.exit(0) |
450 | 68 | 82 | ||
451 | 83 | def usage_version(cmd): | ||
452 | 84 | if cmd in ("help", "--help", "-h"): | ||
453 | 85 | usage() | ||
454 | 86 | |||
455 | 87 | if cmd in ("version", "--version", "-v"): | ||
456 | 88 | version() | ||
457 | 89 | |||
458 | 90 | def try_op_lock(shell, lkfd): | ||
459 | 91 | ''' | ||
460 | 92 | acquire a blocking lock on lockfile, to serialize multiple requests | ||
461 | 93 | ''' | ||
462 | 94 | try: | ||
463 | 95 | fcntl.flock(lkfd, fcntl.LOCK_EX) # wait here until ongoing request is finished | ||
464 | 96 | except Exception as e: | ||
465 | 97 | shell.con.display( | ||
466 | 98 | shell.con.render_text( | ||
467 | 99 | "taking lock on lockfile failed: %s" %str(e), | ||
468 | 100 | 'red')) | ||
469 | 101 | sys.exit(1) | ||
470 | 102 | |||
471 | 103 | def release_op_lock(shell, lkfd): | ||
472 | 104 | ''' | ||
473 | 105 | release blocking lock on lockfile, which can allow other requests process | ||
474 | 106 | ''' | ||
475 | 107 | try: | ||
476 | 108 | fcntl.flock(lkfd, fcntl.LOCK_UN) # allow other requests now | ||
477 | 109 | except Exception as e: | ||
478 | 110 | shell.con.display( | ||
479 | 111 | shell.con.render_text( | ||
480 | 112 | "unlock on lockfile failed: %s" %str(e), | ||
481 | 113 | 'red')) | ||
482 | 114 | sys.exit(1) | ||
483 | 115 | lkfd.close() | ||
484 | 116 | |||
485 | 117 | def completer(text, state): | ||
486 | 118 | options = [x for x in hints if x.startswith(text)] | ||
487 | 119 | try: | ||
488 | 120 | return options[state] | ||
489 | 121 | except IndexError: | ||
490 | 122 | return None | ||
491 | 123 | |||
492 | 124 | def call_daemon(shell, req): | ||
493 | 125 | try: | ||
494 | 126 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
495 | 127 | except socket.error as err: | ||
496 | 128 | shell.con.display(shell.con.render_text(err, 'red')) | ||
497 | 129 | sys.exit(1) | ||
498 | 130 | |||
499 | 131 | try: | ||
500 | 132 | sock.connect(socket_path) | ||
501 | 133 | except socket.error as err: | ||
502 | 134 | shell.con.display(shell.con.render_text(err, 'red')) | ||
503 | 135 | shell.con.display( | ||
504 | 136 | shell.con.render_text("Currently auto_use_daemon is true, " | ||
505 | 137 | "hence please make sure targetclid daemon is running ...\n" | ||
506 | 138 | "(or)\nIncase if you wish to turn auto_use_daemon to false " | ||
507 | 139 | "then run '#targetcli --disable-daemon'", 'red')) | ||
508 | 140 | sys.exit(1) | ||
509 | 141 | |||
510 | 142 | try: | ||
511 | 143 | # send request | ||
512 | 144 | sock.sendall(req) | ||
513 | 145 | except socket.error as err: | ||
514 | 146 | shell.con.display(shell.con.render_text(err, 'red')) | ||
515 | 147 | sys.exit(1) | ||
516 | 148 | |||
517 | 149 | var = sock.recv(4) # get length of data | ||
518 | 150 | sending = struct.unpack('i', var) | ||
519 | 151 | amount_expected = sending[0] | ||
520 | 152 | amount_received = 0 | ||
521 | 153 | |||
522 | 154 | # get the actual data in chunks | ||
523 | 155 | while amount_received < amount_expected: | ||
524 | 156 | data = sock.recv(1024) | ||
525 | 157 | amount_received += len(data) | ||
526 | 158 | print(data.decode(), end ="") | ||
527 | 159 | |||
528 | 160 | sock.send(b'-END@OF@DATA-') | ||
529 | 161 | sock.close() | ||
530 | 162 | sys.exit(0) | ||
531 | 163 | |||
532 | 164 | def get_arguments(shell): | ||
533 | 165 | readline.set_completer(completer) | ||
534 | 166 | readline.set_completer_delims('') | ||
535 | 167 | |||
536 | 168 | if 'libedit' in readline.__doc__: | ||
537 | 169 | readline.parse_and_bind("bind ^I rl_complete") | ||
538 | 170 | else: | ||
539 | 171 | readline.parse_and_bind("tab: complete") | ||
540 | 172 | |||
541 | 173 | if len(sys.argv) > 1: | ||
542 | 174 | command = " ".join(sys.argv[1:]) | ||
543 | 175 | else: | ||
544 | 176 | inputs = [] | ||
545 | 177 | shell.con.display("targetcli shell version %s\n" | ||
546 | 178 | "Entering targetcli batch mode for daemonized approach.\n" | ||
547 | 179 | "Enter multiple commands separated by newline and " | ||
548 | 180 | "type 'exit' to run them all in one go.\n" | ||
549 | 181 | % targetcli_version) | ||
550 | 182 | while True: | ||
551 | 183 | shell.con.raw_write("/> ") | ||
552 | 184 | command = six.moves.input() | ||
553 | 185 | if command.lower() == "exit": | ||
554 | 186 | break | ||
555 | 187 | inputs.append(command) | ||
556 | 188 | command = '%'.join(inputs) # delimit multiple commands with '%' | ||
557 | 189 | |||
558 | 190 | if not command: | ||
559 | 191 | sys.exit(1) | ||
560 | 192 | |||
561 | 193 | usage_version(command); | ||
562 | 194 | |||
563 | 195 | return command | ||
564 | 196 | |||
565 | 69 | def main(): | 197 | def main(): |
566 | 70 | ''' | 198 | ''' |
567 | 71 | Start the targetcli shell. | 199 | Start the targetcli shell. |
568 | 72 | ''' | 200 | ''' |
569 | 201 | shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli')) | ||
570 | 202 | |||
571 | 203 | is_root = False | ||
572 | 73 | if getuid() == 0: | 204 | if getuid() == 0: |
573 | 74 | is_root = True | 205 | is_root = True |
574 | 75 | else: | ||
575 | 76 | is_root = False | ||
576 | 77 | 206 | ||
578 | 78 | shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli')) | 207 | try: |
579 | 208 | lkfd = open(lock_file, 'w+'); | ||
580 | 209 | except IOError as e: | ||
581 | 210 | shell.con.display( | ||
582 | 211 | shell.con.render_text("opening lockfile failed: %s" %str(e), | ||
583 | 212 | 'red')) | ||
584 | 213 | sys.exit(1) | ||
585 | 214 | |||
586 | 215 | try_op_lock(shell, lkfd) | ||
587 | 216 | |||
588 | 217 | use_daemon = False | ||
589 | 218 | if shell.prefs['auto_use_daemon']: | ||
590 | 219 | use_daemon = True | ||
591 | 220 | |||
592 | 221 | disable_daemon=False | ||
593 | 222 | if len(sys.argv) > 1: | ||
594 | 223 | usage_version(sys.argv[1]) | ||
595 | 224 | if sys.argv[1] in ("disable-daemon", "--disable-daemon"): | ||
596 | 225 | disable_daemon=True | ||
597 | 226 | |||
598 | 227 | if use_daemon and not disable_daemon: | ||
599 | 228 | call_daemon(shell, get_arguments(shell).encode()) | ||
600 | 229 | # does not return | ||
601 | 79 | 230 | ||
602 | 80 | try: | 231 | try: |
603 | 81 | root_node = UIRoot(shell, as_root=is_root) | 232 | root_node = UIRoot(shell, as_root=is_root) |
604 | @@ -87,14 +238,11 @@ def main(): | |||
605 | 87 | sys.exit(-1) | 238 | sys.exit(-1) |
606 | 88 | 239 | ||
607 | 89 | if len(sys.argv) > 1: | 240 | if len(sys.argv) > 1: |
608 | 90 | if sys.argv[1] in ("--help", "-h"): | ||
609 | 91 | usage() | ||
610 | 92 | |||
611 | 93 | if sys.argv[1] in ("--version", "-v"): | ||
612 | 94 | version() | ||
613 | 95 | |||
614 | 96 | try: | 241 | try: |
616 | 97 | shell.run_cmdline(" ".join(sys.argv[1:])) | 242 | if disable_daemon: |
617 | 243 | shell.run_cmdline('set global auto_use_daemon=false') | ||
618 | 244 | else: | ||
619 | 245 | shell.run_cmdline(" ".join(sys.argv[1:])) | ||
620 | 98 | except Exception as e: | 246 | except Exception as e: |
621 | 99 | print(str(e), file=sys.stderr) | 247 | print(str(e), file=sys.stderr) |
622 | 100 | sys.exit(1) | 248 | sys.exit(1) |
623 | @@ -117,6 +265,8 @@ def main(): | |||
624 | 117 | shell.log.info("Global pref auto_save_on_exit=true") | 265 | shell.log.info("Global pref auto_save_on_exit=true") |
625 | 118 | root_node.ui_command_saveconfig() | 266 | root_node.ui_command_saveconfig() |
626 | 119 | 267 | ||
627 | 268 | release_op_lock(shell, lkfd) | ||
628 | 269 | |||
629 | 120 | 270 | ||
630 | 121 | if __name__ == "__main__": | 271 | if __name__ == "__main__": |
631 | 122 | main() | 272 | main() |
632 | diff --git a/setup.py b/setup.py | |||
633 | index 7b44304..8dff55e 100755 | |||
634 | --- a/setup.py | |||
635 | +++ b/setup.py | |||
636 | @@ -30,7 +30,10 @@ setup( | |||
637 | 30 | maintainer_email = 'agrover@redhat.com', | 30 | maintainer_email = 'agrover@redhat.com', |
638 | 31 | url = 'http://github.com/open-iscsi/targetcli-fb', | 31 | url = 'http://github.com/open-iscsi/targetcli-fb', |
639 | 32 | packages = ['targetcli'], | 32 | packages = ['targetcli'], |
641 | 33 | scripts = ['scripts/targetcli'], | 33 | scripts = [ |
642 | 34 | 'scripts/targetcli', | ||
643 | 35 | 'daemon/targetclid' | ||
644 | 36 | ], | ||
645 | 34 | classifiers = [ | 37 | classifiers = [ |
646 | 35 | "Programming Language :: Python", | 38 | "Programming Language :: Python", |
647 | 36 | "Programming Language :: Python :: 3", | 39 | "Programming Language :: Python :: 3", |
648 | diff --git a/systemd/targetclid.service b/systemd/targetclid.service | |||
649 | 37 | new file mode 100644 | 40 | new file mode 100644 |
650 | index 0000000..dd1b54c | |||
651 | --- /dev/null | |||
652 | +++ b/systemd/targetclid.service | |||
653 | @@ -0,0 +1,13 @@ | |||
654 | 1 | [Unit] | ||
655 | 2 | Description=Targetcli daemon | ||
656 | 3 | Documentation=man:targetclid(8) | ||
657 | 4 | After=network.target | ||
658 | 5 | |||
659 | 6 | [Service] | ||
660 | 7 | Type=simple | ||
661 | 8 | ExecStart=/usr/bin/targetclid | ||
662 | 9 | Restart=on-failure | ||
663 | 10 | |||
664 | 11 | [Install] | ||
665 | 12 | WantedBy=multi-user.target | ||
666 | 13 | Also=targetclid.socket | ||
667 | diff --git a/systemd/targetclid.socket b/systemd/targetclid.socket | |||
668 | 0 | new file mode 100644 | 14 | new file mode 100644 |
669 | index 0000000..4730fce | |||
670 | --- /dev/null | |||
671 | +++ b/systemd/targetclid.socket | |||
672 | @@ -0,0 +1,9 @@ | |||
673 | 1 | [Unit] | ||
674 | 2 | Description=targetclid socket | ||
675 | 3 | Documentation=man:targetclid(8) | ||
676 | 4 | |||
677 | 5 | [Socket] | ||
678 | 6 | ListenStream=/var/run/targetclid.sock | ||
679 | 7 | |||
680 | 8 | [Install] | ||
681 | 9 | WantedBy=sockets.target | ||
682 | diff --git a/targetcli.8 b/targetcli.8 | |||
683 | index 61f4b64..a73f785 100644 | |||
684 | --- a/targetcli.8 | |||
685 | +++ b/targetcli.8 | |||
686 | @@ -10,6 +10,11 @@ administrator to assign local storage resources backed by either | |||
687 | 10 | files, volumes, local SCSI devices, or ramdisk, and export them to | 10 | files, volumes, local SCSI devices, or ramdisk, and export them to |
688 | 11 | remote systems via network fabrics, such as iSCSI or FCoE. | 11 | remote systems via network fabrics, such as iSCSI or FCoE. |
689 | 12 | .P | 12 | .P |
690 | 13 | There is a daemon component for targetcli, which will greatly improve | ||
691 | 14 | the overall execution time taken by targetcli commands at scale. For | ||
692 | 15 | more details about switching to daemonized mode refer to targetclid(8) | ||
693 | 16 | man page. | ||
694 | 17 | .P | ||
695 | 13 | The configuration layout is tree-based, similar to a filesystem, and | 18 | The configuration layout is tree-based, similar to a filesystem, and |
696 | 14 | is navigated in a similar manner. | 19 | is navigated in a similar manner. |
697 | 15 | .SH USAGE | 20 | .SH USAGE |
698 | @@ -458,8 +463,9 @@ the userid and password for full-feature phase for this ACL. | |||
699 | 458 | .B /etc/target/backup/* | 463 | .B /etc/target/backup/* |
700 | 459 | .SH ENVIRONMENT | 464 | .SH ENVIRONMENT |
701 | 460 | .SS TARGETCLI_HOME | 465 | .SS TARGETCLI_HOME |
703 | 461 | If set, this variable points to a directory that should be used instead of ~/.targetctl | 466 | If set, this variable points to a directory that should be used instead of ~/.targetcli |
704 | 462 | .SH SEE ALSO | 467 | .SH SEE ALSO |
705 | 468 | .BR targetclid (8), | ||
706 | 463 | .BR targetctl (8), | 469 | .BR targetctl (8), |
707 | 464 | .BR tcmu-runner (8) | 470 | .BR tcmu-runner (8) |
708 | 465 | .SH AUTHOR | 471 | .SH AUTHOR |
709 | diff --git a/targetcli/ui_backstore.py b/targetcli/ui_backstore.py | |||
710 | index 275d46b..8692f22 100644 | |||
711 | --- a/targetcli/ui_backstore.py | |||
712 | +++ b/targetcli/ui_backstore.py | |||
713 | @@ -59,14 +59,14 @@ def human_to_bytes(hsize, kilo=1024): | |||
714 | 59 | ''' | 59 | ''' |
715 | 60 | This function converts human-readable amounts of bytes to bytes. | 60 | This function converts human-readable amounts of bytes to bytes. |
716 | 61 | It understands the following units : | 61 | It understands the following units : |
725 | 62 | - I{B} or no unit present for Bytes | 62 | - B or no unit present for Bytes |
726 | 63 | - I{k}, I{K}, I{kB}, I{KB} for kB (kilobytes) | 63 | - k, K, kB, KB for kB (kilobytes) |
727 | 64 | - I{m}, I{M}, I{mB}, I{MB} for MB (megabytes) | 64 | - m, M, mB, MB for MB (megabytes) |
728 | 65 | - I{g}, I{G}, I{gB}, I{GB} for GB (gigabytes) | 65 | - g, G, gB, GB for GB (gigabytes) |
729 | 66 | - I{t}, I{T}, I{tB}, I{TB} for TB (terabytes) | 66 | - t, T, tB, TB for TB (terabytes) |
730 | 67 | 67 | ||
731 | 68 | Note: The definition of I{kilo} defaults to 1kB = 1024Bytes. | 68 | Note: The definition of kilo defaults to 1kB = 1024Bytes. |
732 | 69 | Strictly speaking, those should not be called I{kB} but I{kiB}. | 69 | Strictly speaking, those should not be called "kB" but "kiB". |
733 | 70 | You can override that with the optional kilo parameter. | 70 | You can override that with the optional kilo parameter. |
734 | 71 | 71 | ||
735 | 72 | @param hsize: The human-readable version of the Bytes amount to convert | 72 | @param hsize: The human-readable version of the Bytes amount to convert |
736 | @@ -286,13 +286,13 @@ class UIBackstore(UINode): | |||
737 | 286 | 286 | ||
738 | 287 | def ui_command_delete(self, name, save=None): | 287 | def ui_command_delete(self, name, save=None): |
739 | 288 | ''' | 288 | ''' |
741 | 289 | Recursively deletes the storage object having the specified I{name}. If | 289 | Recursively deletes the storage object having the specified name. If |
742 | 290 | there are LUNs using this storage object, they will be deleted too. | 290 | there are LUNs using this storage object, they will be deleted too. |
743 | 291 | 291 | ||
744 | 292 | EXAMPLE | 292 | EXAMPLE |
745 | 293 | ======= | 293 | ======= |
748 | 294 | B{delete mystorage} | 294 | delete mystorage |
749 | 295 | ------------------- | 295 | ---------------- |
750 | 296 | Deletes the storage object named mystorage, and all associated LUNs. | 296 | Deletes the storage object named mystorage, and all associated LUNs. |
751 | 297 | ''' | 297 | ''' |
752 | 298 | self.assert_root() | 298 | self.assert_root() |
753 | @@ -354,7 +354,7 @@ class UIPSCSIBackstore(UIBackstore): | |||
754 | 354 | def ui_command_create(self, name, dev): | 354 | def ui_command_create(self, name, dev): |
755 | 355 | ''' | 355 | ''' |
756 | 356 | Creates a PSCSI storage object, with supplied name and SCSI device. The | 356 | Creates a PSCSI storage object, with supplied name and SCSI device. The |
758 | 357 | SCSI device I{dev} can either be a path name to the device, in which | 357 | SCSI device "dev" can either be a path name to the device, in which |
759 | 358 | case it is recommended to use the /dev/disk/by-id hierarchy to have | 358 | case it is recommended to use the /dev/disk/by-id hierarchy to have |
760 | 359 | consistent naming should your physical SCSI system be modified, or an | 359 | consistent naming should your physical SCSI system be modified, or an |
761 | 360 | SCSI device ID in the H:C:T:L format, which is not recommended as SCSI | 360 | SCSI device ID in the H:C:T:L format, which is not recommended as SCSI |
762 | @@ -383,17 +383,17 @@ class UIRDMCPBackstore(UIBackstore): | |||
763 | 383 | 383 | ||
764 | 384 | def ui_command_create(self, name, size, nullio=None, wwn=None): | 384 | def ui_command_create(self, name, size, nullio=None, wwn=None): |
765 | 385 | ''' | 385 | ''' |
767 | 386 | Creates an RDMCP storage object. I{size} is the size of the ramdisk. | 386 | Creates an RDMCP storage object. "size" is the size of the ramdisk. |
768 | 387 | 387 | ||
769 | 388 | SIZE SYNTAX | 388 | SIZE SYNTAX |
770 | 389 | =========== | 389 | =========== |
771 | 390 | - If size is an int, it represents a number of bytes. | 390 | - If size is an int, it represents a number of bytes. |
772 | 391 | - If size is a string, the following units can be used: | 391 | - If size is a string, the following units can be used: |
778 | 392 | - B{B} or no unit present for bytes | 392 | - B or no unit present for bytes |
779 | 393 | - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes) | 393 | - k, K, kB, KB for kB (kilobytes) |
780 | 394 | - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes) | 394 | - m, M, mB, MB for MB (megabytes) |
781 | 395 | - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes) | 395 | - g, G, gB, GB for GB (gigabytes) |
782 | 396 | - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes) | 396 | - t, T, tB, TB for TB (terabytes) |
783 | 397 | ''' | 397 | ''' |
784 | 398 | self.assert_root() | 398 | self.assert_root() |
785 | 399 | 399 | ||
786 | @@ -445,14 +445,14 @@ class UIFileIOBackstore(UIBackstore): | |||
787 | 445 | def ui_command_create(self, name, file_or_dev, size=None, write_back=None, | 445 | def ui_command_create(self, name, file_or_dev, size=None, write_back=None, |
788 | 446 | sparse=None, wwn=None): | 446 | sparse=None, wwn=None): |
789 | 447 | ''' | 447 | ''' |
798 | 448 | Creates a FileIO storage object. If I{file_or_dev} is a path | 448 | Creates a FileIO storage object. If "file_or_dev" is a path |
799 | 449 | to a regular file to be used as backend, then the I{size} | 449 | to a regular file to be used as backend, then the "size" |
800 | 450 | parameter is mandatory. Else, if I{file_or_dev} is a path to a | 450 | parameter is mandatory. Else, if "file_or_dev" is a path to a |
801 | 451 | block device, the size parameter B{must} be ommited. If | 451 | block device, the size parameter must be omitted. If |
802 | 452 | present, I{size} is the size of the file to be used, I{file} | 452 | present, "size" is the size of the file to be used, "file" |
803 | 453 | the path to the file or I{dev} the path to a block device. The | 453 | the path to the file or "dev" the path to a block device. The |
804 | 454 | I{write_back} parameter is a boolean controlling write | 454 | "write_back" parameter is a boolean controlling write |
805 | 455 | caching. It is enabled by default. The I{sparse} parameter is | 455 | caching. It is enabled by default. The "sparse" parameter is |
806 | 456 | only applicable when creating a new backing file. It is a | 456 | only applicable when creating a new backing file. It is a |
807 | 457 | boolean stating if the created file should be created as a | 457 | boolean stating if the created file should be created as a |
808 | 458 | sparse file (the default), or fully initialized. | 458 | sparse file (the default), or fully initialized. |
809 | @@ -461,11 +461,11 @@ class UIFileIOBackstore(UIBackstore): | |||
810 | 461 | =========== | 461 | =========== |
811 | 462 | - If size is an int, it represents a number of bytes. | 462 | - If size is an int, it represents a number of bytes. |
812 | 463 | - If size is a string, the following units can be used: | 463 | - If size is a string, the following units can be used: |
818 | 464 | - B{B} or no unit present for bytes | 464 | - B or no unit present for bytes |
819 | 465 | - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes) | 465 | - k, K, kB, KB for kB (kilobytes) |
820 | 466 | - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes) | 466 | - m, M, mB, MB for MB (megabytes) |
821 | 467 | - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes) | 467 | - g, G, gB, GB for GB (gigabytes) |
822 | 468 | - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes) | 468 | - t, T, tB, TB for TB (terabytes) |
823 | 469 | ''' | 469 | ''' |
824 | 470 | self.assert_root() | 470 | self.assert_root() |
825 | 471 | 471 | ||
826 | @@ -557,7 +557,7 @@ class UIBlockBackstore(UIBackstore): | |||
827 | 557 | 557 | ||
828 | 558 | def ui_command_create(self, name, dev, readonly=None, wwn=None): | 558 | def ui_command_create(self, name, dev, readonly=None, wwn=None): |
829 | 559 | ''' | 559 | ''' |
831 | 560 | Creates an Block Storage object. I{dev} is the path to the TYPE_DISK | 560 | Creates an Block Storage object. "dev" is the path to the TYPE_DISK |
832 | 561 | block device to use. | 561 | block device to use. |
833 | 562 | ''' | 562 | ''' |
834 | 563 | self.assert_root() | 563 | self.assert_root() |
835 | @@ -628,11 +628,11 @@ class UIUserBackedBackstore(UIBackstore): | |||
836 | 628 | =========== | 628 | =========== |
837 | 629 | - If size is an int, it represents a number of bytes. | 629 | - If size is an int, it represents a number of bytes. |
838 | 630 | - If size is a string, the following units can be used: | 630 | - If size is a string, the following units can be used: |
844 | 631 | - B{B} or no unit present for bytes | 631 | - B or no unit present for bytes |
845 | 632 | - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes) | 632 | - k, K, kB, KB for kB (kilobytes) |
846 | 633 | - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes) | 633 | - m, M, mB, MB for MB (megabytes) |
847 | 634 | - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes) | 634 | - g, G, gB, GB for GB (gigabytes) |
848 | 635 | - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes) | 635 | - t, T, tB, TB for TB (terabytes) |
849 | 636 | ''' | 636 | ''' |
850 | 637 | 637 | ||
851 | 638 | size = human_to_bytes(size) | 638 | size = human_to_bytes(size) |
852 | @@ -689,6 +689,7 @@ class UIStorageObject(UIRTSLibNode): | |||
853 | 689 | 'emulate_tpws': ('number', 'If set to 1, enable Thin Provisioning Write Same.'), | 689 | 'emulate_tpws': ('number', 'If set to 1, enable Thin Provisioning Write Same.'), |
854 | 690 | 'emulate_ua_intlck_ctrl': ('number', 'If set to 1, enable Unit Attention Interlock.'), | 690 | 'emulate_ua_intlck_ctrl': ('number', 'If set to 1, enable Unit Attention Interlock.'), |
855 | 691 | 'emulate_write_cache': ('number', 'If set to 1, turn on Write Cache Enable.'), | 691 | 'emulate_write_cache': ('number', 'If set to 1, turn on Write Cache Enable.'), |
856 | 692 | 'emulate_pr': ('number', 'If set to 1, enable SCSI Reservations.'), | ||
857 | 692 | 'enforce_pr_isids': ('number', 'If set to 1, enforce persistent reservation ISIDs.'), | 693 | 'enforce_pr_isids': ('number', 'If set to 1, enforce persistent reservation ISIDs.'), |
858 | 693 | 'force_pr_aptpl': ('number', 'If set to 1, force SPC-3 PR Activate Persistence across Target Power Loss operation.'), | 694 | 'force_pr_aptpl': ('number', 'If set to 1, force SPC-3 PR Activate Persistence across Target Power Loss operation.'), |
859 | 694 | 'fabric_max_sectors': ('number', 'Maximum number of sectors the fabric can transfer at once.'), | 695 | 'fabric_max_sectors': ('number', 'Maximum number of sectors the fabric can transfer at once.'), |
860 | diff --git a/targetcli/ui_node.py b/targetcli/ui_node.py | |||
861 | index a6982f1..58a70c6 100644 | |||
862 | --- a/targetcli/ui_node.py | |||
863 | +++ b/targetcli/ui_node.py | |||
864 | @@ -49,6 +49,9 @@ class UINode(ConfigNode): | |||
865 | 49 | self.define_config_group_param( | 49 | self.define_config_group_param( |
866 | 50 | 'global', 'max_backup_files', 'string', | 50 | 'global', 'max_backup_files', 'string', |
867 | 51 | 'Max no. of configurations to be backed up in /etc/target/backup/ directory.') | 51 | 'Max no. of configurations to be backed up in /etc/target/backup/ directory.') |
868 | 52 | self.define_config_group_param( | ||
869 | 53 | 'global', 'auto_use_daemon', 'bool', | ||
870 | 54 | 'If true, commands will be sent to targetclid.') | ||
871 | 52 | 55 | ||
872 | 53 | def assert_root(self): | 56 | def assert_root(self): |
873 | 54 | ''' | 57 | ''' |
874 | @@ -95,7 +98,7 @@ class UINode(ConfigNode): | |||
875 | 95 | 98 | ||
876 | 96 | SEE ALSO | 99 | SEE ALSO |
877 | 97 | ======== | 100 | ======== |
879 | 98 | B{ls} | 101 | ls |
880 | 99 | ''' | 102 | ''' |
881 | 100 | description, is_healthy = self.summary() | 103 | description, is_healthy = self.summary() |
882 | 101 | self.shell.log.info("Status for %s: %s" % (self.path, description)) | 104 | self.shell.log.info("Status for %s: %s" % (self.path, description)) |
883 | diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py | |||
884 | index 38118bd..26815bd 100644 | |||
885 | --- a/targetcli/ui_root.py | |||
886 | +++ b/targetcli/ui_root.py | |||
887 | @@ -24,6 +24,7 @@ import re | |||
888 | 24 | import shutil | 24 | import shutil |
889 | 25 | import stat | 25 | import stat |
890 | 26 | import filecmp | 26 | import filecmp |
891 | 27 | import gzip | ||
892 | 27 | 28 | ||
893 | 28 | from configshell_fb import ExecutionError | 29 | from configshell_fb import ExecutionError |
894 | 29 | from rtslib_fb import RTSRoot | 30 | from rtslib_fb import RTSRoot |
895 | @@ -62,6 +63,38 @@ class UIRoot(UINode): | |||
896 | 62 | if fm.wwns == None or any(fm.wwns): | 63 | if fm.wwns == None or any(fm.wwns): |
897 | 63 | UIFabricModule(fm, self) | 64 | UIFabricModule(fm, self) |
898 | 64 | 65 | ||
899 | 66 | def _compare_files(self, backupfile, savefile): | ||
900 | 67 | ''' | ||
901 | 68 | Compare backfile and saveconfig file | ||
902 | 69 | ''' | ||
903 | 70 | if (os.path.splitext(backupfile)[1] == '.gz'): | ||
904 | 71 | try: | ||
905 | 72 | with gzip.open(backupfile, 'rb') as fbkp: | ||
906 | 73 | fdata_bkp = fbkp.read() | ||
907 | 74 | except IOError as e: | ||
908 | 75 | self.shell.log.warning("Could not gzip open backupfile %s: %s" | ||
909 | 76 | % (backupfile, e.strerror)) | ||
910 | 77 | |||
911 | 78 | else: | ||
912 | 79 | try: | ||
913 | 80 | with open(backupfile, 'rb') as fbkp: | ||
914 | 81 | fdata_bkp = fbkp.read() | ||
915 | 82 | except IOError as e: | ||
916 | 83 | self.shell.log.warning("Could not open backupfile %s: %s" | ||
917 | 84 | % (backupfile, e.strerror)) | ||
918 | 85 | |||
919 | 86 | try: | ||
920 | 87 | with open(savefile, 'rb') as f: | ||
921 | 88 | fdata = f.read() | ||
922 | 89 | except IOError as e: | ||
923 | 90 | self.shell.log.warning("Could not open saveconfig file %s: %s" | ||
924 | 91 | % (savefile, e.strerror)) | ||
925 | 92 | |||
926 | 93 | if fdata_bkp == fdata: | ||
927 | 94 | return True | ||
928 | 95 | else: | ||
929 | 96 | return False | ||
930 | 97 | |||
931 | 65 | def _save_backups(self, savefile): | 98 | def _save_backups(self, savefile): |
932 | 66 | ''' | 99 | ''' |
933 | 67 | Take backup of config-file if needed. | 100 | Take backup of config-file if needed. |
934 | @@ -72,29 +105,30 @@ class UIRoot(UINode): | |||
935 | 72 | 105 | ||
936 | 73 | backup_dir = os.path.dirname(savefile) + "/backup/" | 106 | backup_dir = os.path.dirname(savefile) + "/backup/" |
937 | 74 | backup_name = "saveconfig-" + \ | 107 | backup_name = "saveconfig-" + \ |
939 | 75 | datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json" | 108 | datetime.now().strftime("%Y%m%d-%H:%M:%S") + "-json.gz" |
940 | 76 | backupfile = backup_dir + backup_name | 109 | backupfile = backup_dir + backup_name |
941 | 77 | backup_error = None | 110 | backup_error = None |
942 | 78 | 111 | ||
943 | 79 | if not os.path.exists(backup_dir): | 112 | if not os.path.exists(backup_dir): |
944 | 80 | try: | 113 | try: |
946 | 81 | os.makedirs(backup_dir); | 114 | os.makedirs(backup_dir) |
947 | 82 | except OSError as exe: | 115 | except OSError as exe: |
948 | 83 | raise ExecutionError("Cannot create backup directory [%s] %s." | 116 | raise ExecutionError("Cannot create backup directory [%s] %s." |
950 | 84 | % (backup_dir, exc.strerror)) | 117 | % (backup_dir, exe.strerror)) |
951 | 85 | 118 | ||
952 | 86 | # Only save backups if savefile exits | 119 | # Only save backups if savefile exits |
953 | 87 | if not os.path.exists(savefile): | 120 | if not os.path.exists(savefile): |
954 | 88 | return | 121 | return |
955 | 89 | 122 | ||
956 | 90 | backed_files_list = sorted(glob(os.path.dirname(savefile) + \ | 123 | backed_files_list = sorted(glob(os.path.dirname(savefile) + \ |
958 | 91 | "/backup/*.json")) | 124 | "/backup/saveconfig-*json*")) |
959 | 92 | 125 | ||
960 | 93 | # Save backup if backup dir is empty, or savefile is differnt from recent backup copy | 126 | # Save backup if backup dir is empty, or savefile is differnt from recent backup copy |
962 | 94 | if not backed_files_list or not filecmp.cmp(backed_files_list[-1], savefile): | 127 | if not backed_files_list or not self._compare_files(backed_files_list[-1], savefile): |
963 | 95 | try: | 128 | try: |
966 | 96 | shutil.copy(savefile, backupfile) | 129 | with open(savefile, 'rb') as f_in, gzip.open(backupfile, 'wb') as f_out: |
967 | 97 | 130 | shutil.copyfileobj(f_in, f_out) | |
968 | 131 | f_out.flush() | ||
969 | 98 | except IOError as ioe: | 132 | except IOError as ioe: |
970 | 99 | backup_error = ioe.strerror or "Unknown error" | 133 | backup_error = ioe.strerror or "Unknown error" |
971 | 100 | 134 | ||
972 | @@ -139,7 +173,8 @@ class UIRoot(UINode): | |||
973 | 139 | 173 | ||
974 | 140 | self.shell.log.info("Configuration saved to %s" % savefile) | 174 | self.shell.log.info("Configuration saved to %s" % savefile) |
975 | 141 | 175 | ||
977 | 142 | def ui_command_restoreconfig(self, savefile=default_save_file, clear_existing=False): | 176 | def ui_command_restoreconfig(self, savefile=default_save_file, clear_existing=False, |
978 | 177 | target=None, storage_object=None): | ||
979 | 143 | ''' | 178 | ''' |
980 | 144 | Restores configuration from a file. | 179 | Restores configuration from a file. |
981 | 145 | ''' | 180 | ''' |
982 | @@ -151,7 +186,10 @@ class UIRoot(UINode): | |||
983 | 151 | self.shell.log.info("Restore file %s not found" % savefile) | 186 | self.shell.log.info("Restore file %s not found" % savefile) |
984 | 152 | return | 187 | return |
985 | 153 | 188 | ||
987 | 154 | errors = self.rtsroot.restore_from_file(savefile, clear_existing) | 189 | target = self.ui_eval_param(target, 'string', None) |
988 | 190 | storage_object = self.ui_eval_param(storage_object, 'string', None) | ||
989 | 191 | errors = self.rtsroot.restore_from_file(savefile, clear_existing, | ||
990 | 192 | target, storage_object) | ||
991 | 155 | 193 | ||
992 | 156 | self.refresh() | 194 | self.refresh() |
993 | 157 | 195 | ||
994 | @@ -202,15 +240,15 @@ class UIRoot(UINode): | |||
995 | 202 | PARAMETERS | 240 | PARAMETERS |
996 | 203 | ========== | 241 | ========== |
997 | 204 | 242 | ||
1005 | 205 | I{action} | 243 | action |
999 | 206 | --------- | ||
1000 | 207 | The I{action} is one of: | ||
1001 | 208 | - B{list} gives a short session list | ||
1002 | 209 | - B{detail} gives a detailed list | ||
1003 | 210 | |||
1004 | 211 | I{sid} | ||
1006 | 212 | ------ | 244 | ------ |
1008 | 213 | You can specify an I{sid} to only list this one, | 245 | The action is one of: |
1009 | 246 | - `list`` gives a short session list | ||
1010 | 247 | - `detail` gives a detailed list | ||
1011 | 248 | |||
1012 | 249 | sid | ||
1013 | 250 | --- | ||
1014 | 251 | You can specify an "sid" to only list this one, | ||
1015 | 214 | with or without details. | 252 | with or without details. |
1016 | 215 | 253 | ||
1017 | 216 | SEE ALSO | 254 | SEE ALSO |
1018 | @@ -283,4 +321,3 @@ class UIRoot(UINode): | |||
1019 | 283 | indent_print("(no open sessions)", base_steps) | 321 | indent_print("(no open sessions)", base_steps) |
1020 | 284 | else: | 322 | else: |
1021 | 285 | raise ExecutionError("no session found with sid %i" % int(sid)) | 323 | raise ExecutionError("no session found with sid %i" % int(sid)) |
1022 | 286 | |||
1023 | diff --git a/targetcli/ui_target.py b/targetcli/ui_target.py | |||
1024 | index 6895b38..e8ba6c6 100644 | |||
1025 | --- a/targetcli/ui_target.py | |||
1026 | +++ b/targetcli/ui_target.py | |||
1027 | @@ -34,7 +34,8 @@ from .ui_backstore import complete_path | |||
1028 | 34 | from .ui_node import UINode, UIRTSLibNode | 34 | from .ui_node import UINode, UIRTSLibNode |
1029 | 35 | 35 | ||
1030 | 36 | auth_params = ('userid', 'password', 'mutual_userid', 'mutual_password') | 36 | auth_params = ('userid', 'password', 'mutual_userid', 'mutual_password') |
1032 | 37 | discovery_params = auth_params + ("enable",) | 37 | int_params = ('enable',) |
1033 | 38 | discovery_params = auth_params + int_params | ||
1034 | 38 | 39 | ||
1035 | 39 | class UIFabricModule(UIRTSLibNode): | 40 | class UIFabricModule(UIRTSLibNode): |
1036 | 40 | ''' | 41 | ''' |
1037 | @@ -47,8 +48,12 @@ class UIFabricModule(UIRTSLibNode): | |||
1038 | 47 | self.refresh() | 48 | self.refresh() |
1039 | 48 | if self.rtsnode.has_feature('discovery_auth'): | 49 | if self.rtsnode.has_feature('discovery_auth'): |
1040 | 49 | for param in discovery_params: | 50 | for param in discovery_params: |
1043 | 50 | self.define_config_group_param('discovery_auth', | 51 | if param in int_params: |
1044 | 51 | param, 'string') | 52 | self.define_config_group_param('discovery_auth', |
1045 | 53 | param, 'number') | ||
1046 | 54 | else: | ||
1047 | 55 | self.define_config_group_param('discovery_auth', | ||
1048 | 56 | param, 'string') | ||
1049 | 52 | self.refresh() | 57 | self.refresh() |
1050 | 53 | 58 | ||
1051 | 54 | # Support late params | 59 | # Support late params |
1052 | @@ -167,18 +172,18 @@ class UIFabricModule(UIRTSLibNode): | |||
1053 | 167 | 172 | ||
1054 | 168 | def ui_command_create(self, wwn=None): | 173 | def ui_command_create(self, wwn=None): |
1055 | 169 | ''' | 174 | ''' |
1058 | 170 | Creates a new target. The I{wwn} format depends on the transport(s) | 175 | Creates a new target. The "wwn" format depends on the transport(s) |
1059 | 171 | supported by the fabric module. If the I{wwn} is ommited, then a | 176 | supported by the fabric module. If "wwn" is omitted, then a |
1060 | 172 | target will be created using either a randomly generated WWN of the | 177 | target will be created using either a randomly generated WWN of the |
1061 | 173 | proper type, or the first unused WWN in the list of possible WWNs if | 178 | proper type, or the first unused WWN in the list of possible WWNs if |
1062 | 174 | one is available. If WWNs are constrained to a list (i.e. for hardware | 179 | one is available. If WWNs are constrained to a list (i.e. for hardware |
1063 | 175 | targets addresses) and all WWNs are in use, the target creation will | 180 | targets addresses) and all WWNs are in use, the target creation will |
1065 | 176 | fail. Use the B{info} command to get more information abour WWN type | 181 | fail. Use the `info` command to get more information abour WWN type |
1066 | 177 | and possible values. | 182 | and possible values. |
1067 | 178 | 183 | ||
1068 | 179 | SEE ALSO | 184 | SEE ALSO |
1069 | 180 | ======== | 185 | ======== |
1071 | 181 | B{info} | 186 | info |
1072 | 182 | ''' | 187 | ''' |
1073 | 183 | self.assert_root() | 188 | self.assert_root() |
1074 | 184 | 189 | ||
1075 | @@ -223,12 +228,12 @@ class UIFabricModule(UIRTSLibNode): | |||
1076 | 223 | 228 | ||
1077 | 224 | def ui_command_delete(self, wwn): | 229 | def ui_command_delete(self, wwn): |
1078 | 225 | ''' | 230 | ''' |
1080 | 226 | Recursively deletes the target with the specified I{wwn}, and all | 231 | Recursively deletes the target with the specified wwn, and all |
1081 | 227 | objects hanging under it. | 232 | objects hanging under it. |
1082 | 228 | 233 | ||
1083 | 229 | SEE ALSO | 234 | SEE ALSO |
1084 | 230 | ======== | 235 | ======== |
1086 | 231 | B{create} | 236 | create |
1087 | 232 | ''' | 237 | ''' |
1088 | 233 | self.assert_root() | 238 | self.assert_root() |
1089 | 234 | target = Target(self.rtsnode, wwn, mode='lookup') | 239 | target = Target(self.rtsnode, wwn, mode='lookup') |
1090 | @@ -262,7 +267,7 @@ class UIFabricModule(UIRTSLibNode): | |||
1091 | 262 | def ui_command_info(self): | 267 | def ui_command_info(self): |
1092 | 263 | ''' | 268 | ''' |
1093 | 264 | Displays information about the fabric module, notably the supported | 269 | Displays information about the fabric module, notably the supported |
1095 | 265 | transports(s) and accepted B{wwn} format(s), as long as supported | 270 | transports(s) and accepted wwn format(s), along with supported |
1096 | 266 | features. | 271 | features. |
1097 | 267 | ''' | 272 | ''' |
1098 | 268 | fabric = self.rtsnode | 273 | fabric = self.rtsnode |
1099 | @@ -308,13 +313,13 @@ class UIMultiTPGTarget(UIRTSLibNode): | |||
1100 | 308 | def ui_command_create(self, tag=None): | 313 | def ui_command_create(self, tag=None): |
1101 | 309 | ''' | 314 | ''' |
1102 | 310 | Creates a new Target Portal Group within the target. The | 315 | Creates a new Target Portal Group within the target. The |
1104 | 311 | I{tag} must be a positive integer value, optionally prefaced | 316 | tag must be a positive integer value, optionally prefaced |
1105 | 312 | by 'tpg'. If omitted, the next available Target Portal Group | 317 | by 'tpg'. If omitted, the next available Target Portal Group |
1106 | 313 | Tag (TPGT) will be used. | 318 | Tag (TPGT) will be used. |
1107 | 314 | 319 | ||
1108 | 315 | SEE ALSO | 320 | SEE ALSO |
1109 | 316 | ======== | 321 | ======== |
1111 | 317 | B{delete} | 322 | delete |
1112 | 318 | ''' | 323 | ''' |
1113 | 319 | self.assert_root() | 324 | self.assert_root() |
1114 | 320 | 325 | ||
1115 | @@ -351,12 +356,12 @@ class UIMultiTPGTarget(UIRTSLibNode): | |||
1116 | 351 | 356 | ||
1117 | 352 | def ui_command_delete(self, tag): | 357 | def ui_command_delete(self, tag): |
1118 | 353 | ''' | 358 | ''' |
1121 | 354 | Deletes the Target Portal Group with TPGT I{tag} from the target. The | 359 | Deletes the Target Portal Group with TPGT "tag" from the target. The |
1122 | 355 | I{tag} must be a positive integer matching an existing TPGT. | 360 | tag must be a positive integer matching an existing TPGT. |
1123 | 356 | 361 | ||
1124 | 357 | SEE ALSO | 362 | SEE ALSO |
1125 | 358 | ======== | 363 | ======== |
1127 | 359 | B{create} | 364 | create |
1128 | 360 | ''' | 365 | ''' |
1129 | 361 | self.assert_root() | 366 | self.assert_root() |
1130 | 362 | if tag.startswith("tpg"): | 367 | if tag.startswith("tpg"): |
1131 | @@ -384,7 +389,7 @@ class UIMultiTPGTarget(UIRTSLibNode): | |||
1132 | 384 | @rtype: list of str | 389 | @rtype: list of str |
1133 | 385 | ''' | 390 | ''' |
1134 | 386 | if current_param == 'tag': | 391 | if current_param == 'tag': |
1136 | 387 | tags = [child.name[4:] for child in self.children] | 392 | tags = [child.name[3:] for child in self.children] |
1137 | 388 | completions = [tag for tag in tags if tag.startswith(text)] | 393 | completions = [tag for tag in tags if tag.startswith(text)] |
1138 | 389 | else: | 394 | else: |
1139 | 390 | completions = [] | 395 | completions = [] |
1140 | @@ -515,7 +520,7 @@ class UITPG(UIRTSLibNode): | |||
1141 | 515 | 520 | ||
1142 | 516 | SEE ALSO | 521 | SEE ALSO |
1143 | 517 | ======== | 522 | ======== |
1145 | 518 | B{disable status} | 523 | disable status |
1146 | 519 | ''' | 524 | ''' |
1147 | 520 | self.assert_root() | 525 | self.assert_root() |
1148 | 521 | if self.rtsnode.enable: | 526 | if self.rtsnode.enable: |
1149 | @@ -533,7 +538,7 @@ class UITPG(UIRTSLibNode): | |||
1150 | 533 | 538 | ||
1151 | 534 | SEE ALSO | 539 | SEE ALSO |
1152 | 535 | ======== | 540 | ======== |
1154 | 536 | B{enable status} | 541 | enable status |
1155 | 537 | ''' | 542 | ''' |
1156 | 538 | self.assert_root() | 543 | self.assert_root() |
1157 | 539 | if self.rtsnode.enable: | 544 | if self.rtsnode.enable: |
1158 | @@ -582,18 +587,19 @@ class UINodeACLs(UINode): | |||
1159 | 582 | 587 | ||
1160 | 583 | def ui_command_create(self, wwn, add_mapped_luns=None): | 588 | def ui_command_create(self, wwn, add_mapped_luns=None): |
1161 | 584 | ''' | 589 | ''' |
1164 | 585 | Creates a Node ACL for the initiator node with the specified I{wwn}. | 590 | Creates a Node ACL for the initiator node with the specified wwn. |
1165 | 586 | The node's I{wwn} must match the expected WWN Type of the target's | 591 | The node's wwn must match the expected WWN Type of the target's |
1166 | 587 | fabric module. | 592 | fabric module. |
1167 | 588 | 593 | ||
1172 | 589 | If I{add_mapped_luns} is omitted, the global parameter | 594 | "add_mapped_luns" can be "true" of "false". If true, then |
1173 | 590 | B{auto_add_mapped_luns} will be used, else B{true} or B{false} are | 595 | after creating the ACL, mapped LUNs will be automatically |
1174 | 591 | accepted. If B{true}, then after creating the ACL, mapped LUNs will be | 596 | created for all existing LUNs. If the parameter is omitted, |
1175 | 592 | automatically created for all existing LUNs. | 597 | the global parameter "auto_add_mapped_luns" is used. |
1176 | 593 | 598 | ||
1177 | 594 | SEE ALSO | 599 | SEE ALSO |
1178 | 595 | ======== | 600 | ======== |
1180 | 596 | B{delete} | 601 | delete |
1181 | 602 | |||
1182 | 597 | ''' | 603 | ''' |
1183 | 598 | self.assert_root() | 604 | self.assert_root() |
1184 | 599 | 605 | ||
1185 | @@ -614,11 +620,11 @@ class UINodeACLs(UINode): | |||
1186 | 614 | 620 | ||
1187 | 615 | def ui_command_delete(self, wwn): | 621 | def ui_command_delete(self, wwn): |
1188 | 616 | ''' | 622 | ''' |
1190 | 617 | Deletes the Node ACL with the specified I{wwn}. | 623 | Deletes the Node ACL with the specified wwn. |
1191 | 618 | 624 | ||
1192 | 619 | SEE ALSO | 625 | SEE ALSO |
1193 | 620 | ======== | 626 | ======== |
1195 | 621 | B{create} | 627 | create |
1196 | 622 | ''' | 628 | ''' |
1197 | 623 | self.assert_root() | 629 | self.assert_root() |
1198 | 624 | node_acl = NodeACL(self.tpg, wwn, mode='lookup') | 630 | node_acl = NodeACL(self.tpg, wwn, mode='lookup') |
1199 | @@ -870,14 +876,14 @@ class UINodeACL(UIRTSLibNode): | |||
1200 | 870 | def ui_command_create(self, mapped_lun, tpg_lun_or_backstore, write_protect=None): | 876 | def ui_command_create(self, mapped_lun, tpg_lun_or_backstore, write_protect=None): |
1201 | 871 | ''' | 877 | ''' |
1202 | 872 | Creates a mapping to one of the TPG LUNs for the initiator referenced | 878 | Creates a mapping to one of the TPG LUNs for the initiator referenced |
1206 | 873 | by the ACL. The provided I{tpg_lun_or_backstore} will appear to that | 879 | by the ACL. The provided "tpg_lun_or_backstore" will appear to that |
1207 | 874 | initiator as LUN I{mapped_lun}. If the I{write_protect} flag is set to | 880 | initiator as LUN "mapped_lun". If the "write_protect" flag is set to |
1208 | 875 | B{1}, the initiator will not have write access to the Mapped LUN. | 881 | 1, the initiator will not have write access to the mapped LUN. |
1209 | 876 | 882 | ||
1211 | 877 | A storage object may also be given for the I{tpg_lun_or_backstore} parameter, | 883 | A storage object may also be given for the "tpg_lun_or_backstore" parameter, |
1212 | 878 | in which case the TPG LUN will be created for that backstore before | 884 | in which case the TPG LUN will be created for that backstore before |
1213 | 879 | mapping the LUN to the initiator. If a TPG LUN for the backstore already | 885 | mapping the LUN to the initiator. If a TPG LUN for the backstore already |
1215 | 880 | exists, the Mapped LUN will map to that TPG LUN. | 886 | exists, the mapped LUN will map to that TPG LUN. |
1216 | 881 | 887 | ||
1217 | 882 | Finally, a path to an existing block device or file can be given. If so, | 888 | Finally, a path to an existing block device or file can be given. If so, |
1218 | 883 | a storage object of the appropriate type is created with default parameters, | 889 | a storage object of the appropriate type is created with default parameters, |
1219 | @@ -885,7 +891,7 @@ class UINodeACL(UIRTSLibNode): | |||
1220 | 885 | 891 | ||
1221 | 886 | SEE ALSO | 892 | SEE ALSO |
1222 | 887 | ======== | 893 | ======== |
1224 | 888 | B{delete} | 894 | delete |
1225 | 889 | ''' | 895 | ''' |
1226 | 890 | self.assert_root() | 896 | self.assert_root() |
1227 | 891 | try: | 897 | try: |
1228 | @@ -963,11 +969,11 @@ class UINodeACL(UIRTSLibNode): | |||
1229 | 963 | 969 | ||
1230 | 964 | def ui_command_delete(self, mapped_lun): | 970 | def ui_command_delete(self, mapped_lun): |
1231 | 965 | ''' | 971 | ''' |
1233 | 966 | Deletes the specified I{mapped_lun}. | 972 | Deletes the specified mapped LUN. |
1234 | 967 | 973 | ||
1235 | 968 | SEE ALSO | 974 | SEE ALSO |
1236 | 969 | ======== | 975 | ======== |
1238 | 970 | B{create} | 976 | create |
1239 | 971 | ''' | 977 | ''' |
1240 | 972 | self.assert_root() | 978 | self.assert_root() |
1241 | 973 | for na in self.rtsnodes: | 979 | for na in self.rtsnodes: |
1242 | @@ -1086,25 +1092,25 @@ class UILUNs(UINode): | |||
1243 | 1086 | add_mapped_luns=None): | 1092 | add_mapped_luns=None): |
1244 | 1087 | ''' | 1093 | ''' |
1245 | 1088 | Creates a new LUN in the Target Portal Group, attached to a storage | 1094 | Creates a new LUN in the Target Portal Group, attached to a storage |
1247 | 1089 | object. If the I{lun} parameter is omitted, the first available LUN in | 1095 | object. If the "lun" parameter is omitted, the first available LUN in |
1248 | 1090 | the TPG will be used. If present, it must be a number greater than 0. | 1096 | the TPG will be used. If present, it must be a number greater than 0. |
1250 | 1091 | Alternatively, the syntax I{lunX} where I{X} is a positive number is | 1097 | Alternatively, the syntax "lunX" where "X" is a positive number is |
1251 | 1092 | also accepted. | 1098 | also accepted. |
1252 | 1093 | 1099 | ||
1256 | 1094 | The I{storage_object} may be the path of an existing storage object, | 1100 | The "storage_object" may be the path of an existing storage object, |
1257 | 1095 | i.e. B{/backstore/pscsi0/mydisk} to reference the B{mydisk} storage | 1101 | i.e. "/backstore/pscsi0/mydisk" to reference the "mydisk" storage |
1258 | 1096 | object of the virtual HBA B{pscsi0}. It also may be the path to an | 1102 | object of the virtual HBA "pscsi0". It also may be the path to an |
1259 | 1097 | existing block device or image file, in which case a storage object | 1103 | existing block device or image file, in which case a storage object |
1260 | 1098 | will be created for it first, with default parameters. | 1104 | will be created for it first, with default parameters. |
1261 | 1099 | 1105 | ||
1266 | 1100 | If I{add_mapped_luns} is omitted, the global parameter | 1106 | "add_mapped_luns" can be "true" of "false". If true, then |
1267 | 1101 | B{auto_add_mapped_luns} will be used, else B{true} or B{false} are | 1107 | after creating the ACL, mapped LUNs will be automatically |
1268 | 1102 | accepted. If B{true}, then after creating the LUN, mapped LUNs will be | 1108 | created for all existing LUNs. If the parameter is omitted, |
1269 | 1103 | automatically created for all existing node ACLs, mapping the new LUN. | 1109 | the global parameter "auto_add_mapped_luns" is used. |
1270 | 1104 | 1110 | ||
1271 | 1105 | SEE ALSO | 1111 | SEE ALSO |
1272 | 1106 | ======== | 1112 | ======== |
1274 | 1107 | B{delete} | 1113 | delete |
1275 | 1108 | ''' | 1114 | ''' |
1276 | 1109 | self.assert_root() | 1115 | self.assert_root() |
1277 | 1110 | 1116 | ||
1278 | @@ -1188,15 +1194,15 @@ class UILUNs(UINode): | |||
1279 | 1188 | 1194 | ||
1280 | 1189 | def ui_command_delete(self, lun): | 1195 | def ui_command_delete(self, lun): |
1281 | 1190 | ''' | 1196 | ''' |
1283 | 1191 | Deletes the supplied LUN from the Target Portal Group. The I{lun} must | 1197 | Deletes the supplied LUN from the Target Portal Group. "lun" must |
1284 | 1192 | be a positive number matching an existing LUN. | 1198 | be a positive number matching an existing LUN. |
1285 | 1193 | 1199 | ||
1287 | 1194 | Alternatively, the syntax I{lunX} where I{X} is a positive number is | 1200 | Alternatively, the syntax "lunX" where "X" is a positive number is |
1288 | 1195 | also accepted. | 1201 | also accepted. |
1289 | 1196 | 1202 | ||
1290 | 1197 | SEE ALSO | 1203 | SEE ALSO |
1291 | 1198 | ======== | 1204 | ======== |
1293 | 1199 | B{create} | 1205 | create |
1294 | 1200 | ''' | 1206 | ''' |
1295 | 1201 | self.assert_root() | 1207 | self.assert_root() |
1296 | 1202 | if lun.lower().startswith("lun"): | 1208 | if lun.lower().startswith("lun"): |
1297 | @@ -1303,9 +1309,9 @@ class UIPortals(UINode): | |||
1298 | 1303 | 1309 | ||
1299 | 1304 | def ui_command_create(self, ip_address=None, ip_port=None): | 1310 | def ui_command_create(self, ip_address=None, ip_port=None): |
1300 | 1305 | ''' | 1311 | ''' |
1304 | 1306 | Creates a Network Portal with specified I{ip_address} and | 1312 | Creates a Network Portal with the specified IP address and |
1305 | 1307 | I{ip_port}. If I{ip_port} is omitted, the default port for | 1313 | port. If the port is omitted, the default port for |
1306 | 1308 | the target fabric will be used. If I{ip_address} is omitted, | 1314 | the target fabric will be used. If the IP address is omitted, |
1307 | 1309 | INADDR_ANY (0.0.0.0) will be used. | 1315 | INADDR_ANY (0.0.0.0) will be used. |
1308 | 1310 | 1316 | ||
1309 | 1311 | Choosing IN6ADDR_ANY (::0) will listen on all IPv6 interfaces | 1317 | Choosing IN6ADDR_ANY (::0) will listen on all IPv6 interfaces |
1310 | @@ -1317,7 +1323,7 @@ class UIPortals(UINode): | |||
1311 | 1317 | 1323 | ||
1312 | 1318 | SEE ALSO | 1324 | SEE ALSO |
1313 | 1319 | ======== | 1325 | ======== |
1315 | 1320 | B{delete} | 1326 | delete |
1316 | 1321 | ''' | 1327 | ''' |
1317 | 1322 | self.assert_root() | 1328 | self.assert_root() |
1318 | 1323 | 1329 | ||
1319 | @@ -1379,11 +1385,11 @@ class UIPortals(UINode): | |||
1320 | 1379 | 1385 | ||
1321 | 1380 | def ui_command_delete(self, ip_address, ip_port): | 1386 | def ui_command_delete(self, ip_address, ip_port): |
1322 | 1381 | ''' | 1387 | ''' |
1324 | 1382 | Deletes the Network Portal with specified I{ip_address} and I{ip_port}. | 1388 | Deletes the Network Portal with the specified IP address and port. |
1325 | 1383 | 1389 | ||
1326 | 1384 | SEE ALSO | 1390 | SEE ALSO |
1327 | 1385 | ======== | 1391 | ======== |
1329 | 1386 | B{create} | 1392 | create |
1330 | 1387 | ''' | 1393 | ''' |
1331 | 1388 | self.assert_root() | 1394 | self.assert_root() |
1332 | 1389 | portal = NetworkPortal(self.tpg, self._canonicalize_ip(ip_address), | 1395 | portal = NetworkPortal(self.tpg, self._canonicalize_ip(ip_address), |
1333 | diff --git a/targetcli/version.py b/targetcli/version.py | |||
1334 | index 5e1527f..d29ae93 100644 | |||
1335 | --- a/targetcli/version.py | |||
1336 | +++ b/targetcli/version.py | |||
1337 | @@ -15,4 +15,4 @@ License for the specific language governing permissions and limitations | |||
1338 | 15 | under the License. | 15 | under the License. |
1339 | 16 | ''' | 16 | ''' |
1340 | 17 | 17 | ||
1342 | 18 | __version__ = '2.1.fb49' | 18 | __version__ = '2.1.51' |
1343 | diff --git a/targetclid.8 b/targetclid.8 | |||
1344 | 19 | new file mode 100644 | 19 | new file mode 100644 |
1345 | index 0000000..a783091 | |||
1346 | --- /dev/null | |||
1347 | +++ b/targetclid.8 | |||
1348 | @@ -0,0 +1,77 @@ | |||
1349 | 1 | .TH targetclid 8 | ||
1350 | 2 | .SH NAME | ||
1351 | 3 | .B targetclid | ||
1352 | 4 | \- daemon component for targetcli | ||
1353 | 5 | .SH DESCRIPTION | ||
1354 | 6 | .B targetclid | ||
1355 | 7 | is the daemon component of targetcli, which will help retain state of various | ||
1356 | 8 | configfs object in memory, hence any new request/command can directly use the | ||
1357 | 9 | in memory objects instead of reconstructing them by parsing through the entire | ||
1358 | 10 | configfs files again and again for each and every single command. This will | ||
1359 | 11 | greatly improve the overall execution time taken by targetcli commands at scale. | ||
1360 | 12 | |||
1361 | 13 | .SH USAGE | ||
1362 | 14 | .B targetclid [cmd] | ||
1363 | 15 | .br | ||
1364 | 16 | .B "--help" | ||
1365 | 17 | for additional usage information. | ||
1366 | 18 | .br | ||
1367 | 19 | .B "--version" | ||
1368 | 20 | for version information. | ||
1369 | 21 | .SH QUICKSTART & EXAMPLES | ||
1370 | 22 | .TP | ||
1371 | 23 | To start using the daemon, one need to enable targetclid socket, | ||
1372 | 24 | .br | ||
1373 | 25 | $ systemctl enable targetclid.socket | ||
1374 | 26 | .TP | ||
1375 | 27 | If you would like to use the daemonized approach as default method then, | ||
1376 | 28 | .br | ||
1377 | 29 | $ targetcli set global auto_use_daemon=true | ||
1378 | 30 | .br | ||
1379 | 31 | $ targetcli ls | ||
1380 | 32 | .TP | ||
1381 | 33 | You can use batch mode for sending multiple commands in one go, | ||
1382 | 34 | .br | ||
1383 | 35 | $ targetcli <hit-enter> | ||
1384 | 36 | .br | ||
1385 | 37 | targetcli shell version 2.1.50 | ||
1386 | 38 | .br | ||
1387 | 39 | Entering targetcli batch mode for daemonized approach. | ||
1388 | 40 | .br | ||
1389 | 41 | Enter multiple commands separated by newline and type 'exit' to run them all in one go. | ||
1390 | 42 | .br | ||
1391 | 43 | /> ls | ||
1392 | 44 | .br | ||
1393 | 45 | /> pwd | ||
1394 | 46 | .br | ||
1395 | 47 | /> get global loglevel_file | ||
1396 | 48 | .br | ||
1397 | 49 | /> exit | ||
1398 | 50 | .br | ||
1399 | 51 | .TP | ||
1400 | 52 | You can set preference to stop using daemonized mode even when the daemon is not running, | ||
1401 | 53 | .br | ||
1402 | 54 | $ targetcli --disable-daemon | ||
1403 | 55 | .SH FILES | ||
1404 | 56 | .B /etc/target/saveconfig.json | ||
1405 | 57 | .br | ||
1406 | 58 | .B /etc/target/backup/* | ||
1407 | 59 | .br | ||
1408 | 60 | .B /var/run/targetclid.sock | ||
1409 | 61 | .br | ||
1410 | 62 | .B /var/run/targetclid.pid | ||
1411 | 63 | .SH ENVIRONMENT | ||
1412 | 64 | .SS TARGETCLI_HOME | ||
1413 | 65 | If set, this variable points to a directory that should be used instead of ~/.targetcli | ||
1414 | 66 | .SH SEE ALSO | ||
1415 | 67 | .BR targetcli (8), | ||
1416 | 68 | .BR targetctl (8), | ||
1417 | 69 | .BR tcmu-runner (8) | ||
1418 | 70 | .SH AUTHOR | ||
1419 | 71 | Written by Prasanna Kumar Kalever <prasanna.kalever@redhat.com> | ||
1420 | 72 | .br | ||
1421 | 73 | Man page written by Prasanna Kumar Kalever <prasanna.kalever@redhat.com> | ||
1422 | 74 | .SH REPORTING BUGS | ||
1423 | 75 | Report bugs via <targetcli-fb-devel@lists.fedorahosted.org> | ||
1424 | 76 | .br | ||
1425 | 77 | or <https://github.com/open-iscsi/targetcli-fb/issues> |
@paelzer,
Added you directly as you were the MIR team requester for this.
This is addressing comment #24 requests.
Comment #26 was preempted by @jamespage already.
Would you mind updating the MIR with a summary after this ?
Thanks a lot!