UNPKG

metriks

Version:

A lightweight graphing framework for servers

230 lines (186 loc) 5.49 kB
var _ = require('underscore'); var sys = require('sys'); var fs = require('fs'); var exec = require('child_process').exec; var unflatten = require('flat').unflatten; exports.RRDTool = RRDTool; function RRDTool(config) { _.extend(this, config); } RRDTool.prototype.escape = function(args) { return Array.prototype .slice.call(args) .map(function(argument) { if (argument === undefined || argument === null) { argument = ''; } if (argument === '') { return "''"; } // Escape everything that is potentially unsafe with a backslash // return (argument+'').trim().replace(/([^0-9a-zA-Z-])/g, '\\$1'); // rrdtool requires different escaping return (argument+'').trim().replace(/([^0-9a-zA-Z-\\\"\_\.\/\:])/g, '\\$1'); }) .join(' '); }; RRDTool.prototype.exe = function(cmd, options, cb) { var self = this; var args = []; args.push(cmd); options.forEach(function (val, key) { if (_.isObject(val)) { _.each(val, function(subVal, subKey) { if (subVal === false || subVal === 'false') { return; } if (subKey.length === 1) { args.push('-' + subKey); } else { args.push('--' + subKey.replace(/([A-Z])/g, '-$1').toLowerCase()); } if (subVal === true || subVal === 'true') { return; } args.push(subVal + ''); }); return; } args.push(val + ''); }); var fullCmd = 'rrdtool ' + self.escape(args); console.log(fullCmd); var opts = { encoding: 'utf8', timeout: 50 * 1000, maxBuffer: 200 * 1024, killSignal: 'SIGTERM', }; exec(fullCmd, opts, function (err, stdout, stderr) { if (err !== null || stderr) { return cb('Error while executing ' + fullCmd + '. ' + err + '. stderr: ' + stderr); } return cb(null, stdout); }); }; /** * Different from _.defaults because this goes over the option * Array to apply missing Object properties * @param {[type]} options [description] * @param {[type]} defaultOpts [description] * @return {[type]} [description] */ RRDTool.prototype._default = function(options, defaultOpts) { var defaultKeys = _.keys(defaultOpts); var hasKeys = []; _.each(defaultOpts, function(defaultVal, defaultKey) { var found = false; options.forEach(function (val, key) { if (_.isObject(val)) { _.each(val, function(subVal, subKey) { if (subKey === defaultKey) { found = true; return; } }); } }); if (!found) { options[defaultKey] = defaultVal; } }); return options; }; RRDTool.prototype.create = function(rrdFile, options, cb) { var self = this; options = options || []; options.unshift(rrdFile); options = self._default(options, { start: new Date(), step: 300 }); self.exe('create', options, cb); }; RRDTool.prototype.update = function(rrdFile, time, values, options, cb) { var self = this; options = options || []; options.unshift(rrdFile); options.push(self.rrdTime(time) + ':' + values.join(':')); self.exe('update', options, cb); }; RRDTool.prototype.graph = function (pngFile, options, cb) { var self = this; options = options || []; options.unshift(pngFile); self.exe('graph', options, cb); }; RRDTool.prototype.info = function (rrdFile, options, cb) { var self = this; options = options || []; options.unshift(rrdFile); if (!fs.existsSync(rrdFile)) { // rrd file doesn not exist (yet) // console.log('rrd file doesn not exist (yet) ' + rrdFile); return cb(null, null); } self.exe('info', options, function (err, stdout) { if (err) { return cb(err); } var info = self.explodeTree(stdout); if (!('ds' in info)) { console.log({rrdFile: rrdFile, info: info, stdout: stdout}); return cb(util.format('No ds found in info for %s', rrdFile)); } return cb(null, info); }); }; RRDTool.prototype.explodeTree = function(buf) { var self = this; var flat = {}; buf.split('\n').forEach(function (line) { var parts = line.split(' = '); var rawKey = parts.shift(); var rawVal = parts.join(' = '); var key = rawKey .replace(/[\.\[\]]+/g, '.') .replace(/^\[/, '') .replace(/\]$/, ''); flat[key] = self.toVal(rawVal); }); return unflatten(flat); }; RRDTool.prototype.toVal = function(val) { var self = this; if (self.isNumeric(val)) { val = val * 1; } else { val = (val + '').trim(); if (val.substr(0, 1) === '"' && val.substr(-1) === '"') { val = val.replace(/(^"|"$)/g, ''); } } return val; }; /** * isNumeric is something else than isNumber. We also allow strings posing as numbers. * @param {[type]} n [description] * @return {Boolean} [description] */ RRDTool.prototype.isNumeric = function (n) { return !isNaN(parseFloat(n)) && isFinite(n); }; RRDTool.prototype.rrdTime = function (date) { return Math.round(date.valueOf() / 1000); }; RRDTool.prototype.toTitle = function (str) { // An rrd ds name can only be 19 chars long str = (str + '').replace(/[^a-zA-Z0-9_ ]/g, ''); str = (str).replace(/\s+/g, ' '); return str.substr(0, 19); }; RRDTool.prototype.toDatasourceName = function (str) { // An rrd ds name can only be 19 chars long str = (str + '').replace(/[^a-zA-Z0-9_]/g, '_'); return str.substr(0, 19); };