Last active
February 24, 2022 10:53
-
-
Save mikepea/07b1cede92c119e4f297 to your computer and use it in GitHub Desktop.
Grafana Scripted Dashboard example for collectd with SeparateInstances and StoreRates enabled
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* global _ */ | |
/* | |
* Complex scripted dashboard | |
* This script generates a dashboard object that Grafana can load. It also takes a number of user | |
* supplied URL parameters (int ARGS variable) | |
* | |
* Global accessable variables | |
* window, document, $, jQuery, ARGS, moment | |
* | |
* Return a dashboard object, or a function | |
* | |
* For async scripts, return a function, this function must take a single callback function, | |
* call this function with the dasboard object | |
* | |
* Author: Anatoliy Dobrosynets, Recorded Future, Inc. | |
*/ | |
// accessable variables in this scope | |
var window, document, ARGS, $, jQuery, moment, kbn; | |
// use defaults for URL arguments | |
var arg_env = 'metrics'; | |
var arg_i = 'master'; | |
var arg_span = 4; | |
var arg_from = '2h'; | |
if(!_.isUndefined(ARGS.span)) { | |
arg_span = ARGS.span; | |
} | |
if(!_.isUndefined(ARGS.from)) { | |
arg_from = ARGS.from; | |
} | |
if(!_.isUndefined(ARGS.env)) { | |
arg_env = ARGS.env; | |
} | |
if(!_.isUndefined(ARGS.i)) { | |
arg_i = ARGS.i; // instance name | |
} | |
// return dashboard filter_list | |
// optionally include 'All' | |
function get_filter_object(name,query,show_all){ | |
show_all = (typeof show_all === "undefined") ? true : show_all; | |
var arr = find_filter_values(query); | |
var opts = []; | |
for (var i in arr) { | |
opts.push({"text":arr[i], "value":arr[i]}); | |
}; | |
if (show_all == true) { | |
opts.unshift({"text":"All", "value": '{'+arr.join()+'}'}); | |
}; | |
return { | |
type: "filter", | |
name: name, | |
query: query, | |
options: opts, | |
current: opts[0], | |
includeAll: show_all | |
} | |
}; | |
// execute graphite-api /metrics/find query | |
// return array of metric last names ( func('test.cpu-*') returns ['cpu-0','cpu-1',..] ) | |
function find_filter_values(query){ | |
var search_url = window.location.protocol + '//' + window.location.hostname.replace(/^grafana/,"graphite") + (window.location.port ? ":" + window.location.port : "") + '/metrics/find/?query=' + query; | |
var res = []; | |
var req = new XMLHttpRequest(); | |
req.open('GET', search_url, false); | |
req.send(null); | |
var obj = JSON.parse(req.responseText); | |
for(var key in obj) { | |
if (obj[key].hasOwnProperty("text")) { | |
res.push(obj[key]["text"]); | |
} | |
} | |
return res; | |
}; | |
// execute graphite-api /metrics/expand query | |
// return array of metric full names (func('*.cpu-*') returns ['test.cpu-0','test.cpu-1',..] ) | |
function expand_filter_values(query){ | |
var search_url = window.location.protocol + '//' + window.location.host + '/_graphite/metrics/expand/?query=' + query; | |
var req = new XMLHttpRequest(); | |
req.open('GET', search_url, false); | |
req.send(null); | |
var obj = JSON.parse(req.responseText); | |
if (obj.hasOwnProperty('results')) { | |
return obj['results']; | |
} else { | |
return []; | |
}; | |
}; | |
// used to calculate aliasByNode index in panel template | |
function len(prefix){ | |
return prefix.split('.').length - 2; | |
}; | |
//--------------------------------------------------------------------------------------- | |
function panel_collectd_delta_cpu(title,prefix){ | |
var idx = len(prefix); | |
return { | |
title: title, | |
type: 'graphite', | |
span: arg_span, | |
renderer: "flot", | |
y_formats: ["none"], | |
grid: {max: null, min: 0}, | |
lines: true, | |
fill: 1, | |
linewidth: 1, | |
stack: true, | |
legend: {show: true}, | |
percentage: true, | |
nullPointMode: "null", | |
tooltip: { | |
value_type: "individual", | |
query_as_alias: true | |
}, | |
targets: [ | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.user),'user')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.system),'system')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.idle),'idle')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.wait),'wait')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.steal),'steal')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.nice),'nice')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.softirq),'irq')" }, | |
{ "target": "alias(sumSeries(" + prefix + "[[instance]].cpu.*.cpu.interrupt),'intrpt')" }, | |
], | |
aliasColors: { | |
"user": "#508642", | |
"system": "#EAB839", | |
"wait": "#890F02", | |
"steal": "#E24D42", | |
"idle": "#6ED0E0" | |
} | |
} | |
}; | |
function panel_collectd_memory(title,prefix){ | |
var idx = len(prefix); | |
return { | |
title: title, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["bytes"], | |
grid: {max: null, min: 0}, | |
lines: true, | |
fill: 1, | |
linewidth: 1, | |
stack: true, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(movingMedian(" + prefix + "[[instance]].memory.memory.{used,free,cached,buffered},'15min')," +(idx+4)+ ")" }, | |
], | |
aliasColors: { | |
"free": "#629E51", | |
"used": "#1F78C1", | |
"cached": "#EF843C", | |
"buffered": "#CCA300" | |
} | |
} | |
}; | |
function panel_collectd_loadavg(title,prefix){ | |
var idx = len(prefix); | |
return { | |
title: title, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["none"], | |
grid: {max: null, min: 0}, | |
lines: true, | |
fill: 0, | |
linewidth: 2, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(movingMedian(" + prefix + "[[instance]].load.load.midterm,'10min')," +(idx+4)+ ")" }, | |
] | |
} | |
}; | |
function panel_collectd_swap_size(title,prefix){ | |
var idx = len(prefix); | |
return { | |
title: title, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["bytes"], | |
grid: {max: null, min: 0, leftMin: 0}, | |
lines: true, | |
fill: 1, | |
linewidth: 1, | |
stack: true, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(" + prefix + "[[instance]].swap.swap.{free,used,cached}," +(idx+3)+ ")" }, | |
], | |
aliasColors: { | |
"used": "#1F78C1", | |
"cached": "#EAB839", | |
"free": "#508642" | |
} | |
} | |
}; | |
function panel_collectd_swap_io(title,prefix){ | |
var idx = len(prefix); | |
return { | |
title: title, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["bytes"], | |
grid: {max: null, min: 0}, | |
lines: true, | |
fill: 1, | |
linewidth: 2, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(movingMedian(nonNegativeDerivative(keepLastValue(" + prefix + "[[instance]].swap.swap_io.in,10),0),'5min')," +(idx+3)+ ")" }, | |
{ "target": "aliasByNode(movingMedian(scale(nonNegativeDerivative(keepLastValue(" + prefix + "[[instance]].swap.swap.io-out,10),0),-1),'5min')," +(idx+3)+ ")" }, | |
] | |
} | |
}; | |
function panel_collectd_network_octets(title,prefix,intrf){ | |
intrf = (typeof intrf === "undefined") ? 'eth0' : intrf; | |
var idx = len(prefix); | |
return { | |
title: title + ', ' + intrf, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["bytes"], | |
grid: {max: null, min: null}, | |
lines: true, | |
fill: 1, | |
linewidth: 2, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(movingMedian(keepLastValue(" + prefix + "[[instance]].interface." + intrf + ".if_octets.rx,10),'5min')," +(idx+4)+ ")" }, | |
{ "target": "aliasByNode(movingMedian(scale(keepLastValue(" + prefix + "[[instance]].interface." + intrf + ".if_octets.tx,10),-1),'5min')," +(idx+4)+ ")" } | |
] | |
} | |
}; | |
function panel_collectd_network_packets(title,prefix,intrf){ | |
intrf = (typeof intrf === "undefined") ? 'eth0' : intrf; | |
var idx = len(prefix); | |
return { | |
title: title + ', ' + intrf, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["bytes"], | |
grid: {max: null, min: null}, | |
lines: true, | |
fill: 1, | |
linewidth: 2, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(movingMedian(keepLastValue(" + prefix + "[[instance]].interface." + intrf + ".if_packets.rx,10),'5min')," +(idx+4)+ ")" }, | |
{ "target": "aliasByNode(movingMedian(scale(keepLastValue(" + prefix + "[[instance]].interface." + intrf + ".if_packets.tx,10),-1),'5min')," +(idx+4)+ ")" } | |
] | |
} | |
}; | |
function panel_collectd_df(title,prefix,vol){ | |
vol = (typeof vol === "undefined") ? 'root' : vol; | |
var idx = len(prefix); | |
return { | |
title: title + ', ' + vol, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["bytes"], | |
grid: {max: null, min: 0, leftMin: 0}, | |
lines: true, | |
fill: 1, | |
linewidth: 2, | |
stack: true, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(" + prefix + "[[instance]].df." + vol + ".df_complex.{free,used,reserved}," +(idx+3)+ ")" }, | |
], | |
aliasColors: { | |
"used": "#447EBC", | |
"free": "#508642", | |
"reserved": "#EAB839" | |
} | |
} | |
}; | |
function panel_collectd_disk(title,prefix,vol){ | |
vol = (typeof vol === "undefined") ? 'sda' : vol; | |
var idx = len(prefix); | |
return { | |
title: title + ', ' + vol, | |
type: 'graphite', | |
span: arg_span, | |
y_formats: ["none"], | |
grid: {max: null, min: null}, | |
lines: true, | |
fill: 1, | |
linewidth: 2, | |
nullPointMode: "null", | |
targets: [ | |
{ "target": "aliasByNode(nonNegativeDerivative(" + prefix + "[[instance]].disk." + vol + ".disk_ops.write,10)," +(idx+2)+ "," +(idx+4)+ ")" }, | |
{ "target": "aliasByNode(scale(nonNegativeDerivative(" + prefix + "[[instance]].disk." + vol + ".disk_ops.read,10),-1)," +(idx+2)+ "," +(idx+4)+ ")" } | |
], | |
aliasColors: { | |
"write": "#447EBC", | |
"read": "#508642", | |
} | |
} | |
}; | |
/* | |
row templates | |
*/ | |
function row_delimiter(title){ | |
return { | |
title: "_____ " + title, | |
height: "20px", | |
collapse: false, | |
editable: false, | |
collapsable: false, | |
panels: [{ | |
title: title, | |
editable: false, | |
span: 12, | |
type: "text", | |
mode: "text" | |
}] | |
} | |
}; | |
function row_cpu_memory(title,prefix){ | |
return { | |
title: title, | |
height: '250px', | |
collapse: false, | |
panels: [ | |
panel_collectd_delta_cpu('CPU, %',prefix), | |
panel_collectd_memory('Memory',prefix), | |
panel_collectd_loadavg('Load avg, 10min',prefix) | |
] | |
} | |
}; | |
function row_swap(title,prefix){ | |
return { | |
title: title, | |
height: '250px', | |
collapse: true, | |
panels: [ | |
panel_collectd_swap_size('Swap size',prefix), | |
panel_collectd_swap_io('Swap IO',prefix), | |
] | |
} | |
}; | |
function row_network(title,prefix,filter){ | |
var interfaces = find_filter_values(filter + '.interface.*'); | |
var panels_network = []; | |
for (var i in interfaces) { | |
panels_network.push( | |
panel_collectd_network_octets('network octets',prefix,interfaces[i]), | |
panel_collectd_network_packets('network packets',prefix,interfaces[i]) | |
); | |
}; | |
return { | |
title: title, | |
height: '250px', | |
collapse: true, | |
panels: panels_network | |
} | |
}; | |
function row_disk_space(title,prefix,filter){ | |
var volumes = find_filter_values(filter + '.df.*'); | |
panels_disk_space = []; | |
for (var i in volumes) { | |
panels_disk_space.push(panel_collectd_df('disk space',prefix,volumes[i])); | |
}; | |
return { | |
title: title, | |
height: '250px', | |
collapse: true, | |
panels: panels_disk_space | |
} | |
}; | |
function row_disk_usage(title,prefix,filter){ | |
var volumes = find_filter_values(filter + '.disk.*'); | |
var panels_disk_usage = []; | |
for (var i in volumes) { | |
panels_disk_usage.push(panel_collectd_disk('disk ops read/write',prefix,volumes[i])); | |
}; | |
return { | |
title: title, | |
height: '250px', | |
collapse: true, | |
panels: panels_disk_usage | |
} | |
}; | |
//--------------------------------------------------------------------------------------- | |
return function(callback) { | |
// Setup some variables | |
var dashboard; | |
/* prefix - depends on actual Graphite tree. | |
In my case it depends on environment which can be passed as argument too. | |
.collectd.hosts. | |
.statsd.hosts. | |
.jmxtrans.hosts. | |
*/ | |
//var prefix = arg_env + '.collectd.hosts.'; | |
var prefix = arg_env + '.'; | |
var arg_filter = prefix + arg_i; | |
// set filter | |
var dashboard_filter = { | |
time: { | |
from: "now-" + arg_from, | |
to: "now" | |
}, | |
list: [ | |
get_filter_object("instance",arg_filter,false) | |
] | |
}; | |
// define pulldowns | |
pulldowns = [ | |
{ | |
type: "filtering", | |
collapse: false, | |
notice: false, | |
enable: true | |
}, | |
{ | |
type: "annotations", | |
enable: false | |
} | |
]; | |
// Intialize a skeleton with nothing but a rows array and service object | |
dashboard = { | |
rows : [], | |
services : {} | |
}; | |
dashboard.title = arg_i + ' (' + arg_env + ')'; | |
dashboard.editable = true; | |
dashboard.pulldowns = pulldowns; | |
dashboard.services.filter = dashboard_filter; | |
// custom dashboard rows (appended to the default dashboard rows) | |
var optional_rows = []; | |
$.ajax({ | |
method: 'GET', | |
url: '/' | |
}) | |
.done(function(result) { | |
// costruct dashboard rows | |
dashboard.rows.push( | |
row_cpu_memory('cpu, memory',prefix), | |
row_swap('swap',prefix), | |
row_network('network',prefix,arg_filter), | |
row_disk_space('disk space',prefix,arg_filter), | |
row_disk_usage('disk ops',prefix,arg_filter) | |
); | |
// custom rows | |
for (var i in optional_rows){ | |
dashboard.rows.push(optional_rows[i]); | |
}; | |
// when dashboard is composed call the callback | |
// function and pass the dashboard | |
callback(dashboard); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment