-#!@PYTHON_EXECUTABLE@
+#!@Python3_EXECUTABLE@
# -*- mode:python -*-
# vim: ts=4 sw=4 smarttab expandtab
#
Foundation. See file COPYING.
"""
-from __future__ import print_function
from time import sleep
-import codecs
import grp
import os
import pwd
+import re
+import shutil
+import stat
import sys
import time
import platform
+from typing import Dict, List, Sequence, Tuple
+
try:
input = raw_input
except NameError:
DEVMODEMSG = '*** DEVELOPER MODE: setting PATH, PYTHONPATH and LD_LIBRARY_PATH ***'
-def respawn_in_path(lib_path, pybind_path, pythonlib_path, asan_lib_path):
- execv_cmd = []
- if 'CEPH_DBG' in os.environ:
- execv_cmd += ['@PYTHON_EXECUTABLE@', '-mpdb']
+def add_to_ld_path(path_name, path):
+ paths = re.split('[ :]', os.environ.get(path_name, ''))
+ if path in paths:
+ return 0
+ else:
+ paths.insert(0, path)
+ os.environ[path_name] = ':'.join(paths)
+ return 1
+
+def respawn_in_path(lib_path, pybind_path, pythonlib_path, asan_lib_path):
if platform.system() == "Darwin":
lib_path_var = "DYLD_LIBRARY_PATH"
else:
lib_path_var = "LD_LIBRARY_PATH"
- execv_cmd += sys.argv
+ ld_paths_changed = 0
+ preload_libcxx = os.environ.get('CEPH_PRELOAD_LIBCXX')
+ if preload_libcxx:
+ ld_paths_changed += add_to_ld_path('LD_PRELOAD', preload_libcxx)
if asan_lib_path:
- os.environ['LD_PRELOAD'] = asan_lib_path
- if lib_path_var in os.environ:
- if lib_path not in os.environ[lib_path_var]:
- os.environ[lib_path_var] += ':' + lib_path
- if "CEPH_DEV" not in os.environ:
- print(DEVMODEMSG, file=sys.stderr)
- os.execvp(execv_cmd[0], execv_cmd)
- else:
- os.environ[lib_path_var] = lib_path
+ ld_paths_changed += add_to_ld_path('LD_PRELOAD', asan_lib_path)
+ ld_paths_changed += add_to_ld_path(lib_path_var, lib_path)
+ if ld_paths_changed > 0:
if "CEPH_DEV" not in os.environ:
print(DEVMODEMSG, file=sys.stderr)
+ execv_cmd = []
+ if 'CEPH_DBG' in os.environ:
+ execv_cmd += ['@Python3_EXECUTABLE@', '-mpdb']
+ execv_cmd += sys.argv
os.execvp(execv_cmd[0], execv_cmd)
- sys.path.insert(0, os.path.join(MYDIR, pybind_path))
- sys.path.insert(0, os.path.join(MYDIR, pythonlib_path))
+ else:
+ sys.path.insert(0, pybind_path)
+ sys.path.insert(0, pythonlib_path)
def get_pythonlib_dir():
verbose = False
cluster_handle = None
-# Always use Unicode (UTF-8) for stdout
-if sys.version_info[0] >= 3:
- raw_stdout = sys.stdout.buffer
- raw_stderr = sys.stderr.buffer
-else:
- raw_stdout = sys.__stdout__
- raw_stderr = sys.__stderr__
- sys.stdout = codecs.getwriter('utf-8')(raw_stdout)
- sys.stderr = codecs.getwriter('utf-8')(raw_stderr)
-
def raw_write(buf):
sys.stdout.flush()
- raw_stdout.write(rados.cstr(buf, ''))
+ sys.stdout.buffer.write(buf)
def osdids():
d = json.loads(outbuf.decode('utf-8'))
l = []
l.append(d['active_name'])
- for i in d['standbys']:
- l.append(i['name'])
+ # we can only send tell commands to the active mgr
+ #for i in d['standbys']:
+ # l.append(i['name'])
return l
}
-def parse_cmdargs(args=None, target=''):
+def parse_cmdargs(args=None, target='') -> Tuple[argparse.ArgumentParser,
+ argparse.Namespace,
+ List[str]]:
"""
Consume generic arguments from the start of the ``args``
list. Call this first to handle arguments that are not
parser.add_argument('--cluster', help='cluster name')
parser.add_argument('--admin-daemon', dest='admin_socket',
- help='submit admin-socket commands (\"help\" for help')
+ help='submit admin-socket commands (\"help\" for help)')
parser.add_argument('-s', '--status', action='store_true',
help='show cluster status')
parser.add_argument('--watch-error', action='store_true',
help='watch error events')
- parser.add_argument('--watch-channel', dest="watch_channel",
- choices=['cluster', 'audit', '*'],
- help="which log channel to follow " \
- "when using -w/--watch. One of ['cluster', 'audit', '*']",
- default='cluster')
+ parser.add_argument('-W', '--watch-channel', dest="watch_channel",
+ help="watch live cluster changes on a specific channel "
+ "(e.g., cluster, audit, cephadm, or '*' for all)")
parser.add_argument('--version', '-v', action="store_true", help="display version")
parser.add_argument('--verbose', action="store_true", help="make verbose")
help="make less verbose")
parser.add_argument('-f', '--format', choices=['json', 'json-pretty',
- 'xml', 'xml-pretty', 'plain'], dest='output_format')
+ 'xml', 'xml-pretty', 'plain', 'yaml'], dest='output_format')
parser.add_argument('--connect-timeout', dest='cluster_timeout',
type=int,
""", file=sys.stdout)
-def do_extended_help(parser, args, target, partial):
+def do_extended_help(parser, args, target, partial) -> int:
def help_for_sigs(sigs, partial=None):
- sys.stdout.write(format_help(parse_json_funcsigs(sigs, 'cli'),
- partial=partial))
+ try:
+ while True:
+ out = format_help(parse_json_funcsigs(sigs, 'cli'),
+ partial=partial)
+ if not out and partial:
+ # shorten partial until we get at least one matching command prefix
+ partial = ' '.join(partial.split()[:-1])
+ continue
+ sys.stdout.write(out)
+ break
+ except BrokenPipeError:
+ pass
def help_for_target(target, partial=None):
# wait for osdmap because we know this is sent after the mgrmap
prefix='get_command_descriptions',
timeout=10)
if ret:
- if ret == -errno.EPERM and target[0] in ('osd', 'mds'):
+ if (ret == -errno.EPERM or ret == -errno.EACCES) and target[0] in ('osd', 'mds'):
print("Permission denied. Check that your user has 'allow *' "
"capabilities for the target daemon type.", file=sys.stderr)
elif ret == -errno.EPERM:
yield result
-def format_help(cmddict, partial=None):
+def format_help(cmddict, partial=None) -> str:
"""
Formats all the cmdsigs and helptexts from cmddict into a sorted-by-
cmdsig 2-column display, with each column wrapped and indented to
return fullusage
-def ceph_conf(parsed_args, field, name):
- args = ['ceph-conf']
+def ceph_conf(parsed_args, field, name, pid=None):
+ cmd = 'ceph-conf'
+ bindir = os.path.dirname(__file__)
+ if shutil.which(cmd):
+ args = [cmd]
+ elif shutil.which(cmd, path=bindir):
+ args = [os.path.join(bindir, cmd)]
+ else:
+ raise RuntimeError('"ceph-conf" not found')
if name:
args.extend(['--name', name])
+ if pid:
+ args.extend(['--pid', pid])
# add any args in GLOBAL_ARGS
for key, val in GLOBAL_ARGS.items():
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
outdata, errdata = p.communicate()
- if len(errdata):
+ if p.returncode != 0:
raise RuntimeError('unable to get conf option %s for %s: %s' % (field, name, errdata))
return outdata.rstrip()
+
PROMPT = 'ceph> '
if sys.stdin.isatty():
next_header_print = Termsize().rows - 3
next_header_print -= 1
ret, outbuf, outs = json_command(cluster_handle, target=target,
- argdict=valid_dict, inbuf=inbuf)
+ argdict=valid_dict, inbuf=inbuf, verbose=verbose)
if valid_dict.get('poll', False):
valid_dict['print_header'] = False
if not valid_dict.get('poll', False):
sleep(parsed_args.period)
except KeyboardInterrupt:
print('Interrupted')
- return ret, '', ''
+ return errno.EINTR, '', ''
if ret == errno.ETIMEDOUT:
ret = -ret
if not outs:
return ret, outbuf, outs
-def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
+def new_style_command(parsed_args,
+ cmdargs,
+ target,
+ sigdict,
+ inbuf, verbose) -> Tuple[int, bytes, str]:
"""
Do new-style command dance.
target: daemon to receive command: mon (any) or osd.N
sig = cmd['sig']
print('{0}: {1}'.format(cmdtag, concise_sig(sig)))
- if True:
- if cmdargs:
- # Non interactive mode
- ret, outbuf, outs = do_command(parsed_args, target, cmdargs, sigdict, inbuf, verbose)
- else:
- # Interactive mode (ceph cli)
- if sys.stdin.isatty():
- # do the command-interpreter looping
- # for input to do readline cmd editing
- import readline # noqa
+ if cmdargs:
+ # Non interactive mode
+ ret, outbuf, outs = do_command(parsed_args, target, cmdargs, sigdict, inbuf, verbose)
+ else:
+ # Interactive mode (ceph cli)
+ if sys.stdin.isatty():
+ # do the command-interpreter looping
+ # for input to do readline cmd editing
+ import readline # noqa
- while True:
- try:
- interactive_input = read_input()
- except EOFError:
- # leave user an uncluttered prompt
- return 0, '\n', ''
- if interactive_input is None:
- return 0, '', ''
- cmdargs = parse_cmdargs(shlex.split(interactive_input))[2]
- try:
- target = find_cmd_target(cmdargs)
- except Exception as e:
- print('error handling command target: {0}'.format(e),
- file=sys.stderr)
- continue
- if len(cmdargs) and cmdargs[0] == 'tell':
- print('Can not use \'tell\' in interactive mode.',
- file=sys.stderr)
- continue
- ret, outbuf, outs = do_command(parsed_args, target, cmdargs,
- sigdict, inbuf, verbose)
- if ret < 0:
- ret = -ret
- errstr = errno.errorcode.get(ret, 'Unknown')
- print(u'Error {0}: {1}'.format(errstr, outs), file=sys.stderr)
- else:
- if outs:
- print(outs, file=sys.stderr)
- if outbuf:
- print(outbuf)
+ while True:
+ try:
+ interactive_input = read_input()
+ except EOFError:
+ # leave user an uncluttered prompt
+ return 0, b'\n', ''
+ if interactive_input is None:
+ return 0, b'', ''
+ cmdargs = parse_cmdargs(shlex.split(interactive_input))[2]
+ try:
+ target = find_cmd_target(cmdargs)
+ except Exception as e:
+ print('error handling command target: {0}'.format(e),
+ file=sys.stderr)
+ continue
+ if len(cmdargs) and cmdargs[0] == 'tell':
+ print('Can not use \'tell\' in interactive mode.',
+ file=sys.stderr)
+ continue
+ ret, outbuf, outs = do_command(parsed_args, target, cmdargs,
+ sigdict, inbuf, verbose)
+ if ret < 0:
+ ret = -ret
+ errstr = errno.errorcode.get(ret, 'Unknown')
+ print('Error {0}: {1}'.format(errstr, outs), file=sys.stderr)
+ else:
+ if outs:
+ print(outs, file=sys.stderr)
+ if outbuf:
+ print(outbuf.decode('utf-8'))
return ret, outbuf, outs
match_count = 0
comps = []
for cmdtag, cmd in sigdict.items():
+ flags = cmd.get('flags', 0)
+ if flags & (Flag.OBSOLETE | Flag.HIDDEN):
+ continue
sig = cmd['sig']
j = 0
# iterate over all arguments, except last one
return 0
+def get_admin_socket(parsed_args, name):
+ path = ceph_conf(parsed_args, 'admin_socket', name)
+ try:
+ if stat.S_ISSOCK(os.stat(path).st_mode):
+ return path
+ except OSError:
+ pass
+ # try harder, probably the "name" option is in the form of
+ # "${name}.${pid}"?
+ parts = name.rsplit('.', 1)
+ if len(parts) > 1 and parts[-1].isnumeric():
+ name, pid = parts
+ return ceph_conf(parsed_args, 'admin_socket', name, pid)
+ else:
+ return path
+
+
def maybe_daemon_command(parsed_args, childargs):
"""
Check if --admin-socket, daemon, or daemonperf command
else:
# try resolve daemon name
try:
- sockpath = ceph_conf(parsed_args, 'admin_socket',
- childargs[1])
+ sockpath = get_admin_socket(parsed_args, childargs[1])
except Exception as e:
print('Can\'t get admin socket path: ' + str(e), file=sys.stderr)
return True, errno.EINVAL
return False
-def daemonperf(childargs, sockpath):
+def daemonperf(childargs: Sequence[str], sockpath: str):
"""
Handle daemonperf command; returns errno or 0
return 0
-def get_scrub_timestamps(childargs):
+
+def get_scrub_timestamps(childargs: Sequence[str]) -> Dict[str,
+ Tuple[str, str]]:
last_scrub_stamp = "last_" + childargs[1].replace('-', '_') + "_stamp"
results = dict()
scruball = False
results[stat['pgid']] = scrub_tuple
return results
+
def check_scrub_stamps(waitdata, currdata):
for pg in waitdata.keys():
# Try to handle the case where a pg may not exist in current results
return False
return True
+
def waitscrub(childargs, waitdata):
- print(u'Waiting for {0} to complete...'.format(childargs[1]), file=sys.stdout)
+ print('Waiting for {0} to complete...'.format(childargs[1]), file=sys.stdout)
currdata = get_scrub_timestamps(childargs)
while not check_scrub_stamps(waitdata, currdata):
time.sleep(3)
currdata = get_scrub_timestamps(childargs)
- print(u'{0} completed'.format(childargs[1]), file=sys.stdout)
+ print('{0} completed'.format(childargs[1]), file=sys.stdout)
+
-def wait(childargs, waitdata):
+def wait(childargs: Sequence[str], waitdata):
if childargs[1] in ['scrub', 'deep-scrub']:
waitscrub(childargs, waitdata)
CEPH_RELEASE_TYPE)) # noqa
return 0
+ # --watch-channel|-W implies -w
+ if parsed_args.watch_channel:
+ parsed_args.watch = True
+ elif parsed_args.watch and not parsed_args.watch_channel:
+ parsed_args.watch_channel = 'cluster'
+
global verbose
verbose = parsed_args.verbose
if parsed_args.client_name:
name = parsed_args.client_name
- # default '' means default conf search
- conffile = ''
+ conffile = rados.Rados.DEFAULT_CONF_FILES
if parsed_args.cephconf:
conffile = parsed_args.cephconf
# For now, --admin-daemon is handled as usual. Try it
if injectargs and '--' in injectargs:
injectargs.remove('--')
- # special deprecation warning for 'ceph <type> tell'
- # someday 'mds' will be here too
- if (len(childargs) >= 2 and
- childargs[0] in ['mon', 'osd'] and
- childargs[1] == 'tell'):
- print('"{0} tell" is deprecated; try "tell {0}.<id> <command> [options...]" instead (id can be "*") '.format(childargs[0]),
- file=sys.stderr)
- return 1
-
block = False
waitdata = dict()
if parsed_args.block:
return 1
if parsed_args.help:
- hdr('Monitor commands:')
+ target = None
+ if len(childargs) >= 2 and childargs[0] == 'tell':
+ target = childargs[1].split('.', 1)
+ if not validate_target(target):
+ print('target {0} doesn\'t exist; please pass correct target to tell command (e.g., mon.a, osd.1, mds.a, mgr)'.format(childargs[1]), file=sys.stderr)
+ return 1
+ childargs = childargs[2:]
+ hdr('Tell %s commands:' % target[0])
+ else:
+ hdr('Monitor commands:')
+ target = ('mon', '')
if verbose:
print('[Contacting monitor, timeout after %d seconds]' % timeout)
- return do_extended_help(parser, childargs, ('mon', ''), ' '.join(childargs))
+ return do_extended_help(parser, childargs, target, ' '.join(childargs))
# implement "tell service.id help"
if len(childargs) >= 3 and childargs[0] == 'tell' and childargs[2] == 'help':
- target = childargs[1].split('.')
+ target = childargs[1].split('.', 1)
if validate_target(target):
+ hdr('Tell %s commands' % target[0])
return do_extended_help(parser, childargs, target, None)
else:
print('target {0} doesn\'t exists, please pass correct target to tell command, such as mon.a/'
'osd.1/mds.a/mgr'.format(childargs[1]), file=sys.stderr)
return 1
+
# implement -w/--watch_*
# This is ugly, but Namespace() isn't quite rich enough.
level = ''
# an awfully simple callback
def watch_cb(arg, line, channel, name, who, stamp_sec, stamp_nsec, seq, level, msg):
# Filter on channel
- if sys.version_info[0] >= 3:
- channel = channel.decode('utf-8')
- if (channel == parsed_args.watch_channel or \
- parsed_args.watch_channel == "*"):
+ channel = channel.decode('utf-8')
+ if parsed_args.watch_channel in (channel, '*'):
print(line.decode('utf-8'))
sys.stdout.flush()
if parsed_args.input_file:
try:
if parsed_args.input_file == '-':
- inbuf = sys.stdin.read()
+ inbuf = sys.stdin.buffer.read()
else:
with open(parsed_args.input_file, 'rb') as f:
inbuf = f.read()
if parsed_args.output_file:
try:
if parsed_args.output_file == '-':
- outf = sys.stdout
+ outf = sys.stdout.buffer
else:
outf = open(parsed_args.output_file, 'wb')
except Exception as e:
errno.errorcode.get(ret, 'Unknown'), outs),
file=sys.stderr)
- if ret < 0:
- ret = -ret
- errstr = errno.errorcode.get(ret, 'Unknown')
- print(u'Error {0}: {1}'.format(errstr, outs), file=sys.stderr)
- if len(targets) > 1:
- final_ret = ret
- else:
- return ret
-
- if outs:
- print(prefix + outs, file=sys.stderr)
-
sys.stdout.flush()
if parsed_args.output_file:
except IOError as e:
if e.errno != errno.EPIPE:
raise e
+ final_e = None
+ try:
+ sys.stdout.flush()
+ except IOError as e:
+ if e.errno != errno.EPIPE:
+ final_e = e
- sys.stdout.flush()
+ if ret < 0:
+ ret = -ret
+ errstr = errno.errorcode.get(ret, 'Unknown')
+ print('Error {0}: {1}'.format(errstr, outs), file=sys.stderr)
+ final_ret = ret
+ elif outs:
+ print(prefix + outs, file=sys.stderr)
+
+ if final_e:
+ raise final_e
# Block until command completion (currently scrub and deep_scrub only)
if block:
return 0
if __name__ == '__main__':
- retval = main()
- # shutdown explicitly; Rados() does not
- if cluster_handle:
- run_in_thread(cluster_handle.shutdown)
- sys.exit(retval)
+ try:
+ retval = main()
+ # shutdown explicitly; Rados() does not
+ if retval == 0 and cluster_handle:
+ run_in_thread(cluster_handle.shutdown)
+ except KeyboardInterrupt:
+ print('Interrupted')
+ retval = errno.EINTR
+
+ if retval:
+ # flush explicitly because we aren't exiting in the usual way
+ sys.stdout.flush()
+ sys.stderr.flush()
+ os._exit(retval)
+ else:
+ sys.exit(retval)