diff options
| author | Prasanna Kumar Kalever <prasanna.kalever@redhat.com> | 2019-04-10 14:09:58 +0530 |
|---|---|---|
| committer | Prasanna Kumar Kalever <prasanna.kalever@redhat.com> | 2019-06-11 15:17:40 +0530 |
| commit | 7917ef55f6279fa28519d2160c4ec49453c275ec (patch) | |
| tree | a7cb45ae99afe8dad93ed204ecc24b7efadbb87c /scripts | |
| parent | e84b89ed709f4f291fbdd8c42b48e2ca187a57ba (diff) | |
| download | targetcli-7917ef55f6279fa28519d2160c4ec49453c275ec.tar.gz | |
targetclid: add daemonize component for targetcli
Problem:
-------
Overall creation time of a block using targetcli is raising linearly as the
block count increase.
This is because of the recurring issue involving refresh(reload) at
multiple objects/places, as the LIO's configfs is deeply nested.
Earlier discussion of the problem statement with stats and graphs about delays:
http://bit.ly/targetcli-create-delay
Solution:
--------
Introduce a daemon component for targetcli[d] which will retain state of
Configshell object in memory, so that any new requests can directly use it,
instead of loading the storageObjects/targetObjects again.
Details about "how to use it ?":
-------------------------------
$ systemctl start targetclid
$ systemctl status targetclid
● targetclid.service - Targetcli daemon
Loaded: loaded (/usr/lib/systemd/system/targetclid.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2019-04-10 12:19:51 IST; 2h 17min ago
Main PID: 3950 (targetclid)
Tasks: 3 (limit: 4915)
CGroup: /system.slice/targetclid.service
└─3950 /usr/bin/python /usr/bin/targetclid
Apr 10 12:19:51 localhost.localdomain systemd[1]: Started Targetcli daemon.
$ targetcli help
Usage: /usr/bin/targetcli [--version|--help|CMD|--tcp]
--version Print version
--help Print this information
CMD Run targetcli shell command and exit
<nothing> Enter configuration shell
--tcp CMD Pass targetcli command to targetclid
--tcp <nothing> Enter multi-line command mode for targetclid
See man page for more information.
One line command usage:
----------------------
$ targetcli --tcp CMD
Eg:
$ targetcli --tcp pwd
/
Multiple line commands usage:
----------------------------
$ targetcli --tcp
CMD1
CMD2
.
.
CMDN
exit
Eg:
$ targetcli --tcp
^Tab
/ backstores/ iscsi/ loopback/ vhost/ xen-pvscsi/ cd
clearconfig exit get help ls pwd refresh
restoreconfig saveconfig set status
pwd
get global logfile
get global auto_save_on_exit
/ saveconfig
exit
output follows:
/
logfile=/var/log/gluster-block/gluster-block-configshell.log
auto_save_on_exit=false
Configuration saved to /etc/target/saveconfig.json
Stats with and without changes:
------------------------------
Running simple 'pwd' command after creating 1000 blocks on a node:
Without this change:
$ time targetcli pwd
/
real 0m8.963s
user 0m7.775s
sys 0m1.103s
with daemonize changes:
$ time targetcli --tcp "pwd"
/
real 0m0.126s
user 0m0.099s
sys 0m0.024s
Thanks to Maurizio for hangingout with me for all the discussions involved.
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/targetcli | 102 |
1 files changed, 94 insertions, 8 deletions
diff --git a/scripts/targetcli b/scripts/targetcli index b042ad9..92d3378 100755 --- a/scripts/targetcli +++ b/scripts/targetcli @@ -24,10 +24,19 @@ from os import getuid, getenv from targetcli import UIRoot from rtslib_fb import RTSLibError from configshell_fb import ConfigShell, ExecutionError -import sys from targetcli import __version__ as targetcli_version +import sys +import socket +import struct +import readline +import six + err = sys.stderr +socket_path = '/var/run/targetclid.sock' +hints = ['/', 'backstores/', 'iscsi/', 'loopback/', 'vhost/', 'xen-pvscsi/', + 'cd', 'pwd', 'ls', 'set', 'get', 'help', 'refresh', 'status', + 'clearconfig', 'restoreconfig', 'saveconfig', 'exit'] class TargetCLI(ConfigShell): default_prefs = {'color_path': 'magenta', @@ -54,11 +63,13 @@ class TargetCLI(ConfigShell): } def usage(): - print("Usage: %s [--version|--help|CMD]" % sys.argv[0], file=err) + print("Usage: %s [--version|--help|CMD|--tcp]" % sys.argv[0], file=err) print(" --version\t\tPrint version", file=err) print(" --help\t\tPrint this information", file=err) print(" CMD\t\t\tRun targetcli shell command and exit", file=err) print(" <nothing>\t\tEnter configuration shell", file=err) + print(" --tcp CMD\t\tPass targetcli command to targetclid", file=err) + print(" --tcp <nothing>\tEnter multi-line command mode for targetclid", file=err) print("See man page for more information.", file=err) sys.exit(-1) @@ -66,6 +77,76 @@ def version(): print("%s version %s" % (sys.argv[0], targetcli_version), file=err) sys.exit(0) +def usage_version(cmd): + if cmd in ("help", "--help", "-h"): + usage() + + if cmd in ("version", "--version", "-v"): + version() + +def completer(text, state): + options = [x for x in hints if x.startswith(text)] + try: + return options[state] + except IndexError: + return None + +def call_daemon(shell, req): + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + except socket.error as err: + shell.con.display(shell.con.render_text(err, 'red')) + sys.exit(1) + + try: + sock.connect(socket_path) + except socket.error as err: + shell.con.display(shell.con.render_text(err, 'red')) + sys.exit(1) + + try: + # send request + sock.sendall(req) + except socket.error as err: + shell.con.display(shell.con.render_text(err, 'red')) + sys.exit(1) + + var = sock.recv(4) # get length of data + sending = struct.unpack('i', var) + amount_expected = sending[0] + amount_received = 0 + + # get the actual data in chunks + while amount_received < amount_expected: + data = sock.recv(1024) + amount_received += len(data) + print(data, end ="") + + sock.send(b'-END@OF@DATA-') + sock.close() + sys.exit(0) + +def get_arguments(): + readline.set_completer(completer) + readline.set_completer_delims('') + + if 'libedit' in readline.__doc__: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab: complete") + + if len(sys.argv[1:]) > 1: + command = " ".join(sys.argv[2:]) + else: + inputs = [] + while True: + command = six.moves.input() + if command.lower() == "exit": + break + inputs.append(command) + command = '%'.join(inputs) # delimit multiple commands with '%' + return command + def main(): ''' Start the targetcli shell. @@ -77,6 +158,17 @@ def main(): shell = TargetCLI(getenv("TARGETCLI_HOME", '~/.targetcli')) + if len(sys.argv) > 1: + usage_version(sys.argv[1]) + if sys.argv[1] in ("tcp", "--tcp", "-t"): + if len(sys.argv) > 2: + usage_version(sys.argv[2]) + args = get_arguments() + if not args: + sys.exit(1) + usage_version(args); + call_daemon(shell, args) + try: root_node = UIRoot(shell, as_root=is_root) root_node.refresh() @@ -87,12 +179,6 @@ def main(): sys.exit(-1) if len(sys.argv) > 1: - if sys.argv[1] in ("--help", "-h"): - usage() - - if sys.argv[1] in ("--version", "-v"): - version() - try: shell.run_cmdline(" ".join(sys.argv[1:])) except Exception as e: |
