rhmessaging commits: r1531 - mgmt/cumin/python/cumin.
by rhmessaging-commits@lists.jboss.org
Author: justi9
Date: 2008-01-03 08:32:29 -0500 (Thu, 03 Jan 2008)
New Revision: 1531
Modified:
mgmt/cumin/python/cumin/widgets.py
Log:
Fixes a clients chart bug.
Modified: mgmt/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/cumin/python/cumin/widgets.py 2008-01-03 13:08:02 UTC (rev 1530)
+++ mgmt/cumin/python/cumin/widgets.py 2008-01-03 13:32:29 UTC (rev 1531)
@@ -203,7 +203,9 @@
max_value = 0
for stat in stats:
- max_value = max(max(values[stat]), max_value)
+ vals = values[stat]
+ if vals:
+ max_value = max(max(vals), max_value)
max_value = max_value * 1.1
max_value = max_value + (10 - max_value % 100)
16 years, 11 months
rhmessaging commits: r1530 - mgmt/cumin/python/cumin.
by rhmessaging-commits@lists.jboss.org
Author: justi9
Date: 2008-01-03 08:08:02 -0500 (Thu, 03 Jan 2008)
New Revision: 1530
Modified:
mgmt/cumin/python/cumin/charts.py
mgmt/cumin/python/cumin/client.py
mgmt/cumin/python/cumin/model.py
mgmt/cumin/python/cumin/queue.py
mgmt/cumin/python/cumin/util.py
mgmt/cumin/python/cumin/widgets.py
Log:
Avoid extra queries in chart generation.
Fix chart display.
Make stat.samples return values over a time range.
Render the "" named exchange as "Default" in the queue binding list.
Protect calc_rate from null values.
Modified: mgmt/cumin/python/cumin/charts.py
===================================================================
--- mgmt/cumin/python/cumin/charts.py 2008-01-02 16:43:50 UTC (rev 1529)
+++ mgmt/cumin/python/cumin/charts.py 2008-01-03 13:08:02 UTC (rev 1530)
@@ -1,8 +1,9 @@
from cairo import *
from random import random
-from time import mktime, time
+from time import time
from formats import *
+from util import *
class LineChart(object):
def __init__(self, width, height):
@@ -25,12 +26,12 @@
tzero = time()
- for datetime, value in samples:
+ for dt, value in samples:
if value is None:
print "Warning: unexpected null value" #XXX
value = 0
- t = mktime(datetime.timetuple())
+ t = secs(dt)
x = self.width + (t - tzero)
y = self.height - (value / float(self.max_value)) * self.height
cr.line_to(x, y)
Modified: mgmt/cumin/python/cumin/client.py
===================================================================
--- mgmt/cumin/python/cumin/client.py 2008-01-02 16:43:50 UTC (rev 1529)
+++ mgmt/cumin/python/cumin/client.py 2008-01-03 13:08:02 UTC (rev 1530)
@@ -296,7 +296,7 @@
return "Expires"
def do_render(self, session, data):
- return "<td>%s</td>" % fmt_datetime(data[self.name])
+ return "<td>%s</td>" % data[self.name]
class StatusColumn(ItemTableColumn):
def get_title(self, session, object):
Modified: mgmt/cumin/python/cumin/model.py
===================================================================
--- mgmt/cumin/python/cumin/model.py 2008-01-02 16:43:50 UTC (rev 1529)
+++ mgmt/cumin/python/cumin/model.py 2008-01-03 13:08:02 UTC (rev 1530)
@@ -1,6 +1,7 @@
from mint import *
from wooly import *
from time import mktime
+from datetime import datetime, timedelta
from util import *
from formats import *
@@ -81,13 +82,18 @@
self.cumin_class.add_stat(self)
- def samples(self, object, limit=None):
- stats = object.stats[:limit]
+ def samples(self, object, secs=600):
+ stats = object.stats
+
+ col = self.cumin_class.mint_stats_class.q.recTime
+ dt = datetime.now() - timedelta(seconds=secs)
+ stats = stats.filter(col > dt)
+
stats = stats.orderBy("-recTime")
samples = list()
- for stat in object.stats[:limit]:
+ for stat in stats:
value = getattr(stat, self.name, 0)
samples.append((stat.recTime, value))
Modified: mgmt/cumin/python/cumin/queue.py
===================================================================
--- mgmt/cumin/python/cumin/queue.py 2008-01-02 16:43:50 UTC (rev 1529)
+++ mgmt/cumin/python/cumin/queue.py 2008-01-03 13:08:02 UTC (rev 1530)
@@ -304,7 +304,7 @@
return branch.marshal()
def render_item_name(self, session, binding):
- return binding.exchange.name
+ return binding.exchange.name or "<em>Default</em>"
class QueueForm(CuminForm):
def __init__(self, app, name):
Modified: mgmt/cumin/python/cumin/util.py
===================================================================
--- mgmt/cumin/python/cumin/util.py 2008-01-02 16:43:50 UTC (rev 1529)
+++ mgmt/cumin/python/cumin/util.py 2008-01-03 13:08:02 UTC (rev 1530)
@@ -13,10 +13,12 @@
return expr1
def calc_rate(curr, prev, csecs, psecs):
- return (curr - prev) / float(csecs - psecs)
+ if None not in (curr, prev, csecs, psecs):
+ return (curr - prev) / float(csecs - psecs)
def secs(dt):
- return mktime(dt.timetuple())
+ if dt is not None:
+ return mktime(dt.timetuple())
class Identifiable(object):
def __init__(self, id=None):
Modified: mgmt/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/cumin/python/cumin/widgets.py 2008-01-02 16:43:50 UTC (rev 1529)
+++ mgmt/cumin/python/cumin/widgets.py 2008-01-03 13:08:02 UTC (rev 1530)
@@ -192,11 +192,18 @@
cls = self.app.model.get_class(object)
stats = [cls.get_stat(x) for x in self.stats.get(session)]
+ samples = dict()
+ times = dict()
+ values = dict()
+
+ for stat in stats:
+ samples[stat] = stat.samples(object)
+ values[stat] = [x[1] for x in samples[stat]]
+
max_value = 0
for stat in stats:
- for x in stat.samples(object, 300):
- max_value = max(x[1], max_value)
+ max_value = max(max(values[stat]), max_value)
max_value = max_value * 1.1
max_value = max_value + (10 - max_value % 100)
@@ -208,7 +215,7 @@
colors = ((1,0,0), (0,0,1), (0,1,0))
for stat, color in zip(stats, colors):
- chart.plot_values(stat.samples(object, 300), color=color)
+ chart.plot_values(samples[stat], color=color)
chart.plot_frame()
16 years, 11 months
rhmessaging commits: r1529 - mgmt/cumin/python/cumin.
by rhmessaging-commits@lists.jboss.org
Author: justi9
Date: 2008-01-02 11:43:50 -0500 (Wed, 02 Jan 2008)
New Revision: 1529
Modified:
mgmt/cumin/python/cumin/client.py
mgmt/cumin/python/cumin/formats.py
mgmt/cumin/python/cumin/model.py
mgmt/cumin/python/cumin/util.py
mgmt/cumin/python/cumin/widgets.py
Log:
Corrects rate calculation in the client list.
Adds a calc_rate() with rate computation logic.
Adds a secs() with the slightly tedious python code for going from a
datetime to seconds since epoch.
Formatting fix in fmt_rate.
Changes the initial max range in charts to 10, from 100.
Modified: mgmt/cumin/python/cumin/client.py
===================================================================
--- mgmt/cumin/python/cumin/client.py 2008-01-02 14:41:35 UTC (rev 1528)
+++ mgmt/cumin/python/cumin/client.py 2008-01-02 16:43:50 UTC (rev 1529)
@@ -30,10 +30,12 @@
self.add_sql_column("cfsent", "c.frames_from_client")
self.add_sql_column("cbrecv", "c.bytes_to_client")
self.add_sql_column("cfrecv", "c.frames_to_client")
+ self.add_sql_column("ctime", "c.rec_time")
self.add_sql_column("pbsent", "p.bytes_from_client")
self.add_sql_column("pfsent", "p.frames_from_client")
self.add_sql_column("pbrecv", "p.bytes_to_client")
self.add_sql_column("pfrecv", "p.frames_to_client")
+ self.add_sql_column("ptime", "p.rec_time")
col = self.AddressColumn(app, "addr")
self.add_column(col)
@@ -88,11 +90,14 @@
def do_render(self, session, data):
unit = self.parent.unit.get(session)
-
+
+ csecs = secs(data["ctime"])
+ psecs = secs(data["ptime"])
+
if unit == "b":
- value = data["cbsent"] - data["pbsent"] / float(1)
+ value = calc_rate(data["cbsent"], data["pbsent"], csecs, psecs)
else:
- value = data["cfsent"] - data["pfsent"] / float(1)
+ value = calc_rate(data["cfsent"], data["pfsent"], csecs, psecs)
content = fmt_rate(value, unit == "b" and "byte" or "frame", "sec")
return "<td>%s</td>" % content
@@ -104,10 +109,13 @@
def do_render(self, session, data):
unit = self.parent.unit.get(session)
+ csecs = secs(data["ctime"])
+ psecs = secs(data["ptime"])
+
if unit == "b":
- value = data["cbrecv"] - data["pbrecv"] / float(1)
+ value = calc_rate(data["cbrecv"], data["pbrecv"], csecs, psecs)
else:
- value = data["cfrecv"] - data["pfrecv"] / float(1)
+ value = calc_rate(data["cfrecv"], data["pfrecv"], csecs, psecs)
content = fmt_rate(value, unit == "b" and "byte" or "frame", "sec")
return "<td>%s</td>" % content
Modified: mgmt/cumin/python/cumin/formats.py
===================================================================
--- mgmt/cumin/python/cumin/formats.py 2008-01-02 14:41:35 UTC (rev 1528)
+++ mgmt/cumin/python/cumin/formats.py 2008-01-02 16:43:50 UTC (rev 1529)
@@ -59,10 +59,14 @@
return sign + "".join(elems)
-def fmt_rate(value, unit1, unit2):
- #return "%i <small>%s/%s</small>" % (value, unit1, unit2)
- return "%.0f<small>/%s</small>" % (float(nvl(value, 0)), unit2)
+def fmt_rate(rate, unit1, unit2):
+ if rate == 0:
+ str = "0"
+ else:
+ str = "%0.2f" % float(nvl(rate, 0))
+ return "%s<small>/%s</small>" % (str, unit2)
+
def fmt_predicate(predicate):
return predicate and "Yes" or "No"
Modified: mgmt/cumin/python/cumin/model.py
===================================================================
--- mgmt/cumin/python/cumin/model.py 2008-01-02 14:41:35 UTC (rev 1528)
+++ mgmt/cumin/python/cumin/model.py 2008-01-02 16:43:50 UTC (rev 1529)
@@ -156,7 +156,7 @@
psecs = mktime(ptime.timetuple())
if curr is not None and prev is not None:
- return (curr - prev) / (csecs - psecs)
+ return calc_rate(curr, prev, csecs, psecs)
except AttributeError:
pass
Modified: mgmt/cumin/python/cumin/util.py
===================================================================
--- mgmt/cumin/python/cumin/util.py 2008-01-02 14:41:35 UTC (rev 1528)
+++ mgmt/cumin/python/cumin/util.py 2008-01-02 16:43:50 UTC (rev 1529)
@@ -1,3 +1,5 @@
+from time import mktime
+
def sorted_by(seq, attr="name"):
return sorted(seq, cmp, lambda x: getattr(x, attr))
@@ -10,6 +12,12 @@
else:
return expr1
+def calc_rate(curr, prev, csecs, psecs):
+ return (curr - prev) / float(csecs - psecs)
+
+def secs(dt):
+ return mktime(dt.timetuple())
+
class Identifiable(object):
def __init__(self, id=None):
self.id = id
Modified: mgmt/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/cumin/python/cumin/widgets.py 2008-01-02 14:41:35 UTC (rev 1528)
+++ mgmt/cumin/python/cumin/widgets.py 2008-01-02 16:43:50 UTC (rev 1529)
@@ -199,7 +199,7 @@
max_value = max(x[1], max_value)
max_value = max_value * 1.1
- max_value = max_value + (100 - max_value % 100)
+ max_value = max_value + (10 - max_value % 100)
chart.set_max_value(int(max_value))
chart.plot_x_axis()
16 years, 11 months
rhmessaging commits: r1528 - in mgmt: cumin/python/wooly and 3 other directories.
by rhmessaging-commits@lists.jboss.org
Author: justi9
Date: 2008-01-02 09:41:35 -0500 (Wed, 02 Jan 2008)
New Revision: 1528
Modified:
mgmt/cumin/python/cumin/broker.py
mgmt/cumin/python/cumin/brokercluster.py
mgmt/cumin/python/cumin/brokergroup.py
mgmt/cumin/python/cumin/brokerprofile.py
mgmt/cumin/python/cumin/charts.py
mgmt/cumin/python/cumin/client.py
mgmt/cumin/python/cumin/client.strings
mgmt/cumin/python/cumin/exchange.py
mgmt/cumin/python/cumin/formats.py
mgmt/cumin/python/cumin/model.py
mgmt/cumin/python/cumin/page.py
mgmt/cumin/python/cumin/queue.py
mgmt/cumin/python/cumin/stat.py
mgmt/cumin/python/cumin/stat.strings
mgmt/cumin/python/cumin/util.py
mgmt/cumin/python/cumin/widgets.py
mgmt/cumin/python/wooly/__init__.py
mgmt/cumin/python/wooly/tables.py
mgmt/cumin/python/wooly/widgets.py
mgmt/cumin/resources/wooly.js
mgmt/misc/boneyard.py
mgmt/notes/justin-todo.txt
Log:
Big check in:
- Converts ClientSet to use SqlTable
- Improves the way chart values are plotted
- Changes stats to render "--" for no value
- Goes back to simple scalars for Widget.parent and .name
- Adds a Widget.frame() and simplifies some link-generation logic
- Gets rid of sql hack in CuminStat.samples()
- Updates todos
Modified: mgmt/cumin/python/cumin/broker.py
===================================================================
--- mgmt/cumin/python/cumin/broker.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/broker.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -173,19 +173,22 @@
def show_config_property(self, session, prop):
self.prop.set_config_property(session, prop)
- frame = self.show_mode(session, self.prop)
- return self.page().set_current_frame(session, frame)
+ self.page().set_current_frame(session, self.prop)
+ return self.show_mode(session, self.prop)
def show_queue(self, session, queue):
self.queue.set_object(session, queue)
+ self.page().set_current_frame(session, self.queue)
return self.show_mode(session, self.queue)
def show_exchange(self, session, exchange):
self.exchange.set_object(session, exchange)
+ self.page().set_current_frame(session, self.exchange)
return self.show_mode(session, self.exchange)
def show_client(self, session, client):
self.client.set_object(session, client)
+ self.page().set_current_frame(session, self.client)
return self.show_mode(session, self.client)
def get_title(self, session, broker):
@@ -483,9 +486,9 @@
class BrowserBrokers(BrokerSet):
def do_get_items(self, session, model):
- group = self.parent().group.get(session)
- profile = self.parent().profile.get(session)
- cluster = self.parent().cluster.get(session)
+ group = self.parent.group.get(session)
+ profile = self.parent.profile.get(session)
+ cluster = self.parent.cluster.get(session)
brokers = BrokerRegistration.select()
Modified: mgmt/cumin/python/cumin/brokercluster.py
===================================================================
--- mgmt/cumin/python/cumin/brokercluster.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/brokercluster.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -136,7 +136,7 @@
def process_cancel(self, session, cluster):
branch = session.branch()
- self.parent().show_view(branch)
+ self.parent.show_view(branch)
self.page().set_redirect_url(session, branch.marshal())
def process_submit(self, session, cluster):
Modified: mgmt/cumin/python/cumin/brokergroup.py
===================================================================
--- mgmt/cumin/python/cumin/brokergroup.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/brokergroup.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -129,7 +129,7 @@
def process_cancel(self, session, group):
branch = session.branch()
- self.parent().show_view(branch)
+ self.parent.show_view(branch)
self.page().set_redirect_url(session, branch.marshal())
def process_submit(self, session, group):
Modified: mgmt/cumin/python/cumin/brokerprofile.py
===================================================================
--- mgmt/cumin/python/cumin/brokerprofile.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/brokerprofile.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -137,7 +137,7 @@
def process_cancel(self, session, profile):
branch = session.branch()
- self.parent().show_view(branch)
+ self.parent.show_view(branch)
self.page().set_redirect_url(session, branch.marshal())
def process_submit(self, session, profile):
Modified: mgmt/cumin/python/cumin/charts.py
===================================================================
--- mgmt/cumin/python/cumin/charts.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/charts.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -1,6 +1,6 @@
from cairo import *
from random import random
-from time import mktime
+from time import mktime, time
from formats import *
@@ -18,18 +18,20 @@
def set_value_interval(self, interval):
self.value_interval = interval
- def plot_values(self, values, color=(0, 0, 0)):
+ def plot_values(self, samples, color=(0, 0, 0)):
cr = Context(self.surface)
cr.set_line_width(2)
cr.set_source_rgb(*color)
- xs = range(self.width, 0 - self.value_interval, -self.value_interval)
+ tzero = time()
- for x, value in zip(xs, values):
+ for datetime, value in samples:
if value is None:
print "Warning: unexpected null value" #XXX
value = 0
+ t = mktime(datetime.timetuple())
+ x = self.width + (t - tzero)
y = self.height - (value / float(self.max_value)) * self.height
cr.line_to(x, y)
@@ -44,31 +46,23 @@
cr.stroke()
- def plot_x_axis(self, values, interval=50):
- if values:
- cr = Context(self.surface)
- cr.set_line_width(0.2)
- cr.set_source_rgb(0.8, 0.8, 0.8)
+ def plot_x_axis(self, interval=60):
+ cr = Context(self.surface)
+ cr.set_line_width(0.2)
+ cr.set_source_rgb(0.8, 0.8, 0.8)
- if values:
- tzero = mktime(values[0].timetuple())
+ xs = range(self.width, 0 - interval, -interval)
- xs = range(self.width, 0 - interval, -interval)
+ for x, i in zip(xs, range(0, 120)):
+ cr.move_to(x, 0)
+ cr.line_to(x, self.height + 10)
- for x, i in zip(xs, range(0, 120)):
- cr.move_to(x, 0)
- cr.line_to(x, self.height + 10)
+ if i % 2 == 0:
+ value = self.width - x
+ cr.show_text(fmt_duration_brief(value))
- if i % 4 == 0:
- index = 120 - x // self.value_interval
+ cr.stroke()
- if len(values) > index:
- value = mktime(values[index].timetuple()) - tzero
-
- cr.show_text(fmt_duration_brief(value))
-
- cr.stroke()
-
def plot_y_axis(self):
cr = Context(self.surface)
Modified: mgmt/cumin/python/cumin/client.py
===================================================================
--- mgmt/cumin/python/cumin/client.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/client.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -9,7 +9,7 @@
strings = StringCatalog(__file__)
-class ClientSet(PaginatedItemSet):
+class ClientSet(CuminTable):
def __init__(self, app, name):
super(ClientSet, self).__init__(app, name)
@@ -18,46 +18,100 @@
self.unit.add_state("b", "Bytes")
self.add_child(self.unit)
+ self.table_sql = "client as l"
+ self.where_sql = "where l.vhost_id = %(id)r"
+
+ self.add_sql_join("client_stats as c", "c.id", "l.stats_curr_id")
+ self.add_sql_join("client_stats as p", "p.id", "l.stats_prev_id")
+
+ self.add_sql_column("id", "l.id")
+ self.add_sql_column("addr", "l.address")
+ self.add_sql_column("cbsent", "c.bytes_from_client")
+ self.add_sql_column("cfsent", "c.frames_from_client")
+ self.add_sql_column("cbrecv", "c.bytes_to_client")
+ self.add_sql_column("cfrecv", "c.frames_to_client")
+ self.add_sql_column("pbsent", "p.bytes_from_client")
+ self.add_sql_column("pfsent", "p.frames_from_client")
+ self.add_sql_column("pbrecv", "p.bytes_to_client")
+ self.add_sql_column("pfrecv", "p.frames_to_client")
+
+ col = self.AddressColumn(app, "addr")
+ self.add_column(col)
+
+ #col = self.SessionsColumn(app, "sess")
+ #self.add_column(col)
+
+ col = self.SentColumn(app, "sent")
+ self.add_column(col)
+
+ col = self.ReceivedColumn(app, "recv")
+ self.add_column(col)
+
def get_title(self, session, vhost):
return "Clients %s" % fmt_count(self.get_item_count(session, vhost))
- def render_unit_plural(self, session, vhost):
+ def get_unit_plural(self, session):
return self.unit.get(session) == "b" and "Bytes" or "Frames"
def get_item_count(self, session, vhost):
return vhost.clients.count()
- def do_get_items(self, session, vhost):
- if vhost:
- start, end = self.get_bounds(session)
- return vhost.clients.orderBy("address")[start:end]
+ def get_sql_values(self, session, vhost):
+ return {"id": vhost.id}
- def render_item_link(self, session, client):
- branch = session.branch()
- self.page().show_client(branch, client).show_view(branch)
- return fmt_olink(branch, client, name=client.address)
+ class AddressColumn(ItemTableColumn):
+ def get_title(self, session, vhost):
+ return "Address"
- def render_item_sessions(self, session, client):
- branch = session.branch()
- frame = self.page().show_client(branch, client)
- frame.show_view(branch).show_sessions(branch)
- return fmt_link(branch.marshal(), client.sessions.count())
+ def do_render(self, session, data):
+ client = Identifiable(data["id"])
+ branch = session.branch()
+ self.frame().show_client(branch, client).show_view(branch)
+ content = fmt_olink(branch, client, name=data["addr"])
+ return "<td>%s</td>" % content
- def render_item_from(self, session, client):
- unit = self.unit.get(session)
- key = unit == "b" and "bytesFromClient" or "framesFromClient"
- value = self.app.model.client.get_stat(key).rate(client)
- return fmt_rate(value, unit == "b" and "byte" or "frame", "sec")
-
- def render_item_to(self, session, client):
- unit = self.unit.get(session)
- key = unit == "b" and "bytesToClient" or "framesToClient"
- value = self.app.model.client.get_stat(key).rate(client)
- return fmt_rate(value, unit == "b" and "byte" or "frame", "sec")
-
- def render_item_status(self, session, client):
- return fmt_ostatus(client)
+ class SessionsColumn(ItemTableColumn):
+ def get_title(self, session, vhost):
+ return "Sessions"
+ def do_render(self, session, data):
+ client = Identifiable(data["id"])
+ branch = session.branch()
+ frame = self.frame().show_client(branch, client)
+ frame.show_view(branch).show_sessions(branch)
+ content = fmt_link(branch.marshal(), client.sessions.count())
+ return "<td>%s</td>" % content
+
+ class SentColumn(ItemTableColumn):
+ def get_title(self, session, vhost):
+ return "%s Sent" % self.parent.get_unit_plural(session)
+
+ def do_render(self, session, data):
+ unit = self.parent.unit.get(session)
+
+ if unit == "b":
+ value = data["cbsent"] - data["pbsent"] / float(1)
+ else:
+ value = data["cfsent"] - data["pfsent"] / float(1)
+
+ content = fmt_rate(value, unit == "b" and "byte" or "frame", "sec")
+ return "<td>%s</td>" % content
+
+ class ReceivedColumn(ItemTableColumn):
+ def get_title(self, session, vhost):
+ return "%s Received" % self.parent.get_unit_plural(session)
+
+ def do_render(self, session, data):
+ unit = self.parent.unit.get(session)
+
+ if unit == "b":
+ value = data["cbrecv"] - data["pbrecv"] / float(1)
+ else:
+ value = data["cfrecv"] - data["pfrecv"] / float(1)
+
+ content = fmt_rate(value, unit == "b" and "byte" or "frame", "sec")
+ return "<td>%s</td>" % content
+
class ClientFrame(CuminFrame):
def __init__(self, app, name):
super(ClientFrame, self).__init__(app, name)
@@ -159,7 +213,7 @@
def render_close_href(self, session, client):
branch = session.branch()
- self.parent().show_close(branch)
+ self.parent.show_close(branch)
return branch.marshal()
class ClientStatistics(TabSet):
@@ -234,7 +288,7 @@
return "Expires"
def do_render(self, session, data):
- return "<td>%s</td>" % fmt_datetime(data[self.name()])
+ return "<td>%s</td>" % fmt_datetime(data[self.name])
class StatusColumn(ItemTableColumn):
def get_title(self, session, object):
Modified: mgmt/cumin/python/cumin/client.strings
===================================================================
--- mgmt/cumin/python/cumin/client.strings 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/client.strings 2008-01-02 14:41:35 UTC (rev 1528)
@@ -11,27 +11,20 @@
</div>
<table class="mobjects">
- <tr>
- <th><input type="checkbox"/></th>
- <th>Address</th>
- <th class="ralign">Sessions</th>
- <th class="ralign">{unit_plural} Sent</th>
- <th class="ralign">{unit_plural} Received</th>
- <th>Status</th>
- </tr>
-
- {items}
+ <thead>
+ <tr>
+ <th></th>
+ {headers}
+ </tr>
+ </thead>
+ <tbody>{items}</tbody>
</table>
</form>
[ClientSet.item_html]
<tr>
<td><input type="checkbox"/></td>
- <td>{item_link}</td>
- <td class="ralign">{item_sessions}</td>
- <td class="ralign">{item_from}</td>
- <td class="ralign">{item_to}</td>
- <td>{item_status}</td>
+ {cells}
</tr>
[ClientStatus.javascript]
Modified: mgmt/cumin/python/cumin/exchange.py
===================================================================
--- mgmt/cumin/python/cumin/exchange.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/exchange.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -136,23 +136,19 @@
return "exchange.xml?id=%i" % exchange.id
def render_messages_received(self, session, exchange):
- value = self.app.model.exchange.get_stat("msgReceives").rate(exchange)
- return fmt_rate(value, "msg", "sec")
+ return self.app.model.exchange.get_stat("msgReceives").rate_html(exchange)
def render_messages_routed(self, session, exchange):
- value = self.app.model.exchange.get_stat("msgRoutes").rate(exchange)
- return fmt_rate(value, "msg", "sec")
+ return self.app.model.exchange.get_stat("msgRoutes").rate_html(exchange)
def render_messages_dropped(self, session, exchange):
return self.app.model.exchange.get_stat("msgDrops").value(exchange)
def render_bytes_received(self, session, exchange):
- value = self.app.model.exchange.get_stat("byteReceives").rate(exchange)
- return fmt_rate(value, "byte", "sec")
+ return self.app.model.exchange.get_stat("byteReceives").rate_html(exchange)
def render_bytes_routed(self, session, exchange):
- value = self.app.model.exchange.get_stat("byteRoutes").rate(exchange)
- return fmt_rate(value, "byte", "sec")
+ return self.app.model.exchange.get_stat("byteRoutes").rate_html(exchange)
def render_bytes_dropped(self, session, exchange):
return self.app.model.exchange.get_stat("byteDrops").value(exchange)
@@ -182,7 +178,7 @@
return self.tabs.show_mode(session, self.bindings);
def get_title(self, session, exchange):
- return self.parent().get_title(session, exchange)
+ return self.parent.get_title(session, exchange)
def render_data_url(self, session, exchange):
return "exchange.xml?id=%i" % exchange.id
Modified: mgmt/cumin/python/cumin/formats.py
===================================================================
--- mgmt/cumin/python/cumin/formats.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/formats.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -61,7 +61,7 @@
def fmt_rate(value, unit1, unit2):
#return "%i <small>%s/%s</small>" % (value, unit1, unit2)
- return "%i<small>/%s</small>" % (nvl(value, 0), unit2)
+ return "%.0f<small>/%s</small>" % (float(nvl(value, 0)), unit2)
def fmt_predicate(predicate):
return predicate and "Yes" or "No"
@@ -100,6 +100,9 @@
def fmt_none():
return "<span class=\"none\">None</span>"
+def fmt_none_brief():
+ return "<span class=\"none\">–</span>"
+
def fmt_shorten(string):
if len(string) > 20:
return string[:13] + "..." + string[-6:]
Modified: mgmt/cumin/python/cumin/model.py
===================================================================
--- mgmt/cumin/python/cumin/model.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/model.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -1,7 +1,9 @@
from mint import *
from wooly import *
+from time import mktime
from util import *
+from formats import *
class CuminModel(object):
def __init__(self):
@@ -79,54 +81,92 @@
self.cumin_class.add_stat(self)
+ def samples(self, object, limit=None):
+ stats = object.stats[:limit]
+ stats = stats.orderBy("-recTime")
+
+ samples = list()
+
+ for stat in object.stats[:limit]:
+ value = getattr(stat, self.name, 0)
+ samples.append((stat.recTime, value))
+
+ return samples
+
+ def value_html(self, object):
+ text = self.value_text(object)
+
+ if text:
+ html = text
+ else:
+ html = fmt_none_brief()
+
+ return html
+
+ def value_text(self, object):
+ value = self.value(object)
+
+ if value is None:
+ text = ""
+ elif value == 0:
+ text = "0"
+ else:
+ text = "%i" % value # XXX handle other types
+
+ return text
+
def value(self, object):
try:
- return nvl(getattr(object.statsCurr, self.name), -1)
+ return getattr(object.statsCurr, self.name)
except AttributeError:
- return -1
+ pass
- def samples(self, object, limit=None):
- name = self.cumin_class.name
- cls = self.cumin_class.mint_stats_class
+ def rate_html(self, object):
+ text = self.rate_text(object)
- # XXX get rid of this
- stats = cls.select("%s_id = %i" % (name, object.id),
- orderBy="-id")[:limit]
+ if text:
+ html = "%s<small>/sec</small>" % text
+ else:
+ html = fmt_none_brief()
- samples = list()
+ return html
- for stat in stats:
- time = getattr(stat, "recTime")
- value = getattr(stat, self.name, 0)
- samples.append((time, value))
+ def rate_text(self, object):
+ rate = self.rate(object)
- return samples
+ if rate is None:
+ text = ""
+ elif rate == 0:
+ text = "0"
+ else:
+ text = "%0.2f" % rate
+ return text
+
def rate(self, object):
try:
if object.statsCurr:
curr = getattr(object.statsCurr, self.name)
+ ctime = object.statsCurr.recTime
+ csecs = mktime(ctime.timetuple())
if object.statsPrev:
prev = getattr(object.statsPrev, self.name)
+ ptime = object.statsPrev.recTime
+ psecs = mktime(ptime.timetuple())
if curr is not None and prev is not None:
- return (curr - prev) / float(1)
- else:
- return 0
- else:
- return 0
- else:
- return 0
+ return (curr - prev) / (csecs - psecs)
except AttributeError:
- return -1
+ pass
def write_xml(self, object, writer):
- writer.write("<stat name=\"%s\" value=\"%i\" rate=\"%i\"/>" \
- % (self.name,
- self.value(object) or 0, #XXX need a null value
- self.rate(object) or 0))
+ value = self.value_text(object)
+ rate = self.rate_text(object)
+ writer.write("<stat name=\"%s\" value=\"%s\" rate=\"%s\"/>" \
+ % (self.name, value, rate))
+
class CuminQueue(CuminClass):
def __init__(self, model):
super(CuminQueue, self).__init__(model, "queue", Queue)
Modified: mgmt/cumin/python/cumin/page.py
===================================================================
--- mgmt/cumin/python/cumin/page.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/page.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -126,18 +126,15 @@
def show_queue(self, session, queue):
frame = self.show_broker(session, queue.vhost.broker.registration)
- frame = frame.show_queue(session, queue)
- return self.set_current_frame(session, frame)
+ return frame.show_queue(session, queue)
def show_exchange(self, session, exchange):
frame = self.show_broker(session, exchange.vhost.broker.registration)
- frame = frame.show_exchange(session, exchange)
- return self.set_current_frame(session, frame)
+ return frame.show_exchange(session, exchange)
def show_client(self, session, client):
frame = self.show_broker(session, client.vhost.broker.registration)
- frame = frame.show_client(session, client)
- return self.set_current_frame(session, frame)
+ return frame.show_client(session, client)
def render_class(self, session, object):
return self.modal.get(session) and "modal"
Modified: mgmt/cumin/python/cumin/queue.py
===================================================================
--- mgmt/cumin/python/cumin/queue.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/queue.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -65,32 +65,32 @@
class EnqueuedColumn(TableColumn):
def get_title(self, session):
- return "%s Enqueued" % self.header.parent().get_unit_plural \
+ return "%s Enqueued" % self.header.parent.get_unit_plural \
(session)
def get_data_name(self, session):
- if self.header.parent().unit.get(session) == "b":
+ if self.header.parent.unit.get(session) == "b":
return QueueStats.q.byteTotalEnqueues
else:
return QueueStats.q.msgTotalEnqueues
class DequeuedColumn(TableColumn):
def get_title(self, session):
- return "%s Dequeued" % self.header.parent().get_unit_plural \
+ return "%s Dequeued" % self.header.parent.get_unit_plural \
(session)
def get_data_name(self, session):
- if self.header.parent().unit.get(session) == "b":
+ if self.header.parent.unit.get(session) == "b":
return QueueStats.q.byteTotalDequeues
else:
return QueueStats.q.msgTotalDequeues
class DepthColumn(TableColumn):
def get_title(self, session):
- return "%s Depth" % self.header.parent().get_unit_singular(session)
+ return "%s Depth" % self.header.parent.get_unit_singular(session)
def get_data_name(self, session):
- if self.header.parent().unit.get(session) == "b":
+ if self.header.parent.unit.get(session) == "b":
return QueueStats.q.byteDepth
else:
return QueueStats.q.msgDepth
@@ -279,7 +279,7 @@
def render_purge_href(self, session, queue):
branch = session.branch()
- self.parent().show_purge(branch)
+ self.parent.show_purge(branch)
return branch.marshal()
class QueueBindingSet(BindingSet):
Modified: mgmt/cumin/python/cumin/stat.py
===================================================================
--- mgmt/cumin/python/cumin/stat.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/stat.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -43,24 +43,25 @@
def render_item_value(self, session, args):
stat, object = args
- if stat.link_cb:
- branch = session.branch()
- stat.link_cb(self.page(), branch, object)
- return fmt_link(branch.marshal(), stat.value(object))
- else:
- return stat.value(object)
+ #if stat.link_cb:
+ # branch = session.branch()
+ # stat.link_cb(self.page(), branch, object)
+ # return fmt_link(branch.marshal(), stat.value(object))
+ #else:
+ return stat.value_html(object)
+
def render_item_extra(self, session, args):
stat, object = args
- if False and stat.highlow: #XXX
- return "<small>high</small> <span>%i</span> " + \
- "<small>low</small> <span>%i</span>" \
- % (stat.high(object) or 0, stat.low(object) or 0)
- else:
- unit = self.unit_abbrevs.get(stat.unit, stat.unit)
- return fmt_rate(stat.rate(object), unit, "sec")
+ #if False and stat.highlow: #XXX
+ # return "<small>high</small> <span>%i</span> " + \
+ # "<small>low</small> <span>%i</span>" \
+ # % (stat.high(object) or 0, stat.low(object) or 0)
+ #else:
+ return stat.rate_html(object)
+
def render_item_average(self, session, args):
stat, object = args
return None #XXX "%0.2f" % (sum(stat.values) / float(len(stat.values)))
Modified: mgmt/cumin/python/cumin/stat.strings
===================================================================
--- mgmt/cumin/python/cumin/stat.strings 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/stat.strings 2008-01-02 14:41:35 UTC (rev 1528)
@@ -11,20 +11,28 @@
var stat = object.stat[attr];
var tds = tr.elems("td", null, null, 0, 2);
- tds.next().text().set(stat.value);
-
var td = tds.next();
+ if (stat.value) {
+ td.text().set(stat.value);
+ }
+
+ td = tds.next();
+
var phs = td.elems("span", null, null, 0, 2);
var ph = phs.next();
- if (ph) {
- ph.set(stat.high);
+ if (false && ph) { // XXX disabled
+ if (stat.high) {
+ ph.set(stat.high);
+ }
ph = phs.next();
- ph.set(stat.low);
- } else {
+ if (stat.low) {
+ ph.set(stat.low);
+ }
+ } else if (stat.rate) {
td.text().set(stat.rate);
}
}
Modified: mgmt/cumin/python/cumin/util.py
===================================================================
--- mgmt/cumin/python/cumin/util.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/util.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -9,3 +9,7 @@
return expr2
else:
return expr1
+
+class Identifiable(object):
+ def __init__(self, id=None):
+ self.id = id
Modified: mgmt/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/cumin/python/cumin/widgets.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/cumin/widgets.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -64,12 +64,12 @@
class CuminView(Widget):
def render_edit_href(self, session, object):
branch = session.branch()
- self.parent().show_edit(branch)
+ self.parent.show_edit(branch)
return branch.marshal()
def render_remove_href(self, session, object):
branch = session.branch()
- self.parent().show_remove(branch)
+ self.parent.show_remove(branch)
return branch.marshal()
class CuminForm(Form):
@@ -121,14 +121,14 @@
return "cancel"
def render_content(self, session, object):
- return self.parent().render_cancel_content(session, object)
+ return self.parent.render_cancel_content(session, object)
class Submit(FormButton):
def render_class(self, session, object):
return "submit"
def render_content(self, session, object):
- return self.parent().render_submit_content(session, object)
+ return self.parent.render_submit_content(session, object)
class CuminConfirmForm(CuminForm):
def __init__(self, app, name):
@@ -168,12 +168,15 @@
self.__param = None
- self.iparam = Parameter(app, "param")
- self.add_parameter(self.iparam)
+ param = Parameter(app, "param")
+ self.add_parameter(param)
- self.stats = ListParameter(app, "s", self.iparam)
+ self.stats = ListParameter(app, "s", param)
self.add_parameter(self.stats)
+ self.mode = Parameter(app, "m")
+ self.add_parameter(self.mode)
+
def set_object_parameter(self, param):
self.__param = param
@@ -189,27 +192,23 @@
cls = self.app.model.get_class(object)
stats = [cls.get_stat(x) for x in self.stats.get(session)]
- samples = stats[0].samples(object, 121)
- times = [x[0] for x in samples]
+ max_value = 0
- values = dict()
for stat in stats:
- values[stat] = [x[1] for x in stat.samples(object, 121)]
+ for x in stat.samples(object, 300):
+ max_value = max(x[1], max_value)
- max_value = 0
- for stat in stats:
- max_value = max(max(values[stat]), max_value)
max_value = max_value * 1.1
max_value = max_value + (100 - max_value % 100)
chart.set_max_value(int(max_value))
- chart.plot_x_axis(times)
+ chart.plot_x_axis()
chart.plot_y_axis()
colors = ((1,0,0), (0,0,1), (0,1,0))
for stat, color in zip(stats, colors):
- chart.plot_values(values[stat], color=color)
+ chart.plot_values(stat.samples(object, 300), color=color)
chart.plot_frame()
Modified: mgmt/cumin/python/wooly/__init__.py
===================================================================
--- mgmt/cumin/python/wooly/__init__.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/wooly/__init__.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -21,7 +21,7 @@
def path(self):
if self.__path == None:
- if not self.widget.parent():
+ if not self.widget.parent:
self.__path = self.name
else:
self.__path = self.widget.path() + "." + self.name
@@ -92,8 +92,8 @@
class Widget(object):
def __init__(self, app, name):
self.app = app
- self.__name = name # XXX undo this
- self.__parent = None # XXX undo this
+ self.name = name # XXX undo this
+ self.parent = None # XXX undo this
self.children = list()
self.attributes = list()
self.parameters = list()
@@ -108,6 +108,7 @@
self.__ancestors = None
self.__path = None
self.__page = None
+ self.__frame = None
self.__child_index = None
app.add_widget(self)
@@ -121,54 +122,61 @@
def get_default(self, session):
return list()
- def name(self):
- return self.__name
-
- def parent(self):
- return self.__parent
-
def ancestors(self):
- if not self.__ancestors:
- if not self.parent():
+ if self.__ancestors is None:
+ if self.parent is None:
self.__ancestors = tuple()
else:
- ancs = list(self.parent().ancestors())
- ancs.append(self.parent())
+ ancs = list(self.parent.ancestors())
+ ancs.append(self.parent)
self.__ancestors = tuple(ancs)
return self.__ancestors
def path(self):
- if self.__path == None:
- if not self.parent():
+ if self.__path is None:
+ if self.parent is None:
self.__path = ""
- elif not self.parent().parent():
- self.__path = self.name();
+ elif self.parent.parent is None:
+ self.__path = self.name;
else:
- self.__path = self.parent().path() + "." + self.name()
+ self.__path = self.parent.path() + "." + self.name
return self.__path
def page(self):
- if self.__page == None:
- if not self.parent():
+ if self.__page is None:
+ if self.parent is None:
self.__page = self
else:
- self.__page = self.parent().page()
+ self.__page = self.parent.page()
return self.__page
+ def frame(self):
+ if self.__frame is None:
+ if self.parent is None:
+ assert isinstance(self, Page)
+
+ self.__frame = self
+ else:
+ for anc in self.ancestors():
+ if isinstance(anc, Frame):
+ self.__frame = anc
+
+ return self.__frame
+
def add_child(self, widget):
self.children.append(widget)
- widget.__parent = self
+ widget.parent = self
def get_child(self, name):
if not self.__child_index:
self.__child_index = dict()
for child in self.children:
- self.__child_index[child.name()] = child
+ self.__child_index[child.name] = child
return self.__child_index.get(name, None)
@@ -412,10 +420,10 @@
self.urls = set()
def add_page(self, page):
- if page.parent():
- raise Exception("Page '%s' is not a root widget" % page.name())
+ if page.parent:
+ raise Exception("Page '%s' is not a root widget" % page.name)
- self.pages[page.name()] = page
+ self.pages[page.name] = page
def get_page(self, name):
return self.pages.get(name, self.default_page)
@@ -587,7 +595,7 @@
return url
def marshal_page(self):
- return self.get_page().name()
+ return self.get_page().name
def marshal_url_vars(self, separator=";"):
params = self.get_page().get_saved_parameters(self)
Modified: mgmt/cumin/python/wooly/tables.py
===================================================================
--- mgmt/cumin/python/wooly/tables.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/wooly/tables.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -25,12 +25,12 @@
self.add_child(column)
if self.scolumn.default is None:
- self.scolumn.default = column.name()
+ self.scolumn.default = column.name
def get_selected_column(self, session):
name = self.scolumn.get(session)
for column in self.columns:
- if column.name() == name:
+ if column.name == name:
return column
def is_reversed(self, session):
@@ -56,10 +56,10 @@
column, object = args
branch = session.branch()
- if column.name() == self.scolumn.get(session):
+ if column.name == self.scolumn.get(session):
self.reversed.set(branch, not self.reversed.get(session))
- self.scolumn.set(branch, column.name())
+ self.scolumn.set(branch, column.name)
return branch.marshal()
@@ -71,7 +71,7 @@
data_map = dict()
for col, datum in zip(self.columns, data):
- data_map[col.name()] = datum
+ data_map[col.name] = datum
writer = Writer()
@@ -90,7 +90,7 @@
super(ItemTableColumn, self).__init__(app, name)
def do_render(self, session, data):
- return "<td>%s</td>" % str(data[self.name()])
+ return "<td>%s</td>" % str(data[self.name])
class SqlTable(ItemTable):
def __init__(self, app, name):
@@ -141,7 +141,7 @@
def get_orderby_sql(self, session):
scol = self.get_selected_column(session)
- sqlcol = self.get_sql_column(scol.name())
+ sqlcol = self.get_sql_column(scol.name)
if sqlcol:
column_sql = sqlcol.get_sql(session)
Modified: mgmt/cumin/python/wooly/widgets.py
===================================================================
--- mgmt/cumin/python/wooly/widgets.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/python/wooly/widgets.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -19,13 +19,13 @@
super(ModeSet, self).add_child(mode)
if not self.mode.default:
- self.mode.set_default(mode.name())
+ self.mode.set_default(mode.name)
def get_selected_mode(self, session):
return self.get_child(self.mode.get(session))
def set_selected_mode(self, session, mode):
- self.mode.set(session, mode.name())
+ self.mode.set(session, mode.name)
def show_mode(self, session, mode):
self.set_selected_mode(session, mode)
Modified: mgmt/cumin/resources/wooly.js
===================================================================
--- mgmt/cumin/resources/wooly.js 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/cumin/resources/wooly.js 2008-01-02 14:41:35 UTC (rev 1528)
@@ -340,6 +340,8 @@
}
this.add = function(content) {
+ assert(content);
+
if (typeof content == "string") {
// XXX flatten this out
this.add(new WoolyText(this.doc, null).set(content));
Modified: mgmt/misc/boneyard.py
===================================================================
--- mgmt/misc/boneyard.py 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/misc/boneyard.py 2008-01-02 14:41:35 UTC (rev 1528)
@@ -77,9 +77,9 @@
def render_group_link(self, session, group):
branch = session.branch()
- self.parent().param.set(branch, group)
+ self.parent.param.set(branch, group)
- selected = self.parent().param.get(session) is group
+ selected = self.parent.param.get(session) is group
return mlink(branch.marshal(), "ServerGroup", group.name, selected)
Modified: mgmt/notes/justin-todo.txt
===================================================================
--- mgmt/notes/justin-todo.txt 2007-12-21 21:41:26 UTC (rev 1527)
+++ mgmt/notes/justin-todo.txt 2008-01-02 14:41:35 UTC (rev 1528)
@@ -12,20 +12,24 @@
- Add legends to charts
- - Change the way we plot values; currently it's very stupid
-
- Display current values on right-side and according to color
- Add y-axis ticks and values for reference
+ - Add a "rate" mode to charts
+
+ - Limit samples query by time
+
+ - Return samples data in column-oriented structure
+
+ - Unbreak the query behind stats
+
* Sortify brokers
* Sortify clients
* Sortify sessions
- * Render stats without values as something other than 0, say a --
-
* Mgmtd-broker interaction
- Deal with problem of calling method on broker that is not there
@@ -36,8 +40,14 @@
* Remove sqlobject workaround in CuminStat.samples
+ * Default exchange name in queue and exchange bindings is still ""
+
+ * Add "slowest views" tracking to --bench
+
Deferred
+ * Add a widget for the html client side of chart images
+
* Directly set attr.default for static defaults; eliminate
attr.set_default
16 years, 11 months