]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/ceph.in
update sources to v12.1.0
[ceph.git] / ceph / src / ceph.in
index 89b52cecc5bcb666a2c480a75d85a15d8cefe61a..756cab6448e40bba9558cbbf5eee20d8227bfceb 100755 (executable)
@@ -30,12 +30,15 @@ try:
 except NameError:
     pass
 
-CEPH_GIT_VER="@CEPH_GIT_VER@"
-CEPH_GIT_NICE_VER="@CEPH_GIT_NICE_VER@"
+CEPH_GIT_VER = "@CEPH_GIT_VER@"
+CEPH_GIT_NICE_VER = "@CEPH_GIT_NICE_VER@"
+CEPH_RELEASE = "@CEPH_RELEASE@"
+CEPH_RELEASE_NAME = "@CEPH_RELEASE_NAME@"
+CEPH_RELEASE_TYPE = "@CEPH_RELEASE_TYPE@"
 
 # Flags from src/mon/Monitor.h
-FLAG_NOFORWARD  = (1 << 0)
-FLAG_OBSOLETE   = (1 << 1)
+FLAG_NOFORWARD = (1 << 0)
+FLAG_OBSOLETE = (1 << 1)
 FLAG_DEPRECATED = (1 << 2)
 
 # priorities from src/common/perf_counters.h
@@ -56,6 +59,7 @@ MYPATH = os.path.abspath(__file__)
 MYDIR = os.path.dirname(MYPATH)
 DEVMODEMSG = '*** DEVELOPER MODE: setting PATH, PYTHONPATH and LD_LIBRARY_PATH ***'
 
+
 def respawn_in_path(lib_path, pybind_path, pythonlib_path):
     execv_cmd = ['python']
     if 'CEPH_DBG' in os.environ:
@@ -71,15 +75,18 @@ def respawn_in_path(lib_path, pybind_path, pythonlib_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
-            print(DEVMODEMSG, file=sys.stderr)
+            if "CEPH_DEV" not in os.environ:
+                print(DEVMODEMSG, file=sys.stderr)
             os.execvp(py_binary, execv_cmd + sys.argv)
     else:
         os.environ[lib_path_var] = lib_path
-        print(DEVMODEMSG, file=sys.stderr)
+        if "CEPH_DEV" not in os.environ:
+            print(DEVMODEMSG, file=sys.stderr)
         os.execvp(py_binary, execv_cmd + sys.argv)
     sys.path.insert(0, os.path.join(MYDIR, pybind_path))
     sys.path.insert(0, os.path.join(MYDIR, pythonlib_path))
 
+
 def get_pythonlib_dir():
     """Returns the name of a distutils build directory"""
     return "lib.{version[0]}".format(version=sys.version_info)
@@ -91,7 +98,6 @@ if os.path.exists(os.path.join(os.getcwd(), "CMakeCache.txt")) \
         if l.startswith("ceph_SOURCE_DIR:STATIC="):
             src_path = l.split("=")[1].strip()
 
-
     if src_path is None:
         # Huh, maybe we're not really in a cmake environment?
         pass
@@ -123,7 +129,7 @@ from ceph_argparse import \
     matchnum, validate_command, find_cmd_target, \
     send_command, json_command, run_in_thread
 
-from ceph_daemon import DaemonWatcher, admin_socket
+from ceph_daemon import admin_socket, DaemonWatcher, Termsize
 
 # just a couple of globals
 
@@ -140,40 +146,31 @@ else:
     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(buf)
 
-############################################################################
 
 def osdids():
     ret, outbuf, outs = json_command(cluster_handle, prefix='osd ls')
-    if ret == -errno.EINVAL:
-        # try old mon
-        ret, outbuf, outs = send_command(cluster_handle, cmd=['osd', 'ls'])
     if ret:
         raise RuntimeError('Can\'t contact mon for osd list')
     return [line.decode('utf-8') for line in outbuf.split(b'\n') if line]
 
+
 def monids():
     ret, outbuf, outs = json_command(cluster_handle, prefix='mon dump',
-                                     argdict={'format':'json'})
-    if ret == -errno.EINVAL:
-        # try old mon
-        ret, outbuf, outs = send_command(cluster_handle,
-                                         cmd=['mon', 'dump', '--format=json'])
+                                     argdict={'format': 'json'})
     if ret:
         raise RuntimeError('Can\'t contact mon for mon list')
     d = json.loads(outbuf.decode('utf-8'))
     return [m['name'] for m in d['mons']]
 
+
 def mdsids():
     ret, outbuf, outs = json_command(cluster_handle, prefix='mds dump',
-                                     argdict={'format':'json'})
-    if ret == -errno.EINVAL:
-        # try old mon
-        ret, outbuf, outs = send_command(cluster_handle,
-                                         cmd=['mds', 'dump', '--format=json'])
+                                     argdict={'format': 'json'})
     if ret:
         raise RuntimeError('Can\'t contact mon for mds list')
     d = json.loads(outbuf.decode('utf-8'))
@@ -183,6 +180,61 @@ def mdsids():
         l.append(mdsdict['name'])
     return l
 
+
+def mgrids():
+    ret, outbuf, outs = json_command(cluster_handle, prefix='mgr dump',
+                                     argdict={'format': 'json'})
+    if ret:
+        raise RuntimeError('Can\'t contact mon for mgr list')
+
+    d = json.loads(outbuf.decode('utf-8'))
+    l = []
+    l.append(d['active_name'])
+    for i in d['standbys']:
+        l.append(i['name'])
+    return l
+
+
+def validate_target(target):
+    """
+      this function will return true iff target is a correct
+      target, such as mon.a/osd.2/mds.a/mgr.
+
+      target: array, likes ['osd', '2']
+      return: bool, or raise RuntimeError
+    """
+
+    if len(target) == 2:
+        # for case "service.id"
+        service_name, service_id = target[0], target[1]
+        exist_ids = []
+        if service_name == "mon":
+            exist_ids = monids()
+        elif service_name == "osd":
+            exist_ids = osdids()
+        elif service_name == "mds":
+            exist_ids = mdsids()
+        elif service_name == "mgr":
+            exist_ids = mgrids()
+        else:
+            print('WARN: {0} is not a legal service name, should be one of mon/osd/mds/mgr'.format(service_name),
+                  file=sys.stderr)
+            return False
+
+        if service_id in exist_ids:
+            return True
+        else:
+            print('WARN: the service id you provided does not exist. service id should '
+                  'be one of {0}.'.format('/'.join(exist_ids)), file=sys.stderr)
+            return False
+
+    elif len(target) == 1 and target[0] in ['mgr', 'mon']:
+        return True
+    else:
+        print('WARN: \"{0}\" is not a legal target. it should be one of mon.<id>/osd.<int>/mds.<id>/mgr'.format('.'.join(target)), file=sys.stderr)
+        return False
+
+
 # these args must be passed to all child programs
 GLOBAL_ARGS = {
     'client_id': '--id',
@@ -191,6 +243,7 @@ GLOBAL_ARGS = {
     'cephconf': '--conf',
 }
 
+
 def parse_cmdargs(args=None, target=''):
     # alias: let the line-wrapping be sane
     AP = argparse.ArgumentParser
@@ -259,6 +312,7 @@ def parse_cmdargs(args=None, target=''):
 def hdr(s):
     print('\n', s, '\n', '=' * len(s))
 
+
 def do_basic_help(parser, args):
     """
     Print basic parser help
@@ -268,6 +322,7 @@ def do_basic_help(parser, args):
     parser.print_help()
     print_locally_handled_command_help()
 
+
 def print_locally_handled_command_help():
     hdr("Local commands:")
     print("""
@@ -287,7 +342,7 @@ daemonperf {type.id | path} list|ls [stat-pats] [priority]
     """, file=sys.stdout)
 
 
-def do_extended_help(parser, args):
+def do_extended_help(parser, args, target, partial):
     def help_for_sigs(sigs, partial=None):
         sys.stdout.write(format_help(parse_json_funcsigs(sigs, 'cli'),
                          partial=partial))
@@ -297,18 +352,18 @@ def do_extended_help(parser, args):
                                          prefix='get_command_descriptions',
                                          timeout=10)
         if ret:
-            print("couldn't get command descriptions for {0}: {1}".\
-                format(target, outs), file=sys.stderr)
+            print("couldn't get command descriptions for {0}: {1} ({2})".
+                  format(target, outs, ret), file=sys.stderr)
+            return ret
         else:
-            help_for_sigs(outbuf.decode('utf-8'), partial)
+            return help_for_sigs(outbuf.decode('utf-8'), partial)
 
-    partial = ' '.join(args)
-    if (cluster_handle.state == "connected"):
-        help_for_target(target=('mon', ''), partial=partial)
-    return 0
+    assert(cluster_handle.state == "connected")
+    return help_for_target(target, partial)
 
 DONTSPLIT = string.ascii_letters + '{[<>]}'
 
+
 def wrap(s, width, indent):
     """
     generator to transform s into a sequence of strings width or shorter,
@@ -329,7 +384,7 @@ def wrap(s, width, indent):
     leader = ''
     while len(s):
 
-        if (len(s) <= width):
+        if len(s) <= width:
             # no splitting; just possibly indent
             result = leader + s
             s = ''
@@ -359,11 +414,12 @@ def wrap(s, width, indent):
 
     raise StopIteration
 
+
 def format_help(cmddict, partial=None):
     """
     Formats all the cmdsigs and helptexts from cmddict into a sorted-by-
     cmdsig 2-column display, with each column wrapped and indented to
-    fit into 40 characters.
+    fit into (terminal_width / 2) characters.
     """
 
     fullusage = ''
@@ -371,15 +427,18 @@ def format_help(cmddict, partial=None):
 
         if not cmd['help']:
             continue
-        flags = cmd.get('flags')
-        if (flags is not None and
-            (flags & (FLAG_OBSOLETE | FLAG_DEPRECATED)) != 0):
+        flags = cmd.get('flags', 0)
+        if flags & (FLAG_OBSOLETE | FLAG_DEPRECATED):
             continue
         concise = concise_sig(cmd['sig'])
         if partial and not concise.startswith(partial):
             continue
-        siglines = [l for l in wrap(concise, 40, 1)]
-        helplines = [l for l in wrap(cmd['help'], 39, 1)]
+        width = Termsize().cols - 1  # 1 for the line between sig and help
+        sig_width = int(width / 2)
+        # make sure width == sig_width + help_width, even (width % 2 > 0)
+        help_width = int(width / 2) + (width % 2)
+        siglines = [l for l in wrap(concise, sig_width, 1)]
+        helplines = [l for l in wrap(cmd['help'], help_width, 1)]
 
         # make lists the same length
         maxlen = max(len(siglines), len(helplines))
@@ -387,14 +446,14 @@ def format_help(cmddict, partial=None):
         helplines.extend([''] * (maxlen - len(helplines)))
 
         # so we can zip them for output
-        for (s, h) in zip(siglines, helplines):
-            fullusage += '{0:40s} {1}\n'.format(s, h)
+        for s, h in zip(siglines, helplines):
+            fullusage += '{s:{w}s} {h}\n'.format(s=s, h=h, w=sig_width)
 
     return fullusage
 
 
 def ceph_conf(parsed_args, field, name):
-    args=['ceph-conf']
+    args = ['ceph-conf']
 
     if name:
         args.extend(['--name', name])
@@ -413,7 +472,7 @@ def ceph_conf(parsed_args, field, name):
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE)
     outdata, errdata = p.communicate()
-    if (len(errdata)):
+    if len(errdata):
         raise RuntimeError('unable to get conf option %s for %s: %s' % (field, name, errdata))
     return outdata.rstrip()
 
@@ -475,12 +534,12 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
                 try:
                     target = find_cmd_target(cmdargs)
                 except Exception as e:
-                    print('error handling command target: {0}'.format(e), 
-                        file=sys.stderr)
+                    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)
+                    print('Can not use \'tell\' in interactive mode.',
+                          file=sys.stderr)
                     continue
                 valid_dict = validate_command(sigdict, cmdargs, verbose)
                 if valid_dict:
@@ -493,8 +552,8 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
                                                      argdict=valid_dict)
                     if ret:
                         ret = abs(ret)
-                        print('Error: {0} {1}'.format(ret, errno.errorcode.get(ret, 'Unknown')), 
-                            file=sys.stderr)
+                        print('Error: {0} {1}'.format(ret, errno.errorcode.get(ret, 'Unknown')),
+                              file=sys.stderr)
                     if outbuf:
                         print(outbuf)
                     if outs:
@@ -557,24 +616,21 @@ def complete(sigdict, args, target):
     return 0
 
 
-###
-# ping a monitor
-###
 def ping_monitor(cluster_handle, name, timeout):
     if 'mon.' not in name:
         print('"ping" expects a monitor to ping; try "ping mon.<id>"', file=sys.stderr)
         return 1
 
     mon_id = name[len('mon.'):]
-    if (mon_id == '*') :
+    if mon_id == '*':
         run_in_thread(cluster_handle.connect, timeout=timeout)
-        for m in monids() :
+        for m in monids():
             s = run_in_thread(cluster_handle.ping_monitor, m)
             if s is None:
                 print("mon.{0}".format(m) + '\n' + "Error connecting to monitor.")
             else:
                 print("mon.{0}".format(m) + '\n' + s)
-    else :
+    else:
             s = run_in_thread(cluster_handle.ping_monitor, mon_id)
             print(s)
     return 0
@@ -609,8 +665,8 @@ def maybe_daemon_command(parsed_args, childargs):
             # for both:
             childargs = childargs[2:]
         else:
-            print('{0} requires at least {1} arguments'.format(childargs[0], require_args), 
-                file=sys.stderr) 
+            print('{0} requires at least {1} arguments'.format(childargs[0], require_args),
+                  file=sys.stderr)
             return True, errno.EINVAL
 
     if sockpath and daemon_perf:
@@ -633,6 +689,7 @@ def isnum(s):
     except ValueError:
         return False
 
+
 def daemonperf(childargs, sockpath):
     """
     Handle daemonperf command; returns errno or 0
@@ -670,7 +727,7 @@ def daemonperf(childargs, sockpath):
         arg = childargs.pop(0)
         # 'list'?
         if arg in ['list', 'ls']:
-            do_list = True;
+            do_list = True
             continue
         # prio?
         prio = prio_from_name(arg)
@@ -707,9 +764,6 @@ def daemonperf(childargs, sockpath):
 
     return 0
 
-###
-# main
-###
 
 def main():
     ceph_args = os.environ.get('CEPH_ARGS')
@@ -722,7 +776,11 @@ def main():
     parser, parsed_args, childargs = parse_cmdargs()
 
     if parsed_args.version:
-        print('ceph version {0} ({1})'.format(CEPH_GIT_NICE_VER, CEPH_GIT_VER))  # noqa
+        print('ceph version {0} ({1}) {2} ({3})'.format(
+            CEPH_GIT_NICE_VER,
+            CEPH_GIT_VER,
+            CEPH_RELEASE_NAME,
+            CEPH_RELEASE_TYPE))  # noqa
         return 0
 
     global verbose
@@ -732,8 +790,8 @@ def main():
         print("parsed_args: {0}, childargs: {1}".format(parsed_args, childargs), file=sys.stderr)
 
     if parsed_args.admin_socket_nope:
-        print('--admin-socket is used by daemons; '\
-            'you probably mean --admin-daemon/daemon', file=sys.stderr)
+        print('--admin-socket is used by daemons; '
+              'you probably mean --admin-daemon/daemon', file=sys.stderr)
         return 1
 
     # pass on --id, --name, --conf
@@ -771,9 +829,9 @@ def main():
     # and then set the keys from the dict.  So we must do these
     # "pre-file defaults" first (see common_preinit in librados)
     conf_defaults = {
-        'log_to_stderr':'true',
-        'err_to_stderr':'true',
-        'log_flush_on_exit':'true',
+        'log_to_stderr': 'true',
+        'err_to_stderr': 'true',
+        'log_flush_on_exit': 'true',
     }
 
     if 'injectargs' in childargs:
@@ -781,8 +839,8 @@ def main():
         injectargs = childargs[position:]
         childargs = childargs[:position]
         if verbose:
-            print('Separate childargs {0} from injectargs {1}'.format(childargs, injectargs), 
-                file=sys.stderr)
+            print('Separate childargs {0} from injectargs {1}'.format(childargs, injectargs),
+                  file=sys.stderr)
     else:
         injectargs = None
 
@@ -812,11 +870,11 @@ def main():
 
     # 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)
+    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
 
     if parsed_args.help:
@@ -832,7 +890,7 @@ def main():
             print('"ping" requires a monitor name as argument: "ping mon.<id>"', file=sys.stderr)
             return 1
     if parsed_args.completion:
-        #for completion let timeout be really small
+        # for completion let timeout be really small
         timeout = 3
     try:
         if childargs and childargs[0] == 'ping':
@@ -847,10 +905,18 @@ def main():
     except Exception as e:
         print(str(e), file=sys.stderr)
         return 1
-
     if parsed_args.help:
-        return do_extended_help(parser, childargs)
+        return do_extended_help(parser, childargs, ('mon', ''), ' '.join(childargs))
 
+    # implement "tell service.id help"
+    if len(childargs) >= 3 and childargs[0] == 'tell' and childargs[2] == 'help':
+        target = childargs[1].split('.')
+        if validate_target(target):
+            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 = ''
@@ -869,12 +935,6 @@ def main():
 
         # first do a ceph status
         ret, outbuf, outs = json_command(cluster_handle, prefix='status')
-        if ret == -errno.EINVAL:
-            # try old mon
-            ret, outbuf, outs = send_command(cluster_handle, cmd=['status'])
-            # old mon returns status to outs...ick
-            if ret == 0:
-                outbuf += outs
         if ret:
             print("status query failed: ", outs, file=sys.stderr)
             return ret
@@ -932,8 +992,8 @@ def main():
             childargs = injectargs
         if not len(childargs):
             print('"{0} tell" requires additional arguments.'.format(sys.argv[0]),
-                'Try "{0} tell <name> <command> [options...]" instead.'.format(sys.argv[0]), 
-                file=sys.stderr)
+                  'Try "{0} tell <name> <command> [options...]" instead.'.format(sys.argv[0]),
+                  file=sys.stderr)
             return errno.EINVAL
 
     # fetch JSON sigs from command
@@ -960,66 +1020,51 @@ def main():
 
         ret, outbuf, outs = json_command(cluster_handle, target=target,
                                          prefix='get_command_descriptions')
-        compat = False
-        if ret == -errno.EINVAL:
-            # send command to old monitor or OSD
-            if verbose:
-                print(prefix + '{0} to old {1}'.format(' '.join(childargs), target[0]))
-            compat = True
-            if parsed_args.output_format:
-                childargs.extend(['--format', parsed_args.output_format])
-            ret, outbuf, outs = send_command(cluster_handle, target, childargs,
-                                             inbuf)
-
-            if ret == -errno.EINVAL:
-                # did we race with a mon upgrade?  try again!
-                ret, outbuf, outs = json_command(cluster_handle, target=target,
-                                                 prefix='get_command_descriptions')
-                if ret == 0:
-                    compat = False  # yep, carry on
-        if not compat:
-            if ret:
-                if ret < 0:
-                    outs = 'problem getting command descriptions from {0}.{1}'.format(*target)
-            else:
-                sigdict = parse_json_funcsigs(outbuf.decode('utf-8'), 'cli')
+        if ret:
+            where = '{0}.{1}'.format(*target)
+            if ret > 0:
+                raise RuntimeError('Unexpeceted return code from {0}: {1}'.
+                                   format(where, ret))
+            outs = 'problem getting command descriptions from {0}'.format(where)
+        else:
+            sigdict = parse_json_funcsigs(outbuf.decode('utf-8'), 'cli')
 
-                if parsed_args.completion:
-                    return complete(sigdict, childargs, target)
+            if parsed_args.completion:
+                return complete(sigdict, childargs, target)
 
-                ret, outbuf, outs = new_style_command(parsed_args, childargs, target,
-                                                      sigdict, inbuf, verbose)
+            ret, outbuf, outs = new_style_command(parsed_args, childargs,
+                                                  target, sigdict, inbuf,
+                                                  verbose)
 
-                # debug tool: send any successful command *again* to
-                # verify that it is idempotent.
-                if not ret and 'CEPH_CLI_TEST_DUP_COMMAND' in os.environ:
-                    ret, outbuf, outs = new_style_command(parsed_args, childargs, target,
-                                                          sigdict, inbuf, verbose)
-                    if ret < 0:
-                        ret = -ret
-                        print(prefix + 'Second attempt of previously successful command failed with {0}: {1}'.format(errno.errorcode.get(ret, 'Unknown'), outs), 
-                            file=sys.stderr) 
+            # debug tool: send any successful command *again* to
+            # verify that it is idempotent.
+            if not ret and 'CEPH_CLI_TEST_DUP_COMMAND' in os.environ:
+                ret, outbuf, outs = new_style_command(parsed_args, childargs,
+                                                      target, sigdict, inbuf,
+                                                      verbose)
+                if ret < 0:
+                    ret = -ret
+                    print(prefix +
+                          'Second attempt of previously successful command '
+                          'failed with {0}: {1}'.format(
+                              errno.errorcode.get(ret, 'Unknown'), outs),
+                          file=sys.stderr)
 
         if ret < 0:
             ret = -ret
-            print(u'Error {0}: {1}'.format(errno.errorcode.get(ret, 'Unknown'), outs), file=sys.stderr)
+            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
 
-        # this assumes outs never has useful command output, only status
-        if compat:
-            if ret == 0:
-                # old cli/mon would send status string to stdout on non-error
-                print(outs)
-        else:
-            if outs:
-                print(prefix + outs, file=sys.stderr)
+        if outs:
+            print(prefix + outs, file=sys.stderr)
 
         sys.stdout.flush()
 
-        if (parsed_args.output_file):
+        if parsed_args.output_file:
             outf.write(outbuf)
         else:
             # hack: old code printed status line before many json outputs
@@ -1027,8 +1072,7 @@ def main():
             # to satisfy consumers that skip the first line, but not annoy
             # consumers that don't.
             if parsed_args.output_format and \
-               parsed_args.output_format.startswith('json') and \
-               not compat:
+               parsed_args.output_format.startswith('json'):
                 print()
 
             # if we are prettifying things, normalize newlines.  sigh.
@@ -1046,7 +1090,7 @@ def main():
 
         sys.stdout.flush()
 
-    if (parsed_args.output_file):
+    if parsed_args.output_file:
         outf.close()
 
     if final_ret: