From 1f5ddbf7c960870bc3a4f57cd00101eefc6ad1e2 Mon Sep 17 00:00:00 2001 From: John Spray Date: Wed, 5 Aug 2015 13:08:21 +0100 Subject: [PATCH 1/8] CMake: build dencoder at src/ceph-dencoder ...instead of in src/test/dencoder. While the source lives in test/, the dencoder tool is built and distributed as a first class binary, so it's awkward for paths in dev/test to have it in a unique location. Signed-off-by: John Spray --- src/CMakeLists.txt | 52 +++++++++++++++++++++++++++++++++++++++++ src/test/CMakeLists.txt | 52 ----------------------------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 327a300012e57..e3a9b0e87bb54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -467,6 +467,58 @@ install(TARGETS rados librados-config DESTINATION bin) install(DIRECTORY ${CMAKE_SOURCE_DIR}/src/pybind/ DESTINATION ${PYTHON_INSTDIR}) +## dencoder +set(dencoder_srcs + test/encoding/ceph_dencoder.cc + krbd.cc + common/secret.c + common/TextTable.cc + ) +if(${WITH_RADOSGW}) + list(APPEND dencoder_srcs + rgw/rgw_dencoder.cc + rgw/rgw_acl.cc + rgw/rgw_common.cc + rgw/rgw_env.cc + rgw/rgw_json_enc.cc + ) +endif(${WITH_RADOSGW}) +add_executable(ceph-dencoder ${dencoder_srcs} $) +if(${WITH_RADOSGW}) + set(DENCODER_EXTRALIBS + rgw_a + cls_rgw_client + curl + expat + fcgi + resolv + ) +endif(${WITH_RADOSGW}) +target_link_libraries(ceph-dencoder + librados + librbd + global + osd + mds + mon + osdc + cls_lock_client + cls_refcount_client + cls_log_client + cls_statelog_client + cls_version_client + cls_replica_log_client + cls_kvs + cls_user_client + ${DENCODER_EXTRALIBS} + blkid + udev + keyutils + ${EXTRALIBS} + ${TCMALLOC_LIBS} + ${CMAKE_DL_LIBS} + ) + # Monitor set(lib_mon_srcs auth/cephx/CephxKeyServer.cc diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index de35fa98e54b9..e5a035e12952c 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -92,58 +92,6 @@ add_executable(test_keys ) target_link_libraries(test_keys mon global ${CMAKE_DL_LIBS} ${TCMALLOC_LIBS}) -## dencoder -set(dencoder_srcs - encoding/ceph_dencoder.cc - ${CMAKE_SOURCE_DIR}/src/krbd.cc - ${CMAKE_SOURCE_DIR}/src/common/secret.c - ${CMAKE_SOURCE_DIR}/src/common/TextTable.cc - ) -if(${WITH_RADOSGW}) - list(APPEND dencoder_srcs - ${CMAKE_SOURCE_DIR}/src/rgw/rgw_dencoder.cc - ${CMAKE_SOURCE_DIR}/src/rgw/rgw_acl.cc - ${CMAKE_SOURCE_DIR}/src/rgw/rgw_common.cc - ${CMAKE_SOURCE_DIR}/src/rgw/rgw_env.cc - ${CMAKE_SOURCE_DIR}/src/rgw/rgw_json_enc.cc - ) -endif(${WITH_RADOSGW}) -add_executable(dencoder ${dencoder_srcs} $) -if(${WITH_RADOSGW}) - set(DENCODER_EXTRALIBS - rgw_a - cls_rgw_client - curl - expat - fcgi - resolv - ) -endif(${WITH_RADOSGW}) -target_link_libraries(dencoder - librados - librbd - global - osd - mds - mon - osdc - cls_lock_client - cls_refcount_client - cls_log_client - cls_statelog_client - cls_version_client - cls_replica_log_client - cls_kvs - cls_user_client - ${DENCODER_EXTRALIBS} - blkid - udev - keyutils - ${EXTRALIBS} - ${TCMALLOC_LIBS} - ${CMAKE_DL_LIBS} - ) - # get_command_descriptions add_executable(get_command_descriptions common/get_command_descriptions.cc From af31afa82872476aff185a4a190d135e977da7ab Mon Sep 17 00:00:00 2001 From: John Spray Date: Mon, 3 Aug 2015 15:47:24 +0100 Subject: [PATCH 2/8] init-ceph.in: set executable bit Scripts expect the generated init-ceph script to be +x, and CMake does that if the file you feed into it is +x. This matches what we already do with ceph.in. Signed-off-by: John Spray --- src/init-ceph.in | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/init-ceph.in diff --git a/src/init-ceph.in b/src/init-ceph.in old mode 100644 new mode 100755 From 6545981b219018d52fa084a7d9bfb3eca826007f Mon Sep 17 00:00:00 2001 From: John Spray Date: Tue, 4 Aug 2015 00:09:53 +0100 Subject: [PATCH 3/8] ceph.in: use cmake-style substitution Avoid need for separate ceph.in and ceph.in.cmake files. Signed-off-by: John Spray --- src/CMakeLists.txt | 2 +- src/ceph.in | 4 + src/ceph.in.cmake | 827 --------------------------------------------- 3 files changed, 5 insertions(+), 828 deletions(-) delete mode 100755 src/ceph.in.cmake diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e3a9b0e87bb54..14881e1e998f8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -729,7 +729,7 @@ configure_file(${CMAKE_SOURCE_DIR}/src/ceph-coverage.in configure_file(${CMAKE_SOURCE_DIR}/src/ceph-debugpack.in ${CMAKE_BINARY_DIR}/ceph-debugpack @ONLY) -configure_file(${CMAKE_SOURCE_DIR}/src/ceph.in.cmake +configure_file(${CMAKE_SOURCE_DIR}/src/ceph.in ${CMAKE_BINARY_DIR}/ceph @ONLY) configure_file(${CMAKE_SOURCE_DIR}/src/ceph-crush-location.in diff --git a/src/ceph.in b/src/ceph.in index f55f8c7df17b5..3178ba155c672 100755 --- a/src/ceph.in +++ b/src/ceph.in @@ -1,3 +1,4 @@ +#!@PYTHON_EXECUTABLE@ # -*- mode:python -*- # vim: ts=4 sw=4 smarttab expandtab # @@ -22,6 +23,9 @@ import os import sys import platform +CEPH_GIT_VER="@CEPH_GIT_VER@" +CEPH_GIT_NICE_VER="@CEPH_GIT_NICE_VER@" + # Make life easier on developers: # If in src/, and .libs and pybind exist here, assume we're running # from a Ceph source dir and tweak PYTHONPATH and LD_LIBRARY_PATH diff --git a/src/ceph.in.cmake b/src/ceph.in.cmake deleted file mode 100755 index 805393e75a8ca..0000000000000 --- a/src/ceph.in.cmake +++ /dev/null @@ -1,827 +0,0 @@ -#!@PYTHON_EXECUTABLE@ -# -*- mode:python -*- -# vim: ts=4 sw=4 smarttab expandtab -# -# Processed in Makefile to add python #! line and version variable -# -# - - -""" -ceph.in becomes ceph, the command-line management tool for Ceph clusters. -This is a replacement for tools/ceph.cc and tools/common.cc. - -Copyright (C) 2013 Inktank Storage, Inc. - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public -License version 2, as published by the Free Software -Foundation. See file COPYING. -""" - -import os -import sys - -# Make life easier on developers: -# If in src/, and .libs and pybind exist here, assume we're running -# from a Ceph source dir and tweak PYTHONPATH and LD_LIBRARY_PATH -# to use local files - -MYPATH = os.path.abspath(__file__) -MYDIR = os.path.dirname(MYPATH) -DEVMODEMSG = '*** DEVELOPER MODE: setting PATH, PYTHONPATH and LD_LIBRARY_PATH ***' - -if MYDIR.endswith('src') and \ - os.path.exists(os.path.join(MYDIR, '.libs')) and \ - os.path.exists(os.path.join(MYDIR, 'pybind')): - MYLIBPATH = os.path.join(MYDIR, '.libs') - if 'LD_LIBRARY_PATH' in os.environ: - if MYLIBPATH not in os.environ['LD_LIBRARY_PATH']: - os.environ['LD_LIBRARY_PATH'] += ':' + MYLIBPATH - print >> sys.stderr, DEVMODEMSG - os.execvp('python', ['python'] + sys.argv) - else: - os.environ['LD_LIBRARY_PATH'] = MYLIBPATH - print >> sys.stderr, DEVMODEMSG - os.execvp('python', ['python'] + sys.argv) - sys.path.insert(0, os.path.join(MYDIR, 'pybind')) - if MYDIR not in os.environ['PATH']: - os.environ['PATH'] += ':' + MYDIR - -import argparse -import errno -import json -import rados -import signal -import socket -import string -import struct -import subprocess - -from ceph_argparse import \ - concise_sig, descsort, parse_json_funcsigs, \ - matchnum, validate_command, find_cmd_target, \ - send_command, json_command - -# just a couple of globals - -verbose = False -cluster_handle = None - -############################################################################ - -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 [i for i in outbuf.split('\n') if i != ''] - -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']) - if ret: - raise RuntimeError('Can\'t contact mon for mon list') - d = json.loads(outbuf) - 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']) - if ret: - raise RuntimeError('Can\'t contact mon for mds list') - d = json.loads(outbuf) - l = [] - infodict = d['info'] - for mdsdict in infodict.values(): - l.append(mdsdict['name']) - return l - -def parse_cmdargs(args=None, target=''): - # alias: let the line-wrapping be sane - AP = argparse.ArgumentParser - - # format our own help - parser = AP(description='Ceph administration tool', add_help=False) - - parser.add_argument('--completion', action='store_true', - help=argparse.SUPPRESS) - - parser.add_argument('-h', '--help', help='request mon help', - action='store_true') - - parser.add_argument('-c', '--conf', dest='cephconf', - help='ceph configuration file') - parser.add_argument('-i', '--in-file', dest='input_file', - help='input file') - parser.add_argument('-o', '--out-file', dest='output_file', - help='output file') - - parser.add_argument('--id', '--user', dest='client_id', - help='client id for authentication') - parser.add_argument('--name', '-n', dest='client_name', - help='client name for authentication') - parser.add_argument('--cluster', help='cluster name') - - parser.add_argument('--admin-daemon', dest='admin_socket', - help='submit admin-socket commands (\"help\" for help') - parser.add_argument('--admin-socket', dest='admin_socket_nope', - help='you probably mean --admin-daemon') - - parser.add_argument('-s', '--status', action='store_true', - help='show cluster status') - - parser.add_argument('-w', '--watch', action='store_true', - help='watch live cluster changes') - parser.add_argument('--watch-debug', action='store_true', - help='watch debug events') - parser.add_argument('--watch-info', action='store_true', - help='watch info events') - parser.add_argument('--watch-sec', action='store_true', - help='watch security events') - parser.add_argument('--watch-warn', action='store_true', - help='watch warn events') - parser.add_argument('--watch-error', action='store_true', - help='watch error events') - - parser.add_argument('--version', '-v', action="store_true", help="display version") - parser.add_argument('--verbose', action="store_true", help="make verbose") - parser.add_argument('--concise', dest='verbose', action="store_false", - help="make less verbose") - - parser.add_argument('-f', '--format', choices=['json', 'json-pretty', - 'xml', 'xml-pretty', 'plain'], dest='output_format') - - parser.add_argument('--connect-timeout', dest='cluster_timeout', - type=int, - help='set a timeout for connecting to the cluster') - - # returns a Namespace with the parsed args, and a list of all extras - parsed_args, extras = parser.parse_known_args(args) - - return parser, parsed_args, extras - - -def hdr(s): - print '\n', s, '\n', '=' * len(s) - -def do_basic_help(parser, args): - """ - Print basic parser help - If the cluster is available, get and print monitor help - """ - hdr('General usage:') - parser.print_help() - -def do_extended_help(parser, args): - def help_for_sigs(sigs, partial=None): - sys.stdout.write(format_help(parse_json_funcsigs(sigs, 'cli'), - partial=partial)) - - def help_for_target(target, partial=None): - ret, outbuf, outs = json_command(cluster_handle, target=target, - prefix='get_command_descriptions', - timeout=10) - if ret: - print >> sys.stderr, \ - "couldn't get command descriptions for {0}: {1}".\ - format(target, outs) - else: - help_for_sigs(outbuf, partial) - - partial = ' '.join(args) - if (cluster_handle.state == "connected"): - help_for_target(target=('mon', ''), partial=partial) - return 0 - -DONTSPLIT = string.letters + '{[<>]}' - -def wrap(s, width, indent): - """ - generator to transform s into a sequence of strings width or shorter, - for wrapping text to a specific column width. - Attempt to break on anything but DONTSPLIT characters. - indent is amount to indent 2nd-through-nth lines. - - so "long string long string long string" width=11 indent=1 becomes - 'long string', ' long string', ' long string' so that it can be printed - as - long string - long string - long string - - Consumes s. - """ - result = '' - leader = '' - while len(s): - - if (len(s) <= width): - # no splitting; just possibly indent - result = leader + s - s = '' - yield result - - else: - splitpos = width - while (splitpos > 0) and (s[splitpos-1] in DONTSPLIT): - splitpos -= 1 - - if splitpos == 0: - splitpos = width - - if result: - # prior result means we're mid-iteration, indent - result = leader - else: - # first time, set leader and width for next - leader = ' ' * indent - width -= 1 # for subsequent space additions - - # remove any leading spaces in this chunk of s - result += s[:splitpos].lstrip() - s = s[splitpos:] - - yield result - - 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. - """ - - fullusage = '' - for cmd in sorted(cmddict.itervalues(), cmp=descsort): - - if not cmd['help']: - 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)] - - # make lists the same length - maxlen = max(len(siglines), len(helplines)) - siglines.extend([''] * (maxlen - len(siglines))) - 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) - - return fullusage - -def admin_socket(asok_path, cmd, format=''): - """ - Send a daemon (--admin-daemon) command 'cmd'. asok_path is the - path to the admin socket; cmd is a list of strings; format may be - set to one of the formatted forms to get output in that form - (daemon commands don't support 'plain' output). - """ - - def do_sockio(path, cmd): - """ helper: do all the actual low-level stream I/O """ - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(path) - try: - sock.sendall(cmd + '\0') - len_str = sock.recv(4) - if len(len_str) < 4: - raise RuntimeError("no data returned from admin socket") - l, = struct.unpack(">I", len_str) - ret = '' - - got = 0 - while got < l: - bit = sock.recv(l - got) - ret += bit - got += len(bit) - - except Exception as e: - raise RuntimeError('exception: ' + str(e)) - return ret - - try: - cmd_json = do_sockio(asok_path, - json.dumps({"prefix":"get_command_descriptions"})) - except Exception as e: - raise RuntimeError('exception getting command descriptions: ' + str(e)) - - if cmd == 'get_command_descriptions': - return cmd_json - - sigdict = parse_json_funcsigs(cmd_json, 'cli') - valid_dict = validate_command(sigdict, cmd) - if not valid_dict: - raise RuntimeError('invalid command') - - if format: - valid_dict['format'] = format - - try: - ret = do_sockio(asok_path, json.dumps(valid_dict)) - except Exception as e: - raise RuntimeError('exception: ' + str(e)) - - return ret - - -def ceph_conf(field, name): - p = subprocess.Popen( - args=[ - 'ceph-conf', - '--show-config-value', - field, - '-n', - name, - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - outdata, errdata = p.communicate() - if (len(errdata)): - raise RuntimeError('unable to get conf option %s for %s: %s' % (field, name, errdata)) - return outdata.rstrip() - -def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose): - """ - Do new-style command dance. - target: daemon to receive command: mon (any) or osd.N - sigdict - the parsed output from the new monitor describing commands - inbuf - any -i input file data - verbose - bool - """ - if verbose: - for cmdtag in sorted(sigdict.keys()): - cmd = sigdict[cmdtag] - sig = cmd['sig'] - print '{0}: {1}'.format(cmdtag, concise_sig(sig)) - - got_command = False - - if not got_command: - if cmdargs: - # Validate input args against list of sigs - valid_dict = validate_command(sigdict, cmdargs, verbose) - if valid_dict: - got_command = True - if parsed_args.output_format: - valid_dict['format'] = parsed_args.output_format - else: - return -errno.EINVAL, '', 'invalid command' - else: - # do the command-interpreter looping - # for raw_input to do readline cmd editing - import readline - while True: - interactive_input = raw_input('ceph> ') - if interactive_input in ['q', 'quit', 'Q']: - return 0, '', '' - cmdargs = parse_cmdargs(interactive_input.split())[2] - target = find_cmd_target(cmdargs) - valid_dict = validate_command(sigdict, cmdargs, verbose) - if valid_dict: - if parsed_args.output_format: - valid_dict['format'] = parsed_args.output_format - if verbose: - print >> sys.stderr, "Submitting command ", valid_dict - ret, outbuf, outs = json_command(cluster_handle, - target=target, - argdict=valid_dict) - if ret: - ret = abs(ret) - print >> sys.stderr, \ - 'Error: {0} {1}'.format(ret, errno.errorcode[ret]) - if outbuf: - print outbuf - if outs: - print >> sys.stderr, 'Status:\n', outs - else: - print >> sys.stderr, "Invalid command" - - if verbose: - print >> sys.stderr, "Submitting command ", valid_dict - return json_command(cluster_handle, target=target, argdict=valid_dict, - inbuf=inbuf) - -def complete(sigdict, args, target): - """ - Command completion. Match as much of [args] as possible, - and print every possible match separated by newlines. - Return exitcode. - """ - # XXX this looks a lot like the front of validate_command(). Refactor? - - complete_verbose = 'COMPVERBOSE' in os.environ - - # Repulsive hack to handle tell: lop off 'tell' and target - # and validate the rest of the command. 'target' is already - # determined in our callers, so it's ok to remove it here. - if len(args) and args[0] == 'tell': - args = args[2:] - # look for best match, accumulate possibles in bestcmds - # (so we can maybe give a more-useful error message) - best_match_cnt = 0 - bestcmds = [] - for cmdtag, cmd in sigdict.iteritems(): - sig = cmd['sig'] - matched = matchnum(args, sig, partial=True) - if (matched > best_match_cnt): - if complete_verbose: - print >> sys.stderr, \ - "better match: {0} > {1}: {2}:{3} ".format(matched, - best_match_cnt, cmdtag, concise_sig(sig)) - best_match_cnt = matched - bestcmds = [{cmdtag:cmd}] - elif matched == best_match_cnt: - if complete_verbose: - print >> sys.stderr, \ - "equal match: {0} > {1}: {2}:{3} ".format(matched, - best_match_cnt, cmdtag, concise_sig(sig)) - bestcmds.append({cmdtag:cmd}) - - # look through all matching sigs - comps = [] - for cmddict in bestcmds: - for cmd in cmddict.itervalues(): - sig = cmd['sig'] - # either: - # we match everything fully, so we want the next desc, or - # we match more partially, so we want the partial match - fullindex = matchnum(args, sig, partial=False) - 1 - partindex = matchnum(args, sig, partial=True) - 1 - if complete_verbose: - print >> sys.stderr, '{}: f {} p {} len {}'.format(sig, fullindex, partindex, len(sig)) - if fullindex == partindex and fullindex + 1 < len(sig): - d = sig[fullindex + 1] - else: - d = sig[partindex] - comps.append(str(d)) - if complete_verbose: - print >> sys.stderr, '\n'.join(comps) - print '\n'.join(comps) - - return 0 - -### -# ping a monitor -### -def ping_monitor(cluster_handle, name, timeout): - if 'mon.' not in name: - print >> sys.stderr, '"ping" expects a monitor to ping; try "ping mon."' - return 1 - - mon_id = name[len('mon.'):] - if (mon_id == '*') : - cluster_handle.connect(timeout=timeout) - for m in monids() : - s = cluster_handle.ping_monitor(m) - print "mon.{0}".format(m) + '\n' + s - else : - s = cluster_handle.ping_monitor(mon_id) - print s - return 0 - -### -# main -### - -def main(): - ceph_args = os.environ.get('CEPH_ARGS') - if ceph_args: - sys.argv.extend(ceph_args.split()) - - parser, parsed_args, childargs = parse_cmdargs() - - if parsed_args.version: - print 'ceph version {0} ({1})'.format(CEPH_GIT_NICE_VER, CEPH_GIT_VER) - return 0 - - global verbose - verbose = parsed_args.verbose - - if parsed_args.admin_socket_nope: - print >> sys.stderr, '--admin-socket is used by daemons; '\ - 'you probably mean --admin-daemon/daemon' - return 1 - - # pass on --id, --name, --conf - name = 'client.admin' - if parsed_args.client_id: - name = 'client.' + parsed_args.client_id - if parsed_args.client_name: - name = parsed_args.client_name - - # default '' means default conf search - conffile = '' - if parsed_args.cephconf: - conffile = parsed_args.cephconf - # For now, --admin-daemon is handled as usual. Try it - # first in case we can't connect() to the cluster - - format = parsed_args.output_format - - sockpath = None - if parsed_args.admin_socket: - sockpath = parsed_args.admin_socket - elif len(childargs) > 0 and childargs[0] == "daemon": - # Treat "daemon " or "daemon " like --admin_daemon - if len(childargs) > 2: - if childargs[1].find('/') >= 0: - sockpath = childargs[1] - else: - # try resolve daemon name - try: - sockpath = ceph_conf('admin_socket', childargs[1]) - except Exception as e: - print >> sys.stderr, \ - 'Can\'t get admin socket path: ' + str(e) - return errno.EINVAL - # for both: - childargs = childargs[2:] - else: - print >> sys.stderr, 'daemon requires at least 3 arguments' - return errno.EINVAL - - if sockpath: - try: - print admin_socket(sockpath, childargs, format) - except Exception as e: - print >> sys.stderr, 'admin_socket: {0}'.format(e) - return errno.EINVAL - return 0 - - timeout = None - if parsed_args.cluster_timeout: - timeout = parsed_args.cluster_timeout - - # basic help - if parsed_args.help: - do_basic_help(parser, childargs) - - # handle any 'generic' ceph arguments that we didn't parse here - global cluster_handle - - # rados.Rados() will call rados_create2, and then read the conf file, - # 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', - } - - clustername = 'ceph' - if parsed_args.cluster: - clustername = parsed_args.cluster - - try: - cluster_handle = rados.Rados(name=name, clustername=clustername, - conf_defaults=conf_defaults, - conffile=conffile) - retargs = cluster_handle.conf_parse_argv(childargs) - except rados.Error as e: - print >> sys.stderr, 'Error initializing cluster client: {0}'.\ - format(e.__class__.__name__) - return 1 - - #tmp = childargs - childargs = retargs - if not childargs: - childargs = [] - - # -- means "stop parsing args", but we don't want to see it either - if '--' in childargs: - childargs.remove('--') - - # special deprecation warning for 'ceph tell' - # someday 'mds' will be here too - if len(childargs) >= 2 and \ - childargs[0] in ['mon', 'osd'] and \ - childargs[1] == 'tell': - print >> sys.stderr, '"{0} tell" is deprecated; try "tell {0}." instead (id can be "*") '.format(childargs[0]) - return 1 - - if parsed_args.help: - # short default timeout for -h - if not timeout: - timeout = 5 - - hdr('Monitor commands:') - print '[Contacting monitor, timeout after %d seconds]' % timeout - - if childargs and childargs[0] == 'ping': - if len(childargs) < 2: - print >> sys.stderr, '"ping" requires a monitor name as argument: "ping mon."' - return 1 - - try: - if childargs and childargs[0] == 'ping': - return ping_monitor(cluster_handle, childargs[1], timeout) - cluster_handle.connect(timeout=timeout) - except KeyboardInterrupt: - print >> sys.stderr, 'Cluster connection aborted' - return 1 - except Exception as e: - print >> sys.stderr, 'Error connecting to cluster: {0}'.\ - format(e.__class__.__name__) - return 1 - - if parsed_args.help: - return do_extended_help(parser, childargs) - - # implement -w/--watch_* - # This is ugly, but Namespace() isn't quite rich enough. - level = '' - for k, v in parsed_args._get_kwargs(): - if k.startswith('watch') and v: - if k == 'watch': - level = 'info' - else: - level = k.replace('watch_', '') - if level: - - # an awfully simple callback - def watch_cb(arg, line, who, stamp_sec, stamp_nsec, seq, level, msg): - print line - sys.stdout.flush() - - # 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 >> sys.stderr, "status query failed: ", outs - return ret - print outbuf - - # this instance keeps the watch connection alive, but is - # otherwise unused - logwatch = rados.MonitorLog(cluster_handle, level, watch_cb, 0) - - # loop forever letting watch_cb print lines - try: - signal.pause() - except KeyboardInterrupt: - # or until ^C, at least - return 0 - - # read input file, if any - inbuf = '' - if parsed_args.input_file: - try: - with open(parsed_args.input_file, 'r') as f: - inbuf = f.read() - except Exception as e: - print >> sys.stderr, 'Can\'t open input file {0}: {1}'.format(parsed_args.input_file, e) - return 1 - - # prepare output file, if any - if parsed_args.output_file: - try: - outf = open(parsed_args.output_file, 'w') - except Exception as e: - print >> sys.stderr, \ - 'Can\'t open output file {0}: {1}'.\ - format(parsed_args.output_file, e) - return 1 - - # -s behaves like a command (ceph status). - if parsed_args.status: - childargs.insert(0, 'status') - - target = find_cmd_target(childargs) - - # Repulsive hack to handle tell: lop off 'tell' and target - # and validate the rest of the command. 'target' is already - # determined in our callers, so it's ok to remove it here. - if len(childargs) and childargs[0] == 'tell': - childargs = childargs[2:] - - # fetch JSON sigs from command - # each line contains one command signature (a placeholder name - # of the form 'cmdNNN' followed by an array of argument descriptors) - # as part of the validated argument JSON object - - targets = [target] - - if target[1] == '*': - if target[0] == 'osd': - targets = [(target[0], o) for o in osdids()] - elif target[0] == 'mon': - targets = [(target[0], m) for m in monids()] - - final_ret = 0 - for target in targets: - # prettify? prefix output with target, if there was a wildcard used - prefix = '' - suffix = '' - if not parsed_args.output_file and len(targets) > 1: - prefix = '{0}.{1}: '.format(*target) - suffix = '\n' - - 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, 'cli') - - if parsed_args.completion: - return complete(sigdict, childargs, target) - - 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 >> sys.stderr, prefix + 'Second attempt of previously successful command failed with {0}: {1}'.format(errno.errorcode[ret], outs) - - if ret < 0: - ret = -ret - print >> sys.stderr, prefix + 'Error {0}: {1}'.format(errno.errorcode[ret], outs) - 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 >> sys.stderr, prefix + outs - - if (parsed_args.output_file): - outf.write(outbuf) - else: - # hack: old code printed status line before many json outputs - # (osd dump, etc.) that consumers know to ignore. Add blank line - # 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: - sys.stdout.write('\n') - - # if we are prettifying things, normalize newlines. sigh. - if suffix != '': - outbuf = outbuf.rstrip() - if outbuf != '': - sys.stdout.write(prefix + outbuf + suffix) - - sys.stdout.flush() - - if (parsed_args.output_file): - outf.close() - - if final_ret: - return final_ret - - return 0 - -if __name__ == '__main__': - sys.exit(main()) From 64255b965e63084760c0b2113cf122180af05be0 Mon Sep 17 00:00:00 2001 From: John Spray Date: Tue, 4 Aug 2015 00:33:05 +0100 Subject: [PATCH 4/8] Makefile: build ceph.in the cmake way A little bit more of a tongue-twisting command line to do string replacement, but saves us from having separate ceph.in files for cmake vs autotools. Signed-off-by: John Spray --- src/Makefile-client.am | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Makefile-client.am b/src/Makefile-client.am index d68d70db3185a..14bd712b1b56e 100644 --- a/src/Makefile-client.am +++ b/src/Makefile-client.am @@ -19,11 +19,10 @@ bin_PROGRAMS += ceph-syn ceph: ceph.in ./ceph_ver.h Makefile rm -f $@ $@.tmp - echo "#!/usr/bin/env python" >$@.tmp - grep "#define CEPH_GIT_NICE_VER" $(srcdir)/ceph_ver.h | \ - sed -e 's/#define \(.*VER\) /\1=/' >>$@.tmp - grep "#define CEPH_GIT_VER" $(srcdir)/ceph_ver.h | \ - sed -e 's/#define \(.*VER\) /\1=/' -e 's/=\(.*\)$$/="\1"/' >>$@.tmp + cp $@.in $@.tmp + sed -i "s|@PYTHON_EXECUTABLE@|/usr/bin/env python|" $@.tmp + grep CEPH_GIT_NICE_VER ./ceph_ver.h | cut -f 3 -d " " | sed s/\"//g | xargs -I "{}" sed -i "s/@CEPH_GIT_NICE_VER@/{}/g" $@.tmp + grep CEPH_GIT_VER ./ceph_ver.h | cut -f 3 -d " " | sed s/\"//g | xargs -I "{}" sed -i "s/@CEPH_GIT_VER@/{}/g" $@.tmp cat $(srcdir)/$@.in >>$@.tmp chmod a+x $@.tmp chmod a-w $@.tmp From dac84a7aeb132c318a97965be6ca7b993abb3c42 Mon Sep 17 00:00:00 2001 From: John Spray Date: Mon, 3 Aug 2015 16:07:55 +0100 Subject: [PATCH 5/8] vstart: enable more path customization ...for working with out-of-tree builds. Signed-off-by: John Spray --- src/vstart.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vstart.sh b/src/vstart.sh index c039eb7336493..fb63749edc138 100755 --- a/src/vstart.sh +++ b/src/vstart.sh @@ -16,7 +16,9 @@ if [ -z "${CEPH_VSTART_WRAPPER}" ]; then PATH=$(pwd):$PATH fi -export PYTHONPATH=./pybind +[ -z "$PYBIND" ] && PYBIND=./pybind + +export PYTHONPATH=$PYBIND export LD_LIBRARY_PATH=$CEPH_LIB export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH @@ -315,11 +317,12 @@ echo "ip $IP" echo "port $PORT" +[ -z $CEPH_ADM ] && CEPH_ADM=$CEPH_BIN/ceph if [ "$cephx" -eq 1 ]; then - CEPH_ADM="$CEPH_BIN/ceph -c $conf_fn -k $keyring_fn" + CEPH_ADM="$CEPH_ADM -c $conf_fn -k $keyring_fn" else - CEPH_ADM="$CEPH_BIN/ceph -c $conf_fn" + CEPH_ADM="$CEPH_ADM -c $conf_fn" fi MONS="" @@ -422,6 +425,7 @@ $extra_conf mon osd allow primary affinity = true mon reweight min pgs per osd = 4 mon osd prime pg temp = true + crushtool = $CEPH_BIN/crushtool $DAEMONOPTS $CMONDEBUG $extra_conf From 0695d179ffa0165e5707c18dc921f39214d153d5 Mon Sep 17 00:00:00 2001 From: John Spray Date: Tue, 4 Aug 2015 00:01:22 +0100 Subject: [PATCH 6/8] vstart: detect and handle cmake environ Signed-off-by: John Spray --- src/vstart.sh | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/vstart.sh b/src/vstart.sh index fb63749edc138..c0ca90c165cf9 100755 --- a/src/vstart.sh +++ b/src/vstart.sh @@ -1,5 +1,28 @@ #!/bin/sh +# abort on failure +set -e + +if [ -e CMakeCache.txt ]; then + # Out of tree build, learn source location from CMakeCache.txt + SRC_ROOT=`grep Ceph_SOURCE_DIR CMakeCache.txt | cut -d "=" -f 2` + [ -z "$PYBIND" ] && PYBIND=$SRC_ROOT/src/pybind + [ -z "$CEPH_ADM" ] && CEPH_ADM=./ceph + [ -z "$INIT_CEPH" ] && INIT_CEPH=./init-ceph + [ -z "$CEPH_BIN" ] && CEPH_BIN=src + [ -z "$CEPH_LIB" ] && CEPH_LIB=src + [ -z "$OBJCLASS_PATH" ] && OBJCLASS_PATH=src/cls + + # Gather symlinks to EC plugins in one dir, because with CMake they + # are built into multiple locations + mkdir -p ec_plugins + for file in ./src/erasure-code/*/libec_*.so*; + do + ln -sf ../${file} ec_plugins/`basename $file` + done + [ -z "$EC_PATH" ] && EC_PATH=./ec_plugins +fi + if [ -z "$CEPH_BUILD_ROOT" ]; then [ -z "$CEPH_BIN" ] && CEPH_BIN=. [ -z "$CEPH_LIB" ] && CEPH_LIB=.libs @@ -22,9 +45,6 @@ export PYTHONPATH=$PYBIND export LD_LIBRARY_PATH=$CEPH_LIB export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH -# abort on failure -set -e - [ -z "$CEPH_NUM_MON" ] && CEPH_NUM_MON="$MON" [ -z "$CEPH_NUM_OSD" ] && CEPH_NUM_OSD="$OSD" [ -z "$CEPH_NUM_MDS" ] && CEPH_NUM_MDS="$MDS" From 44ed436c7912bedd8ed781eda725d39d2c41863b Mon Sep 17 00:00:00 2001 From: John Spray Date: Tue, 4 Aug 2015 00:10:47 +0100 Subject: [PATCH 7/8] ceph.in: detect paths in out of tree build a la what we currently do for PYTHONPATH and LD_LIBRARY_PATH, but for cmake out of tree builds. Signed-off-by: John Spray --- src/ceph.in | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/ceph.in b/src/ceph.in index 3178ba155c672..c6c7c498511b7 100755 --- a/src/ceph.in +++ b/src/ceph.in @@ -35,9 +35,10 @@ MYPATH = os.path.abspath(__file__) MYDIR = os.path.dirname(MYPATH) DEVMODEMSG = '*** DEVELOPER MODE: setting PATH, PYTHONPATH and LD_LIBRARY_PATH ***' -if MYDIR.endswith('src') and \ - os.path.exists(os.path.join(MYDIR, '.libs')) and \ - os.path.exists(os.path.join(MYDIR, 'pybind')): +def respawn_in_path(lib_path, pybind_path): + execv_cmd = ['python'] + if 'CEPH_DBG' in os.environ: + execv_cmd += ['-mpdb'] if platform.system() == "Darwin": lib_path_var = "DYLD_LIBRARY_PATH" @@ -45,23 +46,48 @@ if MYDIR.endswith('src') and \ lib_path_var = "LD_LIBRARY_PATH" py_binary = os.environ.get("PYTHON", "python") - MYLIBPATH = os.path.join(MYDIR, '.libs') - execv_cmd = ['python'] - if 'CEPH_DBG' in os.environ: - execv_cmd += ['-mpdb'] + if lib_path_var in os.environ: - if MYLIBPATH not in os.environ[lib_path_var]: - os.environ[lib_path_var] += ':' + MYLIBPATH + if lib_path not in os.environ[lib_path_var]: + os.environ[lib_path_var] += ':' + lib_path print >> sys.stderr, DEVMODEMSG os.execvp(py_binary, execv_cmd + sys.argv) else: - os.environ[lib_path_var] = MYLIBPATH + os.environ[lib_path_var] = lib_path print >> sys.stderr, DEVMODEMSG os.execvp(py_binary, execv_cmd + sys.argv) - sys.path.insert(0, os.path.join(MYDIR, 'pybind')) + sys.path.insert(0, os.path.join(MYDIR, pybind_path)) + +if MYDIR.endswith('src') and \ + os.path.exists(os.path.join(MYDIR, '.libs')) and \ + os.path.exists(os.path.join(MYDIR, 'pybind')): + + respawn_in_path(os.path.join(MYDIR, '.libs'), "pybind") if os.environ.has_key('PATH') and MYDIR not in os.environ['PATH']: os.environ['PATH'] += ':' + MYDIR +elif os.path.exists(os.path.join(os.getcwd(), "CMakeCache.txt")) \ + and os.path.exists(os.path.join(os.getcwd(), "init-ceph")): + src_path = None + for l in open("./CMakeCache.txt").readlines(): + 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 + else: + # Developer mode, but in a cmake build dir instead of the src dir + lib_path = os.path.join(os.getcwd(), "src") + pybind_path = os.path.join(src_path, "src", "pybind") + respawn_in_path(lib_path, pybind_path) + + sys.path.insert(0, os.path.join(MYDIR, pybind_path)) + + # Add src/ to path for e.g. ceph-conf + if os.environ.has_key('PATH') and lib_path not in os.environ['PATH']: + os.environ['PATH'] += ':' + lib_path + import argparse import errno import json From c747e29a668002c9a01e41380bbcc81a411b05bb Mon Sep 17 00:00:00 2001 From: John Spray Date: Tue, 4 Aug 2015 11:12:33 +0100 Subject: [PATCH 8/8] vstart: detect CEPH_BIN in stop.sh too ...for the benefit of rbd. Signed-off-by: John Spray --- src/stop.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stop.sh b/src/stop.sh index 8e45f4116d34e..ea5f2737e6171 100755 --- a/src/stop.sh +++ b/src/stop.sh @@ -18,7 +18,11 @@ test -d dev/osd0/. && test -e dev/sudo && SUDO="sudo" -[ -z "$CEPH_BIN" ] && CEPH_BIN=. +if [ -e CMakeCache.txt ]; then + [ -z "$CEPH_BIN" ] && CEPH_BIN=src +else + [ -z "$CEPH_BIN" ] && CEPH_BIN=. +fi MYUID=$(id -u) MYNAME=$(id -nu)