summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Wall <richard@aziz>2010-06-13 12:37:50 +0100
committerRichard Wall <richard@aziz>2010-06-13 12:37:50 +0100
commitcbaeb0b773cc6401943250edcdac82c6a9a71ca8 (patch)
treeed1fb7a4af5682099d0a718ab0636a5e2c88acc4
parent57b58f83bde08b385a7a997f1d35c43683b34b9b (diff)
Build charts based on a downloaded list of available rrd files
-rw-r--r--index.html91
-rw-r--r--jrrd.js63
-rw-r--r--rrd_finder.rpy20
3 files changed, 126 insertions, 48 deletions
diff --git a/index.html b/index.html
index 8329f41..7e60c00 100644
--- a/index.html
+++ b/index.html
@@ -134,15 +134,34 @@
// Recipes for the charts on this page
var recipes = [
+
+ {
+ title: 'Twisted Web TCP Stats',
+ data: [
+ ['tcpconns-8080-local/tcp_connections-CLOSE_WAIT.rrd', 0, 'CLOSE_WAIT', ''],
+ ['tcpconns-8080-local/tcp_connections-SYN_RECV.rrd', 0, 'SYN_RECV', ''],
+ ['tcpconns-8080-local/tcp_connections-TIME_WAIT.rrd', 0, 'TIME_WAIT', ''],
+ ['tcpconns-8080-local/tcp_connections-CLOSED.rrd', 0, 'CLOSED', ''],
+ ['tcpconns-8080-local/tcp_connections-FIN_WAIT2.rrd', 0, 'FIN_WAIT2', ''],
+ ['tcpconns-8080-local/tcp_connections-FIN_WAIT1.rrd', 0, 'FIN_WAIT1', ''],
+ ['tcpconns-8080-local/tcp_connections-ESTABLISHED.rrd', 0, 'ESTABLISHED', ''],
+ ['tcpconns-8080-local/tcp_connections-LAST_ACK.rrd', 0, 'LAST_ACK', ''],
+ ['tcpconns-8080-local/tcp_connections-LISTEN.rrd', 0, 'LISTEN', ''],
+ ['tcpconns-8080-local/tcp_connections-SYN_SENT.rrd', 0, 'SYN_SENT', ''],
+ ['tcpconns-8080-local/tcp_connections-CLOSING.rrd', 0, 'CLOSING', '']
+ ],
+ options: jQuery.extend(true, {}, baseOptions, stacked)
+ },
+
{
title: 'CPU Usage',
data: [
- ['data/cpu-0/cpu-wait.rrd', 0, 'CPU-0 Wait', 'Jiffies'],
- ['data/cpu-1/cpu-wait.rrd', 0, 'CPU-1 Wait', 'Jiffies'],
- ['data/cpu-0/cpu-system.rrd', 0, 'CPU-0 System', 'Jiffies'],
- ['data/cpu-1/cpu-system.rrd', 0, 'CPU-1 System', 'Jiffies'],
- ['data/cpu-0/cpu-user.rrd', 0, 'CPU-0 User', 'Jiffies'],
- ['data/cpu-1/cpu-user.rrd', 0, 'CPU-1 User', 'Jiffies']
+ ['cpu-0/cpu-wait.rrd', 0, 'CPU-0 Wait', 'Jiffies'],
+ ['cpu-1/cpu-wait.rrd', 0, 'CPU-1 Wait', 'Jiffies'],
+ ['cpu-0/cpu-system.rrd', 0, 'CPU-0 System', 'Jiffies'],
+ ['cpu-1/cpu-system.rrd', 0, 'CPU-1 System', 'Jiffies'],
+ ['cpu-0/cpu-user.rrd', 0, 'CPU-0 User', 'Jiffies'],
+ ['cpu-1/cpu-user.rrd', 0, 'CPU-1 User', 'Jiffies']
],
options: jQuery.extend(true, {}, baseOptions, stacked)
},
@@ -150,10 +169,10 @@
{
title: 'Memory',
data: [
- ['data/memory/memory-buffered.rrd', 0, 'Buffered', 'B'],
- ['data/memory/memory-used.rrd', 0, 'Used', 'B'],
- ['data/memory/memory-cached.rrd', 0, 'Cached', 'B'],
- ['data/memory/memory-free.rrd', 0, 'Free', 'B']
+ ['memory/memory-buffered.rrd', 0, 'Buffered', 'B'],
+ ['memory/memory-used.rrd', 0, 'Used', 'B'],
+ ['memory/memory-cached.rrd', 0, 'Cached', 'B'],
+ ['memory/memory-free.rrd', 0, 'Free', 'B']
],
options: jQuery.extend(true, {}, baseOptions, stacked)
},
@@ -161,10 +180,10 @@
{
title: 'DNS Query Types',
data: [
- ['data/dns/dns_qtype-A.rrd', 0, 'A', 'Q/sec'],
- ['data/dns/dns_qtype-PTR.rrd', 0, 'PTR', 'Q/sec'],
- ['data/dns/dns_qtype-SOA.rrd', 0, 'SOA', 'Q/sec'],
- ['data/dns/dns_qtype-SRV.rrd', 0, 'SRV', 'Q/sec']
+ ['dns/dns_qtype-A.rrd', 0, 'A', 'Q/sec'],
+ ['dns/dns_qtype-PTR.rrd', 0, 'PTR', 'Q/sec'],
+ ['dns/dns_qtype-SOA.rrd', 0, 'SOA', 'Q/sec'],
+ ['dns/dns_qtype-SRV.rrd', 0, 'SRV', 'Q/sec']
],
options: jQuery.extend(true, {}, baseOptions)
},
@@ -172,9 +191,9 @@
{
title: 'DNS Return Codes',
data: [
- ['data/dns/dns_rcode-NOERROR.rrd', 0, 'NOERROR', 'Q/sec'],
- ['data/dns/dns_rcode-NXDOMAIN.rrd', 0, 'NXDOMAIN', 'Q/sec'],
- ['data/dns/dns_rcode-SERVFAIL.rrd', 0, 'SERVFAIL', 'Q/sec']
+ ['dns/dns_rcode-NOERROR.rrd', 0, 'NOERROR', 'Q/sec'],
+ ['dns/dns_rcode-NXDOMAIN.rrd', 0, 'NXDOMAIN', 'Q/sec'],
+ ['dns/dns_rcode-SERVFAIL.rrd', 0, 'SERVFAIL', 'Q/sec']
],
options: jQuery.extend(true, {}, baseOptions)
},
@@ -182,9 +201,9 @@
{
title: 'Load Average',
data: [
- ['data/load/load.rrd', 'shortterm', 'Short Term', ''],
- ['data/load/load.rrd', 'midterm', 'Medium Term', ''],
- ['data/load/load.rrd', 'longterm', 'Long Term', '']
+ ['load/load.rrd', 'shortterm', 'Short Term', ''],
+ ['load/load.rrd', 'midterm', 'Medium Term', ''],
+ ['load/load.rrd', 'longterm', 'Long Term', '']
],
options: jQuery.extend(true, {}, baseOptions)
},
@@ -192,29 +211,37 @@
{
title: 'Wlan0 Throughput',
data: [
- ['data/interface/if_octets-wlan0.rrd', 'tx', 'Transmit', 'b/sec'],
- ['data/interface/if_octets-wlan0.rrd', 'rx', 'Receive', 'b/sec']
+ ['interface/if_octets-wlan0.rrd', 'tx', 'Transmit', 'b/sec'],
+ ['interface/if_octets-wlan0.rrd', 'rx', 'Receive', 'b/sec']
],
options: jQuery.extend(true, {}, baseOptions)
- },
+ }
];
- $(function() {
+ function initialiseCharts(rrdUrlList) {
+ /**
+ * Setup chart date range controls and all charts
+ **/
+
+ // Add dhtml calendars to the date input fields
$(":date").dateinput({format: 'mmm dd yyyy', max: +1});
- // when first date input is changed
$(":date[name=startTime]").data("dateinput").change(function() {
$(":date[name=endTime]").data("dateinput").setMin(this.getValue(), true);
});
$(":date[name=endTime]").data("dateinput").change(function() {
$(":date[name=startTime]").data("dateinput").setMax(this.getValue(), true);
});
+
+ // Extract the chart template from the page
var chartTemplate = $('.chart-container').remove();
var cc = new jrrd.ChartCoordinator($('.chartRangeControl'));
- cc.charts = jQuery.map(recipes, function(recipe, i) {
- return jrrd.Chart.fromRecipe(
- chartTemplate.clone().appendTo('.charts'), recipe);
- });
+ cc.charts = jrrd.collectdChartFactory(
+ rrdUrlList, recipes, function() {
+ // The chart template must be appended to the page early, so
+ // that flot can calculate chart dimensions etc.
+ return chartTemplate.clone().appendTo('.charts');
+ });
// Update all charts when a selection is made on one of them
$('.charts').bind("plotselected", function(event, ranges) {
@@ -233,6 +260,12 @@
// Initialise all the charts
cc.reset();
+ }
+
+ $(function() {
+ // Download a list of available rrd files and use it to generate
+ // any viable chart recipes
+ $.getJSON('rrd_finder.rpy', initialiseCharts);
});
</script>
</head>
diff --git a/jrrd.js b/jrrd.js
index 85f5ebc..f4c3665 100644
--- a/jrrd.js
+++ b/jrrd.js
@@ -457,31 +457,56 @@ jrrd.Chart.prototype.draw = function() {
};
-jrrd.Chart.fromRecipe = function(template, recipe) {
+jrrd.collectdChartFactory = function(rrdUrlList, recipes, templateFactory) {
/**
- * A factory function to generate a I{Chart} from a recipe
+ * A factory function to generate a list of I{Chart} from a list of recipes
+ * and a list of available rrd files in collectd path format.
*
- * @param template: A I{jQuery} containing the element in which the chart is
- * drawn.
- * @param A recipe I{Object} eg
- * {title: '', data: [rrdUrl, rrdDs, label, unit], options: {}}
- * @return: A I{Chart} instance
+ * @param rrdUrlList: A list of rrd download paths
+ * @param recipes: A list of recipe objects
+ * @param templateFactory: A callable which generates an html template for a
+ * chart.
**/
- template.find('.title').text(recipe['title']);
- var c = new jrrd.Chart(template.find('.chart'), recipe['options']);
+ var rrdUrlBlob = rrdUrlList.join('\n')
+
+ var charts = [];
var dataDict = {};
- var ds, label, rrd, unit;
- for(var i=0; i<recipe['data'].length; i++) {
- rrd = recipe['data'][i][0];
- ds = recipe['data'][i][1];
- label = recipe['data'][i][2];
- unit = recipe['data'][i][3];
- if(typeof dataDict[rrd] == 'undefined') {
- dataDict[rrd] = new jrrd.RrdQueryRemote(rrd, unit);
+
+ var recipe, chartData, template, c, i, j, x, ds, label, rrd, unit, re, match;
+
+ for(i=0; i<recipes.length; i++) {
+ recipe = recipes[i];
+ chartData = [];
+
+ for(j=0; j<recipe['data'].length; j++) {
+ rrd = recipe['data'][j][0];
+ ds = recipe['data'][j][1];
+ label = recipe['data'][j][2];
+ unit = recipe['data'][j][3];
+ re = new RegExp('.*/' + rrd, 'gm');
+ match = rrdUrlBlob.match(re);
+ if(!match) {
+ continue;
+ }
+ for(x=0; x<match.length; x++) {
+
+ if(typeof dataDict[match[x]] == 'undefined') {
+ dataDict[match[x]] = new jrrd.RrdQueryRemote(match[x], unit);
+ }
+ chartData.push([label, new jrrd.RrdQueryDsProxy(dataDict[match[x]], ds)]);
+ }
+ }
+ if(chartData.length > 0) {
+ template = templateFactory();
+ template.find('.title').text(recipe['title']);
+ c = new jrrd.Chart(template.find('.chart'), recipe['options']);
+ for(j=0; j<chartData.length; j++) {
+ c.addData.apply(c, chartData[j]);
+ }
+ charts.push(c);
}
- c.addData(label, new jrrd.RrdQueryDsProxy(dataDict[rrd], ds));
}
- return c;
+ return charts;
}
diff --git a/rrd_finder.rpy b/rrd_finder.rpy
new file mode 100644
index 0000000..c7c8517
--- /dev/null
+++ b/rrd_finder.rpy
@@ -0,0 +1,20 @@
+import json
+import os
+
+from twisted.python.filepath import FilePath
+from twisted.web.resource import Resource
+
+RRD_PATH = '/var/lib/collectd/rrd/aziz/'
+URL_BASE = 'data'
+
+class RrdFinder(Resource):
+ isLeaf = True
+ def render_GET(self, request):
+ p = FilePath(RRD_PATH)
+ paths = []
+ for f in p.walk():
+ if f.basename().endswith('.rrd'):
+ paths.append(os.path.join(URL_BASE, *f.segmentsFrom(p)))
+ return json.dumps(paths)
+
+resource = RrdFinder()