| #!/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 |
| from optparse import OptionParser, OptionGroup |
| from time import sleep ### debug |
| import sys |
| import locale |
| import socket |
| import re |
| from qmf.console import Session, Console |
| from qpid.disp import Display, Header, Sorter |
| |
| class Config: |
| def __init__(self): |
| self._host = "localhost" |
| self._connTimeout = 10 |
| self._types = "" |
| self._limit = 50 |
| self._increasing = False |
| self._sortcol = None |
| self._cluster_detail = False |
| self._sasl_mechanism = None |
| |
| config = Config() |
| |
| def OptionsAndArguments(argv): |
| """ Set global variables for options, return arguments """ |
| |
| global config |
| |
| parser = OptionParser(usage="usage: %prog [options] BROKER", |
| description="Example: $ qpid-stat -q broker-host:10000") |
| |
| group1 = OptionGroup(parser, "General Options") |
| group1.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>", help="Maximum time to wait for broker connection (in seconds)") |
| group1.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>", help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.") |
| parser.add_option_group(group1) |
| |
| group2 = OptionGroup(parser, "Display Options") |
| group2.add_option("-b", "--broker", help="Show Brokers", |
| action="store_const", const="b", dest="show") |
| group2.add_option("-c", "--connections", help="Show Connections", |
| action="store_const", const="c", dest="show") |
| group2.add_option("-e", "--exchanges", help="Show Exchanges", |
| action="store_const", const="e", dest="show") |
| group2.add_option("-q", "--queues", help="Show Queues", |
| action="store_const", const="q", dest="show") |
| group2.add_option("-u", "--subscriptions", help="Show Subscriptions", |
| action="store_const", const="u", dest="show") |
| group2.add_option("-S", "--sort-by", metavar="<colname>", |
| help="Sort by column name") |
| group2.add_option("-I", "--increasing", action="store_true", default=False, |
| help="Sort by increasing value (default = decreasing)") |
| group2.add_option("-L", "--limit", default=50, metavar="<n>", |
| help="Limit output to n rows") |
| group2.add_option("-C", "--cluster", action="store_true", default=False, |
| help="Display per-broker cluster detail.") |
| parser.add_option_group(group2) |
| |
| opts, args = parser.parse_args(args=argv) |
| |
| if not opts.show: |
| parser.error("You must specify one of these options: -b, -c, -e, -q. or -u. For details, try $ qpid-stat --help") |
| |
| config._types = opts.show |
| config._sortcol = opts.sort_by |
| config._connTimeout = opts.timeout |
| config._increasing = opts.increasing |
| config._limit = opts.limit |
| config._cluster_detail = opts.cluster |
| config._sasl_mechanism = opts.sasl_mechanism |
| |
| if args: |
| config._host = args[0] |
| |
| return args |
| |
| class IpAddr: |
| def __init__(self, text): |
| if text.find("@") != -1: |
| tokens = text.split("@") |
| text = tokens[1] |
| if text.find(":") != -1: |
| tokens = text.split(":") |
| text = tokens[0] |
| self.port = int(tokens[1]) |
| else: |
| self.port = 5672 |
| self.dottedQuad = socket.gethostbyname(text) |
| nums = self.dottedQuad.split(".") |
| self.addr = (int(nums[0]) << 24) + (int(nums[1]) << 16) + (int(nums[2]) << 8) + int(nums[3]) |
| |
| def bestAddr(self, addrPortList): |
| bestDiff = 0xFFFFFFFFL |
| bestAddr = None |
| for addrPort in addrPortList: |
| diff = IpAddr(addrPort[0]).addr ^ self.addr |
| if diff < bestDiff: |
| bestDiff = diff |
| bestAddr = addrPort |
| return bestAddr |
| |
| class Broker(object): |
| def __init__(self, qmf, broker): |
| self.broker = broker |
| |
| agents = qmf.getAgents() |
| for a in agents: |
| if a.getAgentBank() == '0': |
| self.brokerAgent = a |
| |
| bobj = qmf.getObjects(_class="broker", _package="org.apache.qpid.broker", _agent=self.brokerAgent)[0] |
| self.currentTime = bobj.getTimestamps()[0] |
| try: |
| self.uptime = bobj.uptime |
| except: |
| self.uptime = 0 |
| self.connections = {} |
| self.sessions = {} |
| self.exchanges = {} |
| self.queues = {} |
| self.subscriptions = {} |
| package = "org.apache.qpid.broker" |
| |
| list = qmf.getObjects(_class="connection", _package=package, _agent=self.brokerAgent) |
| for conn in list: |
| if not conn.shadow: |
| self.connections[conn.getObjectId()] = conn |
| |
| list = qmf.getObjects(_class="session", _package=package, _agent=self.brokerAgent) |
| for sess in list: |
| if sess.connectionRef in self.connections: |
| self.sessions[sess.getObjectId()] = sess |
| |
| list = qmf.getObjects(_class="exchange", _package=package, _agent=self.brokerAgent) |
| for exchange in list: |
| self.exchanges[exchange.getObjectId()] = exchange |
| |
| list = qmf.getObjects(_class="queue", _package=package, _agent=self.brokerAgent) |
| for queue in list: |
| self.queues[queue.getObjectId()] = queue |
| |
| list = qmf.getObjects(_class="subscription", _package=package, _agent=self.brokerAgent) |
| for subscription in list: |
| self.subscriptions[subscription.getObjectId()] = subscription |
| |
| def getName(self): |
| return self.broker.getUrl() |
| |
| def getCurrentTime(self): |
| return self.currentTime |
| |
| def getUptime(self): |
| return self.uptime |
| |
| class BrokerManager(Console): |
| def __init__(self): |
| self.brokerName = None |
| self.qmf = None |
| self.broker = None |
| self.brokers = [] |
| self.cluster = None |
| |
| def SetBroker(self, brokerUrl, mechanism): |
| self.url = brokerUrl |
| self.qmf = Session() |
| self.mechanism = mechanism |
| self.broker = self.qmf.addBroker(brokerUrl, config._connTimeout, mechanism) |
| agents = self.qmf.getAgents() |
| for a in agents: |
| if a.getAgentBank() == '0': |
| self.brokerAgent = a |
| |
| def Disconnect(self): |
| """ Release any allocated brokers. Ignore any failures as the tool is |
| shutting down. |
| """ |
| try: |
| if self.broker: |
| self.qmf.delBroker(self.broker) |
| else: |
| for b in self.brokers: self.qmf.delBroker(b.broker) |
| except: |
| pass |
| |
| def _getCluster(self): |
| packages = self.qmf.getPackages() |
| if "org.apache.qpid.cluster" not in packages: |
| return None |
| |
| clusters = self.qmf.getObjects(_class="cluster", _agent=self.brokerAgent) |
| if len(clusters) == 0: |
| print "Clustering is installed but not enabled on the broker." |
| return None |
| |
| self.cluster = clusters[0] |
| |
| def _getHostList(self, urlList): |
| hosts = [] |
| hostAddr = IpAddr(config._host) |
| for url in urlList: |
| if url.find("amqp:") != 0: |
| raise Exception("Invalid URL 1") |
| url = url[5:] |
| addrs = str(url).split(",") |
| addrList = [] |
| for addr in addrs: |
| tokens = addr.split(":") |
| if len(tokens) != 3: |
| raise Exception("Invalid URL 2") |
| addrList.append((tokens[1], tokens[2])) |
| |
| # Find the address in the list that is most likely to be in the same subnet as the address |
| # with which we made the original QMF connection. This increases the probability that we will |
| # be able to reach the cluster member. |
| |
| best = hostAddr.bestAddr(addrList) |
| bestUrl = best[0] + ":" + best[1] |
| hosts.append(bestUrl) |
| return hosts |
| |
| def displaySubs(self, subs, indent, broker=None, conn=None, sess=None, exchange=None, queue=None): |
| if len(subs) == 0: |
| return |
| this = subs[0] |
| remaining = subs[1:] |
| newindent = indent + " " |
| if this == 'b': |
| pass |
| elif this == 'c': |
| if broker: |
| for oid in broker.connections: |
| iconn = broker.connections[oid] |
| self.printConnSub(indent, broker.getName(), iconn) |
| self.displaySubs(remaining, newindent, broker=broker, conn=iconn, |
| sess=sess, exchange=exchange, queue=queue) |
| elif this == 's': |
| pass |
| elif this == 'e': |
| pass |
| elif this == 'q': |
| pass |
| print |
| |
| def displayBroker(self, subs): |
| disp = Display(prefix=" ") |
| heads = [] |
| heads.append(Header('broker')) |
| heads.append(Header('cluster')) |
| heads.append(Header('uptime', Header.DURATION)) |
| heads.append(Header('conn', Header.KMG)) |
| heads.append(Header('sess', Header.KMG)) |
| heads.append(Header('exch', Header.KMG)) |
| heads.append(Header('queue', Header.KMG)) |
| rows = [] |
| for broker in self.brokers: |
| if self.cluster: |
| ctext = "%s(%s)" % (self.cluster.clusterName, self.cluster.status) |
| else: |
| ctext = "<standalone>" |
| row = (broker.getName(), ctext, broker.getUptime(), |
| len(broker.connections), len(broker.sessions), |
| len(broker.exchanges), len(broker.queues)) |
| rows.append(row) |
| title = "Brokers" |
| if config._sortcol: |
| sorter = Sorter(heads, rows, config._sortcol, config._limit, config._increasing) |
| dispRows = sorter.getSorted() |
| else: |
| dispRows = rows |
| disp.formattedTable(title, heads, dispRows) |
| |
| def displayConn(self, subs): |
| disp = Display(prefix=" ") |
| heads = [] |
| if self.cluster: |
| heads.append(Header('broker')) |
| heads.append(Header('client-addr')) |
| heads.append(Header('cproc')) |
| heads.append(Header('cpid')) |
| heads.append(Header('auth')) |
| heads.append(Header('connected', Header.DURATION)) |
| heads.append(Header('idle', Header.DURATION)) |
| heads.append(Header('msgIn', Header.KMG)) |
| heads.append(Header('msgOut', Header.KMG)) |
| rows = [] |
| for broker in self.brokers: |
| for oid in broker.connections: |
| conn = broker.connections[oid] |
| row = [] |
| if self.cluster: |
| row.append(broker.getName()) |
| row.append(conn.address) |
| row.append(conn.remoteProcessName) |
| row.append(conn.remotePid) |
| row.append(conn.authIdentity) |
| row.append(broker.getCurrentTime() - conn.getTimestamps()[1]) |
| idle = broker.getCurrentTime() - conn.getTimestamps()[0] |
| row.append(broker.getCurrentTime() - conn.getTimestamps()[0]) |
| row.append(conn.framesFromClient) |
| row.append(conn.framesToClient) |
| rows.append(row) |
| title = "Connections" |
| if self.cluster: |
| title += " for cluster '%s'" % self.cluster.clusterName |
| if config._sortcol: |
| sorter = Sorter(heads, rows, config._sortcol, config._limit, config._increasing) |
| dispRows = sorter.getSorted() |
| else: |
| dispRows = rows |
| disp.formattedTable(title, heads, dispRows) |
| |
| def displaySession(self, subs): |
| disp = Display(prefix=" ") |
| |
| def displayExchange(self, subs): |
| disp = Display(prefix=" ") |
| heads = [] |
| if self.cluster: |
| heads.append(Header('broker')) |
| heads.append(Header("exchange")) |
| heads.append(Header("type")) |
| heads.append(Header("dur", Header.Y)) |
| heads.append(Header("bind", Header.KMG)) |
| heads.append(Header("msgIn", Header.KMG)) |
| heads.append(Header("msgOut", Header.KMG)) |
| heads.append(Header("msgDrop", Header.KMG)) |
| heads.append(Header("byteIn", Header.KMG)) |
| heads.append(Header("byteOut", Header.KMG)) |
| heads.append(Header("byteDrop", Header.KMG)) |
| rows = [] |
| for broker in self.brokers: |
| for oid in broker.exchanges: |
| ex = broker.exchanges[oid] |
| row = [] |
| if self.cluster: |
| row.append(broker.getName()) |
| row.append(ex.name) |
| row.append(ex.type) |
| row.append(ex.durable) |
| row.append(ex.bindingCount) |
| row.append(ex.msgReceives) |
| row.append(ex.msgRoutes) |
| row.append(ex.msgDrops) |
| row.append(ex.byteReceives) |
| row.append(ex.byteRoutes) |
| row.append(ex.byteDrops) |
| rows.append(row) |
| title = "Exchanges" |
| if self.cluster: |
| title += " for cluster '%s'" % self.cluster.clusterName |
| if config._sortcol: |
| sorter = Sorter(heads, rows, config._sortcol, config._limit, config._increasing) |
| dispRows = sorter.getSorted() |
| else: |
| dispRows = rows |
| disp.formattedTable(title, heads, dispRows) |
| |
| def displayQueue(self, subs): |
| disp = Display(prefix=" ") |
| heads = [] |
| if self.cluster: |
| heads.append(Header('broker')) |
| heads.append(Header("queue")) |
| heads.append(Header("dur", Header.Y)) |
| heads.append(Header("autoDel", Header.Y)) |
| heads.append(Header("excl", Header.Y)) |
| heads.append(Header("msg", Header.KMG)) |
| heads.append(Header("msgIn", Header.KMG)) |
| heads.append(Header("msgOut", Header.KMG)) |
| heads.append(Header("bytes", Header.KMG)) |
| heads.append(Header("bytesIn", Header.KMG)) |
| heads.append(Header("bytesOut", Header.KMG)) |
| heads.append(Header("cons", Header.KMG)) |
| heads.append(Header("bind", Header.KMG)) |
| rows = [] |
| for broker in self.brokers: |
| for oid in broker.queues: |
| q = broker.queues[oid] |
| row = [] |
| if self.cluster: |
| row.append(broker.getName()) |
| row.append(q.name) |
| row.append(q.durable) |
| row.append(q.autoDelete) |
| row.append(q.exclusive) |
| row.append(q.msgDepth) |
| row.append(q.msgTotalEnqueues) |
| row.append(q.msgTotalDequeues) |
| row.append(q.byteDepth) |
| row.append(q.byteTotalEnqueues) |
| row.append(q.byteTotalDequeues) |
| row.append(q.consumerCount) |
| row.append(q.bindingCount) |
| rows.append(row) |
| title = "Queues" |
| if self.cluster: |
| title += " for cluster '%s'" % self.cluster.clusterName |
| if config._sortcol: |
| sorter = Sorter(heads, rows, config._sortcol, config._limit, config._increasing) |
| dispRows = sorter.getSorted() |
| else: |
| dispRows = rows |
| disp.formattedTable(title, heads, dispRows) |
| |
| def displaySubscriptions(self, subs): |
| disp = Display(prefix=" ") |
| heads = [] |
| if self.cluster: |
| heads.append(Header('broker')) |
| heads.append(Header("subscription")) |
| heads.append(Header("queue")) |
| heads.append(Header("connection")) |
| heads.append(Header("processName")) |
| heads.append(Header("processId")) |
| heads.append(Header("browsing", Header.Y)) |
| heads.append(Header("acknowledged", Header.Y)) |
| heads.append(Header("exclusive", Header.Y)) |
| heads.append(Header("creditMode")) |
| heads.append(Header("delivered", Header.KMG)) |
| rows = [] |
| for broker in self.brokers: |
| for oid in broker.subscriptions: |
| s = broker.subscriptions[oid] |
| row = [] |
| try: |
| if self.cluster: |
| row.append(broker.getName()) |
| row.append(s.name) |
| row.append(self.qmf.getObjects(_objectId=s.queueRef)[0].name) |
| connectionRef = self.qmf.getObjects(_objectId=s.sessionRef)[0].connectionRef |
| row.append(self.qmf.getObjects(_objectId=connectionRef)[0].address) |
| row.append(self.qmf.getObjects(_objectId=connectionRef)[0].remoteProcessName) |
| row.append(self.qmf.getObjects(_objectId=connectionRef)[0].remotePid) |
| row.append(s.browsing) |
| row.append(s.acknowledged) |
| row.append(s.exclusive) |
| row.append(s.creditMode) |
| row.append(s.delivered) |
| rows.append(row) |
| except: |
| pass |
| title = "Subscriptions" |
| if self.cluster: |
| title += " for cluster '%s'" % self.cluster.clusterName |
| if config._sortcol: |
| sorter = Sorter(heads, rows, config._sortcol, config._limit, config._increasing) |
| dispRows = sorter.getSorted() |
| else: |
| dispRows = rows |
| disp.formattedTable(title, heads, dispRows) |
| |
| def displayMain(self, main, subs): |
| if main == 'b': self.displayBroker(subs) |
| elif main == 'c': self.displayConn(subs) |
| elif main == 's': self.displaySession(subs) |
| elif main == 'e': self.displayExchange(subs) |
| elif main == 'q': self.displayQueue(subs) |
| elif main == 'u': self.displaySubscriptions(subs) |
| |
| def display(self): |
| if config._cluster_detail or config._types[0] == 'b': |
| # always show cluster detail when dumping broker stats |
| self._getCluster() |
| if self.cluster: |
| memberList = self.cluster.members.split(";") |
| hostList = self._getHostList(memberList) |
| self.qmf.delBroker(self.broker) |
| self.broker = None |
| if config._host.find("@") > 0: |
| authString = config._host.split("@")[0] + "@" |
| else: |
| authString = "" |
| for host in hostList: |
| b = self.qmf.addBroker(authString + host, config._connTimeout) |
| self.brokers.append(Broker(self.qmf, b)) |
| else: |
| self.brokers.append(Broker(self.qmf, self.broker)) |
| |
| self.displayMain(config._types[0], config._types[1:]) |
| |
| |
| def main(argv=None): |
| |
| args = OptionsAndArguments(argv) |
| bm = BrokerManager() |
| |
| try: |
| bm.SetBroker(config._host, config._sasl_mechanism) |
| bm.display() |
| bm.Disconnect() |
| return 0 |
| except KeyboardInterrupt: |
| print |
| except Exception,e: |
| print "Failed: %s - %s" % (e.__class__.__name__, e) |
| |
| bm.Disconnect() # try to deallocate brokers |
| return 1 |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |