diff options
author | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
---|---|---|
committer | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
commit | 66765100f4257159622cefe57bed50125a5ad017 (patch) | |
tree | a88ee23bb194eb91f0ebb2d9b23ff423e3ea8e37 /qpid/tools/src/py/qmf-tool | |
parent | 1aeaa7b16e5ce54f10c901d75c4d40f9f88b9db6 (diff) | |
parent | 88b98b2f4152ef59a671fad55a0d08338b6b78ca (diff) | |
download | qpid-python-66765100f4257159622cefe57bed50125a5ad017.tar.gz |
Creating a branch for experimenting with some ideas for JMS client.rajith_jms_client
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rajith_jms_client@1128369 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/tools/src/py/qmf-tool')
-rwxr-xr-x | qpid/tools/src/py/qmf-tool | 684 |
1 files changed, 684 insertions, 0 deletions
diff --git a/qpid/tools/src/py/qmf-tool b/qpid/tools/src/py/qmf-tool new file mode 100755 index 0000000000..e366d04709 --- /dev/null +++ b/qpid/tools/src/py/qmf-tool @@ -0,0 +1,684 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import os +import optparse +import sys +import socket +from cmd import Cmd +from shlex import split +from threading import Lock +from time import strftime, gmtime +from qpid.disp import Display +import cqpid +import qmf2 + +class Mcli(Cmd): + """ Management Command Interpreter """ + + def __init__(self, dataObject, dispObject): + Cmd.__init__(self) + self.dataObject = dataObject + self.dispObject = dispObject + self.dataObject.setCli(self) + self.prompt = "qmf: " + + def emptyline(self): + pass + + def setPromptMessage(self, p=None): + if p == None: + self.prompt = "qmf: " + else: + self.prompt = "qmf[%s]: " % p + + def do_help(self, data): + print "Management Tool for QMF" + print + print "Agent Commands:" + print " set filter <filter-string> - Filter the list of agents" + print " show filter - Show the agent filter currently in effect" + print " list agents - Print a list of the known Agents" + print " show agent <item-number> - Print detailed information about an Agent" + print " set default <item-number> - Set the default agent for operations" + print + print "Schema Commands:" + print " list packages - Print a list of packages supported by the default agent" + print " list classes [<package-name>] - Print all classes supported byt the default agent" + print " show class <class-name> [<package-name>] - Show details of a class" + print + print "Data Commands:" + print " query <class-name> [<package-name>] [<predicate>] - Query for data from the agent" + print " list - List accumulated query results" + print " clear - Clear accumulated query results" + print " show <id> - Show details from a data object" + print " call <id> <method> [<args>] - Call a method on a data object" + print + print "General Commands:" + print " set time-format short - Select short timestamp format (default)" + print " set time-format long - Select long timestamp format" + print " quit or ^D - Exit the program" + print + + def complete_set(self, text, line, begidx, endidx): + """ Command completion for the 'set' command """ + tokens = split(line[:begidx]) + if len(tokens) == 1: + return [i for i in ('filter ', 'default ', 'time-format ') if i.startswith(text)] + if len(tokens) == 2 and tokens[1] == 'time-format': + return [i for i in ('long', 'short') if i.startswith(text)] + return [] + + def do_set(self, data): + tokens = split(data) + try: + if tokens[0] == "time-format": + self.dispObject.do_setTimeFormat(tokens[1]) + else: + self.dataObject.do_set(data) + except Exception, e: + print "Exception in set command:", e + + def complete_list(self, text, line, begidx, endidx): + tokens = split(line[:begidx]) + if len(tokens) == 1: + return [i for i in ('agents', 'packages', 'classes ') if i.startswith(text)] + return [] + + def do_list(self, data): + try: + self.dataObject.do_list(data) + except Exception, e: + print "Exception in list command:", e + + def complete_show(self, text, line, begidx, endidx): + tokens = split(line[:begidx]) + if len(tokens) == 1: + return [i for i in ('filter', 'agent ', 'class ') if i.startswith(text)] + return [] + + def do_show(self, data): + try: + self.dataObject.do_show(data) + except Exception, e: + print "Exception in show command:", e + + def complete_query(self, text, line, begidx, endidx): + return [] + + def do_query(self, data): + try: + self.dataObject.do_query(data) + except Exception, e: + if e.message.__class__ == qmf2.Data: + e = e.message.getProperties() + print "Exception in query command:", e + + def do_call(self, data): + try: + self.dataObject.do_call(data) + except Exception, e: + if e.message.__class__ == qmf2.Data: + e = e.message.getProperties() + print "Exception in call command:", e + + def do_clear(self, data): + try: + self.dataObject.do_clear(data) + except Exception, e: + print "Exception in clear command:", e + + def do_EOF(self, data): + print "quit" + try: + self.dataObject.do_exit() + except: + pass + return True + + def do_quit(self, data): + try: + self.dataObject.do_exit() + except: + pass + return True + + def postcmd(self, stop, line): + return stop + + def postloop(self): + print "Exiting..." + self.dataObject.close() + + +#====================================================================================================== +# QmfData +#====================================================================================================== +class QmfData: + """ + """ + def __init__(self, disp, url): + self.disp = disp + self.url = url + self.agent_filter = '[]' + self.connection = cqpid.Connection(self.url) + self.connection.open() + self.session = qmf2.ConsoleSession(self.connection) + self.session.setAgentFilter(self.agent_filter) + self.session.open() + self.lock = Lock() + self.cli = None + self.agents = {} # Map of number => agent object + self.deleted_agents = {} # Map of number => agent object + self.agent_numbers = {} # Map of agent name => number + self.next_number = 1 + self.focus_agent = None + self.data_list = {} + self.next_data_index = 1 + + #======================= + # Methods to support CLI + #======================= + def setCli(self, cli): + self.cli = cli + + def close(self): + try: + self.session.close() + self.connection.close() + except: + pass # we're shutting down - ignore any errors + + def do_list(self, data): + tokens = data.split() + if len(tokens) == 0: + self.listData() + elif tokens[0] == 'agents' or tokens[0] == 'agent': + self.listAgents() + elif tokens[0] == 'packages' or tokens[0] == 'package': + self.listPackages() + elif tokens[0] == 'classes' or tokens[0] == 'class': + self.listClasses(tokens[1:]) + + def do_set(self, data): + tokens = split(data) + if len(tokens) == 0: + print "What do you want to set? type 'help' for more information." + return + if tokens[0] == 'filter': + if len(tokens) == 2: + self.setAgentFilter(tokens[1]) + elif tokens[0] == 'default': + if len(tokens) == 2: + self.updateAgents() + number = int(tokens[1]) + self.focus_agent = self.agents[number] + print "Default Agent: %s" % self.focus_agent.getName() + + def do_show(self, data): + tokens = split(data) + if len(tokens) == 0: + print "What do you want to show? Type 'help' for more information." + return + + if tokens[0] == 'agent': + self.showAgent(tokens[1:]) + return + + if tokens[0] == 'filter': + print self.agent_filter + return + + if tokens[0] == "default": + if not self.focus_agent: + self.updateAgents() + if self.focus_agent: + print "Default Agent: %s" % self.focus_agent.getName() + else: + print "Default Agent not set" + return + + if tokens[0] == "class": + self.showClass(tokens[1:]) + return + + if tokens[0].isdigit(): + self.showData(tokens[0]) + return + + print "What do you want to show? Type 'help' for more information." + return + + def do_query(self, data): + tokens = split(data) + if len(tokens) == 0: + print "Class name not specified." + return + cname = tokens[0] + pname = None + pred = None + if len(tokens) >= 2: + if tokens[1][0] == '[': + pred = tokens[1] + else: + pname = tokens[1] + if len(tokens) >= 3: + pred = tokens[2] + query = "{class:'%s'" % cname + if pname: + query += ",package:'%s'" % pname + if pred: + query += ",where:%s" % pred + query += "}" + if not self.focus_agent: + self.updateAgents() + d_list = self.focus_agent.query(query) + local_data_list = {} + for d in d_list: + local_data_list[self.next_data_index] = d + self.next_data_index += 1 + rows = [] + for index,val in local_data_list.items(): + rows.append((index, val.getAddr().getName())) + self.data_list[index] = val + self.disp.table("Data Objects Returned: %d:" % len(d_list), ("Number", "Data Address"), rows) + + def do_call(self, data): + tokens = split(data) + if len(tokens) < 2: + print "Data ID and method-name not specified." + return + idx = int(tokens[0]) + methodName = tokens[1] + args = [] + for arg in tokens[2:]: + ## + ## If the argument is a map, list, boolean, integer, or floating (one decimal point), + ## run it through the Python evaluator so it is converted to the correct type. + ## + ## TODO: use a regex for this instead of this convoluted logic + if arg[0] == '{' or arg[0] == '[' or arg == "True" or arg == "False" or \ + ((arg.count('.') < 2 and (arg.count('-') == 0 or \ + (arg.count('-') == 1 and arg[0] == '-')) and \ + arg.replace('.','').replace('-','').isdigit())): + args.append(eval(arg)) + else: + args.append(arg) + + if not idx in self.data_list: + print "Unknown data index, run 'query' to get a list of data indices" + return + + data = self.data_list[idx] + data._getSchema() + result = data._invoke(methodName, args, {}) + rows = [] + for k,v in result.items(): + rows.append((k,v)) + self.disp.table("Output Parameters:", ("Name", "Value"), rows) + + def do_clear(self, data): + self.data_list = {} + self.next_data_index = 1 + print "Accumulated query results cleared" + + def do_exit(self): + pass + + #==================== + # Sub-Command Methods + #==================== + def setAgentFilter(self, filt): + self.agent_filter = filt + self.session.setAgentFilter(filt) + + def updateAgents(self): + agents = self.session.getAgents() + number_list = [] + for agent in agents: + if agent.getName() not in self.agent_numbers: + number = self.next_number + number_list.append(number) + self.next_number += 1 + self.agent_numbers[agent.getName()] = number + self.agents[number] = agent + else: + ## Track seen agents so we can clean out deleted ones + number = self.agent_numbers[agent.getName()] + number_list.append(number) + if number in self.deleted_agents: + self.agents[number] = self.deleted_agents.pop(number) + deleted = [] + for number in self.agents: + if number not in number_list: + deleted.append(number) + for number in deleted: + self.deleted_agents[number] = self.agents.pop(number) + if not self.focus_agent: + self.focus_agent = self.session.getConnectedBrokerAgent() + + def listAgents(self): + self.updateAgents() + rows = [] + for number in self.agents: + agent = self.agents[number] + if self.focus_agent and agent.getName() == self.focus_agent.getName(): + d = '*' + else: + d = '' + rows.append((d, number, agent.getVendor(), agent.getProduct(), agent.getInstance(), agent.getEpoch())) + self.disp.table("QMF Agents:", ("", "Id", "Vendor", "Product", "Instance", "Epoch"), rows) + + def listPackages(self): + if not self.focus_agent: + raise "Default Agent not set - use 'set default'" + self.focus_agent.loadSchemaInfo() + packages = self.focus_agent.getPackages() + for p in packages: + print " %s" % p + + def getClasses(self, tokens): + if not self.focus_agent: + raise "Default Agent not set - use 'set default'" + return + self.focus_agent.loadSchemaInfo() + if len(tokens) == 1: + classes = self.focus_agent.getSchemaIds(tokens[0]); + else: + packages = self.focus_agent.getPackages() + classes = [] + for p in packages: + classes.extend(self.focus_agent.getSchemaIds(p)) + return classes + + def listClasses(self, tokens): + classes = self.getClasses(tokens) + rows = [] + for c in classes: + rows.append((c.getPackageName(), c.getName(), self.classTypeName(c.getType()))) + self.disp.table("Classes:", ("Package", "Class", "Type"), rows) + + def showClass(self, tokens): + if len(tokens) < 1: + return + classes = self.getClasses([]) + c = tokens[0] + p = None + if len(tokens) == 2: + p = tokens[1] + schema = None + sid = None + for cls in classes: + if c == cls.getName(): + if not p or p == cls.getPackageName(): + schema = self.focus_agent.getSchema(cls) + sid = cls + break + if not sid: + return + print "Class: %s:%s (%s) - %s" % \ + (sid.getPackageName(), sid.getName(), self.classTypeName(sid.getType()), schema.getDesc()) + print " hash: %r" % sid.getHash() + props = schema.getProperties() + methods = schema.getMethods() + rows = [] + for prop in props: + name = prop.getName() + dtype = self.typeName(prop.getType()) + if len(prop.getSubtype()) > 0: + dtype += "(%s)" % prop.getSubtype() + access = self.accessName(prop.getAccess()) + idx = self.yes_blank(prop.isIndex()) + opt = self.yes_blank(prop.isOptional()) + unit = prop.getUnit() + desc = prop.getDesc() + rows.append((name, dtype, idx, access, opt, unit, desc)) + self.disp.table("Properties:", ("Name", "Type", "Index", "Access", "Optional", "Unit", "Description"), rows) + if len(methods) > 0: + for meth in methods: + name = meth.getName() + desc = meth.getDesc() + if len(desc) > 0: + desc = " - " + desc + args = meth.getArguments() + rows = [] + for prop in args: + aname = prop.getName() + dtype = self.typeName(prop.getType()) + if len(prop.getSubtype()) > 0: + dtype += "(%s)" % prop.getSubtype() + unit = prop.getUnit() + adesc = prop.getDesc() + io = self.dirName(prop.getDirection()) + rows.append((aname, dtype, io, unit, adesc)) + print + print " Method: %s%s" % (name, desc) + self.disp.table("Arguments:", ("Name", "Type", "Dir", "Unit", "Description"), rows) + + def showAgent(self, tokens): + self.updateAgents() + for token in tokens: + number = int(token) + agent = self.agents[number] + print + print " ==================================================================================" + print " Agent Id: %d" % number + print " Agent Name: %s" % agent.getName() + print " Epoch: %d" % agent.getEpoch() + print " Attributes:" + attrs = agent.getAttributes() + keys = attrs.keys() + keys.sort() + pairs = [] + for key in keys: + if key == '_timestamp' or key == '_schema_updated': + val = disp.timestamp(attrs[key]) + else: + val = attrs[key] + pairs.append((key, val)) + self.printAlignedPairs(pairs) + agent.loadSchemaInfo() + print " Packages:" + packages = agent.getPackages() + for package in packages: + print " %s" % package + + def showData(self, idx): + num = int(idx) + if not num in self.data_list: + print "Data ID not known, run 'query' first to get data" + return + data = self.data_list[num] + props = data.getProperties() + rows = [] + for k,v in props.items(): + rows.append((k, v)) + self.disp.table("Properties:", ("Name", "Value"), rows) + + def listData(self): + if len(self.data_list) == 0: + print "No Query Results - Use the 'query' command" + return + rows = [] + for index,val in self.data_list.items(): + rows.append((index, val.getAgent().getName(), val.getAddr().getName())) + self.disp.table("Accumulated Query Results:", ('Number', 'Agent', 'Data Address'), rows) + + def printAlignedPairs(self, rows, indent=8): + maxlen = 0 + for first, second in rows: + if len(first) > maxlen: + maxlen = len(first) + maxlen += indent + for first, second in rows: + for i in range(maxlen - len(first)): + print "", + print "%s : %s" % (first, second) + + def classTypeName(self, code): + if code == qmf2.SCHEMA_TYPE_DATA: return "Data" + if code == qmf2.SCHEMA_TYPE_EVENT: return "Event" + return "Unknown" + + def typeName (self, typecode): + """ Convert type-codes to printable strings """ + if typecode == qmf2.SCHEMA_DATA_VOID: return "void" + elif typecode == qmf2.SCHEMA_DATA_BOOL: return "bool" + elif typecode == qmf2.SCHEMA_DATA_INT: return "int" + elif typecode == qmf2.SCHEMA_DATA_FLOAT: return "float" + elif typecode == qmf2.SCHEMA_DATA_STRING: return "string" + elif typecode == qmf2.SCHEMA_DATA_MAP: return "map" + elif typecode == qmf2.SCHEMA_DATA_LIST: return "list" + elif typecode == qmf2.SCHEMA_DATA_UUID: return "uuid" + else: + raise ValueError ("Invalid type code: %s" % str(typecode)) + + def valueByType(self, typecode, val): + if typecode == 1: return "%d" % val + elif typecode == 2: return "%d" % val + elif typecode == 3: return "%d" % val + elif typecode == 4: return "%d" % val + elif typecode == 6: return val + elif typecode == 7: return val + elif typecode == 8: return strftime("%c", gmtime(val / 1000000000)) + elif typecode == 9: + if val < 0: val = 0 + sec = val / 1000000000 + min = sec / 60 + hour = min / 60 + day = hour / 24 + result = "" + if day > 0: + result = "%dd " % day + if hour > 0 or result != "": + result += "%dh " % (hour % 24) + if min > 0 or result != "": + result += "%dm " % (min % 60) + result += "%ds" % (sec % 60) + return result + + elif typecode == 10: return str(self.idRegistry.displayId(val)) + elif typecode == 11: + if val: + return "True" + else: + return "False" + + elif typecode == 12: return "%f" % val + elif typecode == 13: return "%f" % val + elif typecode == 14: return "%r" % val + elif typecode == 15: return "%r" % val + elif typecode == 16: return "%d" % val + elif typecode == 17: return "%d" % val + elif typecode == 18: return "%d" % val + elif typecode == 19: return "%d" % val + elif typecode == 20: return "%r" % val + elif typecode == 21: return "%r" % val + elif typecode == 22: return "%r" % val + else: + raise ValueError ("Invalid type code: %s" % str(typecode)) + + def accessName (self, code): + """ Convert element access codes to printable strings """ + if code == qmf2.ACCESS_READ_CREATE: return "ReadCreate" + elif code == qmf2.ACCESS_READ_WRITE: return "ReadWrite" + elif code == qmf2.ACCESS_READ_ONLY: return "ReadOnly" + else: + raise ValueError ("Invalid access code: %s" % str(code)) + + def dirName(self, io): + if io == qmf2.DIR_IN: return "in" + elif io == qmf2.DIR_OUT: return "out" + elif io == qmf2.DIR_IN_OUT: return "in_out" + else: + raise ValueError("Invalid direction code: %r" % io) + + def notNone (self, text): + if text == None: + return "" + else: + return text + + def yes_blank(self, val): + if val: + return "Y" + return "" + + def objectIndex(self, obj): + if obj._objectId.isV2: + return obj._objectId.getObject() + result = "" + first = True + props = obj.getProperties() + for prop in props: + if prop[0].index: + if not first: + result += "." + result += self.valueByType(prop[0].type, prop[1]) + first = None + return result + +def Usage(): + print "Usage: qpid-tool [[<username>/<password>@]<target-host>[:<tcp-port>]]" + print + +#========================================================= +# Main Program +#========================================================= + +# Get host name and port if specified on the command line +cargs = sys.argv[1:] +_host = "localhost" + +if len(cargs) > 0: + _host = cargs[0] + +if _host[0] == '-': + Usage() + if _host != '-h' and _host != "--help": + print "qpid-tool: error: no such option:", _host + sys.exit(1) + +disp = Display() + +# Attempt to make a connection to the target broker +try: + data = QmfData(disp, _host) +except Exception, e: + if str(e).find("Exchange not found") != -1: + print "Management not enabled on broker: Use '-m yes' option on broker startup." + else: + print "Failed: %s - %s" % (e.__class__.__name__, e) + sys.exit(1) + +# Instantiate the CLI interpreter and launch it. +cli = Mcli(data, disp) +print("Management Tool for QMF") +try: + cli.cmdloop() +except KeyboardInterrupt: + print + print "Exiting..." +except Exception, e: + print "Failed: %s - %s" % (e.__class__.__name__, e) + +# alway attempt to cleanup broker resources +data.close() |