summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Wall <richard@aziz>2010-04-19 01:47:44 +0100
committerRichard Wall <richard@aziz>2010-04-19 01:47:44 +0100
commit2216f7560ba709c442ad7ef32c28ee9f38aac683 (patch)
tree71bd0983fbd7d718e7c9d223e847388f185a8b3c
parent26c2c26830ce6b086995d98e78c33938e69e9a87 (diff)
Add more documentation
-rw-r--r--README31
-rw-r--r--index.html68
-rw-r--r--jrrd.js149
3 files changed, 227 insertions, 21 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..f5da6ed
--- /dev/null
+++ b/README
@@ -0,0 +1,31 @@
+jrrd 0.0.1
+
+
+What is this?
+=============
+Wrappers and convenience fuctions for working with the javascriptRRD, Flot and
+Collectd.
+
+
+Debian / Ubuntu Quick Start
+===========================
+To get this demo working, you will need to serve this page from a
+local webserver and serve the folder that contains your RRD files.
+
+This demo is designed to work with the RRD files generated by
+<a href="http://collectd.org/">Collectd</a>.
+
+# Install and configure collectd (enable the rrd plugin)
+$ aptitude install collectd
+
+# Create a project folder
+$ mkdir -p ~/Projects/jrrd
+$ cd ~/Projects/jrrd
+
+# Link to the collectd rrd folder
+$ ln -f /var/lib/collectd/rrd/localhost data
+
+# Start a local webserver
+$ aptitude install twisted
+$ twistd -n web --port 8080 --path .
+
diff --git a/index.html b/index.html
index 2b6b5a0..840a5b3 100644
--- a/index.html
+++ b/index.html
@@ -7,6 +7,8 @@
<style type="text/css">
body {
font-family: sans;
+ width: 800px;
+ margin: 20px auto 0 auto;
}
form div {
@@ -14,10 +16,10 @@
}
h2 {
- width: 750px;
- padding: 0 0 0 50px;
+ padding: 0 0 0 55px;
margin: 20px auto 5px auto;
font-size: 14px;
+ text-align: left;
}
.loading {
@@ -27,14 +29,12 @@
}
.range-preview {
- width: 800px;
height:50px;
margin: 0 auto 0 auto;
position: relative;
}
.chart {
- width:800px;
height:200px;
margin: 0 auto 0 auto;
}
@@ -60,19 +60,29 @@
padding: 0;
border: none;
}
+
+ .notice {
+ border: 1px solid Green;
+ background: #FFDDFF;
+ margin-bottom: 20px;
+ padding: 5px;
+ }
</style>
- <script type="text/javascript" src="assets/javascript/jquery-1.4.2.min.js"></script>
- <script type="text/javascript" src="assets/javascript/flot/excanvas.min.js"></script>
- <script type="text/javascript" src="assets/javascript/flot/jquery.flot.js"></script>
- <script type="text/javascript" src="assets/javascript/flot/jquery.flot.stack.js"></script>
- <script type="text/javascript" src="assets/javascript/flot/jquery.flot.selection.js"></script>
- <script type="text/javascript" src="assets/javascript/javascriptrrd/binaryXHR.js"></script>
- <script type="text/javascript" src="assets/javascript/javascriptrrd/rrdFile.js"></script>
- <script type="text/javascript" src="assets/javascript/MochiKit/Base.js"></script>
- <script type="text/javascript" src="assets/javascript/MochiKit/Async.js"></script>
+
+ <script type="text/javascript" src="http://svn.mochikit.com/mochikit/trunk/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="http://svn.mochikit.com/mochikit/trunk/MochiKit/Async.js"></script>
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
+ <script type="text/javascript" src="http://flot.googlecode.com/svn/trunk/excanvas.min.js"></script>
+ <script type="text/javascript" src="http://flot.googlecode.com/svn/trunk/jquery.flot.js"></script>
+ <script type="text/javascript" src="http://flot.googlecode.com/svn/trunk/jquery.flot.stack.js"></script>
+ <script type="text/javascript" src="http://flot.googlecode.com/svn/trunk/jquery.flot.selection.js"></script>
+
+ <script type="text/javascript" src="http://javascriptrrd.cvs.sourceforge.net/viewvc/*checkout*/javascriptrrd/v0/src/lib/binaryXHR.js?revision=1.5&content-type=text%2Fplain"></script>
+ <script type="text/javascript" src="http://javascriptrrd.cvs.sourceforge.net/viewvc/*checkout*/javascriptrrd/v0/src/lib/rrdFile.js?revision=1.8&content-type=text%2Fplain"></script>
+
<script type="text/javascript" src="jrrd.js"></script>
<script type="text/javascript">
-
+ // Options common to all the chart on this page
var baseOptions = {
grid: {
clickable: false,
@@ -100,6 +110,7 @@
}
};
+ // Extra options to generate a stacked chart
var stacked = {
series: {
stack: true,
@@ -109,6 +120,7 @@
}
};
+ // Recipes for the charts on this page
var recipes = [
{
title: 'CPU Usage',
@@ -156,7 +168,7 @@
},
{
- title: 'Wlan0 Throughput (B/sec)',
+ title: 'Wlan0 Throughput',
data: [
['data/interface/if_octets-wlan0.rrd', 'tx', 'Transmit', 'b/sec'],
['data/interface/if_octets-wlan0.rrd', 'rx', 'Receive', 'b/sec']
@@ -174,11 +186,13 @@
chartTemplate.clone().appendTo('.charts'), recipe);
});
+ // Update all charts when a selection is made on one of them
$('.charts').bind("plotselected", function(event, ranges) {
cc.setTimeRange(new Date(ranges.xaxis.from),
new Date(ranges.xaxis.to));
});
+ // Show a loading icon when a chart is being redrawn
$('.chart-container').live('chart_loading', function(e) {
$(this).find('.title').addClass('loading');
});
@@ -187,12 +201,36 @@
$(this).find('.title').removeClass('loading');
});
+ // Initialise all the charts
cc.reset();
});
</script>
</head>
<body>
+ <div class="notice">
+ <p>To get this demo working, you will need to serve this page from a
+ local webserver and serve the folder that contains your RRD files.</p>
+ <p>This demo is designed to work with the RRD files generated by
+ <a href="http://collectd.org/">Collectd</a>.</p>
+ <h3>Debian / Ubuntu Quick Start</h3>
+ <pre>
+ # Install and configure collectd (enable the rrd plugin)
+ $ aptitude install collectd
+
+ # Create a project folder
+ $ mkdir -p ~/Projects/jrrd
+ $ cd ~/Projects/jrrd
+
+ # Link to the collectd rrd folder
+ $ ln -f /var/lib/collectd/rrd/localhost data
+
+ # Start a local webserver
+ $ aptitude install twisted
+ $ twistd -n web --port 8080 --path .
+ </pre>
+ </div>
+
<form method="GET" class="chartRangeControl">
<div>
<label>Start: <input type="text" name="startTime" /></label>
diff --git a/jrrd.js b/jrrd.js
index 5fcb670..3f9070a 100644
--- a/jrrd.js
+++ b/jrrd.js
@@ -4,15 +4,27 @@
* Wrappers and convenience fuctions for working with the javascriptRRD, jQuery,
* and flot charting packages.
*
- * javascriptRRD - http://javascriptrrd.sourceforge.net/
- * jQuery - http://jquery.com/
- * flot - http://code.google.com/p/flot/
+ * Designed to work well with the RRD files generated by Collectd:
+ * - http://collectd.org/
+ *
+ * Requirements:
+ * - JavascriptRRD: http://javascriptrrd.sourceforge.net/
+ * - jQuery: http://jquery.com/
+ * - Flot: http://code.google.com/p/flot/
+ * - MochiKit.Async: http://www.mochikit.com/
*/
if(typeof jrrd == 'undefined') {
var jrrd = {};
}
+/**
+ * Download a binary file asynchronously using the jQuery.ajax function
+ *
+ * @param url: The url of the object to be downloaded
+ * @return: A I{MochiKit.Async.Deferred} which will callback with an instance of
+ * I{javascriptrrd.BinaryFile}
+ **/
jrrd.downloadBinary = function(url) {
var d = new MochiKit.Async.Deferred();
@@ -68,6 +80,20 @@ jrrd.RrdQuery = function(rrd, unit) {
};
jrrd.RrdQuery.prototype.getData = function(startTime, endTime, dsId, cfName) {
+ /**
+ * Generate a Flot compatible data object containing rows between start and
+ * end time. The rows are taken from the first RRA whose data spans the
+ * requested time range.
+ *
+ * @param startTime: The I{Date} start time
+ * @param endTime: The I{Date} end time
+ * @param dsId: An index I{Number} or key I{String} identifying the RRD
+ * datasource (DS).
+ * @param cfName: The name I{String} of an RRD consolidation function (CF)
+ * eg AVERAGE, MIN, MAX
+ * @return: A Flot compatible data series I{Object}
+ * eg {label:'', data:[], unit: ''}
+ **/
var startTimestamp = startTime.getTime()/1000;
var lastUpdated = this.rrd.getLastUpdate();
@@ -130,7 +156,13 @@ jrrd.RrdQuery.prototype.getData = function(startTime, endTime, dsId, cfName) {
return {label: ds.getName(), data: flotData, unit: this.unit};
};
-
+/**
+ * A wrapper around RrdQuery which provides asynchronous access to the data in a
+ * remote RRD file.
+ *
+ * @param url: The url I{String} of a remote RRD file
+ * @param unit: The unit suffix I{String} of this data eg 'bit/sec'
+ **/
jrrd.RrdQueryRemote = function(url, unit) {
this.url = url;
this.unit = unit;
@@ -139,6 +171,14 @@ jrrd.RrdQueryRemote = function(url, unit) {
};
jrrd.RrdQueryRemote.prototype.getData = function(startTime, endTime, dsId) {
+ /**
+ * Return a Flot compatible data series asynchronously.
+ *
+ * @param startTime: The start time I{Date}
+ * @param endTime: The end time I{Date}
+ * @returns: A I{MochiKit.Async.Deferred} which calls back with a flot data
+ * series object I{Object}
+ **/
var endTimestamp = endTime.getTime()/1000;
// Download the rrd if there has never been a download or if the last
@@ -180,7 +220,13 @@ jrrd.RrdQueryRemote.prototype.getData = function(startTime, endTime, dsId) {
return ret;
};
-
+/**
+ * Wraps a I{RrdQueryRemote} to provide access to a different RRD DSs within a
+ * single RrdDataSource.
+ *
+ * @param rrdQuery: An I{RrdQueryRemote}
+ * @param dsId: An index or keyname of an RRD DS
+ **/
jrrd.RrdQueryDsProxy = function(rrdQuery, dsId) {
this.rrdQuery = rrdQuery;
this.dsId = dsId;
@@ -188,21 +234,43 @@ jrrd.RrdQueryDsProxy = function(rrdQuery, dsId) {
};
jrrd.RrdQueryDsProxy.prototype.getData = function(startTime, endTime) {
+ /**
+ * Call I{RrdQueryRemote.getData} with a particular dsId
+ **/
return this.rrdQuery.getData(startTime, endTime, this.dsId);
};
+/**
+ * A class for creating a Flot chart from a series of RRD Queries
+ *
+ * @param template: A I{jQuery} containing a single element into which the chart
+ * will be drawn
+ * @param options: An I{Object} containing Flot options which describe how the
+ * chart should be drawn.
+ **/
jrrd.Chart = function(template, options) {
this.template = template;
this.options = jQuery.extend(true, {yaxis: {}}, options);
this.data = [];
var self = this;
+
+ // Listen for clicks on the legend items - onclick enable / disable the
+ // corresponding data source.
$('.legend tr', this.template[0]).live('click', function(e) {
self.switchDataEnabled($(this).children('.legendLabel').text());
self.draw();
});
+
this.options['yaxis']['ticks'] = function(axis) {
+ /**
+ * Choose a suitable SI multiplier based on the min and max values from
+ * the axis and then generate appropriate yaxis tick labels.
+ *
+ * @param axis: An I{Object} with min and max properties
+ * @return: An array of ~5 tick labels
+ **/
var siPrefixes = {
0: '',
1: 'K',
@@ -250,6 +318,16 @@ jrrd.Chart = function(template, options) {
};
jrrd.Chart.prototype.addData = function(label, db, enabled) {
+ /**
+ * Add details of a remote RRD data source whose data will be added to this
+ * chart.
+ *
+ * @param label: A I{String} label for this data which will be shown in the
+ * chart legend
+ * @param db: The url of the remote RRD database
+ * @param enabled: true if you want this data plotted on the chart, false
+ * if not.
+ **/
if(typeof enabled == 'undefined') {
enabled = true;
}
@@ -257,6 +335,12 @@ jrrd.Chart.prototype.addData = function(label, db, enabled) {
};
jrrd.Chart.prototype.switchDataEnabled = function(label) {
+ /**
+ * Enable / Disable a single data source
+ *
+ * @param label: The label I{String} of the data source to be enabled /
+ * disabled
+ **/
for(var i=0; i<this.data.length; i++) {
if(this.data[i][0] == label) {
this.data[i][2] = !this.data[i][2];
@@ -265,12 +349,26 @@ jrrd.Chart.prototype.switchDataEnabled = function(label) {
};
jrrd.Chart.prototype.setTimeRange = function(startTime, endTime) {
+ /**
+ * Alter the time range of this chart and redraw
+ *
+ * @param startTime: The start time I{Date}
+ * @param endTime: The end time I{Date}
+ **/
this.startTime = startTime;
this.endTime = endTime;
this.draw();
}
jrrd.Chart.prototype.draw = function() {
+ /**
+ * Draw the chart
+ * A 'chart_loading' event is triggered before the data is requested
+ * A 'chart_loaded' event is triggered when the chart has been drawn
+ *
+ * @return: A I{MochiKit.Async.Deferred} which calls back with the chart
+ * data when the chart has been rendered.
+ **/
this.template.trigger('chart_loading');
var result;
var results = [];
@@ -333,6 +431,7 @@ jrrd.Chart.prototype.draw = function() {
'text-align': 'right'});
self.template.append(yaxisUnitLabel);
yaxisUnitLabel.position(self.template.position());
+ return data;
}, this)
.addErrback(
function(self, failure) {
@@ -347,6 +446,15 @@ jrrd.Chart.prototype.draw = function() {
jrrd.Chart.fromRecipe = function(template, recipe) {
+ /**
+ * A factory function to generate a I{Chart} from a recipe
+ *
+ * @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
+ **/
template.find('.title').text(recipe['title']);
var c = new jrrd.Chart(template.find('.chart'), recipe['options']);
var dataDict = {};
@@ -365,20 +473,33 @@ jrrd.Chart.fromRecipe = function(template, recipe) {
}
+/**
+ * Presents the user with a form and a timeline with which they can choose a
+ * time range and co-ordinates the refreshing of a series of charts.
+ *
+ * @param ui: A one element I{jQuery} containing an input form and placeholders
+ * for the timeline and for the series of charts.
+ **/
jrrd.ChartCoordinator = function(ui) {
this.ui = ui;
this.charts = [];
var self = this;
+
+ // Update the time ranges and redraw charts when the form is submitted
this.ui.bind('submit', function(e) {
self.update();
return false;
});
+ // Reset all the charts to the default time range when the reset button is
+ // pressed.
this.ui.bind('reset', function(e) {
self.reset();
return false;
});
+
+ // Style and configuration of the range timeline
var rangePreviewOptions = {
grid: {
borderWidth: 1
@@ -400,12 +521,15 @@ jrrd.ChartCoordinator = function(ui) {
var MONTH = DAY * 31;
var YEAR = DAY * 365;
+ // Dummy data for the range timeline
var data = [
[now - WEEK, null],
[now, null]];
- this.rangePreview = $.plot(this.ui.find('.range-preview'), [data], rangePreviewOptions);
+ this.rangePreview = $.plot(this.ui.find('.range-preview'), [data],
+ rangePreviewOptions);
+ // When a selection is made on the range timeline, redraw all the charts.
this.ui.bind("plotselected", function(event, ranges) {
self.setTimeRange(new Date(ranges.xaxis.from),
new Date(ranges.xaxis.to));
@@ -413,6 +537,10 @@ jrrd.ChartCoordinator = function(ui) {
};
jrrd.ChartCoordinator.prototype.update = function() {
+ /**
+ * Grab the start and end time from the ui form, highlight the range on the
+ * range timeline and set the time range of all the charts and redraw.
+ **/
var startTime = new Date(this.ui[0].startTime.value);
var endTime = new Date(this.ui[0].endTime.value);
var ranges = {
@@ -428,12 +556,21 @@ jrrd.ChartCoordinator.prototype.update = function() {
};
jrrd.ChartCoordinator.prototype.setTimeRange = function(startTime, endTime) {
+ /**
+ * Set the start and end time fields in the form and trigger an update
+ *
+ * @param startTime: The start time I{Date}
+ * @param endTime: The end time I{Date}
+ **/
this.ui[0].startTime.value = startTime.toString().split(' ').slice(1,5).join(' ');
this.ui[0].endTime.value = endTime.toString().split(' ').slice(1,5).join(' ');
this.update();
};
jrrd.ChartCoordinator.prototype.reset = function() {
+ /**
+ * Reset all charts and the input form to the default time range - last hour
+ **/
this.setTimeRange(new Date(new Date().getTime()-1*60*60*1000),
new Date());
};