Author: croberts
Date: 2012-11-15 15:03:29 +0000 (Thu, 15 Nov 2012)
New Revision: 5541
Modified:
branches/croberts/cumin/python/cumin/grid/dashboard.py
branches/croberts/cumin/python/cumin/grid/dashboard.strings
branches/croberts/cumin/python/cumin/stat.py
branches/croberts/cumin/python/cumin/stat.strings
branches/croberts/cumin/resources/app.css
branches/croberts/cumin/resources/app.js
branches/croberts/wooly/python/wooly/pages.strings
Log:
More changes to include tree navigation for hierarchical accounting groups on the
accounting usage chart.
Modified: branches/croberts/cumin/python/cumin/grid/dashboard.py
===================================================================
--- branches/croberts/cumin/python/cumin/grid/dashboard.py 2012-11-06 21:34:31 UTC (rev
5540)
+++ branches/croberts/cumin/python/cumin/grid/dashboard.py 2012-11-15 15:03:29 UTC (rev
5541)
@@ -1,4 +1,5 @@
import logging
+import re
from wooly import Widget, Attribute
from wooly.util import StringCatalog, Writer
@@ -7,6 +8,7 @@
from wooly.widgets import RadioModeSet, WidgetSet
from wooly.template import WidgetTemplate
from wooly.forms import StringInput
+from wooly.sql import SqlOperation
from parsley.stringex import rpartition
from rosemary.sqlquery import SqlQueryOptions
@@ -80,15 +82,11 @@
# chart.duration.param.default= "86400"
# charts.add_child(chart)
- chart = self.AccountingChart(app, "accuse",
app.model.com_redhat_grid_plumage.Accountant)
- chart.max_samples = 250
- chart.chart_type = "genusechart"
- chart.duration.param.default = "86400"
- chart.leftlegend = False
- chart.height = 150
- chart.width = 400
- charts.add_child(chart)
+ chartholder = AccountingChartWidget(app, "accchart")
+ charts.add_child(chartholder)
+
+
def render_title(self, session):
return "Accounting"
@@ -106,7 +104,99 @@
class AccountingChart(ReportingChart):
def render_title(self, session):
- return "Usage by accounting group (cpus)"
+ return "Usage by accounting group (cpus)"
+
+class AccountingChartWidget(Widget):
+ def __init__(self, app, name):
+ super(AccountingChartWidget, self).__init__(app, name)
+
+ control = self.AccountingChartControl(app, "actrl")
+ self.add_child(control)
+
+ chart = self.AccountingChart(app, "accuse",
app.model.com_redhat_grid_plumage.Accountant)
+ chart.max_samples = 250
+ chart.chart_type = "genusechart"
+ chart.duration.param.default = "86400"
+ chart.leftlegend = False
+ chart.height = 150
+ chart.width = 400
+ self.add_child(chart)
+
+ class AccountingChartControl(Widget):
+ def __init__(self, app, name):
+ super(AccountingChartWidget.AccountingChartControl, self).__init__(app,
name)
+ self.app = app
+ self.groupLoader = AccGroupSqlOperation(app)
+
+ def render_chart_div_name(self, session):
+ return self.parent.children_by_name['accuse'].path
+
+ def render_accounting_groups(self, session):
+ display_groups = []
+ trees = {}
+ groups_string = ""
+ groups = self.groupLoader.execute(self.app.session).fetchall()
+ groups = [g[0] for g in groups]
+ top_groups = self.groupLoader.getTopLevelGroups(groups)
+ for t_group in top_groups:
+ trees[t_group] = self.groupLoader.build_tree(groups, t_group)
+
+ groups_string = self.processSubTree(trees, "", True)
+
+ return groups_string
+
+ def processSubTree(self, subtree, startString, initialUL=False):
+ if initialUL:
+ subtree_string = "%s <ul id=\"%s\">" %
(startString, self.path)
+ else:
+ subtree_string = "%s <ul>" % startString
+ for node in subtree:
+ if len(subtree[node]) > 0:
+ subtree_string += """<li><a
href="#">%s</a>""" % node.replace("<",
"<").replace(">", ">")
+ subtree_string = self.processSubTree(subtree[node], subtree_string)
+ subtree_string += "</li>"
+ else:
+ subtree_string += """<li><a
href="#">%s</a></li>""" %
node.replace("<", "<").replace(">",
">")
+ subtree_string += "</ul>"
+ return subtree_string
+
+
+ class AccountingChart(ReportingChart):
+ def render_title(self, session):
+ return "Usage by accounting group (cpus)"
+
+class AccGroupSqlOperation(SqlOperation):
+ def __init__(self, app):
+ super(AccGroupSqlOperation, self).__init__(app)
+
+ def get_connection(self, session):
+ return self.app.database.get_read_connection()
+
+ def getTopLevelGroups(self, groups):
+ topLevelGroups = []
+ for group in groups:
+ split_name = group.split(".",1)
+ if split_name[0] not in topLevelGroups:
+ topLevelGroups.append(split_name[0])
+ return topLevelGroups
+
+ def build_tree(self, nodes, topNode):
+ tree = {}
+ self.build_tree_recursive(tree, topNode, nodes)
+
+ return tree
+
+ def build_tree_recursive(self, tree, parent, nodes):
+ children = []
+ for n in nodes:
+ (nparent, sep, leaf) = n.rpartition(".")
+ if nparent == parent:
+ children.append(n)
+
+ for child in children:
+ tree[child] = {}
+ self.build_tree_recursive(tree[child], child, nodes)
+
class DashboardSummary(Widget):
def __init__(self, app, name, collector):
Modified: branches/croberts/cumin/python/cumin/grid/dashboard.strings
===================================================================
--- branches/croberts/cumin/python/cumin/grid/dashboard.strings 2012-11-06 21:34:31 UTC
(rev 5540)
+++ branches/croberts/cumin/python/cumin/grid/dashboard.strings 2012-11-15 15:03:29 UTC
(rev 5541)
@@ -317,6 +317,14 @@
</div>
</div>
+
+
+[DashboardAccounting.html]
+<div class="{class}">
+ {charts}
+</div>
+<div style="clear:both;"></div>
+
[DashboardAccounting.css]
div.DashboardAccounting ul.WidgetSet {
padding: 0;
@@ -324,13 +332,74 @@
list-style: none;
}
-div.DashboardAccounting ul.WidgetSet li {
- display: inline;
- float: left;
-}
-[DashboardAccounting.html]
+[AccountingChartWidget.html]
<div class="{class}">
- {charts}
+ {actrl}
+ {accuse}
</div>
-<div style="clear:both;"></div>
+
+[AccountingChartWidget.css]
+div.AccountingChartWidget>div {
+ float: left;
+}
+
+
+[AccountingChartControl.html]
+<div id="controlTree" class="{class}"></div>
+<div style="display:none;">
+ {accounting_groups}
+</div>
+
+<script type="text/javascript">
+//<![CDATA[
+var controlTree;
+window.addEvent('domready', function () {
+ controlTree = new MooTreeControl({
+ div: 'controlTree',
+ mode: null,
+ grid: true,
+ onSelect: function(node, state) {
+ //do nothing
+ },
+ onExpand: function(node, state) {
+ var exposed_nodes = [];
+ for(i=0; i < node.nodes.length; i++) {
+ exposed_nodes[i] = node.nodes[i].text;
+ }
+ var chart = cumin.getChart("{chart_div_name}");
+ var chart_url =
chart.getParent().getElement("a").getAttribute("href");
+ var nodes = [];
+ getExposedNodes(nodes, this.root);
+ var new_url = get_new_chart_url("{chart_div_name}", chart_url,
"groupex", nodes);
+ chart.getParent().getElements('a')[chart_href].set('href', new_url);
+ drawSingleChart(chart, true);
+ }
+ },{
+ text: 'Accounting Groups',
+ open: true
+ });
+ controlTree.adopt('{id}');
+
+});
+
+function getExposedNodes(nodeList, node) {
+ if(node.open == true) {
+ for(var i=0, count=node.nodes.length; i < count; i++) {
+ getExposedNodes(nodeList, node.nodes[i]);
+ }
+ } else {
+ nodeList.push(node.text);
+ }
+}
+//]]>
+</script>
+
+[AccountingChartControl.css]
+div.AccountingChartControl {
+ width : 300px;
+}
+
+[AccGroupSqlOperation.sql]
+select distinct(agroup) from
"com.redhat.grid.plumage"."Accountant_samples"
+ order by agroup asc;
Modified: branches/croberts/cumin/python/cumin/stat.py
===================================================================
--- branches/croberts/cumin/python/cumin/stat.py 2012-11-06 21:34:31 UTC (rev 5540)
+++ branches/croberts/cumin/python/cumin/stat.py 2012-11-15 15:03:29 UTC (rev 5541)
@@ -902,13 +902,13 @@
duration = self.get_duration(session)
end_seconds_ago = self.get_end_seconds(session)
delta = self.get_delta(session)
-
+
interval = self.page.get_interval(session, duration, width)
msg = "Chart parameters: duration: %s end_secends_ago: %s interval:
%s" % (str(duration), str(end_seconds_ago), str(interval))
log.debug(msg)
# get the most recent samples
- samples = self.fetch_samples(adapter, duration, interval,
+ samples = self.fetch_samples(session, adapter, duration, interval,
self.fix_method(method, mode),
mode, delta, stats,
end_seconds_ago=end_seconds_ago)
@@ -925,7 +925,7 @@
if axis_max != max_of_axiis:
msg = "Y-Axis changed. New chart parameters: time_span: %s
end_secends_ago: %s interval: %s" % (str(time_span), str(end_seconds_ago),
str(interval))
log.debug(msg)
- samples = self.fetch_samples(adapter, time_span, interval,
+ samples = self.fetch_samples(session, adapter, time_span, interval,
self.fix_method(method, mode),
mode, False, stats,
end_seconds_ago=end_seconds_ago)
@@ -1003,7 +1003,7 @@
return y_axis, y_axis_right
- def fetch_samples(self, adapter, dur, interval, method, mode, delta, stats,
end_seconds_ago=0):
+ def fetch_samples(self, session, adapter, dur, interval, method, mode, delta, stats,
end_seconds_ago=0):
return dict()
def get_max_min(self, session, stats, samples):
@@ -1022,7 +1022,7 @@
# get more samples than needed to allow chart to clip correctly (explains the +
600)
samples[stat] = adapter[stat].samples(stat, dur+600, interval, method,
secs2=end_seconds_ago, delta=delta)
- def fetch_samples(self, adapter, dur, interval, method, mode, delta, stats,
end_seconds_ago=0):
+ def fetch_samples(self, session, adapter, dur, interval, method, mode, delta, stats,
end_seconds_ago=0):
samples = dict()
if mode == "rate":
for stat in stats:
@@ -1182,18 +1182,29 @@
self.accGroups = self.AccGroupSqlOperation(app)
self.accUse = self.AccGroupUseSqlOperation(app, self)
- def fetch_samples(self, adapter, dur, interval, method, mode, delta, stats,
end_seconds_ago=0):
+ def fetch_samples(self, session, adapter, dur, interval, method, mode, delta, stats,
end_seconds_ago=0):
samples = dict()
- groups = self.accGroups.execute(self.app.session).fetchall()
+ groups_expanded = self.page.group_expand.get(session)
+ #use groups from url, if present, otherwise grab "all" form database
+ groups = groups_expanded if len(groups_expanded) > 0 else
self.getTopLevelGroups(self.accGroups.execute(self.app.session).fetchall())
for group in groups:
- group_name = group[0]
+ group_name = group if isinstance(group, unicode) or isinstance(group, str)
else group[0]
result = self.accUse.execute(self.app.session, group_name, dur,
interval).fetchall()
if len(result) > 0:
samples[group_name] = result
return samples
+ def getTopLevelGroups(self, groups):
+ topLevelGroups = []
+ for group in groups:
+ group = group if isinstance(group, unicode) else group[0]
+ split_name = group.split(".",1)
+ if split_name[0] not in topLevelGroups:
+ topLevelGroups.append(split_name[0])
+ return topLevelGroups
+
def get_line_title(self, stat):
return stat.replace("<", "").replace(">",
"")
@@ -1401,6 +1412,13 @@
self.group_selection = Parameter(app, "groupc")
self.group_selection.default = None
self.add_parameter(self.group_selection)
+
+ groupex_val = Parameter(app, "gp")
+ self.add_parameter(groupex_val)
+
+ self.group_expand = ListParameter(app, "groupex", groupex_val)
+ self.group_expand.default = []
+ self.add_parameter(self.group_expand)
def get_content_type(self, session):
return "text/plain"
Modified: branches/croberts/cumin/python/cumin/stat.strings
===================================================================
--- branches/croberts/cumin/python/cumin/stat.strings 2012-11-06 21:34:31 UTC (rev 5540)
+++ branches/croberts/cumin/python/cumin/stat.strings 2012-11-15 15:03:29 UTC (rev 5541)
@@ -286,27 +286,13 @@
setTimeout(function () {changeDuration(state, a, id, attempt+1);}, 100);
return false;
}
- newurl = get_chart_url(id,
$(chart).getParent().getElements('a')[chart_href].get('href'), state);
+ newurl = get_new_chart_url(id,
$(chart).getParent().getElements('a')[chart_href].get('href'),
'duration', state);
$(chart).getParent().getElements('a')[chart_href].set('href',
newurl);
drawSingleChart(chart, true);
}
return false;
}
-// called by open-flash-chart.swf in response to chart.geturl()
-function get_chart_url(id, url, state) {
- var chart = cumin.getChart(id);
- if (chart == null)
- return false;
- var branch = wooly.session.branch(url);
- var now = new Date().getTime();
- branch['elapsed'] = now; // force an entire update
- branch['duration'] = state;
- url = branch.marshal();
-
- return url;
-}
-
function gotChartPoints(text, oImg) {
var mImg = $(oImg);
mImg.store('points', eval("("+text+")"));
@@ -436,20 +422,6 @@
return false;
}
-function get_new_chart_url(id, url, param, value) {
- var chart = cumin.getChart(id);
- if (chart == null)
- return false;
-
- var branch = wooly.session.branch(url);
- var now = new Date().getTime();
- branch['elapsed'] = now; // force an entire update
- branch[param] = value;
- url = branch.marshal();
-
- return url;
-}
-
[FilterInput.html]
<!-- <input id="{id}" type="text"
name="{name}"></input><input type="button"
value="Update filter" {onclick}/> -->
{title}: <select {onchange}>{user_list}</select>
@@ -482,8 +454,8 @@
[AccGroupUseSqlOperation.sql]
select max("Accountant_samples"."ts") as interval_end,
-cast(avg("Accountant_samples"."resused") as integer) as value,
+cast(max("Accountant_samples"."resused") as integer) as value,
stddev("Accountant_samples"."resused") as dev from
"com.redhat.grid.plumage"."Accountant_samples"
-where ("Accountant_samples"."user" like '{accgroup}') and
"Accountant_samples"."ts" >= now() - interval '{duration}
seconds'
+where ("Accountant_samples"."user" like '{accgroup}%') and
"Accountant_samples"."ts" >= now() - interval '{duration}
seconds'
group by floor(extract(epoch from "Accountant_samples"."ts") /
{interval}) order by interval_end desc limit all offset 0;
\ No newline at end of file
Modified: branches/croberts/cumin/resources/app.css
===================================================================
--- branches/croberts/cumin/resources/app.css 2012-11-06 21:34:31 UTC (rev 5540)
+++ branches/croberts/cumin/resources/app.css 2012-11-15 15:03:29 UTC (rev 5541)
@@ -824,3 +824,29 @@
{
color: #9FCFFF;
}
+
+
+.mooTree_node {
+ font-family: Verdana, Arial, Helvetica;
+ font-size: 10px;
+ white-space: nowrap;
+}
+
+.mooTree_text {
+ padding-top: 3px;
+ height: 15px;
+ cursor: pointer;
+}
+
+.mooTree_img {
+ float: left;
+ width: 18px;
+ height: 18px;
+ overflow: hidden;
+}
+
+.mooTree_selected {
+ background-color: #e0f0ff;
+ font-weight: bold;
+}
+
Modified: branches/croberts/cumin/resources/app.js
===================================================================
--- branches/croberts/cumin/resources/app.js 2012-11-06 21:34:31 UTC (rev 5540)
+++ branches/croberts/cumin/resources/app.js 2012-11-15 15:03:29 UTC (rev 5541)
@@ -634,7 +634,7 @@
gridPadding: dataContainer['gridPadding'],
grid: { background: '#FFFFFF' },
legend: {
- show: true,
+ show: dataContainer['labels'][0] == "No data" ? false:true,
location: dataContainer['legendLocation'],
renderer: $j.jqplot.EnhancedLegendRenderer,
yoffset: 0,
@@ -671,7 +671,7 @@
fillAndStroke:dataContainer['fillAndStroke'],
fillAlpha: dataContainer['fillAlpha'],
rendererOptions: {
- highlightMouseOver: false,
+ highlightMouseOver: true,
highlightMouseDown: false,
highlightColor: null,
markerRenderer: $j.jqplot.MarkerRenderer,
@@ -720,6 +720,7 @@
// if the chart already exists, perform a "replot()" on the chart, otherwise,
draw the chart from scratch
if(allCharts[divName] != undefined) {
if(allCharts[divName]["forceRedraw"] == true) {
+ //console.log("Data is: " +
JSON.stringify(dataContainer['y_coordinate_values']));
allCharts[divName].destroy();
allCharts[divName] = $j.jqplot(holder,
dataContainer['y_coordinate_values'],chartOptionsObject);
} else {
@@ -736,14 +737,20 @@
} else {
$j.jqplot.preDrawHooks.push(cuminChartPreDraw);
$j.jqplot.postDrawHooks.push(cuminChartPostDraw);
+ //$j.jqplot.eventListenerHooks.push(['jqplotClick', plot1ClickHandler]);
allCharts[divName] = $j.jqplot(holder,
dataContainer['y_coordinate_values'],chartOptionsObject);
allCharts[divName].target.bind('jqplotResetZoom', cuminChartResetZoom);
allCharts[divName].target.bind('jqplotZoom', cuminChartZoom);
+ allCharts[divName].target.bind('jqplotDataHighlight', cuminChartDataClick);
startChartMonitor(divName);
}
} // end function drawChart
+cuminChartDataClick = function (ev, seriesIndex, pointIndex, data) {
+ console.log('series: '+seriesIndex+', point: '+pointIndex+',
data: '+data);
+}
+
// called on the jqplotZoom event
cuminChartZoom = function(gridpos, datapos, plot, cursor) {
// console.log("cuminZoom");
@@ -764,6 +771,17 @@
// console.log("customPreDraw");
}
+function plot1ClickHandler(ev, gridpos, datapos, neighbor, plot) {
+ if(plot.targetId == "#bargraph") {
+
+ var column = getColumnLocation(plot,gridpos,line1.length);
+
+ //make2nd('doc'+plot.data[0][parseInt(column)][2],
plot.data[0][parseInt(column)][0]);
+
+ }
+}
+
+
// used to let us tweak the contents of the tooltip according to our whim, gets called
from highlighter plugin showTooltip function
customTooltip = function(str, seriesIndex, pointIndex, plot) {
value = plot.series[seriesIndex].data[pointIndex][1];
@@ -899,3 +917,16 @@
return "anonymous";
}
+function get_new_chart_url(id, url, param, value) {
+ var chart = cumin.getChart(id);
+ if (chart == null)
+ return false;
+
+ var branch = wooly.session.branch(url);
+ var now = new Date().getTime();
+ branch['elapsed'] = now; // force an entire update
+ branch[param] = value;
+ url = branch.marshal();
+
+ return url;
+}
Modified: branches/croberts/wooly/python/wooly/pages.strings
===================================================================
--- branches/croberts/wooly/python/wooly/pages.strings 2012-11-06 21:34:31 UTC (rev 5540)
+++ branches/croberts/wooly/python/wooly/pages.strings 2012-11-15 15:03:29 UTC (rev 5541)
@@ -23,12 +23,14 @@
<script type="text/javascript"
src="resource?name=jquery.js"></script>
<script type="text/javascript"
src="resource?name=jquery.jqplot.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.pieRenderer.js"></script>
+ <script type="text/javascript"
src="resource?name=jqplot.barRenderer.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.donutRenderer.min.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.categoryAxisRenderer.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.enhancedLegendRenderer.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.highlighter.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.cursor.js"></script>
<script type="text/javascript"
src="resource?name=jqplot.dateAxisRenderer.js"></script>
+ <script type="text/javascript"
src="resource?name=mootree.js"></script>
<script type="text/javascript">
$j = jQuery.noConflict();