|
#!/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 |
|
import sys |
|
import locale |
|
import socket |
|
import re |
|
from qpid.messaging import Connection |
|
|
|
home = os.environ.get("QPID_TOOLS_HOME", os.path.normpath("/usr/share/qpid-tools")) |
|
sys.path.append(os.path.join(home, "python")) |
|
|
|
from qpidtoollibs import BrokerAgent |
|
from qpidtoollibs import Display, Header, Sorter, YN, Commas, TimeLong |
|
|
|
|
|
class Config: |
|
def __init__(self): |
|
self._host = "localhost" |
|
self._connTimeout = 10 |
|
self._types = "" |
|
self._limit = 50 |
|
self._increasing = False |
|
self._sortcol = None |
|
|
|
config = Config() |
|
conn_options = {} |
|
|
|
def OptionsAndArguments(argv): |
|
""" Set global variables for options, return arguments """ |
|
|
|
global config |
|
global conn_options |
|
|
|
usage = \ |
|
"""%prog -g [options] |
|
%prog -c [options] |
|
%prog -e [options] |
|
%prog -q [options] [queue-name] |
|
%prog -u [options] |
|
%prog -m [options] |
|
%prog --acl [options]""" |
|
|
|
parser = OptionParser(usage=usage) |
|
|
|
group1 = OptionGroup(parser, "General Options") |
|
group1.add_option("-b", "--broker", action="store", type="string", default="localhost", metavar="<url>", |
|
help="URL of the broker to query") |
|
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-MD5, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.") |
|
group1.add_option("--sasl-service-name", action="store", type="string", help="SASL service name to use") |
|
group1.add_option("--ssl-certificate", action="store", type="string", metavar="<cert>", help="Client SSL certificate (PEM Format)") |
|
group1.add_option("--ssl-key", action="store", type="string", metavar="<key>", help="Client SSL private key (PEM Format)") |
|
group1.add_option("--ha-admin", action="store_true", help="Allow connection to a HA backup broker.") |
|
parser.add_option_group(group1) |
|
|
|
group2 = OptionGroup(parser, "Command Options") |
|
group2.add_option("-g", "--general", help="Show General Broker Stats", action="store_const", const="g", 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("-m", "--memory", help="Show Broker Memory Stats", action="store_const", const="m", dest="show") |
|
group2.add_option( "--acl", help="Show Access Control List Stats", action="store_const", const="acl", dest="show") |
|
parser.add_option_group(group2) |
|
|
|
group3 = OptionGroup(parser, "Display Options") |
|
group3.add_option("-S", "--sort-by", metavar="<colname>", help="Sort by column name") |
|
group3.add_option("-I", "--increasing", action="store_true", default=False, help="Sort by increasing value (default = decreasing)") |
|
group3.add_option("-L", "--limit", type="int", default=50, metavar="<n>", help="Limit output to n rows") |
|
parser.add_option_group(group3) |
|
|
|
opts, args = parser.parse_args(args=argv) |
|
|
|
if not opts.show: |
|
parser.error("You must specify one of these options: -g, -c, -e, -q, -m, or -u. For details, try $ qpid-stat --help") |
|
|
|
config._types = opts.show |
|
config._sortcol = opts.sort_by |
|
config._host = opts.broker |
|
config._connTimeout = opts.timeout |
|
config._increasing = opts.increasing |
|
config._limit = opts.limit |
|
|
|
if opts.sasl_mechanism: |
|
conn_options['sasl_mechanisms'] = opts.sasl_mechanism |
|
if opts.sasl_service_name: |
|
conn_options['sasl_service'] = opts.sasl_service_name |
|
if opts.ssl_certificate: |
|
conn_options['ssl_certfile'] = opts.ssl_certificate |
|
if opts.ssl_key: |
|
if not opts.ssl_certificate: |
|
parser.error("missing '--ssl-certificate' (required by '--ssl-key')") |
|
conn_options['ssl_keyfile'] = opts.ssl_key |
|
if opts.ha_admin: |
|
conn_options['client_properties'] = {'qpid.ha-admin' : 1} |
|
|
|
return args |
|
|
|
class BrokerManager: |
|
def __init__(self): |
|
self.brokerName = None |
|
self.connection = None |
|
self.broker = None |
|
self.cluster = None |
|
|
|
def SetBroker(self, brokerUrl): |
|
self.url = brokerUrl |
|
self.connection = Connection.establish(self.url, **conn_options) |
|
self.broker = BrokerAgent(self.connection) |
|
|
|
def Disconnect(self): |
|
""" Release any allocated brokers. Ignore any failures as the tool is |
|
shutting down. |
|
""" |
|
try: |
|
self.connection.close() |
|
except: |
|
pass |
|
|
|
def displayBroker(self): |
|
disp = Display(prefix=" ") |
|
heads = [] |
|
heads.append(Header('uptime', Header.DURATION)) |
|
heads.append(Header('cluster', Header.NONE)) |
|
heads.append(Header('connections', Header.COMMAS)) |
|
heads.append(Header('sessions', Header.COMMAS)) |
|
heads.append(Header('exchanges', Header.COMMAS)) |
|
heads.append(Header('queues', Header.COMMAS)) |
|
rows = [] |
|
broker = self.broker.getBroker() |
|
cluster = self.broker.getCluster() |
|
clusterInfo = cluster and cluster.clusterName + "<" + cluster.status + ">" or "<standalone>" |
|
connections = self.getConnectionMap() |
|
sessions = self.getSessionMap() |
|
exchanges = self.getExchangeMap() |
|
queues = self.getQueueMap() |
|
row = (broker.getUpdateTime() - broker.getCreateTime(), |
|
clusterInfo, |
|
len(connections), len(sessions), |
|
len(exchanges), len(queues)) |
|
rows.append(row) |
|
disp.formattedTable('Broker Summary:', heads, rows) |
|
|
|
if 'queueCount' not in broker.values: |
|
return |
|
|
|
print |
|
heads = [] |
|
heads.append(Header('Statistic')) |
|
heads.append(Header('Messages', Header.COMMAS)) |
|
heads.append(Header('Bytes', Header.COMMAS)) |
|
rows = [] |
|
rows.append(['queue-depth', broker.msgDepth, broker.byteDepth]) |
|
rows.append(['total-enqueues', broker.msgTotalEnqueues, broker.byteTotalEnqueues]) |
|
rows.append(['total-dequeues', broker.msgTotalDequeues, broker.byteTotalDequeues]) |
|
rows.append(['persistent-enqueues', broker.msgPersistEnqueues, broker.bytePersistEnqueues]) |
|
rows.append(['persistent-dequeues', broker.msgPersistDequeues, broker.bytePersistDequeues]) |
|
rows.append(['transactional-enqueues', broker.msgTxnEnqueues, broker.byteTxnEnqueues]) |
|
rows.append(['transactional-dequeues', broker.msgTxnDequeues, broker.byteTxnDequeues]) |
|
rows.append(['flow-to-disk-depth', broker.msgFtdDepth, broker.byteFtdDepth]) |
|
rows.append(['flow-to-disk-enqueues', broker.msgFtdEnqueues, broker.byteFtdEnqueues]) |
|
rows.append(['flow-to-disk-dequeues', broker.msgFtdDequeues, broker.byteFtdDequeues]) |
|
rows.append(['acquires', broker.acquires, None]) |
|
rows.append(['releases', broker.releases, None]) |
|
rows.append(['discards-no-route', broker.discardsNoRoute, None]) |
|
rows.append(['discards-ttl-expired', broker.discardsTtl, None]) |
|
rows.append(['discards-limit-overflow', broker.discardsOverflow, None]) |
|
rows.append(['discards-ring-overflow', broker.discardsRing, None]) |
|
rows.append(['discards-lvq-replace', broker.discardsLvq, None]) |
|
rows.append(['discards-subscriber-reject', broker.discardsSubscriber, None]) |
|
rows.append(['discards-purged', broker.discardsPurge, None]) |
|
rows.append(['reroutes', broker.reroutes, None]) |
|
rows.append(['abandoned', broker.abandoned, None]) |
|
rows.append(['abandoned-via-alt', broker.abandonedViaAlt, None]) |
|
disp.formattedTable('Aggregate Broker Statistics:', heads, rows) |
|
|
|
|
|
def displayConn(self): |
|
disp = Display(prefix=" ") |
|
heads = [] |
|
heads.append(Header('connection')) |
|
heads.append(Header('cproc')) |
|
heads.append(Header('cpid')) |
|
heads.append(Header('mech')) |
|
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 = [] |
|
connections = self.broker.getAllConnections() |
|
broker = self.broker.getBroker() |
|
for conn in connections: |
|
row = [] |
|
row.append(conn.address) |
|
if conn.remoteProcessName: row.append(conn.remoteProcessName) |
|
else: row.append("-") |
|
row.append(conn.remotePid) |
|
if conn.saslMechanism: row.append(conn.saslMechanism) |
|
else: row.append("-") |
|
if conn.authIdentity: row.append(conn.authIdentity) |
|
else: row.append("-") |
|
row.append(broker.getUpdateTime() - conn.getCreateTime()) |
|
row.append(broker.getUpdateTime() - conn.getUpdateTime()) |
|
row.append(conn.msgsFromClient) |
|
row.append(conn.msgsToClient) |
|
rows.append(row) |
|
title = "Connections" |
|
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): |
|
disp = Display(prefix=" ") |
|
|
|
def displayExchange(self): |
|
disp = Display(prefix=" ") |
|
heads = [] |
|
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 = [] |
|
exchanges = self.broker.getAllExchanges() |
|
for ex in exchanges: |
|
row = [] |
|
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 config._sortcol: |
|
sorter = Sorter(heads, rows, config._sortcol, config._limit, config._increasing) |
|
dispRows = sorter.getSorted() |
|
else: |
|
dispRows = rows |
|
disp.formattedTable(title, heads, dispRows) |
|
|
|
def displayQueues(self): |
|
disp = Display(prefix=" ") |
|
heads = [] |
|
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 = [] |
|
queues = self.broker.getAllQueues() |
|
for q in queues: |
|
row = [] |
|
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 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, name): |
|
queue = self.broker.getQueue(name) |
|
if not queue: |
|
print "Queue '%s' not found" % name |
|
return |
|
|
|
disp = Display(prefix=" ") |
|
heads = [] |
|
heads.append(Header('Name')) |
|
heads.append(Header('Durable', Header.YN)) |
|
heads.append(Header('AutoDelete', Header.YN)) |
|
heads.append(Header('Exclusive', Header.YN)) |
|
heads.append(Header('FlowStopped', Header.YN)) |
|
heads.append(Header('FlowStoppedCount', Header.COMMAS)) |
|
heads.append(Header('Consumers', Header.COMMAS)) |
|
heads.append(Header('Bindings', Header.COMMAS)) |
|
rows = [] |
|
rows.append([queue.name, queue.durable, queue.autoDelete, queue.exclusive, |
|
queue.flowStopped, queue.flowStoppedCount, |
|
queue.consumerCount, queue.bindingCount]) |
|
disp.formattedTable("Properties:", heads, rows) |
|
print |
|
|
|
heads = [] |
|
heads.append(Header('Property')) |
|
heads.append(Header('Value')) |
|
rows = [] |
|
rows.append(['arguments', queue.arguments]) |
|
rows.append(['alt-exchange', queue.altExchange]) |
|
disp.formattedTable("Optional Properties:", heads, rows) |
|
print |
|
|
|
heads = [] |
|
heads.append(Header('Statistic')) |
|
heads.append(Header('Messages', Header.COMMAS)) |
|
heads.append(Header('Bytes', Header.COMMAS)) |
|
rows = [] |
|
rows.append(['queue-depth', queue.msgDepth, queue.byteDepth]) |
|
rows.append(['total-enqueues', queue.msgTotalEnqueues, queue.byteTotalEnqueues]) |
|
rows.append(['total-dequeues', queue.msgTotalDequeues, queue.byteTotalDequeues]) |
|
rows.append(['persistent-enqueues', queue.msgPersistEnqueues, queue.bytePersistEnqueues]) |
|
rows.append(['persistent-dequeues', queue.msgPersistDequeues, queue.bytePersistDequeues]) |
|
rows.append(['transactional-enqueues', queue.msgTxnEnqueues, queue.byteTxnEnqueues]) |
|
rows.append(['transactional-dequeues', queue.msgTxnDequeues, queue.byteTxnDequeues]) |
|
rows.append(['flow-to-disk-depth', queue.msgFtdDepth, queue.byteFtdDepth]) |
|
rows.append(['flow-to-disk-enqueues', queue.msgFtdEnqueues, queue.byteFtdEnqueues]) |
|
rows.append(['flow-to-disk-dequeues', queue.msgFtdDequeues, queue.byteFtdDequeues]) |
|
rows.append(['acquires', queue.acquires, None]) |
|
rows.append(['releases', queue.releases, None]) |
|
rows.append(['discards-ttl-expired', queue.discardsTtl, None]) |
|
rows.append(['discards-limit-overflow', queue.discardsOverflow, None]) |
|
rows.append(['discards-ring-overflow', queue.discardsRing, None]) |
|
rows.append(['discards-lvq-replace', queue.discardsLvq, None]) |
|
rows.append(['discards-subscriber-reject', queue.discardsSubscriber, None]) |
|
rows.append(['discards-purged', queue.discardsPurge, None]) |
|
rows.append(['reroutes', queue.reroutes, None]) |
|
disp.formattedTable("Statistics:", heads, rows) |
|
|
|
|
|
def displaySubscriptions(self): |
|
disp = Display(prefix=" ") |
|
heads = [] |
|
heads.append(Header("subscr")) |
|
heads.append(Header("queue")) |
|
heads.append(Header("conn")) |
|
heads.append(Header("procName")) |
|
heads.append(Header("procId")) |
|
heads.append(Header("browse", Header.Y)) |
|
heads.append(Header("acked", Header.Y)) |
|
heads.append(Header("excl", Header.Y)) |
|
heads.append(Header("creditMode")) |
|
heads.append(Header("delivered", Header.COMMAS)) |
|
heads.append(Header("sessUnacked", Header.COMMAS)) |
|
rows = [] |
|
subscriptions = self.broker.getAllSubscriptions() |
|
sessions = self.getSessionMap() |
|
connections = self.getConnectionMap() |
|
for s in subscriptions: |
|
row = [] |
|
try: |
|
row.append(s.name) |
|
row.append(s.queueRef) |
|
session = sessions[s.sessionRef] |
|
connection = connections[session.connectionRef] |
|
row.append(connection.address) |
|
if connection.remoteProcessName: row.append(connection.remoteProcessName) |
|
else: row.append("-") |
|
row.append(connection.remotePid) |
|
row.append(s.browsing) |
|
row.append(s.acknowledged) |
|
row.append(s.exclusive) |
|
row.append(s.creditMode) |
|
row.append(s.delivered) |
|
row.append(session.unackedMessages) |
|
rows.append(row) |
|
except: |
|
pass |
|
title = "Subscriptions" |
|
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 displayMemory(self): |
|
disp = Display(prefix=" ") |
|
heads = [Header('Statistic'), Header('Value', Header.COMMAS)] |
|
rows = [] |
|
memory = self.broker.getMemory() |
|
for k,v in memory.values.items(): |
|
if k != 'name': |
|
rows.append([k, v]) |
|
disp.formattedTable('Broker Memory Statistics:', heads, rows) |
|
|
|
def displayAcl(self): |
|
acl = self.broker.getAcl() |
|
if not acl: |
|
print "ACL Policy Module is not installed" |
|
return |
|
disp = Display(prefix=" ") |
|
heads = [Header('Statistic'), Header('Value')] |
|
rows = [] |
|
rows.append(['policy-file', acl.policyFile]) |
|
rows.append(['enforcing', YN(acl.enforcingAcl)]) |
|
rows.append(['has-transfer-acls', YN(acl.transferAcl)]) |
|
rows.append(['last-acl-load', TimeLong(acl.lastAclLoad)]) |
|
rows.append(['acl-denials', Commas(acl.aclDenyCount)]) |
|
disp.formattedTable('ACL Policy Statistics:', heads, rows) |
|
|
|
def getExchangeMap(self): |
|
exchanges = self.broker.getAllExchanges() |
|
emap = {} |
|
for e in exchanges: |
|
emap[e.name] = e |
|
return emap |
|
|
|
def getQueueMap(self): |
|
queues = self.broker.getAllQueues() |
|
qmap = {} |
|
for q in queues: |
|
qmap[q.name] = q |
|
return qmap |
|
|
|
def getSessionMap(self): |
|
sessions = self.broker.getAllSessions() |
|
smap = {} |
|
for s in sessions: |
|
smap[s.name] = s |
|
return smap |
|
|
|
def getConnectionMap(self): |
|
connections = self.broker.getAllConnections() |
|
cmap = {} |
|
for c in connections: |
|
cmap[c.address] = c |
|
return cmap |
|
|
|
def displayMain(self, names, main): |
|
if main == 'g': self.displayBroker() |
|
elif main == 'c': self.displayConn() |
|
elif main == 's': self.displaySession() |
|
elif main == 'e': self.displayExchange() |
|
elif main == 'q': |
|
if len(names) >= 1: |
|
self.displayQueue(names[0]) |
|
else: |
|
self.displayQueues() |
|
elif main == 'u': self.displaySubscriptions() |
|
elif main == 'm': self.displayMemory() |
|
elif main == 'acl': self.displayAcl() |
|
|
|
def display(self, names): |
|
self.displayMain(names, config._types) |
|
|
|
|
|
def main(argv=None): |
|
|
|
args = OptionsAndArguments(argv) |
|
bm = BrokerManager() |
|
|
|
try: |
|
bm.SetBroker(config._host) |
|
bm.display(args) |
|
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()) |