Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
Simone Mainardi 2016-08-25 10:39:02 +02:00
commit 298256772c
55 changed files with 4254 additions and 784 deletions

View file

@ -1,25 +1,3 @@
#body {
position: relative;
}
#body > p, li > p {
line-height: 1.5em;
}
#body > p {
width: 720px;
}
#body > blockquote {
width: 640px;
}
#body > pre {
border-left: solid 2px #ccc;
padding-left: 18px;
margin: 2em 0 2em -20px;
}
svg {
font: 10px sans-serif;
}

62
httpdocs/js/cubism.rrd-server.js vendored Normal file
View file

@ -0,0 +1,62 @@
/* Adapted from https://github.com/cliffeh/cubism-rrdserver */
cubism.rrdserver = function(context) {
var source = {};
var getMetric = function(rrd, name, showbg, cf) {
cf = cf || 'AVERAGE';
var metric = context.metric(function(start, stop, step, callback) {
// make sure we're working with ints (and seconds)
start = +start/1000, stop = +stop/1000, step = +step/1000;
d3.json(rrd
+ '&cf=' + cf
+ '&start=' + start
+ '&stop=' + stop
+ '&step=' + step,
function(data) {
var datastep = data['step'] * 1000;
var up = data['up'];
var down = data['down'];
var bg = data['bg'];
var datasize = bg.length;
var k = Math.ceil(step / datastep);
var res = [];
var upval = 0;
var downval = 0;
var i;
// aggregate data: TODO test with step != 60*1000
for (i=0; i<datasize; i++) {
if (showbg) {
downval += bg[i];
} else {
upval += up[i];
downval += down[i];
}
if (i % k == k-1) {
// majority vote
res.push(upval >= downval ? -upval : downval);
upval = downval = 0;
}
}
callback(null, res);
});
}, name);
return metric;
}
source.metric = function(rrd, name, cf) {
return getMetric(rrd, name, cf);
}
source.toString = function() {
return "cubism.rrd-server";
};
return source;
}

View file

@ -1,11 +1,7 @@
(function(exports){
var cubism = exports.cubism = {version: "1.2.0"};
var cubism = exports.cubism = {version: "1.6.0"};
var cubism_id = 0;
function cubism_title(d) {
return("&nbsp;<A HREF=\""
+ http_prefix /* this must be defined in the lua caller */
+ "/lua/host_details.lua?host="+d+"\">"+d+"</A>"); }
function cubism_identity(d) { return(d); }
function cubism_identity(d) { return d; }
cubism.option = function(name, defaultValue) {
var values = cubism.options(name);
return values.length ? values[0] : defaultValue;
@ -189,6 +185,191 @@ cubism_contextPrototype.cube = function(host) {
};
var cubism_cubeFormatDate = d3.time.format.iso;
/* librato (http://dev.librato.com/v1/post/metrics) source
* If you want to see an example of how to use this source, check: https://gist.github.com/drio/5792680
*/
cubism_contextPrototype.librato = function(user, token) {
var source = {},
context = this;
auth_string = "Basic " + btoa(user + ":" + token);
avail_rsts = [ 1, 60, 900, 3600 ];
/* Given a step, find the best librato resolution to use.
*
* Example:
*
* (s) : cubism step
*
* avail_rsts 1 --------------- 60 --------------- 900 ---------------- 3600
* | (s) |
* | |
* [low_res top_res]
*
* return: low_res (60)
*/
function find_ideal_librato_resolution(step) {
var highest_res = avail_rsts[0],
lowest_res = avail_rsts[avail_rsts.length]; // high and lowest available resolution from librato
/* If step is outside the highest or lowest librato resolution, pick them and we are done */
if (step >= lowest_res)
return lowest_res;
if (step <= highest_res)
return highest_res;
/* If not, find in what resolution interval the step lands. */
var iof, top_res, i;
for (i=step; i<=lowest_res; i++) {
iof = avail_rsts.indexOf(i);
if (iof > -1) {
top_res = avail_rsts[iof];
break;
}
}
var low_res;
for (i=step; i>=highest_res; i--) {
iof = avail_rsts.indexOf(i);
if (iof > -1) {
low_res = avail_rsts[iof];
break;
}
}
/* What's the closest librato resolution given the step ? */
return ((top_res-step) < (step-low_res)) ? top_res : low_res;
}
function find_librato_resolution(sdate, edate, step) {
var i_size = edate - sdate, // interval size
month = 2419200,
week = 604800,
two_days = 172800,
ideal_res;
if (i_size > month)
return 3600;
ideal_res = find_ideal_librato_resolution(step);
/*
* Now we have the ideal resolution, but due to the retention policies at librato, maybe we have
* to use a higher resolution.
* http://support.metrics.librato.com/knowledgebase/articles/66838-understanding-metrics-roll-ups-retention-and-grap
*/
if (i_size > week && ideal_res < 900)
return 900;
else if (i_size > two_days && ideal_res < 60)
return 60;
else
return ideal_res;
}
/* All the logic to query the librato API is here */
var librato_request = function(composite) {
var url_prefix = "https://metrics-api.librato.com/v1/metrics";
function make_url(sdate, edate, step) {
var params = "compose=" + composite +
"&start_time=" + sdate +
"&end_time=" + edate +
"&resolution=" + find_librato_resolution(sdate, edate, step);
return url_prefix + "?" + params;
}
/*
* We are most likely not going to get the same number of measurements
* cubism expects for a particular context: We have to perform down/up
* sampling
*/
function down_up_sampling(isdate, iedate, step, librato_mm) {
var av = [];
for (i=isdate; i<=iedate; i+=step) {
var int_mes = [];
while (librato_mm.length && librato_mm[0].measure_time <= i) {
int_mes.push(librato_mm.shift().value);
}
var v;
if (int_mes.length) { /* Compute the average */
v = int_mes.reduce(function(a, b) { return a + b }) / int_mes.length;
} else { /* No librato values on interval */
v = (av.length) ? av[av.length-1] : 0;
}
av.push(v);
}
return av;
}
request = {};
request.fire = function(isdate, iedate, step, callback_done) {
var a_values = []; /* Store partial values from librato */
/*
* Librato has a limit in the number of measurements we get back in a request (100).
* We recursively perform requests to the API to ensure we have all the data points
* for the interval we are working on.
*/
function actual_request(full_url) {
d3.json(full_url)
.header("X-Requested-With", "XMLHttpRequest")
.header("Authorization", auth_string)
.header("Librato-User-Agent", 'cubism/' + cubism.version)
.get(function (error, data) { /* Callback; data available */
if (!error) {
if (data.measurements.length === 0) {
return
}
data.measurements[0].series.forEach(function(o) { a_values.push(o); });
var still_more_values = 'query' in data && 'next_time' in data.query;
if (still_more_values) {
actual_request(make_url(data.query.next_time, iedate, step));
} else {
var a_adjusted = down_up_sampling(isdate, iedate, step, a_values);
callback_done(a_adjusted);
}
}
});
}
actual_request(make_url(isdate, iedate, step));
};
return request;
};
/*
* The user will use this method to create a cubism source (librato in this case)
* and call .metric() as necessary to create metrics.
*/
source.metric = function(m_composite) {
return context.metric(function(start, stop, step, callback) {
/* All the librato logic is here; .fire() retrieves the metrics' data */
librato_request(m_composite)
.fire(cubism_libratoFormatDate(start),
cubism_libratoFormatDate(stop),
cubism_libratoFormatDate(step),
function(a_values) { callback(null, a_values); });
}, m_composite += "");
};
/* This is not used when the source is librato */
source.toString = function() {
return "librato";
};
return source;
};
var cubism_libratoFormatDate = function(time) {
return Math.floor(time / 1000);
};
cubism_contextPrototype.graphite = function(host) {
if (!arguments.length) host = "";
var source = {},
@ -202,7 +383,7 @@ cubism_contextPrototype.graphite = function(host) {
// Apply the summarize, if necessary.
if (step !== 1e4) target = "summarize(" + target + ",'"
+ (!(step % 36e5) ? step / 36e5 + "hour" : !(step % 6e4) ? step / 6e4 + "min" : step + "sec")
+ (!(step % 36e5) ? step / 36e5 + "hour" : !(step % 6e4) ? step / 6e4 + "min" : step / 1e3 + "sec")
+ "','" + sum + "')";
d3.text(host + "/render?format=raw"
@ -258,6 +439,99 @@ function cubism_graphiteParse(text) {
.slice(1) // the first value is always None?
.map(function(d) { return +d; });
}
cubism_contextPrototype.gangliaWeb = function(config) {
var host = '',
uriPathPrefix = '/ganglia2/';
if (arguments.length) {
if (config.host) {
host = config.host;
}
if (config.uriPathPrefix) {
uriPathPrefix = config.uriPathPrefix;
/* Add leading and trailing slashes, as appropriate. */
if( uriPathPrefix[0] != '/' ) {
uriPathPrefix = '/' + uriPathPrefix;
}
if( uriPathPrefix[uriPathPrefix.length - 1] != '/' ) {
uriPathPrefix += '/';
}
}
}
var source = {},
context = this;
source.metric = function(metricInfo) {
/* Store the members from metricInfo into local variables. */
var clusterName = metricInfo.clusterName,
metricName = metricInfo.metricName,
hostName = metricInfo.hostName,
isReport = metricInfo.isReport || false,
titleGenerator = metricInfo.titleGenerator ||
/* Reasonable (not necessarily pretty) default for titleGenerator. */
function(unusedMetricInfo) {
/* unusedMetricInfo is, well, unused in this default case. */
return ('clusterName:' + clusterName +
' metricName:' + metricName +
(hostName ? ' hostName:' + hostName : ''));
},
onChangeCallback = metricInfo.onChangeCallback;
/* Default to plain, simple metrics. */
var metricKeyName = isReport ? 'g' : 'm';
var gangliaWebMetric = context.metric(function(start, stop, step, callback) {
function constructGangliaWebRequestQueryParams() {
return ('c=' + clusterName +
'&' + metricKeyName + '=' + metricName +
(hostName ? '&h=' + hostName : '') +
'&cs=' + start/1000 + '&ce=' + stop/1000 + '&step=' + step/1000 + '&graphlot=1');
}
d3.json(host + uriPathPrefix + 'graph.php?' + constructGangliaWebRequestQueryParams(),
function(result) {
if( !result ) {
return callback(new Error("Unable to fetch GangliaWeb data"));
}
callback(null, result[0].data);
});
}, titleGenerator(metricInfo));
gangliaWebMetric.toString = function() {
return titleGenerator(metricInfo);
};
/* Allow users to run their custom code each time a gangliaWebMetric changes.
*
* TODO Consider abstracting away the naked Cubism call, and instead exposing
* a callback that takes in the values array (maybe alongwith the original
* start and stop 'naked' parameters), since it's handy to have the entire
* dataset at your disposal (and users will likely implement onChangeCallback
* primarily to get at this dataset).
*/
if (onChangeCallback) {
gangliaWebMetric.on('change', onChangeCallback);
}
return gangliaWebMetric;
};
// Returns the gangliaWeb host + uriPathPrefix.
source.toString = function() {
return host + uriPathPrefix;
};
return source;
};
function cubism_metric(context) {
if (!(context instanceof cubism_context)) throw new Error("invalid context");
this.context = context;
@ -324,7 +598,7 @@ cubism_contextPrototype.metric = function(request, name) {
var start0 = new Date(stop - steps * step);
request(start0, stop, step, function(error, data) {
fetching = false;
if (error) return console.warn(error);
if (error) return console.warn(error);
var i = isFinite(start) ? Math.round((start0 - start) / step) : 0;
for (var j = 0, m = data.length; j < m; ++j) values[j + i] = data[j];
event.change.call(metric, start, stop);
@ -467,14 +741,14 @@ cubism_contextPrototype.horizon = function() {
scale = d3.scale.linear().interpolate(d3.interpolateRound),
metric = cubism_identity,
extent = null,
title = cubism_title,
title = cubism_identity,
format = d3.format(".2s"),
colors = ["#08519c","#3182bd","#6baed6","#bdd7e7","#bae4b3","#74c476","#31a354","#006d2c"];
function horizon(selection) {
selection
.on("mousemove.horizon", function() { context.focus(d3.mouse(this)[0]); })
.on("mousemove.horizon", function() { context.focus(Math.round(d3.mouse(this)[0])); })
.on("mouseout.horizon", function() { context.focus(null); });
selection.append("canvas")
@ -483,10 +757,10 @@ cubism_contextPrototype.horizon = function() {
selection.append("span")
.attr("class", "title")
.html(title);
.text(title);
// Hide value
// selection.append("span") .attr("class", "value");
selection.append("span")
.attr("class", "value");
selection.each(function(d, i) {
var that = this,
@ -551,6 +825,7 @@ cubism_contextPrototype.horizon = function() {
for (var i = i0, n = width, y1; i < n; ++i) {
y1 = metric_.valueAt(i);
if (y1 <= 0) { negative = true; continue; }
if (y1 === undefined) continue;
canvas.fillRect(i, y1 = scale(y1), 1, y0 - y1);
}
}
@ -690,7 +965,7 @@ cubism_contextPrototype.comparison = function() {
function comparison(selection) {
selection
.on("mousemove.comparison", function() { context.focus(d3.mouse(this)[0]); })
.on("mousemove.comparison", function() { context.focus(Math.round(d3.mouse(this)[0])); })
.on("mouseout.comparison", function() { context.focus(null); });
selection.append("canvas")
@ -908,9 +1183,10 @@ cubism_contextPrototype.axis = function() {
scale = context.scale,
axis_ = d3.svg.axis().scale(scale);
var format = context.step() < 6e4 ? cubism_axisFormatSeconds
var formatDefault = context.step() < 6e4 ? cubism_axisFormatSeconds
: context.step() < 864e5 ? cubism_axisFormatMinutes
: cubism_axisFormatDays;
var format = formatDefault;
function axis(selection) {
var id = ++cubism_id,
@ -957,6 +1233,12 @@ cubism_contextPrototype.axis = function() {
}
};
axis.focusFormat = function(_) {
if (!arguments.length) return format == formatDefault ? null : _;
format = _ == null ? formatDefault : _;
return axis;
};
return d3.rebind(axis, axis_,
"orient",
"ticks",
@ -1010,7 +1292,7 @@ cubism_contextPrototype.rule = function() {
context.on("focus.rule-" + id, function(i) {
line.datum(i)
.style("display", i == null ? "none" : null)
.style("left", cubism_ruleLeft);
.style("left", i == null ? null : cubism_ruleLeft);
});
}

1049
httpdocs/js/cubism_ntop.v1.js vendored Normal file

File diff suppressed because it is too large Load diff