rabbitmq-server/deps/rabbitmq_management/bin/rabbitmqadmin

589 lines
19 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env python
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is RabbitMQ Management Plugin.
#
# The Initial Developer of the Original Code is VMware, Inc.
# Copyright (c) 2007-2010 VMware, Inc. All rights reserved.
from optparse import OptionParser
import sys
import httplib
import urllib
import base64
import json
2010-10-19 18:52:38 +08:00
import os
import socket
LISTABLE = ['connections', 'channels', 'exchanges', 'queues', 'bindings',
2010-11-09 19:02:22 +08:00
'users', 'vhosts', 'permissions', 'nodes']
2011-01-03 21:20:13 +08:00
SHOWABLE = ['overview']
PROMOTE_COLUMNS = ['vhost', 'name', 'type',
'source', 'destination', 'destination_type', 'routing_key']
2010-10-19 18:17:11 +08:00
URIS = {
'exchange': '/exchanges/{vhost}/{name}',
'queue': '/queues/{vhost}/{name}',
'binding': '/bindings/{vhost}/e/{source}/{destination_char}/{destination}',
'binding_del':'/bindings/{vhost}/e/{source}/{destination_char}/{destination}/{properties_key}',
'vhost': '/vhosts/{name}',
'user': '/users/{name}',
'permission': '/permissions/{vhost}/{user}'
}
2010-10-19 17:39:25 +08:00
DECLARABLE = {
2010-10-19 18:17:11 +08:00
'exchange': {'mandatory': ['name', 'type'],
'optional': {'auto_delete': 'false', 'durable': 'true',
'internal': 'false'}},
2010-10-19 18:17:11 +08:00
'queue': {'mandatory': ['name'],
'optional': {'auto_delete': 'false', 'durable': 'true'}},
'binding': {'mandatory': ['source', 'destination_type', 'destination',
'routing_key'],
'optional': {}},
'vhost': {'mandatory': ['name'],
'optional': {}},
'user': {'mandatory': ['name', 'password', 'administrator'],
'optional': {}},
2010-11-27 01:42:13 +08:00
'permission': {'mandatory': ['vhost', 'user', 'configure', 'write', 'read'],
2010-10-19 18:17:11 +08:00
'optional': {}}
}
DELETABLE = {
'exchange': {'mandatory': ['name']},
'queue': {'mandatory': ['name']},
'binding': {'mandatory': ['source', 'destination_type', 'destination',
'properties_key']},
'vhost': {'mandatory': ['name']},
'user': {'mandatory': ['name']},
'permission': {'mandatory': ['vhost', 'user']}
2010-10-19 17:39:25 +08:00
}
2010-10-19 01:51:42 +08:00
2010-10-19 18:29:39 +08:00
CLOSABLE = {
'connection': {'mandatory': ['name'],
'optional': {},
'uri': '/connections/{name}'}
}
PURGABLE = {
'queue': {'mandatory': ['name'],
'optional': {},
'uri': '/queues/{vhost}/{name}/contents'}
}
2010-10-19 18:17:11 +08:00
for k in DECLARABLE:
DECLARABLE[k]['uri'] = URIS[k]
for k in DELETABLE:
DELETABLE[k]['uri'] = URIS[k]
DELETABLE[k]['optional'] = {}
DELETABLE['binding']['uri'] = URIS['binding_del']
def make_usage():
2010-10-19 01:51:42 +08:00
usage = """usage: %prog [options] cmd
where cmd is one of:
2010-10-19 18:17:11 +08:00
"""
for l in LISTABLE:
usage += " list {0} [<column>...]\n".format(l)
2011-01-03 21:20:13 +08:00
for s in SHOWABLE:
usage += " show {0} [<column>...]\n".format(s)
2010-10-19 18:29:39 +08:00
usage += fmt_usage_stanza(DECLARABLE, 'declare')
usage += fmt_usage_stanza(DELETABLE, 'delete')
usage += fmt_usage_stanza(CLOSABLE, 'close')
usage += fmt_usage_stanza(PURGABLE, 'purge')
2010-10-19 18:17:11 +08:00
usage += """
export FILE
import FILE"""
return usage
2010-10-19 18:29:39 +08:00
def fmt_usage_stanza(root, verb):
2010-10-19 18:40:10 +08:00
def fmt_args(args):
res = " ".join(["{0}=...".format(a) for a in args['mandatory']])
2010-10-19 18:40:10 +08:00
opts = " ".join("{0}=...".format(o) for o in args['optional'].keys())
if opts != "":
res += " [{0}]".format(opts)
return res
2010-10-19 18:29:39 +08:00
text = "\n"
for k in root.keys():
2010-10-19 18:40:10 +08:00
text += " {0} {1} {2}\n".format(verb, k, fmt_args(root[k]))
2010-10-19 18:29:39 +08:00
return text
2010-10-19 01:51:42 +08:00
parser = OptionParser(usage=make_usage())
def make_parser():
parser.add_option("-H", "--host", dest="hostname", default="localhost",
help="connect to host HOST [default: %default]",
metavar="HOST")
parser.add_option("-P", "--port", dest="port", default="55672",
help="connect to port PORT [default: %default]",
metavar="PORT")
parser.add_option("-V", "--vhost", dest="vhost",
2010-10-19 01:51:42 +08:00
help="connect to vhost VHOST [default: all vhosts for list, '/' for declare]",
metavar="VHOST")
parser.add_option("-u", "--username", dest="username", default="guest",
help="connect using username USERNAME [default: %default]",
metavar="USERNAME")
parser.add_option("-p", "--password", dest="password", default="guest",
help="connect using password PASSWORD [default: %default]",
metavar="PASSWORD")
parser.add_option("-q", "--quiet", action="store_false", dest="verbose",
default=True, help="suppress status messages")
parser.add_option("-f", "--format", dest="format", default="table",
help="format for listing commands - one of [" + ", ".join(FORMATS.keys()) + "] [default: %default]")
parser.add_option("-d", "--depth", dest="depth", default="1",
help="maximum depth to recurse for listing tables [default: %default]")
parser.add_option("--bash-completion", action="store_true",
dest="bash_completion", default=False,
help="Print bash completion script")
2010-10-19 18:52:38 +08:00
def assert_usage(expr, error):
if not expr:
2010-10-19 18:53:27 +08:00
print "\nERROR: {0}\n".format(error)
print "{0} --help for help\n".format(os.path.basename(sys.argv[0]))
sys.exit(1)
def column_sort_key(col):
if col in PROMOTE_COLUMNS:
return (1, PROMOTE_COLUMNS.index(col))
else:
return (2, col)
def main():
make_parser()
(options, args) = parser.parse_args()
if options.bash_completion:
print_bash_completion()
exit(0)
2010-10-19 18:52:38 +08:00
assert_usage(len(args) > 0, 'Action not specified')
mgmt = Management(options, args[1:])
mode = "invoke_" + args[0]
2010-10-19 18:52:38 +08:00
assert_usage(hasattr(mgmt, mode),
'Action {0} not understood'.format(args[0]))
method = getattr(mgmt, "invoke_%s" % args[0])
method()
def die(str):
sys.stderr.write("*** {0}".format(str))
exit(1)
class Management:
def __init__(self, options, args):
self.options = options
self.args = args
def get(self, path):
return self.http("GET", path, "")
def put(self, path, body):
return self.http("PUT", path, body)
def post(self, path, body):
return self.http("POST", path, body)
2010-10-19 18:17:11 +08:00
def delete(self, path):
return self.http("DELETE", path, "")
def http(self, method, path, body):
conn = httplib.HTTPConnection(self.options.hostname, self.options.port)
headers = {"Authorization":
"Basic " + base64.b64encode(self.options.username + ":" +
self.options.password)}
if body != "":
headers["Content-Type"] = "application/json"
try:
conn.request(method, "/api%s" % path, body, headers)
except socket.error as e:
die("Could not connect: {0}".format(e))
resp = conn.getresponse()
if resp.status == 400:
die(json.loads(resp.read())['reason'])
if resp.status == 401:
die("Access refused: {0}".format(path))
if resp.status == 404:
die("Not found: {0}".format(path))
if resp.status < 200 or resp.status > 400:
raise Exception("Received %d %s for path %s\n%s"
% (resp.status, resp.reason, path, resp.read()))
return resp.read()
def verbose(self, string):
if self.options.verbose:
print string
def get_arg(self):
2010-10-19 18:52:38 +08:00
assert_usage(len(self.args) == 1, 'Exactly one argument required')
return self.args[0]
def invoke_export(self):
path = self.get_arg()
config = self.get("/all-configuration")
with open(path, 'w') as f:
f.write(config)
self.verbose("Exported configuration for %s to \"%s\""
% (self.options.hostname, path))
def invoke_import(self):
path = self.get_arg()
with open(path, 'r') as f:
config = f.read()
self.post("/all-configuration", config)
self.verbose("Imported configuration for %s from \"%s\""
% (self.options.hostname, path))
def invoke_list(self):
2011-01-03 21:20:13 +08:00
uri = self.list_show_uri(LISTABLE, 'list')
format_list(self.get(uri), self.args[1:], self.options)
def invoke_show(self):
uri = self.list_show_uri(SHOWABLE, 'show')
format_list('[{0}]'.format(self.get(uri)), self.args[1:], self.options)
def list_show_uri(self, obj_types, verb):
obj_type = self.args[0]
2011-01-03 21:20:13 +08:00
assert_usage(obj_type in obj_types,
"Don't know how to {0} {1}".format(verb, obj_type))
uri = "/%s" % obj_type
if self.options.vhost:
uri += "/%s" % urllib.quote_plus(self.options.vhost)
2011-01-03 21:20:13 +08:00
return uri
2010-10-19 01:51:42 +08:00
def invoke_declare(self):
2010-10-19 18:17:11 +08:00
(obj_type, uri, payload) = self.declare_delete_parse(DECLARABLE)
if obj_type == 'binding':
self.post(uri, json.dumps(payload))
else:
self.put(uri, json.dumps(payload))
self.verbose("{0} declared".format(obj_type))
def invoke_delete(self):
(obj_type, uri, payload) = self.declare_delete_parse(DELETABLE)
self.delete(uri)
self.verbose("{0} deleted".format(obj_type))
2010-10-19 18:29:39 +08:00
def invoke_close(self):
(obj_type, uri, payload) = self.declare_delete_parse(CLOSABLE)
self.delete(uri)
self.verbose("{0} closed".format(obj_type))
def invoke_purge(self):
(obj_type, uri, payload) = self.declare_delete_parse(PURGABLE)
self.delete(uri)
self.verbose("{0} purged".format(obj_type))
2010-10-19 18:17:11 +08:00
def declare_delete_parse(self, root):
2010-10-19 18:52:38 +08:00
assert_usage(len(self.args) > 0, 'Type not specified')
2010-10-19 01:51:42 +08:00
obj_type = self.args[0]
2010-10-19 18:52:38 +08:00
assert_usage(obj_type in root,
'Type {0} not recognised'.format(obj_type))
2010-10-19 18:17:11 +08:00
args = self.args[1:]
mandatory = root[obj_type]['mandatory']
optional = root[obj_type]['optional']
2010-10-19 01:51:42 +08:00
args = self.args[1:]
payload = {}
for k in optional.keys():
payload[k] = optional[k]
for arg in args:
assert_usage("=" in arg,
'Argument "{0}" not in format name=value'.format(arg))
(name, value) = arg.split("=")
assert_usage(name in mandatory or name in optional.keys(),
'Argument "{0}" not recognised'.format(name))
2010-10-19 18:40:10 +08:00
payload[name] = value
for m in mandatory:
assert_usage(m in payload.keys(),
'mandatory argument "{0}" required'.format(m))
2010-10-19 01:51:42 +08:00
payload['arguments'] = {}
2010-10-19 17:39:25 +08:00
payload['vhost'] = self.options.vhost or '/'
uri_args = {}
for k in payload:
uri_args[k] = urllib.quote_plus(payload[k])
if k == 'destination_type':
uri_args['destination_char'] = payload[k][0]
2010-10-19 18:17:11 +08:00
uri = root[obj_type]['uri'].format(**uri_args)
return (obj_type, uri, payload)
2010-10-19 01:51:42 +08:00
def format_list(json_list, columns, options):
format = options.format
formatter = None
if format == "raw_json":
print json_list
return
elif format == "pretty_json":
enc = json.JSONEncoder(False, False, True, True, True, 2)
print enc.encode(json.loads(json_list))
return
else:
formatter = FORMATS[format]
2010-10-19 18:52:38 +08:00
assert_usage(formatter != None,
"Format {0} not recognised".format(format))
formatter_instance = formatter(columns, options)
formatter_instance.display(json_list)
class Lister:
def verbose(self, string):
if self.options.verbose:
print string
def display(self, json_list):
depth = sys.maxint
if len(self.columns) == 0:
depth = int(self.options.depth)
(columns, table) = self.list_to_table(json.loads(json_list), depth)
if len(table) > 0:
self.display_list(columns, table)
else:
self.verbose("No items")
def list_to_table(self, items, max_depth):
columns = {}
column_ix = {}
row = None
table = []
def add(prefix, depth, item, fun):
for key in item:
column = prefix == '' and key or (prefix + '_' + key)
if type(item[key]) == dict:
if depth < max_depth:
add(column, depth + 1, item[key], fun)
2010-11-09 19:02:22 +08:00
elif type(item[key]) == list:
# TODO - ATM this is only applications inside nodes. Not
# sure how to handle this sensibly.
pass
else:
fun(column, item[key])
def add_to_columns(col, val):
columns[col] = True
def add_to_row(col, val):
if col in column_ix:
row[column_ix[col]] = str(val)
if len(self.columns) == 0:
for item in items:
add('', 1, item, add_to_columns)
columns = columns.keys()
columns.sort(key=column_sort_key)
else:
columns = self.columns
for i in xrange(0, len(columns)):
column_ix[columns[i]] = i
for item in items:
row = len(columns) * ['']
add('', 1, item, add_to_row)
table.append(row)
return (columns, table)
class TSVList(Lister):
def __init__(self, columns, options):
self.columns = columns
self.options = options
def display_list(self, columns, table):
head = ""
for col in columns:
head += col + "\t"
self.verbose(head)
for row in table:
line = ""
for cell in row:
line += cell + "\t"
print line
class LongList(Lister):
def __init__(self, columns, options):
self.columns = columns
self.options = options
def display_list(self, columns, table):
sep = "\n" + "-" * 80 + "\n"
max_width = 0
for col in columns:
max_width = max(max_width, len(col))
fmt = "{0:>" + str(max_width) + "}: {1}"
print sep
for i in xrange(0, len(table)):
for j in xrange(0, len(columns)):
print fmt.format(columns[j], table[i][j])
print sep
class TableList(Lister):
def __init__(self, columns, options):
self.columns = columns
self.options = options
def display_list(self, columns, table):
total = [columns]
total.extend(table)
self.ascii_table(total)
def ascii_table(self, rows):
table = ""
col_widths = [0] * len(rows[0])
for i in xrange(0, len(rows[0])):
for j in xrange(0, len(rows)):
col_widths[i] = max(col_widths[i], len(rows[j][i]))
self.ascii_bar(col_widths)
self.ascii_row(col_widths, rows[0], "^")
self.ascii_bar(col_widths)
for row in rows[1:]:
self.ascii_row(col_widths, row, "<")
self.ascii_bar(col_widths)
def ascii_row(self, col_widths, row, align):
txt = "|"
for i in xrange(0, len(col_widths)):
fmt = " {0:" + align + str(col_widths[i]) + "} "
txt += fmt.format(row[i]) + "|"
print txt
def ascii_bar(self, col_widths):
txt = "+"
for w in col_widths:
txt += ("-" * (w + 2)) + "+"
print txt
class KeyValueList(Lister):
def __init__(self, columns, options):
self.columns = columns
self.options = options
def display_list(self, columns, table):
for i in xrange(0, len(table)):
row = []
for j in xrange(0, len(columns)):
row.append("{0}=\"{1}\"".format(columns[j], table[i][j]))
print " ".join(row)
# TODO handle spaces etc in completable names
class BashList(Lister):
def __init__(self, columns, options):
self.columns = columns
self.options = options
def display_list(self, columns, table):
ix = None
for i in xrange(0, len(columns)):
if columns[i] == 'name':
ix = i
if ix is not None:
res = []
for row in table:
res.append(row[ix])
print " ".join(res)
FORMATS = {
'raw_json' : None, # Special cased
'pretty_json' : None, # Ditto
'tsv' : TSVList,
'long' : LongList,
'table' : TableList,
'kvp' : KeyValueList,
'bash' : BashList
}
def print_bash_completion():
script = """# This is a bash completion script for rabbitmqadmin.
# Redirect it to a file, then source it or copy it to /etc/bash_completion.d
# to get tab completion. rabbitmqadmin must be on your PATH for this to work.
_rabbitmqadmin()
{
local cur prev opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
2011-01-03 21:20:13 +08:00
opts="list show declare delete close purge import export"
fargs="--help --host --port --vhost --username --password --format --depth"
case "${prev}" in
list)
COMPREPLY=( $(compgen -W '""" + " ".join(LISTABLE) + """' -- ${cur}) )
return 0
;;
2011-01-03 21:20:13 +08:00
show)
COMPREPLY=( $(compgen -W '""" + " ".join(SHOWABLE) + """' -- ${cur}) )
return 0
;;
declare)
COMPREPLY=( $(compgen -W '""" + " ".join(DECLARABLE.keys()) + """' -- ${cur}) )
return 0
;;
delete)
COMPREPLY=( $(compgen -W '""" + " ".join(DELETABLE.keys()) + """' -- ${cur}) )
return 0
;;
close)
COMPREPLY=( $(compgen -W '""" + " ".join(CLOSABLE.keys()) + """' -- ${cur}) )
return 0
;;
purge)
COMPREPLY=( $(compgen -W '""" + " ".join(PURGABLE.keys()) + """' -- ${cur}) )
return 0
;;
export)
COMPREPLY=( $(compgen -f ${cur}) )
return 0
;;
import)
COMPREPLY=( $(compgen -f ${cur}) )
return 0
;;
--host)
COMPREPLY=( $(compgen -A hostname ${cur}) )
return 0
;;
--vhost)
opts="$(rabbitmqadmin -q -f bash list vhosts)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--username)
opts="$(rabbitmqadmin -q -f bash list users)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--format)
COMPREPLY=( $(compgen -W \"""" + " ".join(FORMATS.keys()) + """\" -- ${cur}) )
return 0
;;
"""
for l in LISTABLE:
key = l[0:len(l) - 1]
script += " " + key + """)
opts="$(rabbitmqadmin -q -f bash list """ + l + """)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
"""
script += """ *)
;;
esac
COMPREPLY=($(compgen -W "${opts} ${fargs}" -- ${cur}))
return 0
}
complete -F _rabbitmqadmin rabbitmqadmin
"""
print script
if __name__ == "__main__":
main()