#!/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. # ############################################################################### ## This file is being obsoleted by qmf/console.py ############################################################################### import qpid import re import socket import struct import os import platform import locale from qpid.connection import Timeout from qpid.management import managementChannel, managementClient from threading import Lock from disp import Display from shlex import split from qpid.connection import Connection from qpid.util import connect class Broker: def __init__ (self, text): rex = re.compile(r""" # [ [ / ] @] [ : ] ^ (?: ([^/]*) (?: / ([^@]*) )? @)? ([^:]+) (?: :([0-9]+))?$""", re.X) match = rex.match(text) if not match: raise ValueError("'%s' is not a valid broker url" % (text)) user, password, host, port = match.groups() if port: self.port = int(port) else: self.port = 5672 for addr in socket.getaddrinfo(host, self.port): if addr[1] == socket.AF_INET: self.host = addr[4][0] self.username = user or "guest" self.password = password or "guest" def name (self): return self.host + ":" + str (self.port) class ManagementData: # # Data Structure: # # Please note that this data structure holds only the most recent # configuration and instrumentation data for each object. It does # not hold the detailed historical data that is sent from the broker. # The only historical data it keeps are the high and low watermarks # for hi-lo statistics. # # tables :== {class-key} # {} # (timestamp, config-record, inst-record) # class-key :== (, , ) # timestamp :== (, , ) # config-record :== [element] # inst-record :== [element] # element :== (, ) # def registerObjId (self, objId): if not objId.index() in self.idBackMap: self.idBackMap[objId.index()] = self.nextId self.idMap[self.nextId] = objId self.nextId += 1 def displayObjId (self, objIdIndex): if objIdIndex in self.idBackMap: return self.idBackMap[objIdIndex] else: return 0 def rawObjId (self, displayId): if displayId in self.idMap: return self.idMap[displayId] else: return None def displayClassName (self, cls): (packageName, className, hash) = cls rev = self.schema[cls][4] if rev == 0: suffix = "" else: suffix = ".%d" % rev return packageName + ":" + className + suffix def dataHandler (self, context, className, list, timestamps): """ Callback for configuration and instrumentation data updates """ self.lock.acquire () try: # If this class has not been seen before, create an empty dictionary to # hold objects of this class if className not in self.tables: self.tables[className] = {} # Register the ID so a more friendly presentation can be displayed objId = list[0][1] oidx = objId.index() self.registerObjId (objId) # If this object hasn't been seen before, create a new object record with # the timestamps and empty lists for configuration and instrumentation data. if oidx not in self.tables[className]: self.tables[className][oidx] = (timestamps, [], []) (unused, oldConf, oldInst) = self.tables[className][oidx] # For config updates, simply replace old config list with the new one. if context == 0: #config self.tables[className][oidx] = (timestamps, list, oldInst) # For instrumentation updates, carry the minimum and maximum values for # "hi-lo" stats forward. elif context == 1: #inst if len (oldInst) == 0: newInst = list else: newInst = [] for idx in range (len (list)): (key, value) = list[idx] if key.find ("High") == len (key) - 4: if oldInst[idx][1] > value: value = oldInst[idx][1] if key.find ("Low") == len (key) - 3: if oldInst[idx][1] < value: value = oldInst[idx][1] newInst.append ((key, value)) self.tables[className][oidx] = (timestamps, oldConf, newInst) finally: self.lock.release () def ctrlHandler (self, context, op, data): if op == self.mclient.CTRL_BROKER_INFO: pass elif op == self.mclient.CTRL_HEARTBEAT: pass def configHandler (self, context, className, list, timestamps): self.dataHandler (0, className, list, timestamps); def instHandler (self, context, className, list, timestamps): self.dataHandler (1, className, list, timestamps); def methodReply (self, broker, sequence, status, sText, args): """ Callback for method-reply messages """ self.lock.acquire () try: line = "Call Result: " + self.methodsPending[sequence] + \ " " + str (status) + " (" + sText + ")" print line, args del self.methodsPending[sequence] finally: self.lock.release () def closeHandler (self, context, reason): if self.operational: print "Connection to broker lost:", reason self.operational = False if self.cli != None: self.cli.setPromptMessage ("Broker Disconnected") def schemaHandler (self, context, classKey, configs, insts, methods, events): """ Callback for schema updates """ if classKey not in self.schema: schemaRev = 0 for key in self.schema: if classKey[0] == key[0] and classKey[1] == key[1]: schemaRev += 1 self.schema[classKey] = (configs, insts, methods, events, schemaRev) def setCli (self, cliobj): self.cli = cliobj def __init__ (self, disp, host, username="guest", password="guest"): self.lock = Lock () self.tables = {} self.schema = {} self.bootSequence = 0 self.operational = False self.disp = disp self.cli = None self.lastUnit = None self.methodSeq = 1 self.methodsPending = {} self.sessionId = "%s.%d" % (platform.uname()[1], os.getpid()) self.broker = Broker (host) sock = connect (self.broker.host, self.broker.port) oldTimeout = sock.gettimeout() sock.settimeout(10) self.conn = Connection (sock, username=self.broker.username, password=self.broker.password) def aborted(): raise Timeout("Waiting for connection to be established with broker") oldAborted = self.conn.aborted self.conn.aborted = aborted self.conn.start () sock.settimeout(oldTimeout) self.conn.aborted = oldAborted self.mclient = managementClient ("unused", self.ctrlHandler, self.configHandler, self.instHandler, self.methodReply, self.closeHandler) self.mclient.schemaListener (self.schemaHandler) self.mch = self.mclient.addChannel (self.conn.session(self.sessionId)) self.operational = True self.idMap = {} self.idBackMap = {} self.nextId = 101 def close (self): pass def refName (self, oid): if oid == None: return "NULL" return str (self.displayObjId (oid.index())) def valueDisplay (self, classKey, key, value): if value == None: return "" for kind in range (2): schema = self.schema[classKey][kind] for item in schema: if item[0] == key: typecode = item[1] unit = item[2] if (typecode >= 1 and typecode <= 5) or typecode == 12 or typecode == 13 or \ (typecode >= 16 and typecode <= 19): if unit == None or unit == self.lastUnit: return str (value) else: self.lastUnit = unit suffix = "" if value != 1: suffix = "s" return str (value) + " " + unit + suffix elif typecode == 6 or typecode == 7: # strings return value elif typecode == 8: if value == 0: return "--" return self.disp.timestamp (value) elif typecode == 9: return str (value) elif typecode == 10: return self.refName (value) elif typecode == 11: if value == 0: return "False" else: return "True" elif typecode == 14: return str (value) elif typecode == 15: return str (value) return "*type-error*" def getObjIndex (self, classKey, config): """ Concatenate the values from index columns to form a unique object name """ result = "" schemaConfig = self.schema[classKey][0] for item in schemaConfig: if item[5] == 1 and item[0] != "id": if result != "": result = result + "." for key,val in config: if key == item[0]: result = result + self.valueDisplay (classKey, key, val) return result def getClassKey (self, className): delimPos = className.find(":") if delimPos == -1: schemaRev = 0 delim = className.find(".") if delim != -1: schemaRev = int(className[delim + 1:]) name = className[0:delim] else: name = className for key in self.schema: if key[1] == name and self.schema[key][4] == schemaRev: return key else: package = className[0:delimPos] name = className[delimPos + 1:] schemaRev = 0 delim = name.find(".") if delim != -1: schemaRev = int(name[delim + 1:]) name = name[0:delim] for key in self.schema: if key[0] == package and key[1] == name: if self.schema[key][4] == schemaRev: return key return None def classCompletions (self, prefix): """ Provide a list of candidate class names for command completion """ self.lock.acquire () complist = [] try: for name in self.tables: if name.find (prefix) == 0: complist.append (name) finally: self.lock.release () return complist def typeName (self, typecode): """ Convert type-codes to printable strings """ if typecode == 1: return "uint8" elif typecode == 2: return "uint16" elif typecode == 3: return "uint32" elif typecode == 4: return "uint64" elif typecode == 5: return "bool" elif typecode == 6: return "short-string" elif typecode == 7: return "long-string" elif typecode == 8: return "abs-time" elif typecode == 9: return "delta-time" elif typecode == 10: return "reference" elif typecode == 11: return "boolean" elif typecode == 12: return "float" elif typecode == 13: return "double" elif typecode == 14: return "uuid" elif typecode == 15: return "field-table" elif typecode == 16: return "int8" elif typecode == 17: return "int16" elif typecode == 18: return "int32" elif typecode == 19: return "int64" elif typecode == 20: return "object" elif typecode == 21: return "list" elif typecode == 22: return "array" else: raise ValueError ("Invalid type code: %d" % typecode) def accessName (self, code): """ Convert element access codes to printable strings """ if code == 1: return "ReadCreate" elif code == 2: return "ReadWrite" elif code == 3: return "ReadOnly" else: raise ValueError ("Invalid access code: %d" %code) def notNone (self, text): if text == None: return "" else: return text def isOid (self, id): for char in str (id): if not char.isdigit () and not char == '-': return False return True def listOfIds (self, classKey, tokens): """ Generate a tuple of object ids for a classname based on command tokens. """ list = [] if len(tokens) == 0 or tokens[0] == "all": for id in self.tables[classKey]: list.append (self.displayObjId (id)) elif tokens[0] == "active": for id in self.tables[classKey]: if self.tables[classKey][id][0][2] == 0: list.append (self.displayObjId (id)) else: for token in tokens: if self.isOid (token): if token.find ("-") != -1: ids = token.split("-", 2) for id in range (int (ids[0]), int (ids[1]) + 1): if self.getClassForId (self.rawObjId (long (id))) == classKey: list.append (id) else: list.append (int(token)) list.sort () result = () for item in list: result = result + (item,) return result def listClasses (self): """ Generate a display of the list of classes """ self.lock.acquire () try: rows = [] sorted = self.tables.keys () sorted.sort () for name in sorted: active = 0 deleted = 0 for record in self.tables[name]: isdel = False ts = self.tables[name][record][0] if ts[2] > 0: isdel = True if isdel: deleted = deleted + 1 else: active = active + 1 rows.append ((self.displayClassName (name), active, deleted)) if len (rows) != 0: self.disp.table ("Management Object Types:", ("ObjectType", "Active", "Deleted"), rows) else: print "Waiting for next periodic update" finally: self.lock.release () def listObjects (self, tokens): """ Generate a display of a list of objects in a class """ if len(tokens) == 0: print "Error - No class name provided" return self.lock.acquire () try: classKey = self.getClassKey (tokens[0]) if classKey == None: print ("Object type %s not known" % tokens[0]) else: rows = [] if classKey in self.tables: ids = self.listOfIds(classKey, tokens[1:]) for objId in ids: (ts, config, inst) = self.tables[classKey][self.rawObjId(objId).index()] createTime = self.disp.timestamp (ts[1]) destroyTime = "-" if ts[2] > 0: destroyTime = self.disp.timestamp (ts[2]) objIndex = self.getObjIndex (classKey, config) row = (objId, createTime, destroyTime, objIndex) rows.append (row) self.disp.table ("Objects of type %s" % self.displayClassName(classKey), ("ID", "Created", "Destroyed", "Index"), rows) finally: self.lock.release () def showObjects (self, tokens): """ Generate a display of object data for a particular class """ self.lock.acquire () try: self.lastUnit = None if self.isOid (tokens[0]): if tokens[0].find ("-") != -1: rootId = int (tokens[0][0:tokens[0].find ("-")]) else: rootId = int (tokens[0]) classKey = self.getClassForId (self.rawObjId (rootId)) remaining = tokens if classKey == None: print "Id not known: %d" % int (tokens[0]) raise ValueError () else: classKey = self.getClassKey (tokens[0]) remaining = tokens[1:] if classKey not in self.tables: print "Class not known: %s" % tokens[0] raise ValueError () userIds = self.listOfIds (classKey, remaining) if len (userIds) == 0: print "No object IDs supplied" raise ValueError () ids = [] for id in userIds: if self.getClassForId (self.rawObjId (long (id))) == classKey: ids.append (self.rawObjId (long (id))) rows = [] timestamp = None config = self.tables[classKey][ids[0].index()][1] for eIdx in range (len (config)): key = config[eIdx][0] if key != "id": row = ("property", key) for id in ids: if timestamp == None or \ timestamp < self.tables[classKey][id.index()][0][0]: timestamp = self.tables[classKey][id.index()][0][0] (key, value) = self.tables[classKey][id.index()][1][eIdx] row = row + (self.valueDisplay (classKey, key, value),) rows.append (row) inst = self.tables[classKey][ids[0].index()][2] for eIdx in range (len (inst)): key = inst[eIdx][0] if key != "id": row = ("statistic", key) for id in ids: (key, value) = self.tables[classKey][id.index()][2][eIdx] row = row + (self.valueDisplay (classKey, key, value),) rows.append (row) titleRow = ("Type", "Element") for id in ids: titleRow = titleRow + (self.refName(id),) caption = "Object of type %s:" % self.displayClassName(classKey) if timestamp != None: caption = caption + " (last sample time: " + self.disp.timestamp (timestamp) + ")" self.disp.table (caption, titleRow, rows) except: pass self.lock.release () def schemaSummary (self): """ Generate a display of the list of classes in the schema """ self.lock.acquire () try: rows = [] sorted = self.schema.keys () sorted.sort () for classKey in sorted: tuple = self.schema[classKey] row = (self.displayClassName(classKey), len (tuple[0]), len (tuple[1]), len (tuple[2])) rows.append (row) self.disp.table ("Classes in Schema:", ("Class", "Properties", "Statistics", "Methods"), rows) finally: self.lock.release () def schemaTable (self, className): """ Generate a display of details of the schema of a particular class """ self.lock.acquire () try: classKey = self.getClassKey (className) if classKey == None: print ("Class name %s not known" % className) raise ValueError () rows = [] schemaRev = self.schema[classKey][4] for config in self.schema[classKey][0]: name = config[0] if name != "id": typename = self.typeName(config[1]) unit = self.notNone (config[2]) desc = self.notNone (config[3]) access = self.accessName (config[4]) extra = "" if config[5] == 1: extra += "index " if config[6] != None: extra += "Min: " + str(config[6]) + " " if config[7] != None: extra += "Max: " + str(config[7]) + " " if config[8] != None: extra += "MaxLen: " + str(config[8]) + " " if config[9] == 1: extra += "optional " rows.append ((name, typename, unit, access, extra, desc)) for config in self.schema[classKey][1]: name = config[0] if name != "id": typename = self.typeName(config[1]) unit = self.notNone (config[2]) desc = self.notNone (config[3]) rows.append ((name, typename, unit, "", "", desc)) titles = ("Element", "Type", "Unit", "Access", "Notes", "Description") self.disp.table ("Schema for class '%s':" % self.displayClassName(classKey), titles, rows) for mname in self.schema[classKey][2]: (mdesc, args) = self.schema[classKey][2][mname] caption = "\nMethod '%s' %s" % (mname, self.notNone (mdesc)) rows = [] for arg in args: name = arg[0] typename = self.typeName (arg[1]) dir = arg[2] unit = self.notNone (arg[3]) desc = self.notNone (arg[4]) extra = "" if arg[5] != None: extra = extra + "Min: " + str (arg[5]) if arg[6] != None: extra = extra + "Max: " + str (arg[6]) if arg[7] != None: extra = extra + "MaxLen: " + str (arg[7]) if arg[8] != None: extra = extra + "Default: " + str (arg[8]) rows.append ((name, typename, dir, unit, extra, desc)) titles = ("Argument", "Type", "Direction", "Unit", "Notes", "Description") self.disp.table (caption, titles, rows) except Exception,e: pass self.lock.release () def getClassForId (self, objId): """ Given an object ID, return the class key for the referenced object """ for classKey in self.tables: if objId.index() in self.tables[classKey]: return classKey return None def callMethod (self, userOid, methodName, args): self.lock.acquire () methodOk = True try: classKey = self.getClassForId (self.rawObjId (userOid)) if classKey == None: raise ValueError () if methodName not in self.schema[classKey][2]: print "Method '%s' not valid for class '%s'" % (methodName, self.displayClassName(classKey)) raise ValueError () schemaMethod = self.schema[classKey][2][methodName] count = 0 for arg in range(len(schemaMethod[1])): if schemaMethod[1][arg][2].find("I") != -1: count += 1 if len (args) != count: print "Wrong number of method args: Need %d, Got %d" % (count, len (args)) raise ValueError () namedArgs = {} idx = 0 for arg in range(len(schemaMethod[1])): if schemaMethod[1][arg][2].find("I") != -1: namedArgs[schemaMethod[1][arg][0]] = args[idx] idx += 1 self.methodSeq = self.methodSeq + 1 self.methodsPending[self.methodSeq] = methodName except Exception, e: methodOk = False self.lock.release () if methodOk: # try: self.mclient.callMethod (self.mch, self.methodSeq, self.rawObjId (userOid), classKey, methodName, namedArgs) # except ValueError, e: # print "Error invoking method:", e def makeIdRow (self, displayId): if displayId in self.idMap: objId = self.idMap[displayId] else: return None if objId.getFlags() == 0: flags = "" else: flags = str(objId.getFlags()) seq = objId.getSequence() if seq == 0: seqText = "" else: seqText = str(seq) return (displayId, flags, seqText, objId.getBroker(), objId.getBank(), hex(objId.getObject())) def listIds (self, select): rows = [] if select == 0: sorted = self.idMap.keys() sorted.sort() for displayId in sorted: row = self.makeIdRow (displayId) rows.append(row) else: row = self.makeIdRow (select) if row == None: print "Display Id %d not known" % select return rows.append(row) self.disp.table("Translation of Display IDs:", ("DisplayID", "Flags", "BootSequence", "Broker", "Bank", "Object"), rows) def do_list (self, data): tokens = data.split () if len (tokens) == 0: self.listClasses () else: self.listObjects (tokens) def do_show (self, data): tokens = data.split () self.showObjects (tokens) def do_schema (self, data): if data == "": self.schemaSummary () else: self.schemaTable (data) def do_call (self, data): encTokens = data.split () try: tokens = [a.decode(locale.getpreferredencoding()) for a in encArgs] except: tokens = encTokens if len (tokens) < 2: print "Not enough arguments supplied" return displayId = long (tokens[0]) methodName = tokens[1] args = tokens[2:] self.callMethod (displayId, methodName, args) def do_id (self, data): if data == "": select = 0 else: select = int(data) self.listIds(select) def do_exit (self): self.mclient.removeChannel (self.mch)