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
def show_human_help(prefix): ''' Dump table showing commands matching prefix ''' # XXX There ought to be a better discovery mechanism than an HTML table s = '<html><body><table border=1><th>Possible commands:</th><th>Method</th><th>Description</th>' permmap = {'r': 'GET', 'rw': 'PUT', 'rx': 'GET', 'rwx': 'PUT'} line = '' for cmdsig in sorted(app.ceph_sigdict.itervalues(), cmp=descsort): concise = concise_sig(cmdsig['sig']) flavor = cmdsig.get('flavor', 'mon') if flavor == 'tell': concise = 'tell/<target>/' + concise if concise.startswith(prefix): line = ['<tr><td>'] wrapped_sig = textwrap.wrap( concise_sig_for_uri(cmdsig['sig'], flavor), 40) for sigline in wrapped_sig: line.append(flask.escape(sigline) + '\n') line.append('</td><td>') line.append(permmap[cmdsig['perm']]) line.append('</td><td>') line.append(flask.escape(cmdsig['help'])) line.append('</td></tr>\n') s += ''.join(line) s += '</table></body></html>' if line: return s else: return ''
def show_human_help(prefix): ''' Dump table showing commands matching prefix ''' # XXX There ought to be a better discovery mechanism than an HTML table s = '<html><body><table border=1><th>Possible commands:</th><th>Method</th><th>Description</th>' permmap = {'r': 'GET', 'rw': 'PUT', 'rx': 'GET', 'rwx': 'PUT'} line = '' for cmdsig in sorted(app.ceph_sigdict.itervalues(), cmp=descsort): concise = concise_sig(cmdsig['sig']) flavor = cmdsig.get('flavor', 'mon') if flavor == 'tell': concise = 'tell/<target>/' + concise if concise.startswith(prefix): line = ['<tr><td>'] wrapped_sig = textwrap.wrap( concise_sig_for_uri(cmdsig['sig'], flavor), 40 ) for sigline in wrapped_sig: line.append(flask.escape(sigline) + '\n') line.append('</td><td>') line.append(permmap[cmdsig['perm']]) line.append('</td><td>') line.append(flask.escape(cmdsig['help'])) line.append('</td></tr>\n') s += ''.join(line) s += '</table></body></html>' if line: return s else: return ''
def show_human_help(prefix): """ Dump table showing commands matching prefix """ # XXX There ought to be a better discovery mechanism than an HTML table s = "<html><body><table border=1><th>Possible commands:</th><th>Method</th><th>Description</th>" permmap = {"r": "GET", "rw": "PUT"} line = "" for cmdsig in sorted(app.ceph_sigdict.itervalues(), cmp=descsort): concise = concise_sig(cmdsig["sig"]) flavor = cmdsig.get("flavor", "mon") if flavor == "tell": concise = "tell/<target>/" + concise if concise.startswith(prefix): line = ["<tr><td>"] wrapped_sig = textwrap.wrap(concise_sig_for_uri(cmdsig["sig"], flavor), 40) for sigline in wrapped_sig: line.append(flask.escape(sigline) + "\n") line.append("</td><td>") line.append(permmap[cmdsig["perm"]]) line.append("</td><td>") line.append(flask.escape(cmdsig["help"])) line.append("</td></tr>\n") s += "".join(line) s += "</table></body></html>" if line: return s else: return ""
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 handler(catchall_path=None, fmt=None, target=None): ''' Main endpoint handler; generic for every endpoint, including catchall. Handles the catchall, anything with <.fmt>, anything with embedded <target>. Partial match or ?help cause the HTML-table "show_human_help" output. ''' ep = catchall_path or flask.request.endpoint ep = ep.replace('.<fmt>', '') if ep[0] != '/': ep = '/' + ep # demand that endpoint begin with app.ceph_baseurl if not ep.startswith(app.ceph_baseurl): return make_response(fmt, '', 'Page not found', 404) rel_ep = ep[len(app.ceph_baseurl) + 1:] # Extensions override Accept: headers override defaults if not fmt: if 'application/json' in flask.request.accept_mimetypes.values(): fmt = 'json' elif 'application/xml' in flask.request.accept_mimetypes.values(): fmt = 'xml' prefix = '' pgid = None cmdtarget = 'mon', '' if target: # got tell/<target>; validate osdid or pgid name = CephOsdName() pgidobj = CephPgid() try: name.valid(target) except ArgumentError: # try pgid try: pgidobj.valid(target) except ArgumentError: return flask.make_response("invalid osdid or pgid", 400) else: # it's a pgid pgid = pgidobj.val cmdtarget = 'pg', pgid else: # it's an osd cmdtarget = name.nametype, name.nameid # prefix does not include tell/<target>/ prefix = ' '.join(rel_ep.split('/')[2:]).strip() else: # non-target command: prefix is entire path prefix = ' '.join(rel_ep.split('/')).strip() # show "match as much as you gave me" help for unknown endpoints if ep not in app.ceph_urls: helptext = show_human_help(prefix) if helptext: resp = flask.make_response(helptext, 400) resp.headers['Content-Type'] = 'text/html' return resp else: return make_response(fmt, '', 'Invalid endpoint ' + ep, 400) found = None exc = '' for urldict in app.ceph_urls[ep]: if flask.request.method not in urldict['methods']: continue paramsig = urldict['paramsig'] # allow '?help' for any specifically-known endpoint if 'help' in flask.request.args: response = flask.make_response('{0}: {1}'.format( prefix + concise_sig(paramsig), urldict['help'])) response.headers['Content-Type'] = 'text/plain' return response # if there are parameters for this endpoint, process them if paramsig: args = {} for k, l in flask.request.args.iterlists(): if len(l) == 1: args[k] = l[0] else: args[k] = l # is this a valid set of params? try: argdict = validate(args, paramsig) found = urldict break except Exception as e: exc += str(e) continue else: if flask.request.args: continue found = urldict argdict = {} break if not found: return make_response(fmt, '', exc + '\n', 400) argdict['format'] = fmt or 'plain' argdict['module'] = found['module'] argdict['perm'] = found['perm'] if pgid: argdict['pgid'] = pgid if not cmdtarget: cmdtarget = ('mon', '') app.logger.debug('sending command prefix %s argdict %s', prefix, argdict) for _ in range(DEFAULT_TRIES): ret, outbuf, outs = json_command(app.ceph_cluster, prefix=prefix, target=cmdtarget, inbuf=flask.request.data, argdict=argdict, timeout=DEFAULT_TIMEOUT) if ret != -errno.EINTR: break else: return make_response(fmt, '', 'Timedout: {0} ({1})'.format(outs, ret), 504) if ret: return make_response(fmt, '', 'Error: {0} ({1})'.format(outs, ret), 400) response = make_response(fmt, outbuf, outs or 'OK', 200) if fmt: contenttype = 'application/' + fmt.replace('-pretty', '') else: contenttype = 'text/plain' response.headers['Content-Type'] = contenttype return response
def handler(catchall_path=None, fmt=None, target=None): ''' Main endpoint handler; generic for every endpoint, including catchall. Handles the catchall, anything with <.fmt>, anything with embedded <target>. Partial match or ?help cause the HTML-table "show_human_help" output. ''' ep = catchall_path or flask.request.endpoint ep = ep.replace('.<fmt>', '') if ep[0] != '/': ep = '/' + ep # demand that endpoint begin with app.ceph_baseurl if not ep.startswith(app.ceph_baseurl): return make_response(fmt, '', 'Page not found', 404) rel_ep = ep[len(app.ceph_baseurl) + 1:] # Extensions override Accept: headers override defaults if not fmt: if 'application/json' in flask.request.accept_mimetypes.values(): fmt = 'json' elif 'application/xml' in flask.request.accept_mimetypes.values(): fmt = 'xml' prefix = '' pgid = None cmdtarget = 'mon', '' if target: # got tell/<target>; validate osdid or pgid name = CephOsdName() pgidobj = CephPgid() try: name.valid(target) except ArgumentError: # try pgid try: pgidobj.valid(target) except ArgumentError: return flask.make_response("invalid osdid or pgid", 400) else: # it's a pgid pgid = pgidobj.val cmdtarget = 'pg', pgid else: # it's an osd cmdtarget = name.nametype, name.nameid # prefix does not include tell/<target>/ prefix = ' '.join(rel_ep.split('/')[2:]).strip() else: # non-target command: prefix is entire path prefix = ' '.join(rel_ep.split('/')).strip() # show "match as much as you gave me" help for unknown endpoints if ep not in app.ceph_urls: helptext = show_human_help(prefix) if helptext: resp = flask.make_response(helptext, 400) resp.headers['Content-Type'] = 'text/html' return resp else: return make_response(fmt, '', 'Invalid endpoint ' + ep, 400) found = None exc = '' for urldict in app.ceph_urls[ep]: if flask.request.method not in urldict['methods']: continue paramsig = urldict['paramsig'] # allow '?help' for any specifically-known endpoint if 'help' in flask.request.args: response = flask.make_response('{0}: {1}'. format(prefix + concise_sig(paramsig), urldict['help'])) response.headers['Content-Type'] = 'text/plain' return response # if there are parameters for this endpoint, process them if paramsig: args = {} for k, l in flask.request.args.iterlists(): if len(l) == 1: args[k] = l[0] else: args[k] = l # is this a valid set of params? try: argdict = validate(args, paramsig) found = urldict break except Exception as e: exc += str(e) continue else: if flask.request.args: continue found = urldict argdict = {} break if not found: return make_response(fmt, '', exc + '\n', 400) argdict['format'] = fmt or 'plain' argdict['module'] = found['module'] argdict['perm'] = found['perm'] if pgid: argdict['pgid'] = pgid if not cmdtarget: cmdtarget = ('mon', '') app.logger.debug('sending command prefix %s argdict %s', prefix, argdict) ret, outbuf, outs = json_command(app.ceph_cluster, prefix=prefix, target=cmdtarget, inbuf=flask.request.data, argdict=argdict) if ret: return make_response(fmt, '', 'Error: {0} ({1})'.format(outs, ret), 400) response = make_response(fmt, outbuf, outs or 'OK', 200) if fmt: contenttype = 'application/' + fmt.replace('-pretty', '') else: contenttype = 'text/plain' response.headers['Content-Type'] = contenttype return response
def handler(catchall_path=None, fmt=None, target=None): """ Main endpoint handler; generic for every endpoint, including catchall. Handles the catchall, anything with <.fmt>, anything with embedded <target>. Partial match or ?help cause the HTML-table "show_human_help" output. """ ep = catchall_path or flask.request.endpoint ep = ep.replace(".<fmt>", "") if ep[0] != "/": ep = "/" + ep # demand that endpoint begin with app.ceph_baseurl if not ep.startswith(app.ceph_baseurl): return make_response(fmt, "", "Page not found", 404) rel_ep = ep[len(app.ceph_baseurl) + 1 :] # Extensions override Accept: headers override defaults if not fmt: if "application/json" in flask.request.accept_mimetypes.values(): fmt = "json" elif "application/xml" in flask.request.accept_mimetypes.values(): fmt = "xml" prefix = "" pgid = None cmdtarget = "mon", "" if target: # got tell/<target>; validate osdid or pgid name = CephOsdName() pgidobj = CephPgid() try: name.valid(target) except ArgumentError: # try pgid try: pgidobj.valid(target) except ArgumentError: return flask.make_response("invalid osdid or pgid", 400) else: # it's a pgid pgid = pgidobj.val cmdtarget = "pg", pgid else: # it's an osd cmdtarget = name.nametype, name.nameid # prefix does not include tell/<target>/ prefix = " ".join(rel_ep.split("/")[2:]).strip() else: # non-target command: prefix is entire path prefix = " ".join(rel_ep.split("/")).strip() # show "match as much as you gave me" help for unknown endpoints if not ep in app.ceph_urls: helptext = show_human_help(prefix) if helptext: resp = flask.make_response(helptext, 400) resp.headers["Content-Type"] = "text/html" return resp else: return make_response(fmt, "", "Invalid endpoint " + ep, 400) found = None exc = "" for urldict in app.ceph_urls[ep]: if flask.request.method not in urldict["methods"]: continue paramsig = urldict["paramsig"] # allow '?help' for any specifically-known endpoint if "help" in flask.request.args: response = flask.make_response("{0}: {1}".format(prefix + concise_sig(paramsig), urldict["help"])) response.headers["Content-Type"] = "text/plain" return response # if there are parameters for this endpoint, process them if paramsig: args = {} for k, l in flask.request.args.iterlists(): if len(l) == 1: args[k] = l[0] else: args[k] = l # is this a valid set of params? try: argdict = validate(args, paramsig) found = urldict break except Exception as e: exc += str(e) continue else: if flask.request.args: continue found = urldict argdict = {} break if not found: return make_response(fmt, "", exc + "\n", 400) argdict["format"] = fmt or "plain" argdict["module"] = found["module"] argdict["perm"] = found["perm"] if pgid: argdict["pgid"] = pgid if not cmdtarget: cmdtarget = ("mon", "") app.logger.debug("sending command prefix %s argdict %s", prefix, argdict) ret, outbuf, outs = json_command( app.ceph_cluster, prefix=prefix, target=cmdtarget, inbuf=flask.request.data, argdict=argdict ) if ret: return make_response(fmt, "", "Error: {0} ({1})".format(outs, ret), 400) response = make_response(fmt, outbuf, outs or "OK", 200) if fmt: contenttype = "application/" + fmt.replace("-pretty", "") else: contenttype = "text/plain" response.headers["Content-Type"] = contenttype return response
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: if sys.stdin.isatty(): # do the command-interpreter looping # for raw_input to do readline cmd editing import readline while True: interactive_input = read_input() 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 >> sys.stderr, \ 'error handling command target: {0}'.format(e) return 1, '', '' 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)