rrd-grapher 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/.rvmrc +1 -0
- data/Gemfile +11 -0
- data/Guardfile +123 -0
- data/README.md +91 -0
- data/Rakefile +42 -0
- data/example_app/Gemfile +9 -0
- data/example_app/Guardfile +18 -0
- data/example_app/app.rb +38 -0
- data/example_app/assets/javascripts/available_rrds.coffee +13 -0
- data/example_app/assets/javascripts/collectd.coffee +48 -0
- data/example_app/config.ru +9 -0
- data/example_app/public/.gitignore +1 -0
- data/example_app/public/chosen/chosen-sprite.png +0 -0
- data/example_app/public/chosen/chosen.css +340 -0
- data/example_app/public/chosen/chosen.jquery.js +786 -0
- data/example_app/public/chosen/chosen.jquery.min.js +10 -0
- data/example_app/public/javascripts/available_rrds.js +16 -0
- data/example_app/public/javascripts/collectd.js +64 -0
- data/example_app/public/javascripts/jquery.timeago.js +148 -0
- data/example_app/public/stylesheets/Aristo/images/bg_fallback.png +0 -0
- data/example_app/public/stylesheets/Aristo/images/icon_sprite.png +0 -0
- data/example_app/public/stylesheets/Aristo/images/progress_bar.gif +0 -0
- data/example_app/public/stylesheets/Aristo/images/slider_handles.png +0 -0
- data/example_app/public/stylesheets/Aristo/images/ui-icons_222222_256x240.png +0 -0
- data/example_app/public/stylesheets/Aristo/images/ui-icons_454545_256x240.png +0 -0
- data/example_app/public/stylesheets/Aristo/theme.css +738 -0
- data/example_app/views/available_rrds.haml +26 -0
- data/example_app/views/collectd.haml +30 -0
- data/example_app/views/layout.haml +19 -0
- data/example_app/views/stylesheets/available_rrds.scss +7 -0
- data/example_notifier/Gemfile +2 -0
- data/example_notifier/notifier.rb +25 -0
- data/lib/rrd-grapher/assets/javascripts/app-dev.js +20 -0
- data/lib/rrd-grapher/assets/javascripts/app.js +20 -0
- data/lib/rrd-grapher/assets/javascripts/classes/format.coffee +35 -0
- data/lib/rrd-grapher/assets/javascripts/classes/graph.coffee +306 -0
- data/lib/rrd-grapher/assets/javascripts/classes/graph_definition.coffee +216 -0
- data/lib/rrd-grapher/assets/javascripts/classes/serie.coffee +13 -0
- data/lib/rrd-grapher/assets/javascripts/classes/size.coffee +5 -0
- data/lib/rrd-grapher/assets/javascripts/classes/static_line.coffee +44 -0
- data/lib/rrd-grapher/assets/javascripts/classes/time.coffee +17 -0
- data/lib/rrd-grapher/notifier/alarm_manager.rb +190 -0
- data/lib/rrd-grapher/notifier/alarm_trigger.rb +187 -0
- data/lib/rrd-grapher/notifier/alarms.rb +79 -0
- data/lib/rrd-grapher/notifier/collectdrb.rb +86 -0
- data/lib/rrd-grapher/notifier/data_struct.rb +46 -0
- data/lib/rrd-grapher/notifier/default_user_handler.rb +36 -0
- data/lib/rrd-grapher/notifier/parsers/bindata_parser.rb +144 -0
- data/lib/rrd-grapher/notifier/parsers/ruby_parser.rb +134 -0
- data/lib/rrd-grapher/notifier/structures.rb +80 -0
- data/lib/rrd-grapher/notifier.rb +87 -0
- data/lib/rrd-grapher/public/favicon.ico +0 -0
- data/lib/rrd-grapher/public/javascripts/app-dev.js +13709 -0
- data/lib/rrd-grapher/public/javascripts/app.js +4057 -0
- data/lib/rrd-grapher/public/javascripts/backbone/backbone.js +1155 -0
- data/lib/rrd-grapher/public/javascripts/backbone/backbone.min.js +32 -0
- data/lib/rrd-grapher/public/javascripts/backbone/underscore.js +841 -0
- data/lib/rrd-grapher/public/javascripts/backbone/underscore.min.js +27 -0
- data/lib/rrd-grapher/public/javascripts/classes/format.js +42 -0
- data/lib/rrd-grapher/public/javascripts/classes/graph.js +360 -0
- data/lib/rrd-grapher/public/javascripts/classes/graph_definition.js +298 -0
- data/lib/rrd-grapher/public/javascripts/classes/serie.js +32 -0
- data/lib/rrd-grapher/public/javascripts/classes/size.js +7 -0
- data/lib/rrd-grapher/public/javascripts/classes/static_line.js +48 -0
- data/lib/rrd-grapher/public/javascripts/classes/time.js +17 -0
- data/lib/rrd-grapher/public/javascripts/flot/.gitignore +4 -0
- data/lib/rrd-grapher/public/javascripts/flot/excanvas.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.colorhelpers.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.crosshair.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.fillbetween.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.image.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.js +2604 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.navigate.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.pie.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.resize.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.selection.js +345 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.selection.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.stack.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.symbol.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/flot/jquery.flot.threshold.min.js +1 -0
- data/lib/rrd-grapher/public/javascripts/jquery/jquery-1.6.2.js +8981 -0
- data/lib/rrd-grapher/public/javascripts/jquery/jquery-1.6.2.min.js +18 -0
- data/lib/rrd-grapher/public/javascripts/jquery/jquery-ui-1.8.11.min.js +783 -0
- data/lib/rrd-grapher/public/javascripts/jquery/jquery.showtime.js +63 -0
- data/lib/rrd-grapher/public/javascripts/jquery/jquery.tpl.min.js +10 -0
- data/lib/rrd-grapher/resources.rb +14 -0
- data/lib/rrd-grapher/rrd.rb +238 -0
- data/lib/rrd-grapher/rrd_server.rb +78 -0
- data/lib/rrd-grapher/version.rb +4 -0
- data/lib/rrd-grapher/views/stylesheets/app.scss +111 -0
- data/lib/rrd-grapher.rb +12 -0
- data/rrd-grapher.gemspec +47 -0
- data/spec/common.rb +70 -0
- data/spec/data/myrouter.rrd +0 -0
- data/spec/data/subdata.rrd +0 -0
- data/spec/data/test.rrd +0 -0
- data/spec/factories.rb +23 -0
- data/spec/javascripts/helpers/jasmine-jquery-1.3.0.js +283 -0
- data/spec/javascripts/source/format_spec.coffee +18 -0
- data/spec/javascripts/source/graph_def_spec.coffee +27 -0
- data/spec/javascripts/source/graph_spec.coffee +63 -0
- data/spec/javascripts/source/serie_spec.coffee +28 -0
- data/spec/javascripts/source/static_line_spec.coffee +13 -0
- data/spec/javascripts/source/time_spec.coffee +26 -0
- data/spec/javascripts/support/jasmine.yml +78 -0
- data/spec/javascripts/support/jasmine_config.rb +23 -0
- data/spec/javascripts/support/jasmine_runner.rb +32 -0
- data/spec/unit/alarm_manager_spec.rb +252 -0
- data/spec/unit/alarm_trigger_spec.rb +26 -0
- data/spec/unit/data_struct_spec.rb +55 -0
- data/spec/unit/notifier_spec.rb +45 -0
- data/spec/unit/parsers/bindata_parser_spec.rb +184 -0
- data/spec/unit/parsers/ruby_parser_spec.rb +184 -0
- data/spec/unit/rrd_spec.rb +50 -0
- data/spec/unit/structures_spec.rb +28 -0
- data/tests/4series.rrd +0 -0
- data/tests/analyze_rrd.rb +62 -0
- data/tests/exact.rrd +0 -0
- data/tests/exact2.rrd +0 -0
- data/tests/filler.rb +46 -0
- metadata +414 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery.showTime
|
3
|
+
* http://plugins.jquery.com/project/showTime
|
4
|
+
*
|
5
|
+
* Converts seconds to Hours, Minutes and Seconds with configurable
|
6
|
+
* dividers (":","h", ...).
|
7
|
+
*
|
8
|
+
* Usage:
|
9
|
+
* input: <p class=".secs">345</p>
|
10
|
+
* jQuery('.secs').showTime();
|
11
|
+
* output: <p class=".secs">5:45</p>
|
12
|
+
*
|
13
|
+
* input: <p class=".secs">345</p>
|
14
|
+
* jQuery('.secs').showTime( { div_hours: "h ", div_minutes: "m ", div_seconds: "s " } );
|
15
|
+
* output: <p class=".secs">5m 45s</p>
|
16
|
+
*
|
17
|
+
* @author Martin Fischer
|
18
|
+
* @date 21-02-2011
|
19
|
+
* @param int time in seconds
|
20
|
+
* @version 0.2
|
21
|
+
*/
|
22
|
+
jQuery.fn.showTime = function(settings){
|
23
|
+
var conf = {
|
24
|
+
div_hours: ":",
|
25
|
+
div_minutes: ":",
|
26
|
+
div_seconds: "",
|
27
|
+
show_null: false
|
28
|
+
};
|
29
|
+
|
30
|
+
if (settings) {
|
31
|
+
$.extend(conf, settings);
|
32
|
+
} // END if
|
33
|
+
|
34
|
+
if(typeof settings === "object" || !settings) {
|
35
|
+
return this.each(function() {
|
36
|
+
var status = jQuery(this).data('showtime');
|
37
|
+
var time = parseInt(jQuery(this).text());
|
38
|
+
if( typeof time === "number" && !isNaN(time) && status != "done" ) {
|
39
|
+
var hours = Math.floor(time / 3600);
|
40
|
+
time = time - hours * 3600;
|
41
|
+
var minutes = Math.floor(time / 60);
|
42
|
+
var seconds = time - minutes * 60;
|
43
|
+
var div = conf.show_divider_empty_follower;
|
44
|
+
var h = conf.div_hours;
|
45
|
+
var m = conf.div_minutes;
|
46
|
+
var s = conf.div_seconds;
|
47
|
+
|
48
|
+
var timestring = (hours > 0) ? hours + "" + h : "";
|
49
|
+
if (hours > 0) {
|
50
|
+
timestring += (minutes < 10) ? "0"+minutes+m : minutes + "" + m ;
|
51
|
+
} else {
|
52
|
+
timestring += (minutes > 0) ? minutes + "" + m : "";
|
53
|
+
}
|
54
|
+
if (minutes > 0) {
|
55
|
+
timestring += (seconds < 10) ? "0"+seconds + s : seconds + "" + s;
|
56
|
+
} else {
|
57
|
+
timestring += (seconds > 0) ? seconds + "" + s : "";
|
58
|
+
}
|
59
|
+
jQuery(this).html(timestring).data('showtime','done');
|
60
|
+
} // END if
|
61
|
+
}); // END each
|
62
|
+
} // END if
|
63
|
+
}; // END jQuery.showTime
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery Templates Plugin 1.0.0pre
|
3
|
+
* http://github.com/jquery/jquery-tmpl
|
4
|
+
* Requires jQuery 1.4.2
|
5
|
+
*
|
6
|
+
* Copyright Software Freedom Conservancy, Inc.
|
7
|
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
8
|
+
* http://jquery.org/license
|
9
|
+
*/
|
10
|
+
(function(a){var r=a.fn.domManip,d="_tmplitem",q=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,b={},f={},e,p={key:0,data:{}},i=0,c=0,l=[];function g(g,d,h,e){var c={data:e||(e===0||e===false)?e:d?d.data:{},_wrap:d?d._wrap:null,tmpl:null,parent:d||null,nodes:[],calls:u,nest:w,wrap:x,html:v,update:t};g&&a.extend(c,g,{nodes:[],parent:d});if(h){c.tmpl=h;c._ctnt=c._ctnt||c.tmpl(a,c);c.key=++i;(l.length?f:b)[i]=c}return c}a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(f,d){a.fn[f]=function(n){var g=[],i=a(n),k,h,m,l,j=this.length===1&&this[0].parentNode;e=b||{};if(j&&j.nodeType===11&&j.childNodes.length===1&&i.length===1){i[d](this[0]);g=this}else{for(h=0,m=i.length;h<m;h++){c=h;k=(h>0?this.clone(true):this).get();a(i[h])[d](k);g=g.concat(k)}c=0;g=this.pushStack(g,f,i.selector)}l=e;e=null;a.tmpl.complete(l);return g}});a.fn.extend({tmpl:function(d,c,b){return a.tmpl(this[0],d,c,b)},tmplItem:function(){return a.tmplItem(this[0])},template:function(b){return a.template(b,this[0])},domManip:function(d,m,k){if(d[0]&&a.isArray(d[0])){var g=a.makeArray(arguments),h=d[0],j=h.length,i=0,f;while(i<j&&!(f=a.data(h[i++],"tmplItem")));if(f&&c)g[2]=function(b){a.tmpl.afterManip(this,b,k)};r.apply(this,g)}else r.apply(this,arguments);c=0;!e&&a.tmpl.complete(b);return this}});a.extend({tmpl:function(d,h,e,c){var i,k=!c;if(k){c=p;d=a.template[d]||a.template(null,d);f={}}else if(!d){d=c.tmpl;b[c.key]=c;c.nodes=[];c.wrapped&&n(c,c.wrapped);return a(j(c,null,c.tmpl(a,c)))}if(!d)return[];if(typeof h==="function")h=h.call(c||{});e&&e.wrapped&&n(e,e.wrapped);i=a.isArray(h)?a.map(h,function(a){return a?g(e,c,d,a):null}):[g(e,c,d,h)];return k?a(j(c,null,i)):i},tmplItem:function(b){var c;if(b instanceof a)b=b[0];while(b&&b.nodeType===1&&!(c=a.data(b,"tmplItem"))&&(b=b.parentNode));return c||p},template:function(c,b){if(b){if(typeof b==="string")b=o(b);else if(b instanceof a)b=b[0]||{};if(b.nodeType)b=a.data(b,"tmpl")||a.data(b,"tmpl",o(b.innerHTML));return typeof c==="string"?(a.template[c]=b):b}return c?typeof c!=="string"?a.template(null,c):a.template[c]||a.template(null,q.test(c)?c:a(c)):null},encode:function(a){return(""+a).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'")}});a.extend(a.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){__=__.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(__,$1,$2);__=[];",close:"call=$item.calls();__=call._.concat($item.wrap(call,__));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){__.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){__.push($.encode($1a));}"},"!":{open:""}},complete:function(){b={}},afterManip:function(f,b,d){var e=b.nodeType===11?a.makeArray(b.childNodes):b.nodeType===1?[b]:[];d.call(f,b);m(e);c++}});function j(e,g,f){var b,c=f?a.map(f,function(a){return typeof a==="string"?e.key?a.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+d+'="'+e.key+'" $2'):a:j(a,e,a._ctnt)}):e;if(g)return c;c=c.join("");c.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(f,c,e,d){b=a(e).get();m(b);if(c)b=k(c).concat(b);if(d)b=b.concat(k(d))});return b?b:k(c)}function k(c){var b=document.createElement("div");b.innerHTML=c;return a.makeArray(b.childNodes)}function o(b){return new Function("jQuery","$item","var $=jQuery,call,__=[],$data=$item.data;with($data){__.push('"+a.trim(b).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(m,l,k,g,b,c,d){var j=a.tmpl.tag[k],i,e,f;if(!j)throw"Unknown template tag: "+k;i=j._default||[];if(c&&!/\w$/.test(b)){b+=c;c=""}if(b){b=h(b);d=d?","+h(d)+")":c?")":"";e=c?b.indexOf(".")>-1?b+h(c):"("+b+").call($item"+d:b;f=c?e:"(typeof("+b+")==='function'?("+b+").call($item):("+b+"))"}else f=e=i.$1||"null";g=h(g);return"');"+j[l?"close":"open"].split("$notnull_1").join(b?"typeof("+b+")!=='undefined' && ("+b+")!=null":"true").split("$1a").join(f).split("$1").join(e).split("$2").join(g||i.$2||"")+"__.push('"})+"');}return __;")}function n(c,b){c._wrap=j(c,true,a.isArray(b)?b:[q.test(b)?b:a(b).html()]).join("")}function h(a){return a?a.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function s(b){var a=document.createElement("div");a.appendChild(b.cloneNode(true));return a.innerHTML}function m(o){var n="_"+c,k,j,l={},e,p,h;for(e=0,p=o.length;e<p;e++){if((k=o[e]).nodeType!==1)continue;j=k.getElementsByTagName("*");for(h=j.length-1;h>=0;h--)m(j[h]);m(k)}function m(j){var p,h=j,k,e,m;if(m=j.getAttribute(d)){while(h.parentNode&&(h=h.parentNode).nodeType===1&&!(p=h.getAttribute(d)));if(p!==m){h=h.parentNode?h.nodeType===11?0:h.getAttribute(d)||0:0;if(!(e=b[m])){e=f[m];e=g(e,b[h]||f[h]);e.key=++i;b[i]=e}c&&o(m)}j.removeAttribute(d)}else if(c&&(e=a.data(j,"tmplItem"))){o(e.key);b[e.key]=e;h=a.data(j.parentNode,"tmplItem");h=h?h.key:0}if(e){k=e;while(k&&k.key!=h){k.nodes.push(j);k=k.parent}delete e._ctnt;delete e._wrap;a.data(j,"tmplItem",e)}function o(a){a=a+n;e=l[a]=l[a]||g(e,b[e.parent.key+n]||e.parent)}}}function u(a,d,c,b){if(!a)return l.pop();l.push({_:a,tmpl:d,item:this,data:c,options:b})}function w(d,c,b){return a.tmpl(a.template(d),c,b,this)}function x(b,d){var c=b.options||{};c.wrapped=d;return a.tmpl(a.template(b.tmpl),b.data,c,b.item)}function v(d,c){var b=this._wrap;return a.map(a(a.isArray(b)?b.join(""):b).filter(d||"*"),function(a){return c?a.innerText||a.textContent:a.outerHTML||s(a)})}function t(){var b=this.nodes;a.tmpl(null,null,null,this).insertBefore(b[0]);a(b).remove()}})(jQuery);
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'rrd'
|
2
|
+
|
3
|
+
unless Float.const_defined?(:NAN)
|
4
|
+
class Float
|
5
|
+
NAN = (0/0.0)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module RRDReader
|
10
|
+
|
11
|
+
class InvalidRRARange < RuntimeError; end;
|
12
|
+
|
13
|
+
class RRA
|
14
|
+
|
15
|
+
attr_reader :cf, :rows, :current_row
|
16
|
+
|
17
|
+
# {"cf"=>"AVERAGE", "rows"=>24, "cur_row"=>1, "pdp_per_row"=>1, "xff"=>0.5, "cdp_prep"=>0},
|
18
|
+
def initialize(step, h)
|
19
|
+
@step = step
|
20
|
+
|
21
|
+
@cf = translate_function( h.delete('cf') )
|
22
|
+
@rows = h.delete('rows').to_i
|
23
|
+
@cur_row = h.delete('cur_row').to_i
|
24
|
+
@pdp_per_row = h.delete('pdp_per_row').to_i
|
25
|
+
@xff = h.delete('xff').to_f
|
26
|
+
@cdp_prep = h.delete('cdp_prep').to_f
|
27
|
+
end
|
28
|
+
|
29
|
+
def current_row; @cur_row; end
|
30
|
+
|
31
|
+
def translate_function(cf)
|
32
|
+
case cf
|
33
|
+
when "AVERAGE" then :average
|
34
|
+
when "MIN" then :min
|
35
|
+
when "MAX" then :max
|
36
|
+
else
|
37
|
+
raise "Unknown function: #{cf}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# one point is saved every X seconds
|
42
|
+
def interval
|
43
|
+
@pdp_per_row * @step
|
44
|
+
end
|
45
|
+
|
46
|
+
# <rows> points are stored which translate to a duration of X
|
47
|
+
def duration
|
48
|
+
@rows * interval()
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_json(*args)
|
52
|
+
ret = {
|
53
|
+
:function => @cf,
|
54
|
+
:row => @rows,
|
55
|
+
:current_row => @cur_row,
|
56
|
+
:interval => self.interval,
|
57
|
+
:duration => self.duration
|
58
|
+
}
|
59
|
+
|
60
|
+
ret.to_json(*args)
|
61
|
+
end
|
62
|
+
|
63
|
+
def clip_time(from, to, last_update)
|
64
|
+
# compute start/end time for this rra
|
65
|
+
# last
|
66
|
+
# first_slot_time = last_update
|
67
|
+
diff = to -from
|
68
|
+
|
69
|
+
raise InvalidRRARange if diff > duration
|
70
|
+
|
71
|
+
# from/to must be a multiple of <interval>
|
72
|
+
to = (to / interval).to_i * interval
|
73
|
+
from = to - diff
|
74
|
+
|
75
|
+
[from, to]
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class File
|
81
|
+
attr_reader :short_name, :rrd_version, :step, :last_update
|
82
|
+
|
83
|
+
attr_reader :ds, :rra
|
84
|
+
alias :archives :rra
|
85
|
+
|
86
|
+
def initialize(path, base_path = nil)
|
87
|
+
if base_path
|
88
|
+
@path = ::File.join(base_path, path)
|
89
|
+
@short_name = path
|
90
|
+
else
|
91
|
+
@path = @short_name = path
|
92
|
+
end
|
93
|
+
|
94
|
+
unless ::File.exists?(@path)
|
95
|
+
puts "RRD Not found: '#{@path}'"
|
96
|
+
raise Sinatra::NotFound
|
97
|
+
end
|
98
|
+
parse_data(@path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_json(*args)
|
102
|
+
ret = {
|
103
|
+
:path => @path,
|
104
|
+
:short_name => ::File.basename(@short_name, '.rrd'),
|
105
|
+
:step => @step,
|
106
|
+
:sources => self.ds.keys,
|
107
|
+
:last_update => @last_update.iso8601
|
108
|
+
}
|
109
|
+
|
110
|
+
|
111
|
+
ret.to_json(*args)
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
# options:
|
117
|
+
# - rra : clip from/to to ensure returned values come from this rra
|
118
|
+
#
|
119
|
+
def get_values(consolidation_function, from, to, opts = {})
|
120
|
+
|
121
|
+
options = {}
|
122
|
+
|
123
|
+
rra_id = opts[:rra]
|
124
|
+
if rra_id
|
125
|
+
options[:resolution] = @rra[rra_id].interval
|
126
|
+
from, to = @rra[rra_id].clip_time(from, to, @last_update)
|
127
|
+
end
|
128
|
+
|
129
|
+
options[:start] = from.to_i
|
130
|
+
options[:end] = to.to_i
|
131
|
+
|
132
|
+
line_params = RRD.to_line_parameters(options)
|
133
|
+
# RRD::Wrapper.xport("--start", "1266933600", "--end", "1266944400", "DEF:xx=#{RRD_FILE}:cpu0:AVERAGE", "XPORT:xx:Legend 0")
|
134
|
+
ds_name = @ds.keys[0]
|
135
|
+
ret = RRD::Wrapper.xport(*line_params, "DEF:v1=#{@path}:#{ds_name}:#{consolidation_function}", "XPORT:v1:v1")
|
136
|
+
|
137
|
+
if ret.is_a?(Array)
|
138
|
+
ret = ret[1..-1].inject({}) do |h, (t,v)|
|
139
|
+
h[t] = v.finite? ? v : nil
|
140
|
+
h
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
ret
|
145
|
+
end
|
146
|
+
|
147
|
+
def xport_values(consolidation_function, from, to, opts = {})
|
148
|
+
args = []
|
149
|
+
args += ["--start", from.to_i.to_s]
|
150
|
+
args += ["--end", to.to_i.to_s]
|
151
|
+
|
152
|
+
if opts[:maxrows]
|
153
|
+
rows = opts[:maxrows].to_i
|
154
|
+
raise ArgumentError, "maxrows needs to be greater or equal to 10" unless rows >= 10
|
155
|
+
args += ["--maxrows", rows.to_s]
|
156
|
+
end
|
157
|
+
|
158
|
+
if opts[:rra]
|
159
|
+
rra_id = opts[:rra].to_i
|
160
|
+
interval = @rra[rra_id].interval
|
161
|
+
args += ['--step', interval.to_s]
|
162
|
+
end
|
163
|
+
|
164
|
+
if opts[:rrdcached]
|
165
|
+
args += ['--daemon', opts[:rrdcached]]
|
166
|
+
end
|
167
|
+
|
168
|
+
if opts[:ds_name]
|
169
|
+
ds_name = opts[:ds_name]
|
170
|
+
else
|
171
|
+
ds_name = @ds.keys[0]
|
172
|
+
end
|
173
|
+
|
174
|
+
args += ["DEF:v1=#{@path}:#{ds_name}:#{consolidation_function}", "XPORT:v1:v1"]
|
175
|
+
|
176
|
+
ret = RRD::Wrapper.xport(*args)
|
177
|
+
if ret.is_a?(Array)
|
178
|
+
ret = ret[1..-1].inject({}) do |h, (t,v)|
|
179
|
+
h[t.to_i] = v.finite? ? v : nil
|
180
|
+
h
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
ret
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
# rrdtool graph /dev/null \
|
189
|
+
# DEF:min=exact.rrd:value:MIN \
|
190
|
+
# DEF:max=exact.rrd:value:MAX \
|
191
|
+
# PRINT:min:MAX:%.2lf \
|
192
|
+
# PRINT:max:MAX:%.2lf
|
193
|
+
|
194
|
+
# def get_minmax(from = nil, to = nil)
|
195
|
+
# args = []
|
196
|
+
# args += ["--start", from.to_i.to_s] if from
|
197
|
+
# args += ["--end", to.to_i.to_s] if to
|
198
|
+
#
|
199
|
+
# end
|
200
|
+
|
201
|
+
private
|
202
|
+
def parse_data(path)
|
203
|
+
data = RRD::Wrapper.info(path)
|
204
|
+
|
205
|
+
@rra = []
|
206
|
+
@ds = {}
|
207
|
+
|
208
|
+
# and now ds and rra
|
209
|
+
# TODO: parse these eventually
|
210
|
+
# "rra[0].cdp_prep[0].value"=>NaN,
|
211
|
+
# "rra[0].cdp_prep[0].unknown_datapoints"=>7,
|
212
|
+
data.each do |key, val|
|
213
|
+
case key
|
214
|
+
when 'step' then @step = val
|
215
|
+
when 'rrd_version' then @rrd_version = val
|
216
|
+
when 'filename' then @filename = val
|
217
|
+
when 'last_update' then @last_update = Time.at(val)
|
218
|
+
|
219
|
+
# ds[value2].unknown_sec
|
220
|
+
when %r{^ds\[([a-zA-Z0-9_]+)\]\.([a-zA-Z_]+)}
|
221
|
+
@ds[$1] ||= {}
|
222
|
+
@ds[$1][$2] = val
|
223
|
+
|
224
|
+
when %r{^rra\[([0-9]+)\]\.([a-zA-Z_]+)}
|
225
|
+
@rra[$1.to_i] ||= {}
|
226
|
+
@rra[$1.to_i][$2] = val
|
227
|
+
|
228
|
+
when 'header_size' # ignore it
|
229
|
+
|
230
|
+
else
|
231
|
+
puts "Unknown key: #{key}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
@rra.map!{|v| RRA.new(@step, v) }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
require 'sinatra/base'
|
3
|
+
|
4
|
+
require 'rrd'
|
5
|
+
require File.expand_path('../rrd', __FILE__)
|
6
|
+
|
7
|
+
module RRDGrapher
|
8
|
+
class RRDServer < Sinatra::Base
|
9
|
+
|
10
|
+
def initialize(app, opts = {})
|
11
|
+
@app = app
|
12
|
+
@root_path = opts.delete(:root_path)
|
13
|
+
# use rrdcached ?
|
14
|
+
# value must be the path to the unix socket
|
15
|
+
@rrdcached = opts.delete(:rrdcached)
|
16
|
+
|
17
|
+
unless opts.empty?
|
18
|
+
raise "Unknown options: #{opts.inspect}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
set :public, nil
|
23
|
+
set :views, nil
|
24
|
+
set :reload_templates, false
|
25
|
+
|
26
|
+
before do
|
27
|
+
content_type :json
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/rrd' do
|
31
|
+
ret = []
|
32
|
+
|
33
|
+
Dir.chdir(@root_path) do
|
34
|
+
Dir["**/*.rrd"].each do |rrd_path|
|
35
|
+
ret << load_rrd(rrd_path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
ret.to_json(false)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
get '/rrd/:path/rra' do
|
44
|
+
rrd = load_rrd( "#{params[:path]}.rrd" )
|
45
|
+
rrd.archives.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
get '/rrd/:path/ds' do
|
49
|
+
rrd = load_rrd( "#{params[:path]}.rrd" )
|
50
|
+
rrd.ds.to_json
|
51
|
+
end
|
52
|
+
|
53
|
+
# optional parameters:
|
54
|
+
# - rra: if present the range will be clipped wihin it
|
55
|
+
#
|
56
|
+
get %r{/rrd/(.+)/values/([0-9.]+)/([0-9.]+)} do
|
57
|
+
args = {}
|
58
|
+
|
59
|
+
path, from, to = params[:captures]
|
60
|
+
|
61
|
+
args[:rra] = params[:rra].to_i if params[:rra]
|
62
|
+
args[:maxrows] = params[:maxrows].to_i if params[:maxrows]
|
63
|
+
args[:ds_name] = params[:ds_name] if params[:ds_name]
|
64
|
+
args[:rrdcached] = @rrdcached
|
65
|
+
|
66
|
+
rrd = load_rrd( "#{path}.rrd" )
|
67
|
+
ret = rrd.xport_values("AVERAGE", from.to_i, to.to_i, args)
|
68
|
+
|
69
|
+
ret.to_json
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def load_rrd(short_path)
|
74
|
+
RRDReader::File.new(short_path, @root_path)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
.hidden {
|
2
|
+
display : none;
|
3
|
+
}
|
4
|
+
|
5
|
+
.transparent {
|
6
|
+
filter:alpha(opacity=50);
|
7
|
+
-moz-opacity:0.5;
|
8
|
+
-khtml-opacity: 0.5;
|
9
|
+
opacity: 0.5;
|
10
|
+
}
|
11
|
+
|
12
|
+
#config_panel {
|
13
|
+
position: fixed;
|
14
|
+
left: 0;
|
15
|
+
top: 0;
|
16
|
+
width: 100%;
|
17
|
+
height: 40px;
|
18
|
+
border-bottom: 1px solid black;
|
19
|
+
background-color: white;
|
20
|
+
z-index: 40;
|
21
|
+
padding: 2px 30px;
|
22
|
+
|
23
|
+
#rangeselect {
|
24
|
+
width: 200px;
|
25
|
+
}
|
26
|
+
|
27
|
+
// http://www.1kbgrid.com/
|
28
|
+
/* ================ */
|
29
|
+
/* = The 1Kb Grid = */ /* 12 columns, 60 pixels each, with 20 pixel gutter */
|
30
|
+
/* ================ */
|
31
|
+
|
32
|
+
.grid_1 { width:60px; }
|
33
|
+
.grid_2 { width:140px; }
|
34
|
+
.grid_3 { width:220px; }
|
35
|
+
.grid_4 { width:300px; }
|
36
|
+
.grid_5 { width:380px; }
|
37
|
+
.grid_6 { width:460px; }
|
38
|
+
.grid_7 { width:540px; }
|
39
|
+
.grid_8 { width:620px; }
|
40
|
+
.grid_9 { width:700px; }
|
41
|
+
.grid_10 { width:780px; }
|
42
|
+
.grid_11 { width:860px; }
|
43
|
+
.grid_12 { width:940px; }
|
44
|
+
|
45
|
+
.column {
|
46
|
+
margin: 0 10px;
|
47
|
+
// overflow: hidden;
|
48
|
+
float: left;
|
49
|
+
display: inline;
|
50
|
+
}
|
51
|
+
.row {
|
52
|
+
width: 960px;
|
53
|
+
margin: 0 auto;
|
54
|
+
// overflow: hidden;
|
55
|
+
}
|
56
|
+
.row .row {
|
57
|
+
margin: 0 -10px;
|
58
|
+
width: auto;
|
59
|
+
display: inline-block;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
DIV.graph_container {
|
64
|
+
|
65
|
+
margin-top: 50px;
|
66
|
+
|
67
|
+
.graph_title {
|
68
|
+
margin: 25px 0 5px 0;
|
69
|
+
text-align: center;
|
70
|
+
font-size: 1.2em;
|
71
|
+
}
|
72
|
+
|
73
|
+
.graph {
|
74
|
+
display: inline-block;
|
75
|
+
width: 700px;
|
76
|
+
height: 200px;
|
77
|
+
}
|
78
|
+
|
79
|
+
.legend {
|
80
|
+
display: inline-block;
|
81
|
+
margin: 2px 20px;
|
82
|
+
height: 100%;
|
83
|
+
width: 400px;
|
84
|
+
vertical-align: top;
|
85
|
+
|
86
|
+
TABLE {
|
87
|
+
width: 100%;
|
88
|
+
border-collapse: collapse;
|
89
|
+
|
90
|
+
TH {
|
91
|
+
border-bottom: 2px solid black;
|
92
|
+
}
|
93
|
+
|
94
|
+
TD {
|
95
|
+
text-align: right;
|
96
|
+
}
|
97
|
+
|
98
|
+
.legendColorBox .outerBorder {
|
99
|
+
padding: 1px;
|
100
|
+
border: 1px solid #aaa;
|
101
|
+
overflow: hidden;
|
102
|
+
width: 14px;
|
103
|
+
}
|
104
|
+
|
105
|
+
TH, TD {
|
106
|
+
padding: 1px 8px;
|
107
|
+
}
|
108
|
+
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
data/lib/rrd-grapher.rb
ADDED
data/rrd-grapher.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/rrd-grapher/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rrd-grapher"
|
7
|
+
s.version = RRDGRapher::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Julien Ammous"]
|
10
|
+
s.email = []
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Graphing toolkit for RRD}
|
13
|
+
s.description = s.summary
|
14
|
+
|
15
|
+
s.rubyforge_project = "rrd-grapher"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency("sinatra", "~> 1.2.6")
|
23
|
+
s.add_dependency("sass", "~> 3.1.5")
|
24
|
+
s.add_dependency("rrd-ffi", "~> 0.2.7")
|
25
|
+
s.add_dependency("rest-client", "~> 1.6.3")
|
26
|
+
s.add_dependency("i18n", "~> 0.6.0")
|
27
|
+
s.add_dependency("eventmachine", "~> 0.12.10")
|
28
|
+
s.add_dependency("fiber_pool", "~> 1.0.0")
|
29
|
+
|
30
|
+
|
31
|
+
# s.add_development_dependency("jasmine", "~> 1.0.2.1")
|
32
|
+
s.add_development_dependency("jasmine", "~> 1.1.0.rc3")
|
33
|
+
s.add_development_dependency("schmurfy-bacon", "~> 1.2.1")
|
34
|
+
s.add_development_dependency("mocha", "~> 0.9.12")
|
35
|
+
s.add_development_dependency("factory_girl", "~> 2.0.4")
|
36
|
+
s.add_development_dependency("simplecov", "~> 0.4.2")
|
37
|
+
s.add_development_dependency("coffee-script", "~> 2.2.0")
|
38
|
+
s.add_development_dependency("therubyracer", "~> 0.9.2")
|
39
|
+
s.add_development_dependency("guard-coffeescript", "~> 0.3.2")
|
40
|
+
s.add_development_dependency("guard-livereload", "~> 0.2.1")
|
41
|
+
s.add_development_dependency("guard-sprockets", "~> 0.1.4")
|
42
|
+
s.add_development_dependency("guard-yard", "~> 1.0.0")
|
43
|
+
s.add_development_dependency("rdiscount", "~> 1.6.8")
|
44
|
+
s.add_development_dependency("rb-fsevent")
|
45
|
+
s.add_development_dependency("growl")
|
46
|
+
s.add_development_dependency("rake")
|
47
|
+
end
|
data/spec/common.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
$:.reject! { |e| e.include? 'TextMate' }
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
puts "Testing with ruby #{RUBY_VERSION} and rubygems #{Gem::VERSION}"
|
5
|
+
puts "Working path: #{Dir.pwd}"
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
require 'bacon'
|
9
|
+
require 'mocha'
|
10
|
+
require 'factory_girl'
|
11
|
+
|
12
|
+
|
13
|
+
require File.expand_path('../factories', __FILE__)
|
14
|
+
|
15
|
+
ROOT = File.expand_path('../../', __FILE__)
|
16
|
+
|
17
|
+
if (RUBY_VERSION >= "1.9") && ENV['COVERAGE']
|
18
|
+
require 'simplecov'
|
19
|
+
SimpleCov.start do
|
20
|
+
add_filter '/gems/'
|
21
|
+
add_filter '/spec/'
|
22
|
+
add_filter 'default_user_handler.rb'
|
23
|
+
|
24
|
+
root(ROOT)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def with(what)
|
30
|
+
yield(what)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
module Bacon
|
35
|
+
module MochaRequirementsCounter
|
36
|
+
def self.increment
|
37
|
+
Counter[:requirements] += 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Context
|
42
|
+
include Mocha::API
|
43
|
+
|
44
|
+
alias_method :it_before_mocha, :it
|
45
|
+
|
46
|
+
def it(description)
|
47
|
+
it_before_mocha(description) do
|
48
|
+
begin
|
49
|
+
mocha_setup
|
50
|
+
yield
|
51
|
+
mocha_verify(MochaRequirementsCounter)
|
52
|
+
rescue Mocha::ExpectationError => e
|
53
|
+
raise Error.new(:failed, "#{e.message}\n#{e.backtrace[0...10].join("\n")}")
|
54
|
+
ensure
|
55
|
+
mocha_teardown
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
unless ENV['COVERAGE']
|
63
|
+
def focus(test_label)
|
64
|
+
# silence_warnings do
|
65
|
+
Bacon.const_set(:RestrictName, %r{#{test_label}})
|
66
|
+
# end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Bacon.summary_on_exit()
|
Binary file
|
Binary file
|
data/spec/data/test.rrd
ADDED
Binary file
|
data/spec/factories.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
FactoryGirl.define do
|
3
|
+
factory :data_common, :default_strategy => :build do
|
4
|
+
time { Time.now }
|
5
|
+
host "localhost"
|
6
|
+
plugin "memory"
|
7
|
+
plugin_instance nil
|
8
|
+
|
9
|
+
type "memory"
|
10
|
+
type_instance "active"
|
11
|
+
end
|
12
|
+
|
13
|
+
factory :data_point, :parent => :data_common, :class => "RRDNotifier::Packet" do
|
14
|
+
values { [rand(200)] }
|
15
|
+
interval 10
|
16
|
+
end
|
17
|
+
|
18
|
+
factory :notification, :parent => :data_common, :class => "RRDNotifier::Packet" do
|
19
|
+
severity 1
|
20
|
+
message "notification message"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|