Merge ~rafaeldtinoco/ubuntu/+source/targetcli-fb:focal-merge-upstream-2.1.51 into ubuntu/+source/targetcli-fb:ubuntu/focal-devel

Proposed by Rafael David Tinoco
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)
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://bugs.launchpad.net/ubuntu/+source/python-rtslib-fb/+bug/1854362

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://salsa.debian.org/linux-blocks-team/targetcli-fb/-/merge_requests/5
  - https://salsa.debian.org/linux-blocks-team/targetcli-fb/-/merge_requests/4
  - https://salsa.debian.org/linux-blocks-team/targetcli-fb/-/merge_requests/3

To post a comment you must log in.
Revision history for this message
Rafael David Tinoco (rafaeldtinoco) wrote :

@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!

Revision history for this message
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 ...

Revision history for this message
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.

Revision history for this message
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>

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

I really think we need to do more for targetclid (https://github.com/open-iscsi/targetcli-fb/commit/7917ef55f6279fa28519d2160c4ec49453c275ec)

- ./usr/bin/targetclid in installed but missing a man page.

- try the new socket activation (daemon is still off by default https://github.com/open-iscsi/targetcli-fb/commit/797778eeb2997d34af7fd3760195f7f8d08470f4)

- changelog needs epoch mentioned (uh I just found there even is a lintian entry for it https://lintian.debian.org/tags/epoch-change-without-comment.html)

- we miss the targetclid.service/socket (https://github.com/open-iscsi/targetcli-fb/commit/ad37f94ae72d0e3d5963ce182e2897c84af9c039)

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>

Revision history for this message
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

[1]: https://code.launchpad.net/~paelzer/ubuntu/+source/targetcli-fb/+git/targetcli-fb/+ref/focal-merge-upstream-2.1.51-extended

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :
Revision history for this message
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://bugs.launchpad.net/ubuntu/+source/ceph-iscsi/+bug/1854362/comments/34
[2]: https://bugs.launchpad.net/ubuntu/+source/ceph-iscsi/+bug/1854362/comments/35

review: Needs Fixing
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

I got the service right, this is from an install:

ubuntu@focal-targetcli:~$ sudo apt install targetcli-fb
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://ppa.launchpad.net/ci-train-ppa-service/3956/ubuntu focal/main amd64 targetcli-fb all 1:2.1.51-0ubuntu1~ppa3 [35.1 kB]
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-fb_1%3a2.1.51-0ubuntu1~ppa3_all.deb ...
Unpacking targetcli-fb (1:2.1.51-0ubuntu1~ppa3) ...
Setting up targetcli-fb (1:2.1.51-0ubuntu1~ppa3) ...
/usr/lib/python3.8/subprocess.py:838: RuntimeWarning: line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used
  self.stdin = io.open(p2cwrite, 'wb', bufsize)
Created symlink /etc/systemd/system/multi-user.target.wants/targetclid.service → /lib/systemd/system/targetclid.service.
Created symlink /etc/systemd/system/sockets.target.wants/targetclid.socket → /lib/systemd/system/targetclid.socket.
Processing triggers for man-db (2.9.0-2) ...
ubuntu@focal-targetcli:~$ systemctl status targetclid.socket targetclid.service
● targetclid.socket - targetclid socket
     Loaded: loaded (/lib/systemd/system/targetclid.socket; enabled; vendor preset: enabled)
     Active: active (listening) since Thu 2020-02-27 15:21:06 UTC; 5s ago
   Triggers: ● targetclid.service
       Docs: man:targetclid(8)
     Listen: /run/targetclid.sock (Stream)
     CGroup: /system.slice/targetclid.socket

Feb 27 15:21:06 focal-targetcli systemd[1]: Listening on targetclid socket.

● targetclid.service - Targetcli daemon
     Loaded: loaded (/lib/systemd/system/targetclid.service; enabled; vendor preset: enabled)
     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/.targetcli/prefs.bin.
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>

Revision history for this message
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.

[1]: https://code.launchpad.net/~paelzer/ubuntu/+source/targetcli-fb/+git/targetcli-fb/+ref/focal-merge-upstream-2.1.51-extended

review: Approve
Revision history for this message
Rafael David Tinoco (rafaeldtinoco) wrote :

> I was going through the upstream changes again one by one.
> at https://github.com/open-iscsi/targetcli-fb/compare/v2.1.fb49...v2.1.51
>
> FYI on reserver/release - new feature https://github.com/open-iscsi/targetcli-
> fb/commit/fa71860b0d819a691683c1fdcb70c255653b5851

That's nice! Important "feature" for this need.

Revision history for this message
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-fb_2.1.51-0ubuntu1_all.deb'.
 dpkg-genbuildinfo --build=binary
 dpkg-genchanges -sa --build=binary >../targetcli-fb_2.1.51-0ubuntu1_amd64.changes
dpkg-genchanges: info: binary-only upload (no source code included)
 dpkg-source --after-build .
dpkg-source: info: unapplying 0003-Use-etc-rtslib-fb-target-instead-of-etc-target.patch
dpkg-buildpackage: info: binary-only upload (no source included)
 signfile targetcli-fb_2.1.51-0ubuntu1_amd64.buildinfo
 signfile targetcli-fb_2.1.51-0ubuntu1_amd64.changes

- installation:

(c)rafaeldtinoco@targetclifb:~/.../targetcli-fb$ sudo dpkg -i ../targetcli-fb_2.1.51-0ubuntu1_all.deb
Selecting previously unselected package targetcli-fb.
(Reading database ... 76255 files and directories currently installed.)
Preparing to unpack .../targetcli-fb_2.1.51-0ubuntu1_all.deb ...
Unpacking targetcli-fb (1:2.1.51-0ubuntu1) ...
Setting up targetcli-fb (1:2.1.51-0ubuntu1) ...
/usr/lib/python3.8/subprocess.py:838: RuntimeWarning: line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used
  self.stdin = io.open(p2cwrite, 'wb', bufsize)
Created symlink /etc/systemd/system/multi-user.target.wants/targetclid.service → /lib/systemd/system/targetclid.service.
Created symlink /etc/systemd/system/sockets.target.wants/targetclid.socket → /lib/systemd/system/targetclid.socket.

- service:

(c)rafaeldtinoco@targetclifb:~/.../targetcli-fb$ systemctl status targetclid.service
● targetclid.service - Targetcli daemon
     Loaded: loaded (/lib/systemd/system/targetclid.service; enabled; vendor preset: enabled)
     Active: inactive (dead)
TriggeredBy: ● targetclid.socket
       Docs: man:targetclid(8)

(c)rafaeldtinoco@targetclifb:~/.../targetcli-fb$ systemctl start targetclid.service

(c)rafaeldtinoco@targetclifb:~/.../targetcli-fb$ systemctl status targetclid.service
● targetclid.service - Targetcli daemon
     Loaded: loaded (/lib/systemd/system/targetclid.service; enabled; vendor preset: enabled)
     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.slice/targetclid.service
             └─15181 /usr/bin/python3 /usr/bin/targetclid

I'm replacing my LIO storage machine with this package to make sure its working... and then I'll upload if you are good.

Revision history for this message
Rafael David Tinoco (rafaeldtinoco) wrote :

Okay so with previous package I had:

https://pastebin.ubuntu.com/p/zR3rQg47kY/

With the new package after restoring config:

https://pastebin.ubuntu.com/p/BW7XXKPqdg/

Client is working:

/> sessions
alias: clufocal01 sid: 1 type: Normal session-state: LOGGED_IN

Will upload this version.

review: Approve
Revision history for this message
Rafael David Tinoco (rafaeldtinoco) wrote :

$ git log -1 --pretty=oneline --tags
327f616 (HEAD -> focal-merge-upstream-2.1.51, tag: upload/1%2.1.51-0ubuntu1, rafaeldtinoco/focal-merge-upstream-2.1.51, pae ....

(c)rafaeldtinoco@targetclifb:~/.../targetcli-fb$ git push pkg upload/1%2.1.51-0ubuntu1
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.launchpad.net/~usd-import-team/ubuntu/+source/targetcli-fb
 * [new tag] upload/1%2.1.51-0ubuntu1 -> upload/1%2.1.51-0ubuntu1

(c)rafaeldtinoco@targetclifb:~/.../targetcli-fb$ dput ubuntu ../targetcli-fb_2.1.51-0ubuntu1_source.changes
Checking signature on .changes
gpg: ../targetcli-fb_2.1.51-0ubuntu1_source.changes: Valid signature from A93E0E0AD83C0D0F
Checking signature on .dsc
gpg: ../targetcli-fb_2.1.51-0ubuntu1.dsc: Valid signature from A93E0E0AD83C0D0F
Uploading to ubuntu (via ftp to upload.ubuntu.com):
  Uploading targetcli-fb_2.1.51-0ubuntu1.dsc: done.
  Uploading targetcli-fb_2.1.51.orig.tar.gz: done.
  Uploading targetcli-fb_2.1.51-0ubuntu1.debian.tar.xz: done.
  Uploading targetcli-fb_2.1.51-0ubuntu1_source.buildinfo: done.
  Uploading targetcli-fb_2.1.51-0ubuntu1_source.changes: done.
Successfully uploaded packages.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/daemon/targetclid b/daemon/targetclid
0new file mode 1006440new file mode 100644
index 0000000..fb472dc
--- /dev/null
+++ b/daemon/targetclid
@@ -0,0 +1,276 @@
1#!/usr/bin/python
2
3'''
4targetclid
5
6This file is part of targetcli-fb.
7Copyright (c) 2019 by Red Hat, Inc.
8
9Licensed under the Apache License, Version 2.0 (the "License"); you may
10not use this file except in compliance with the License. You may obtain
11a copy of the License at
12
13 http://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software
16distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18License for the specific language governing permissions and limitations
19under the License.
20'''
21
22from __future__ import print_function
23from targetcli import UIRoot
24from targetcli import __version__ as targetcli_version
25from configshell_fb import ConfigShell
26from os import getuid, getenv, unlink
27from threading import Thread
28
29import sys
30import socket
31import struct
32import fcntl
33import signal
34import errno
35
36
37err = sys.stderr
38
39class TargetCLI:
40 def __init__(self):
41 '''
42 initializer
43 '''
44 # socket for unix communication
45 self.socket_path = '/var/run/targetclid.sock'
46 # pid file for defending on multiple daemon runs
47 self.pid_file = '/var/run/targetclid.pid'
48
49 self.NoSignal = True
50 self.sock = None
51
52 # shell console methods
53 self.shell = ConfigShell(getenv("TARGETCLI_HOME", '~/.targetcli'))
54 self.con = self.shell.con
55 self.display = self.shell.con.display
56 self.render = self.shell.con.render_text
57
58 # Handle SIGINT SIGTERM SIGHUP gracefully
59 signal.signal(signal.SIGINT, self.signal_handler)
60 signal.signal(signal.SIGTERM, self.signal_handler)
61 signal.signal(signal.SIGHUP, self.signal_handler)
62
63 try:
64 self.pfd = open(self.pid_file, 'w+')
65 except IOError as e:
66 self.display(
67 self.render(
68 "opening pidfile failed: %s" %str(e),
69 'red'))
70 sys.exit(1)
71
72 self.try_pidfile_lock()
73
74 is_root = False
75 if getuid() == 0:
76 is_root = True
77
78 try:
79 root_node = UIRoot(self.shell, as_root=is_root)
80 root_node.refresh()
81 except Exception as error:
82 self.display(self.render(str(error), 'red'))
83 if not is_root:
84 self.display(self.render("Retry as root.", 'red'))
85 self.pfd.close()
86 sys.exit(1)
87
88 # Keep track, for later use
89 self.con_stdout_ = self.con._stdout
90 self.con_stderr_ = self.con._stderr
91
92
93 def __del__(self):
94 '''
95 destructor
96 '''
97 if hasattr(self, 'pfd'):
98 self.pfd.close()
99
100
101 def signal_handler(self, signum, frame):
102 '''
103 signal handler
104 '''
105 self.NoSignal = False
106 if self.sock:
107 self.sock.close()
108
109
110 def try_pidfile_lock(self):
111 '''
112 get lock on pidfile, which is to check if targetclid is running
113 '''
114 # check if targetclid is already running
115 lock = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
116 try:
117 fcntl.fcntl(self.pfd, fcntl.F_SETLK, lock)
118 except Exception:
119 self.display(self.render("targetclid is already running...", 'red'))
120 self.pfd.close()
121 sys.exit(1)
122
123
124 def release_pidfile_lock(self):
125 '''
126 release lock on pidfile
127 '''
128 lock = struct.pack('hhllhh', fcntl.F_UNLCK, 0, 0, 0, 0, 0)
129 try:
130 fcntl.fcntl(self.pfd, fcntl.F_SETLK, lock)
131 except Exception as e:
132 self.display(
133 self.render(
134 "fcntl(UNLCK) on pidfile failed: %s" %str(e),
135 'red'))
136 self.pfd.close()
137 sys.exit(1)
138 self.pfd.close()
139
140
141 def client_thread(self, connection):
142 '''
143 Handle commands from client
144 '''
145 # load the prefs
146 self.shell.prefs.load()
147
148 still_listen = True
149 # Receive the data in small chunks and retransmit it
150 while still_listen:
151 data = connection.recv(65535)
152 if b'-END@OF@DATA-' in data:
153 connection.close()
154 still_listen = False
155 else:
156 self.con._stdout = self.con._stderr = f = open("/tmp/data.txt", "w")
157 try:
158 # extract multiple commands delimited with '%'
159 list_data = data.decode().split('%')
160 for cmd in list_data:
161 self.shell.run_cmdline(cmd)
162 except Exception as e:
163 print(str(e), file=f) # push error to stream
164
165 # Restore
166 self.con._stdout = self.con_stdout_
167 self.con._stderr = self.con_stderr_
168 f.close()
169
170 with open('/tmp/data.txt', 'r') as f:
171 output = f.read()
172 var = struct.pack('i', len(output))
173 connection.sendall(var) # length of string
174 if len(output):
175 connection.sendall(output.encode()) # actual string
176
177
178def usage():
179 print("Usage: %s [--version|--help]" % sys.argv[0], file=err)
180 print(" --version\t\tPrint version", file=err)
181 print(" --help\t\tPrint this information", file=err)
182 sys.exit(0)
183
184
185def version():
186 print("%s version %s" % (sys.argv[0], targetcli_version), file=err)
187 sys.exit(0)
188
189
190def usage_version(cmd):
191 if cmd in ("help", "--help", "-h"):
192 usage()
193
194 if cmd in ("version", "--version", "-v"):
195 version()
196
197
198def main():
199 '''
200 start targetclid
201 '''
202 if len(sys.argv) > 1:
203 usage_version(sys.argv[1])
204 print("unrecognized option: %s" % (sys.argv[1]))
205 sys.exit(-1)
206
207 to = TargetCLI()
208
209 if getenv('LISTEN_PID'):
210 # the systemd-activation path, using next available FD
211 fn = sys.stderr.fileno() + 1
212 try:
213 sock = socket.fromfd(fn, socket.AF_UNIX, socket.SOCK_STREAM)
214 except socket.error as err:
215 to.display(to.render(err.strerror, 'red'))
216 sys.exit(1)
217
218 # save socket so a signal can clea it up
219 to.sock = sock
220 else:
221 # Make sure file doesn't exist already
222 try:
223 unlink(to.socket_path)
224 except:
225 pass
226
227 # Create a TCP/IP socket
228 try:
229 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
230 except socket.error as err:
231 to.display(to.render(err.strerror, 'red'))
232 sys.exit(1)
233
234 # save socket so a signal can clea it up
235 to.sock = sock
236
237 # Bind the socket path
238 try:
239 sock.bind(to.socket_path)
240 except socket.error as err:
241 to.display(to.render(err.strerror, 'red'))
242 sys.exit(1)
243
244 # Listen for incoming connections
245 try:
246 sock.listen(1)
247 except socket.error as err:
248 to.display(to.render(err.strerror, 'red'))
249 sys.exit(1)
250
251 while to.NoSignal:
252 try:
253 # Wait for a connection
254 connection, client_address = sock.accept()
255 except socket.error as err:
256 if err.errno != errno.EBADF or to.NoSignal:
257 to.display(to.render(err.strerror, 'red'))
258 break
259
260 thread = Thread(target=to.client_thread, args=(connection,))
261 thread.start()
262 try:
263 thread.join()
264 except:
265 to.display(to.render(str(error), 'red'))
266
267 to.release_pidfile_lock()
268
269 if not to.NoSignal:
270 to.display(to.render("Signal received, quiting gracefully!", 'green'))
271 sys.exit(0)
272 sys.exit(1)
273
274
275if __name__ == "__main__":
276 main()
diff --git a/debian/changelog b/debian/changelog
index 6948d6d..493dc40 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,20 @@
1targetcli-fb (1:2.1.51-0ubuntu1) focal; urgency=medium
2
3 [ Rafael David Tinoco ]
4 * New upstream version 2.1.51
5 - Upstream changed their versioning scheme in a way that makes the
6 latest version lower than the previous one therefore we had to add
7 an epoch bump.
8 * debian/watch: fix the watch file for upstream versions without fb.
9
10 [ Christian Ehrhardt ]
11 * This now provides an optional daemon targetclid to be used for
12 operations at scale to retain state between commands in memory.
13 - d/manpages: install targetclid man page
14 - d/rules: install and enable targetclid service and socket
15
16 -- Rafael David Tinoco <rafaeldtinoco@ubuntu.com> Thu, 27 Feb 2020 02:30:18 +0000
17
1targetcli-fb (2.1.fb49-1) unstable; urgency=medium18targetcli-fb (2.1.fb49-1) unstable; urgency=medium
219
3 * New upstream version 2.1.fb4920 * New upstream version 2.1.fb49
diff --git a/debian/control b/debian/control
index a1ce30a..9c717c2 100644
--- a/debian/control
+++ b/debian/control
@@ -1,7 +1,8 @@
1Source: targetcli-fb1Source: targetcli-fb
2Section: admin2Section: admin
3Priority: optional3Priority: optional
4Maintainer: Debian LIO Target Packagers <team+linux-blocks@tracker.debian.org>4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
5XSBC-Original-Maintainer: Debian LIO Target Packagers <team+linux-blocks@tracker.debian.org>
5Uploaders: Christophe Vu-Brugier <cvubrugier@fastmail.fm>,6Uploaders: Christophe Vu-Brugier <cvubrugier@fastmail.fm>,
6 Ritesh Raj Sarraf <rrs@debian.org>,7 Ritesh Raj Sarraf <rrs@debian.org>,
7 Christian Seiler <christian@iwakd.de>8 Christian Seiler <christian@iwakd.de>
diff --git a/debian/manpages b/debian/manpages
index 07dd87b..78f958a 100644
--- a/debian/manpages
+++ b/debian/manpages
@@ -1 +1,2 @@
1targetcli.81targetcli.8
2targetclid.8
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
index 00f6d3d..e7d0324 100644
--- 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
@@ -13,7 +13,7 @@ out of the box.
1313
14--- a/targetcli.814--- a/targetcli.8
15+++ b/targetcli.815+++ b/targetcli.8
16@@ -355,7 +355,7 @@16@@ -360,7 +360,7 @@
17 Save the current configuration settings to a file, from which settings17 Save the current configuration settings to a file, from which settings
18 will be restored if the system is rebooted. By default, this will save18 will be restored if the system is rebooted. By default, this will save
19 the configuration to19 the configuration to
@@ -22,7 +22,7 @@ out of the box.
22 .P22 .P
23 This command is executed from the configuration root node.23 This command is executed from the configuration root node.
24 .P24 .P
25@@ -415,7 +415,7 @@25@@ -420,7 +420,7 @@
26 to change working path to newly-created nodes. Global settings26 to change working path to newly-created nodes. Global settings
27 are user-specific and are saved to ~/.targetcli/ upon exit, unlike27 are user-specific and are saved to ~/.targetcli/ upon exit, unlike
28 other groups, which are system-wide and kept in28 other groups, which are system-wide and kept in
@@ -31,7 +31,7 @@ out of the box.
31 .SS BACKSTORE-SPECIFIC31 .SS BACKSTORE-SPECIFIC
32 .B attribute32 .B attribute
33 .br33 .br
34@@ -453,9 +453,9 @@34@@ -458,9 +458,9 @@
35 /iscsi/<target_iqn>/tpgX/acls/<initiator_iqn> configuration node. Set35 /iscsi/<target_iqn>/tpgX/acls/<initiator_iqn> configuration node. Set
36 the userid and password for full-feature phase for this ACL.36 the userid and password for full-feature phase for this ACL.
37 .SH FILES37 .SH FILES
@@ -42,10 +42,10 @@ out of the box.
42+.B /etc/rtslib-fb-target/backup/*42+.B /etc/rtslib-fb-target/backup/*
43 .SH ENVIRONMENT43 .SH ENVIRONMENT
44 .SS TARGETCLI_HOME44 .SS TARGETCLI_HOME
45 If set, this variable points to a directory that should be used instead of ~/.targetctl45 If set, this variable points to a directory that should be used instead of ~/.targetcli
46--- a/targetcli/ui_root.py46--- a/targetcli/ui_root.py
47+++ b/targetcli/ui_root.py47+++ b/targetcli/ui_root.py
48@@ -33,8 +33,8 @@48@@ -34,8 +34,8 @@
49 from .ui_node import UINode49 from .ui_node import UINode
50 from .ui_target import UIFabricModule50 from .ui_target import UIFabricModule
51 51
diff --git a/debian/rules b/debian/rules
index 89af47e..dd11732 100755
--- a/debian/rules
+++ b/debian/rules
@@ -2,3 +2,14 @@
22
3%:3%:
4 dh $@ --with python3 -Spybuild4 dh $@ --with python3 -Spybuild
5
6override_dh_installsystemd:
7 # helper for the cli, not an individual entity, keeping it within main pkg
8 dh_installsystemd -ptargetcli-fb --name=targetclid --no-restart-on-upgrade --no-start targetclid.service
9 dh_installsystemd -ptargetcli-fb --name=targetclid --no-restart-on-upgrade targetclid.socket
10
11override_dh_install:
12 # not (yet) installed by upstream but required
13 cp -v systemd/targetclid.service debian/targetcli-fb.targetclid.service
14 cp -v systemd/targetclid.socket debian/targetcli-fb.targetclid.socket
15 dh_install
diff --git a/debian/watch b/debian/watch
index c2ed1a0..7a50f79 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,3 +1,3 @@
1version=41version=4
2opts=uversionmangle=s/fb//,filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/targetcli-fb-$1\.tar\.gz/ \2opts=uversionmangle=s/fb//,dversionmangle=s/fb//,filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/targetcli-fb-$1\.tar\.gz/ \
3 https://github.com/open-iscsi/targetcli-fb/releases .*/v?(\d\S+)\.tar\.gz3 https://github.com/open-iscsi/targetcli-fb/releases .*/v?(\d\S+)\.tar\.gz
diff --git a/scripts/targetcli b/scripts/targetcli
index b042ad9..04e5aba 100755
--- a/scripts/targetcli
+++ b/scripts/targetcli
@@ -24,10 +24,22 @@ from os import getuid, getenv
24from targetcli import UIRoot24from targetcli import UIRoot
25from rtslib_fb import RTSLibError25from rtslib_fb import RTSLibError
26from configshell_fb import ConfigShell, ExecutionError26from configshell_fb import ConfigShell, ExecutionError
27import sys
28from targetcli import __version__ as targetcli_version27from targetcli import __version__ as targetcli_version
2928
29import sys
30import socket
31import struct
32import readline
33import six
34import fcntl
35
30err = sys.stderr36err = sys.stderr
37# lockfile for serializing multiple targetcli requests
38lock_file = '/var/run/targetcli.lock'
39socket_path = '/var/run/targetclid.sock'
40hints = ['/', 'backstores/', 'iscsi/', 'loopback/', 'vhost/', 'xen-pvscsi/',
41 'cd', 'pwd', 'ls', 'set', 'get', 'help', 'refresh', 'status',
42 'clearconfig', 'restoreconfig', 'saveconfig', 'exit']
3143
32class TargetCLI(ConfigShell):44class TargetCLI(ConfigShell):
33 default_prefs = {'color_path': 'magenta',45 default_prefs = {'color_path': 'magenta',
@@ -51,14 +63,16 @@ class TargetCLI(ConfigShell):
51 'auto_save_on_exit': True,63 'auto_save_on_exit': True,
52 'max_backup_files': '10',64 'max_backup_files': '10',
53 'auto_add_default_portal': True,65 'auto_add_default_portal': True,
66 'auto_use_daemon': False,
54 }67 }
5568
56def usage():69def usage():
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)
58 print(" --version\t\tPrint version", file=err)71 print(" --version\t\tPrint version", file=err)
59 print(" --help\t\tPrint this information", file=err)72 print(" --help\t\tPrint this information", file=err)
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)
61 print(" <nothing>\t\tEnter configuration shell", file=err)74 print(" <nothing>\t\tEnter configuration shell", file=err)
75 print(" --disable-daemon\tTurn-off the global auto use daemon flag", file=err)
62 print("See man page for more information.", file=err)76 print("See man page for more information.", file=err)
63 sys.exit(-1)77 sys.exit(-1)
6478
@@ -66,16 +80,153 @@ def version():
66 print("%s version %s" % (sys.argv[0], targetcli_version), file=err)80 print("%s version %s" % (sys.argv[0], targetcli_version), file=err)
67 sys.exit(0)81 sys.exit(0)
6882
83def usage_version(cmd):
84 if cmd in ("help", "--help", "-h"):
85 usage()
86
87 if cmd in ("version", "--version", "-v"):
88 version()
89
90def try_op_lock(shell, lkfd):
91 '''
92 acquire a blocking lock on lockfile, to serialize multiple requests
93 '''
94 try:
95 fcntl.flock(lkfd, fcntl.LOCK_EX) # wait here until ongoing request is finished
96 except Exception as e:
97 shell.con.display(
98 shell.con.render_text(
99 "taking lock on lockfile failed: %s" %str(e),
100 'red'))
101 sys.exit(1)
102
103def release_op_lock(shell, lkfd):
104 '''
105 release blocking lock on lockfile, which can allow other requests process
106 '''
107 try:
108 fcntl.flock(lkfd, fcntl.LOCK_UN) # allow other requests now
109 except Exception as e:
110 shell.con.display(
111 shell.con.render_text(
112 "unlock on lockfile failed: %s" %str(e),
113 'red'))
114 sys.exit(1)
115 lkfd.close()
116
117def completer(text, state):
118 options = [x for x in hints if x.startswith(text)]
119 try:
120 return options[state]
121 except IndexError:
122 return None
123
124def call_daemon(shell, req):
125 try:
126 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
127 except socket.error as err:
128 shell.con.display(shell.con.render_text(err, 'red'))
129 sys.exit(1)
130
131 try:
132 sock.connect(socket_path)
133 except socket.error as err:
134 shell.con.display(shell.con.render_text(err, 'red'))
135 shell.con.display(
136 shell.con.render_text("Currently auto_use_daemon is true, "
137 "hence please make sure targetclid daemon is running ...\n"
138 "(or)\nIncase if you wish to turn auto_use_daemon to false "
139 "then run '#targetcli --disable-daemon'", 'red'))
140 sys.exit(1)
141
142 try:
143 # send request
144 sock.sendall(req)
145 except socket.error as err:
146 shell.con.display(shell.con.render_text(err, 'red'))
147 sys.exit(1)
148
149 var = sock.recv(4) # get length of data
150 sending = struct.unpack('i', var)
151 amount_expected = sending[0]
152 amount_received = 0
153
154 # get the actual data in chunks
155 while amount_received < amount_expected:
156 data = sock.recv(1024)
157 amount_received += len(data)
158 print(data.decode(), end ="")
159
160 sock.send(b'-END@OF@DATA-')
161 sock.close()
162 sys.exit(0)
163
164def get_arguments(shell):
165 readline.set_completer(completer)
166 readline.set_completer_delims('')
167
168 if 'libedit' in readline.__doc__:
169 readline.parse_and_bind("bind ^I rl_complete")
170 else:
171 readline.parse_and_bind("tab: complete")
172
173 if len(sys.argv) > 1:
174 command = " ".join(sys.argv[1:])
175 else:
176 inputs = []
177 shell.con.display("targetcli shell version %s\n"
178 "Entering targetcli batch mode for daemonized approach.\n"
179 "Enter multiple commands separated by newline and "
180 "type 'exit' to run them all in one go.\n"
181 % targetcli_version)
182 while True:
183 shell.con.raw_write("/> ")
184 command = six.moves.input()
185 if command.lower() == "exit":
186 break
187 inputs.append(command)
188 command = '%'.join(inputs) # delimit multiple commands with '%'
189
190 if not command:
191 sys.exit(1)
192
193 usage_version(command);
194
195 return command
196
69def main():197def main():
70 '''198 '''
71 Start the targetcli shell.199 Start the targetcli shell.
72 '''200 '''
201 shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli'))
202
203 is_root = False
73 if getuid() == 0:204 if getuid() == 0:
74 is_root = True205 is_root = True
75 else:
76 is_root = False
77206
78 shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli'))207 try:
208 lkfd = open(lock_file, 'w+');
209 except IOError as e:
210 shell.con.display(
211 shell.con.render_text("opening lockfile failed: %s" %str(e),
212 'red'))
213 sys.exit(1)
214
215 try_op_lock(shell, lkfd)
216
217 use_daemon = False
218 if shell.prefs['auto_use_daemon']:
219 use_daemon = True
220
221 disable_daemon=False
222 if len(sys.argv) > 1:
223 usage_version(sys.argv[1])
224 if sys.argv[1] in ("disable-daemon", "--disable-daemon"):
225 disable_daemon=True
226
227 if use_daemon and not disable_daemon:
228 call_daemon(shell, get_arguments(shell).encode())
229 # does not return
79230
80 try:231 try:
81 root_node = UIRoot(shell, as_root=is_root)232 root_node = UIRoot(shell, as_root=is_root)
@@ -87,14 +238,11 @@ def main():
87 sys.exit(-1)238 sys.exit(-1)
88239
89 if len(sys.argv) > 1:240 if len(sys.argv) > 1:
90 if sys.argv[1] in ("--help", "-h"):
91 usage()
92
93 if sys.argv[1] in ("--version", "-v"):
94 version()
95
96 try:241 try:
97 shell.run_cmdline(" ".join(sys.argv[1:]))242 if disable_daemon:
243 shell.run_cmdline('set global auto_use_daemon=false')
244 else:
245 shell.run_cmdline(" ".join(sys.argv[1:]))
98 except Exception as e:246 except Exception as e:
99 print(str(e), file=sys.stderr)247 print(str(e), file=sys.stderr)
100 sys.exit(1)248 sys.exit(1)
@@ -117,6 +265,8 @@ def main():
117 shell.log.info("Global pref auto_save_on_exit=true")265 shell.log.info("Global pref auto_save_on_exit=true")
118 root_node.ui_command_saveconfig()266 root_node.ui_command_saveconfig()
119267
268 release_op_lock(shell, lkfd)
269
120270
121if __name__ == "__main__":271if __name__ == "__main__":
122 main()272 main()
diff --git a/setup.py b/setup.py
index 7b44304..8dff55e 100755
--- a/setup.py
+++ b/setup.py
@@ -30,7 +30,10 @@ setup(
30 maintainer_email = 'agrover@redhat.com',30 maintainer_email = 'agrover@redhat.com',
31 url = 'http://github.com/open-iscsi/targetcli-fb',31 url = 'http://github.com/open-iscsi/targetcli-fb',
32 packages = ['targetcli'],32 packages = ['targetcli'],
33 scripts = ['scripts/targetcli'],33 scripts = [
34 'scripts/targetcli',
35 'daemon/targetclid'
36 ],
34 classifiers = [37 classifiers = [
35 "Programming Language :: Python",38 "Programming Language :: Python",
36 "Programming Language :: Python :: 3",39 "Programming Language :: Python :: 3",
diff --git a/systemd/targetclid.service b/systemd/targetclid.service
37new file mode 10064440new file mode 100644
index 0000000..dd1b54c
--- /dev/null
+++ b/systemd/targetclid.service
@@ -0,0 +1,13 @@
1[Unit]
2Description=Targetcli daemon
3Documentation=man:targetclid(8)
4After=network.target
5
6[Service]
7Type=simple
8ExecStart=/usr/bin/targetclid
9Restart=on-failure
10
11[Install]
12WantedBy=multi-user.target
13Also=targetclid.socket
diff --git a/systemd/targetclid.socket b/systemd/targetclid.socket
0new file mode 10064414new file mode 100644
index 0000000..4730fce
--- /dev/null
+++ b/systemd/targetclid.socket
@@ -0,0 +1,9 @@
1[Unit]
2Description=targetclid socket
3Documentation=man:targetclid(8)
4
5[Socket]
6ListenStream=/var/run/targetclid.sock
7
8[Install]
9WantedBy=sockets.target
diff --git a/targetcli.8 b/targetcli.8
index 61f4b64..a73f785 100644
--- a/targetcli.8
+++ b/targetcli.8
@@ -10,6 +10,11 @@ administrator to assign local storage resources backed by either
10files, volumes, local SCSI devices, or ramdisk, and export them to10files, volumes, local SCSI devices, or ramdisk, and export them to
11remote systems via network fabrics, such as iSCSI or FCoE.11remote systems via network fabrics, such as iSCSI or FCoE.
12.P12.P
13There is a daemon component for targetcli, which will greatly improve
14the overall execution time taken by targetcli commands at scale. For
15more details about switching to daemonized mode refer to targetclid(8)
16man page.
17.P
13The configuration layout is tree-based, similar to a filesystem, and18The configuration layout is tree-based, similar to a filesystem, and
14is navigated in a similar manner.19is navigated in a similar manner.
15.SH USAGE20.SH USAGE
@@ -458,8 +463,9 @@ the userid and password for full-feature phase for this ACL.
458.B /etc/target/backup/*463.B /etc/target/backup/*
459.SH ENVIRONMENT464.SH ENVIRONMENT
460.SS TARGETCLI_HOME465.SS TARGETCLI_HOME
461If set, this variable points to a directory that should be used instead of ~/.targetctl466If set, this variable points to a directory that should be used instead of ~/.targetcli
462.SH SEE ALSO467.SH SEE ALSO
468.BR targetclid (8),
463.BR targetctl (8),469.BR targetctl (8),
464.BR tcmu-runner (8)470.BR tcmu-runner (8)
465.SH AUTHOR471.SH AUTHOR
diff --git a/targetcli/ui_backstore.py b/targetcli/ui_backstore.py
index 275d46b..8692f22 100644
--- a/targetcli/ui_backstore.py
+++ b/targetcli/ui_backstore.py
@@ -59,14 +59,14 @@ def human_to_bytes(hsize, kilo=1024):
59 '''59 '''
60 This function converts human-readable amounts of bytes to bytes.60 This function converts human-readable amounts of bytes to bytes.
61 It understands the following units :61 It understands the following units :
62 - I{B} or no unit present for Bytes62 - B or no unit present for Bytes
63 - I{k}, I{K}, I{kB}, I{KB} for kB (kilobytes)63 - k, K, kB, KB for kB (kilobytes)
64 - I{m}, I{M}, I{mB}, I{MB} for MB (megabytes)64 - m, M, mB, MB for MB (megabytes)
65 - I{g}, I{G}, I{gB}, I{GB} for GB (gigabytes)65 - g, G, gB, GB for GB (gigabytes)
66 - I{t}, I{T}, I{tB}, I{TB} for TB (terabytes)66 - t, T, tB, TB for TB (terabytes)
6767
68 Note: The definition of I{kilo} defaults to 1kB = 1024Bytes.68 Note: The definition of kilo defaults to 1kB = 1024Bytes.
69 Strictly speaking, those should not be called I{kB} but I{kiB}.69 Strictly speaking, those should not be called "kB" but "kiB".
70 You can override that with the optional kilo parameter.70 You can override that with the optional kilo parameter.
7171
72 @param hsize: The human-readable version of the Bytes amount to convert72 @param hsize: The human-readable version of the Bytes amount to convert
@@ -286,13 +286,13 @@ class UIBackstore(UINode):
286286
287 def ui_command_delete(self, name, save=None):287 def ui_command_delete(self, name, save=None):
288 '''288 '''
289 Recursively deletes the storage object having the specified I{name}. If289 Recursively deletes the storage object having the specified name. If
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.
291291
292 EXAMPLE292 EXAMPLE
293 =======293 =======
294 B{delete mystorage}294 delete mystorage
295 -------------------295 ----------------
296 Deletes the storage object named mystorage, and all associated LUNs.296 Deletes the storage object named mystorage, and all associated LUNs.
297 '''297 '''
298 self.assert_root()298 self.assert_root()
@@ -354,7 +354,7 @@ class UIPSCSIBackstore(UIBackstore):
354 def ui_command_create(self, name, dev):354 def ui_command_create(self, name, dev):
355 '''355 '''
356 Creates a PSCSI storage object, with supplied name and SCSI device. The356 Creates a PSCSI storage object, with supplied name and SCSI device. The
357 SCSI device I{dev} can either be a path name to the device, in which357 SCSI device "dev" can either be a path name to the device, in which
358 case it is recommended to use the /dev/disk/by-id hierarchy to have358 case it is recommended to use the /dev/disk/by-id hierarchy to have
359 consistent naming should your physical SCSI system be modified, or an359 consistent naming should your physical SCSI system be modified, or an
360 SCSI device ID in the H:C:T:L format, which is not recommended as SCSI360 SCSI device ID in the H:C:T:L format, which is not recommended as SCSI
@@ -383,17 +383,17 @@ class UIRDMCPBackstore(UIBackstore):
383383
384 def ui_command_create(self, name, size, nullio=None, wwn=None):384 def ui_command_create(self, name, size, nullio=None, wwn=None):
385 '''385 '''
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.
387387
388 SIZE SYNTAX388 SIZE SYNTAX
389 ===========389 ===========
390 - If size is an int, it represents a number of bytes.390 - If size is an int, it represents a number of bytes.
391 - If size is a string, the following units can be used:391 - If size is a string, the following units can be used:
392 - B{B} or no unit present for bytes392 - B or no unit present for bytes
393 - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes)393 - k, K, kB, KB for kB (kilobytes)
394 - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes)394 - m, M, mB, MB for MB (megabytes)
395 - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes)395 - g, G, gB, GB for GB (gigabytes)
396 - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes)396 - t, T, tB, TB for TB (terabytes)
397 '''397 '''
398 self.assert_root()398 self.assert_root()
399399
@@ -445,14 +445,14 @@ class UIFileIOBackstore(UIBackstore):
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,
446 sparse=None, wwn=None):446 sparse=None, wwn=None):
447 '''447 '''
448 Creates a FileIO storage object. If I{file_or_dev} is a path448 Creates a FileIO storage object. If "file_or_dev" is a path
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"
450 parameter is mandatory. Else, if I{file_or_dev} is a path to a450 parameter is mandatory. Else, if "file_or_dev" is a path to a
451 block device, the size parameter B{must} be ommited. If451 block device, the size parameter must be omitted. If
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"
453 the path to the file or I{dev} the path to a block device. The453 the path to the file or "dev" the path to a block device. The
454 I{write_back} parameter is a boolean controlling write454 "write_back" parameter is a boolean controlling write
455 caching. It is enabled by default. The I{sparse} parameter is455 caching. It is enabled by default. The "sparse" parameter is
456 only applicable when creating a new backing file. It is a456 only applicable when creating a new backing file. It is a
457 boolean stating if the created file should be created as a457 boolean stating if the created file should be created as a
458 sparse file (the default), or fully initialized.458 sparse file (the default), or fully initialized.
@@ -461,11 +461,11 @@ class UIFileIOBackstore(UIBackstore):
461 ===========461 ===========
462 - If size is an int, it represents a number of bytes.462 - If size is an int, it represents a number of bytes.
463 - If size is a string, the following units can be used:463 - If size is a string, the following units can be used:
464 - B{B} or no unit present for bytes464 - B or no unit present for bytes
465 - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes)465 - k, K, kB, KB for kB (kilobytes)
466 - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes)466 - m, M, mB, MB for MB (megabytes)
467 - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes)467 - g, G, gB, GB for GB (gigabytes)
468 - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes)468 - t, T, tB, TB for TB (terabytes)
469 '''469 '''
470 self.assert_root()470 self.assert_root()
471471
@@ -557,7 +557,7 @@ class UIBlockBackstore(UIBackstore):
557557
558 def ui_command_create(self, name, dev, readonly=None, wwn=None):558 def ui_command_create(self, name, dev, readonly=None, wwn=None):
559 '''559 '''
560 Creates an Block Storage object. I{dev} is the path to the TYPE_DISK560 Creates an Block Storage object. "dev" is the path to the TYPE_DISK
561 block device to use.561 block device to use.
562 '''562 '''
563 self.assert_root()563 self.assert_root()
@@ -628,11 +628,11 @@ class UIUserBackedBackstore(UIBackstore):
628 ===========628 ===========
629 - If size is an int, it represents a number of bytes.629 - If size is an int, it represents a number of bytes.
630 - If size is a string, the following units can be used:630 - If size is a string, the following units can be used:
631 - B{B} or no unit present for bytes631 - B or no unit present for bytes
632 - B{k}, B{K}, B{kB}, B{KB} for kB (kilobytes)632 - k, K, kB, KB for kB (kilobytes)
633 - B{m}, B{M}, B{mB}, B{MB} for MB (megabytes)633 - m, M, mB, MB for MB (megabytes)
634 - B{g}, B{G}, B{gB}, B{GB} for GB (gigabytes)634 - g, G, gB, GB for GB (gigabytes)
635 - B{t}, B{T}, B{tB}, B{TB} for TB (terabytes)635 - t, T, tB, TB for TB (terabytes)
636 '''636 '''
637637
638 size = human_to_bytes(size)638 size = human_to_bytes(size)
@@ -689,6 +689,7 @@ class UIStorageObject(UIRTSLibNode):
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.'),
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.'),
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.'),
692 'emulate_pr': ('number', 'If set to 1, enable SCSI Reservations.'),
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.'),
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.'),
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.'),
diff --git a/targetcli/ui_node.py b/targetcli/ui_node.py
index a6982f1..58a70c6 100644
--- a/targetcli/ui_node.py
+++ b/targetcli/ui_node.py
@@ -49,6 +49,9 @@ class UINode(ConfigNode):
49 self.define_config_group_param(49 self.define_config_group_param(
50 'global', 'max_backup_files', 'string',50 'global', 'max_backup_files', 'string',
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.')
52 self.define_config_group_param(
53 'global', 'auto_use_daemon', 'bool',
54 'If true, commands will be sent to targetclid.')
5255
53 def assert_root(self):56 def assert_root(self):
54 '''57 '''
@@ -95,7 +98,7 @@ class UINode(ConfigNode):
9598
96 SEE ALSO99 SEE ALSO
97 ========100 ========
98 B{ls}101 ls
99 '''102 '''
100 description, is_healthy = self.summary()103 description, is_healthy = self.summary()
101 self.shell.log.info("Status for %s: %s" % (self.path, description))104 self.shell.log.info("Status for %s: %s" % (self.path, description))
diff --git a/targetcli/ui_root.py b/targetcli/ui_root.py
index 38118bd..26815bd 100644
--- a/targetcli/ui_root.py
+++ b/targetcli/ui_root.py
@@ -24,6 +24,7 @@ import re
24import shutil24import shutil
25import stat25import stat
26import filecmp26import filecmp
27import gzip
2728
28from configshell_fb import ExecutionError29from configshell_fb import ExecutionError
29from rtslib_fb import RTSRoot30from rtslib_fb import RTSRoot
@@ -62,6 +63,38 @@ class UIRoot(UINode):
62 if fm.wwns == None or any(fm.wwns):63 if fm.wwns == None or any(fm.wwns):
63 UIFabricModule(fm, self)64 UIFabricModule(fm, self)
6465
66 def _compare_files(self, backupfile, savefile):
67 '''
68 Compare backfile and saveconfig file
69 '''
70 if (os.path.splitext(backupfile)[1] == '.gz'):
71 try:
72 with gzip.open(backupfile, 'rb') as fbkp:
73 fdata_bkp = fbkp.read()
74 except IOError as e:
75 self.shell.log.warning("Could not gzip open backupfile %s: %s"
76 % (backupfile, e.strerror))
77
78 else:
79 try:
80 with open(backupfile, 'rb') as fbkp:
81 fdata_bkp = fbkp.read()
82 except IOError as e:
83 self.shell.log.warning("Could not open backupfile %s: %s"
84 % (backupfile, e.strerror))
85
86 try:
87 with open(savefile, 'rb') as f:
88 fdata = f.read()
89 except IOError as e:
90 self.shell.log.warning("Could not open saveconfig file %s: %s"
91 % (savefile, e.strerror))
92
93 if fdata_bkp == fdata:
94 return True
95 else:
96 return False
97
65 def _save_backups(self, savefile):98 def _save_backups(self, savefile):
66 '''99 '''
67 Take backup of config-file if needed.100 Take backup of config-file if needed.
@@ -72,29 +105,30 @@ class UIRoot(UINode):
72105
73 backup_dir = os.path.dirname(savefile) + "/backup/"106 backup_dir = os.path.dirname(savefile) + "/backup/"
74 backup_name = "saveconfig-" + \107 backup_name = "saveconfig-" + \
75 datetime.now().strftime("%Y%m%d-%H:%M:%S") + ".json"108 datetime.now().strftime("%Y%m%d-%H:%M:%S") + "-json.gz"
76 backupfile = backup_dir + backup_name109 backupfile = backup_dir + backup_name
77 backup_error = None110 backup_error = None
78111
79 if not os.path.exists(backup_dir):112 if not os.path.exists(backup_dir):
80 try:113 try:
81 os.makedirs(backup_dir);114 os.makedirs(backup_dir)
82 except OSError as exe:115 except OSError as exe:
83 raise ExecutionError("Cannot create backup directory [%s] %s."116 raise ExecutionError("Cannot create backup directory [%s] %s."
84 % (backup_dir, exc.strerror))117 % (backup_dir, exe.strerror))
85118
86 # Only save backups if savefile exits119 # Only save backups if savefile exits
87 if not os.path.exists(savefile):120 if not os.path.exists(savefile):
88 return121 return
89122
90 backed_files_list = sorted(glob(os.path.dirname(savefile) + \123 backed_files_list = sorted(glob(os.path.dirname(savefile) + \
91 "/backup/*.json"))124 "/backup/saveconfig-*json*"))
92125
93 # Save backup if backup dir is empty, or savefile is differnt from recent backup copy126 # Save backup if backup dir is empty, or savefile is differnt from recent backup copy
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):
95 try:128 try:
96 shutil.copy(savefile, backupfile)129 with open(savefile, 'rb') as f_in, gzip.open(backupfile, 'wb') as f_out:
97130 shutil.copyfileobj(f_in, f_out)
131 f_out.flush()
98 except IOError as ioe:132 except IOError as ioe:
99 backup_error = ioe.strerror or "Unknown error"133 backup_error = ioe.strerror or "Unknown error"
100134
@@ -139,7 +173,8 @@ class UIRoot(UINode):
139173
140 self.shell.log.info("Configuration saved to %s" % savefile)174 self.shell.log.info("Configuration saved to %s" % savefile)
141175
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,
177 target=None, storage_object=None):
143 '''178 '''
144 Restores configuration from a file.179 Restores configuration from a file.
145 '''180 '''
@@ -151,7 +186,10 @@ class UIRoot(UINode):
151 self.shell.log.info("Restore file %s not found" % savefile)186 self.shell.log.info("Restore file %s not found" % savefile)
152 return187 return
153188
154 errors = self.rtsroot.restore_from_file(savefile, clear_existing)189 target = self.ui_eval_param(target, 'string', None)
190 storage_object = self.ui_eval_param(storage_object, 'string', None)
191 errors = self.rtsroot.restore_from_file(savefile, clear_existing,
192 target, storage_object)
155193
156 self.refresh()194 self.refresh()
157195
@@ -202,15 +240,15 @@ class UIRoot(UINode):
202 PARAMETERS240 PARAMETERS
203 ==========241 ==========
204242
205 I{action}243 action
206 ---------
207 The I{action} is one of:
208 - B{list} gives a short session list
209 - B{detail} gives a detailed list
210
211 I{sid}
212 ------244 ------
213 You can specify an I{sid} to only list this one,245 The action is one of:
246 - `list`` gives a short session list
247 - `detail` gives a detailed list
248
249 sid
250 ---
251 You can specify an "sid" to only list this one,
214 with or without details.252 with or without details.
215253
216 SEE ALSO254 SEE ALSO
@@ -283,4 +321,3 @@ class UIRoot(UINode):
283 indent_print("(no open sessions)", base_steps)321 indent_print("(no open sessions)", base_steps)
284 else:322 else:
285 raise ExecutionError("no session found with sid %i" % int(sid))323 raise ExecutionError("no session found with sid %i" % int(sid))
286
diff --git a/targetcli/ui_target.py b/targetcli/ui_target.py
index 6895b38..e8ba6c6 100644
--- a/targetcli/ui_target.py
+++ b/targetcli/ui_target.py
@@ -34,7 +34,8 @@ from .ui_backstore import complete_path
34from .ui_node import UINode, UIRTSLibNode34from .ui_node import UINode, UIRTSLibNode
3535
36auth_params = ('userid', 'password', 'mutual_userid', 'mutual_password')36auth_params = ('userid', 'password', 'mutual_userid', 'mutual_password')
37discovery_params = auth_params + ("enable",)37int_params = ('enable',)
38discovery_params = auth_params + int_params
3839
39class UIFabricModule(UIRTSLibNode):40class UIFabricModule(UIRTSLibNode):
40 '''41 '''
@@ -47,8 +48,12 @@ class UIFabricModule(UIRTSLibNode):
47 self.refresh()48 self.refresh()
48 if self.rtsnode.has_feature('discovery_auth'):49 if self.rtsnode.has_feature('discovery_auth'):
49 for param in discovery_params:50 for param in discovery_params:
50 self.define_config_group_param('discovery_auth',51 if param in int_params:
51 param, 'string')52 self.define_config_group_param('discovery_auth',
53 param, 'number')
54 else:
55 self.define_config_group_param('discovery_auth',
56 param, 'string')
52 self.refresh()57 self.refresh()
5358
54 # Support late params59 # Support late params
@@ -167,18 +172,18 @@ class UIFabricModule(UIRTSLibNode):
167172
168 def ui_command_create(self, wwn=None):173 def ui_command_create(self, wwn=None):
169 '''174 '''
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)
171 supported by the fabric module. If the I{wwn} is ommited, then a176 supported by the fabric module. If "wwn" is omitted, then a
172 target will be created using either a randomly generated WWN of the177 target will be created using either a randomly generated WWN of the
173 proper type, or the first unused WWN in the list of possible WWNs if178 proper type, or the first unused WWN in the list of possible WWNs if
174 one is available. If WWNs are constrained to a list (i.e. for hardware179 one is available. If WWNs are constrained to a list (i.e. for hardware
175 targets addresses) and all WWNs are in use, the target creation will180 targets addresses) and all WWNs are in use, the target creation will
176 fail. Use the B{info} command to get more information abour WWN type181 fail. Use the `info` command to get more information abour WWN type
177 and possible values.182 and possible values.
178183
179 SEE ALSO184 SEE ALSO
180 ========185 ========
181 B{info}186 info
182 '''187 '''
183 self.assert_root()188 self.assert_root()
184189
@@ -223,12 +228,12 @@ class UIFabricModule(UIRTSLibNode):
223228
224 def ui_command_delete(self, wwn):229 def ui_command_delete(self, wwn):
225 '''230 '''
226 Recursively deletes the target with the specified I{wwn}, and all231 Recursively deletes the target with the specified wwn, and all
227 objects hanging under it.232 objects hanging under it.
228233
229 SEE ALSO234 SEE ALSO
230 ========235 ========
231 B{create}236 create
232 '''237 '''
233 self.assert_root()238 self.assert_root()
234 target = Target(self.rtsnode, wwn, mode='lookup')239 target = Target(self.rtsnode, wwn, mode='lookup')
@@ -262,7 +267,7 @@ class UIFabricModule(UIRTSLibNode):
262 def ui_command_info(self):267 def ui_command_info(self):
263 '''268 '''
264 Displays information about the fabric module, notably the supported269 Displays information about the fabric module, notably the supported
265 transports(s) and accepted B{wwn} format(s), as long as supported270 transports(s) and accepted wwn format(s), along with supported
266 features.271 features.
267 '''272 '''
268 fabric = self.rtsnode273 fabric = self.rtsnode
@@ -308,13 +313,13 @@ class UIMultiTPGTarget(UIRTSLibNode):
308 def ui_command_create(self, tag=None):313 def ui_command_create(self, tag=None):
309 '''314 '''
310 Creates a new Target Portal Group within the target. The315 Creates a new Target Portal Group within the target. The
311 I{tag} must be a positive integer value, optionally prefaced316 tag must be a positive integer value, optionally prefaced
312 by 'tpg'. If omitted, the next available Target Portal Group317 by 'tpg'. If omitted, the next available Target Portal Group
313 Tag (TPGT) will be used.318 Tag (TPGT) will be used.
314319
315 SEE ALSO320 SEE ALSO
316 ========321 ========
317 B{delete}322 delete
318 '''323 '''
319 self.assert_root()324 self.assert_root()
320325
@@ -351,12 +356,12 @@ class UIMultiTPGTarget(UIRTSLibNode):
351356
352 def ui_command_delete(self, tag):357 def ui_command_delete(self, tag):
353 '''358 '''
354 Deletes the Target Portal Group with TPGT I{tag} from the target. The359 Deletes the Target Portal Group with TPGT "tag" from the target. The
355 I{tag} must be a positive integer matching an existing TPGT.360 tag must be a positive integer matching an existing TPGT.
356361
357 SEE ALSO362 SEE ALSO
358 ========363 ========
359 B{create}364 create
360 '''365 '''
361 self.assert_root()366 self.assert_root()
362 if tag.startswith("tpg"):367 if tag.startswith("tpg"):
@@ -384,7 +389,7 @@ class UIMultiTPGTarget(UIRTSLibNode):
384 @rtype: list of str389 @rtype: list of str
385 '''390 '''
386 if current_param == 'tag':391 if current_param == 'tag':
387 tags = [child.name[4:] for child in self.children]392 tags = [child.name[3:] for child in self.children]
388 completions = [tag for tag in tags if tag.startswith(text)]393 completions = [tag for tag in tags if tag.startswith(text)]
389 else:394 else:
390 completions = []395 completions = []
@@ -515,7 +520,7 @@ class UITPG(UIRTSLibNode):
515520
516 SEE ALSO521 SEE ALSO
517 ========522 ========
518 B{disable status}523 disable status
519 '''524 '''
520 self.assert_root()525 self.assert_root()
521 if self.rtsnode.enable:526 if self.rtsnode.enable:
@@ -533,7 +538,7 @@ class UITPG(UIRTSLibNode):
533538
534 SEE ALSO539 SEE ALSO
535 ========540 ========
536 B{enable status}541 enable status
537 '''542 '''
538 self.assert_root()543 self.assert_root()
539 if self.rtsnode.enable:544 if self.rtsnode.enable:
@@ -582,18 +587,19 @@ class UINodeACLs(UINode):
582587
583 def ui_command_create(self, wwn, add_mapped_luns=None):588 def ui_command_create(self, wwn, add_mapped_luns=None):
584 '''589 '''
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.
586 The node's I{wwn} must match the expected WWN Type of the target's591 The node's wwn must match the expected WWN Type of the target's
587 fabric module.592 fabric module.
588593
589 If I{add_mapped_luns} is omitted, the global parameter594 "add_mapped_luns" can be "true" of "false". If true, then
590 B{auto_add_mapped_luns} will be used, else B{true} or B{false} are595 after creating the ACL, mapped LUNs will be automatically
591 accepted. If B{true}, then after creating the ACL, mapped LUNs will be596 created for all existing LUNs. If the parameter is omitted,
592 automatically created for all existing LUNs.597 the global parameter "auto_add_mapped_luns" is used.
593598
594 SEE ALSO599 SEE ALSO
595 ========600 ========
596 B{delete}601 delete
602
597 '''603 '''
598 self.assert_root()604 self.assert_root()
599605
@@ -614,11 +620,11 @@ class UINodeACLs(UINode):
614620
615 def ui_command_delete(self, wwn):621 def ui_command_delete(self, wwn):
616 '''622 '''
617 Deletes the Node ACL with the specified I{wwn}.623 Deletes the Node ACL with the specified wwn.
618624
619 SEE ALSO625 SEE ALSO
620 ========626 ========
621 B{create}627 create
622 '''628 '''
623 self.assert_root()629 self.assert_root()
624 node_acl = NodeACL(self.tpg, wwn, mode='lookup')630 node_acl = NodeACL(self.tpg, wwn, mode='lookup')
@@ -870,14 +876,14 @@ class UINodeACL(UIRTSLibNode):
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):
871 '''877 '''
872 Creates a mapping to one of the TPG LUNs for the initiator referenced878 Creates a mapping to one of the TPG LUNs for the initiator referenced
873 by the ACL. The provided I{tpg_lun_or_backstore} will appear to that879 by the ACL. The provided "tpg_lun_or_backstore" will appear to that
874 initiator as LUN I{mapped_lun}. If the I{write_protect} flag is set to880 initiator as LUN "mapped_lun". If the "write_protect" flag is set to
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.
876882
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,
878 in which case the TPG LUN will be created for that backstore before884 in which case the TPG LUN will be created for that backstore before
879 mapping the LUN to the initiator. If a TPG LUN for the backstore already885 mapping the LUN to the initiator. If a TPG LUN for the backstore already
880 exists, the Mapped LUN will map to that TPG LUN.886 exists, the mapped LUN will map to that TPG LUN.
881887
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,
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,
@@ -885,7 +891,7 @@ class UINodeACL(UIRTSLibNode):
885891
886 SEE ALSO892 SEE ALSO
887 ========893 ========
888 B{delete}894 delete
889 '''895 '''
890 self.assert_root()896 self.assert_root()
891 try:897 try:
@@ -963,11 +969,11 @@ class UINodeACL(UIRTSLibNode):
963969
964 def ui_command_delete(self, mapped_lun):970 def ui_command_delete(self, mapped_lun):
965 '''971 '''
966 Deletes the specified I{mapped_lun}.972 Deletes the specified mapped LUN.
967973
968 SEE ALSO974 SEE ALSO
969 ========975 ========
970 B{create}976 create
971 '''977 '''
972 self.assert_root()978 self.assert_root()
973 for na in self.rtsnodes:979 for na in self.rtsnodes:
@@ -1086,25 +1092,25 @@ class UILUNs(UINode):
1086 add_mapped_luns=None):1092 add_mapped_luns=None):
1087 '''1093 '''
1088 Creates a new LUN in the Target Portal Group, attached to a storage1094 Creates a new LUN in the Target Portal Group, attached to a storage
1089 object. If the I{lun} parameter is omitted, the first available LUN in1095 object. If the "lun" parameter is omitted, the first available LUN in
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.
1091 Alternatively, the syntax I{lunX} where I{X} is a positive number is1097 Alternatively, the syntax "lunX" where "X" is a positive number is
1092 also accepted.1098 also accepted.
10931099
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,
1095 i.e. B{/backstore/pscsi0/mydisk} to reference the B{mydisk} storage1101 i.e. "/backstore/pscsi0/mydisk" to reference the "mydisk" storage
1096 object of the virtual HBA B{pscsi0}. It also may be the path to an1102 object of the virtual HBA "pscsi0". It also may be the path to an
1097 existing block device or image file, in which case a storage object1103 existing block device or image file, in which case a storage object
1098 will be created for it first, with default parameters.1104 will be created for it first, with default parameters.
10991105
1100 If I{add_mapped_luns} is omitted, the global parameter1106 "add_mapped_luns" can be "true" of "false". If true, then
1101 B{auto_add_mapped_luns} will be used, else B{true} or B{false} are1107 after creating the ACL, mapped LUNs will be automatically
1102 accepted. If B{true}, then after creating the LUN, mapped LUNs will be1108 created for all existing LUNs. If the parameter is omitted,
1103 automatically created for all existing node ACLs, mapping the new LUN.1109 the global parameter "auto_add_mapped_luns" is used.
11041110
1105 SEE ALSO1111 SEE ALSO
1106 ========1112 ========
1107 B{delete}1113 delete
1108 '''1114 '''
1109 self.assert_root()1115 self.assert_root()
11101116
@@ -1188,15 +1194,15 @@ class UILUNs(UINode):
11881194
1189 def ui_command_delete(self, lun):1195 def ui_command_delete(self, lun):
1190 '''1196 '''
1191 Deletes the supplied LUN from the Target Portal Group. The I{lun} must1197 Deletes the supplied LUN from the Target Portal Group. "lun" must
1192 be a positive number matching an existing LUN.1198 be a positive number matching an existing LUN.
11931199
1194 Alternatively, the syntax I{lunX} where I{X} is a positive number is1200 Alternatively, the syntax "lunX" where "X" is a positive number is
1195 also accepted.1201 also accepted.
11961202
1197 SEE ALSO1203 SEE ALSO
1198 ========1204 ========
1199 B{create}1205 create
1200 '''1206 '''
1201 self.assert_root()1207 self.assert_root()
1202 if lun.lower().startswith("lun"):1208 if lun.lower().startswith("lun"):
@@ -1303,9 +1309,9 @@ class UIPortals(UINode):
13031309
1304 def ui_command_create(self, ip_address=None, ip_port=None):1310 def ui_command_create(self, ip_address=None, ip_port=None):
1305 '''1311 '''
1306 Creates a Network Portal with specified I{ip_address} and1312 Creates a Network Portal with the specified IP address and
1307 I{ip_port}. If I{ip_port} is omitted, the default port for1313 port. If the port is omitted, the default port for
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,
1309 INADDR_ANY (0.0.0.0) will be used.1315 INADDR_ANY (0.0.0.0) will be used.
13101316
1311 Choosing IN6ADDR_ANY (::0) will listen on all IPv6 interfaces1317 Choosing IN6ADDR_ANY (::0) will listen on all IPv6 interfaces
@@ -1317,7 +1323,7 @@ class UIPortals(UINode):
13171323
1318 SEE ALSO1324 SEE ALSO
1319 ========1325 ========
1320 B{delete}1326 delete
1321 '''1327 '''
1322 self.assert_root()1328 self.assert_root()
13231329
@@ -1379,11 +1385,11 @@ class UIPortals(UINode):
13791385
1380 def ui_command_delete(self, ip_address, ip_port):1386 def ui_command_delete(self, ip_address, ip_port):
1381 '''1387 '''
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.
13831389
1384 SEE ALSO1390 SEE ALSO
1385 ========1391 ========
1386 B{create}1392 create
1387 '''1393 '''
1388 self.assert_root()1394 self.assert_root()
1389 portal = NetworkPortal(self.tpg, self._canonicalize_ip(ip_address),1395 portal = NetworkPortal(self.tpg, self._canonicalize_ip(ip_address),
diff --git a/targetcli/version.py b/targetcli/version.py
index 5e1527f..d29ae93 100644
--- a/targetcli/version.py
+++ b/targetcli/version.py
@@ -15,4 +15,4 @@ License for the specific language governing permissions and limitations
15under the License.15under the License.
16'''16'''
1717
18__version__ = '2.1.fb49'18__version__ = '2.1.51'
diff --git a/targetclid.8 b/targetclid.8
19new file mode 10064419new file mode 100644
index 0000000..a783091
--- /dev/null
+++ b/targetclid.8
@@ -0,0 +1,77 @@
1.TH targetclid 8
2.SH NAME
3.B targetclid
4\- daemon component for targetcli
5.SH DESCRIPTION
6.B targetclid
7is the daemon component of targetcli, which will help retain state of various
8configfs object in memory, hence any new request/command can directly use the
9in memory objects instead of reconstructing them by parsing through the entire
10configfs files again and again for each and every single command. This will
11greatly improve the overall execution time taken by targetcli commands at scale.
12
13.SH USAGE
14.B targetclid [cmd]
15.br
16.B "--help"
17for additional usage information.
18.br
19.B "--version"
20for version information.
21.SH QUICKSTART & EXAMPLES
22.TP
23To start using the daemon, one need to enable targetclid socket,
24.br
25$ systemctl enable targetclid.socket
26.TP
27If you would like to use the daemonized approach as default method then,
28.br
29$ targetcli set global auto_use_daemon=true
30.br
31$ targetcli ls
32.TP
33You can use batch mode for sending multiple commands in one go,
34.br
35$ targetcli <hit-enter>
36.br
37targetcli shell version 2.1.50
38.br
39Entering targetcli batch mode for daemonized approach.
40.br
41Enter multiple commands separated by newline and type 'exit' to run them all in one go.
42.br
43/> ls
44.br
45/> pwd
46.br
47/> get global loglevel_file
48.br
49/> exit
50.br
51.TP
52You can set preference to stop using daemonized mode even when the daemon is not running,
53.br
54$ targetcli --disable-daemon
55.SH FILES
56.B /etc/target/saveconfig.json
57.br
58.B /etc/target/backup/*
59.br
60.B /var/run/targetclid.sock
61.br
62.B /var/run/targetclid.pid
63.SH ENVIRONMENT
64.SS TARGETCLI_HOME
65If set, this variable points to a directory that should be used instead of ~/.targetcli
66.SH SEE ALSO
67.BR targetcli (8),
68.BR targetctl (8),
69.BR tcmu-runner (8)
70.SH AUTHOR
71Written by Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
72.br
73Man page written by Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
74.SH REPORTING BUGS
75Report bugs via <targetcli-fb-devel@lists.fedorahosted.org>
76.br
77or <https://github.com/open-iscsi/targetcli-fb/issues>

Subscribers

People subscribed via source and target branches