-'use strict';\r
-\r
-var url = require('url');\r
-var http = require('http');\r
-var util = require('util');\r
-\r
-/*\r
-var netdata = require('netdata');\r
-\r
-var example_chart = {\r
- id: 'id', // the unique id of the chart\r
- name: 'name', // the name of the chart\r
- title: 'title', // the title of the chart\r
- units: 'units', // the units of the chart dimensions\r
- family: 'family', // the family of the chart\r
- category: 'category', // the category of the chart\r
- type: netdata.chartTypes.line, // the type of the chart\r
- priority: 0, // the priority relative to others in the same family and category\r
- update_every: 1, // the expected update frequency of the chart\r
- dimensions: {\r
- 'dim1': {\r
- id: 'dim1', // the unique id of the dimension\r
- name: 'name', // the name of the dimension\r
- algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm\r
- multiplier: 1, // the multiplier\r
- divisor: 1, // the divisor\r
- hidden: false, // is hidden (boolean)\r
- },\r
- 'dim2': {\r
- id: 'dim2', // the unique id of the dimension\r
- name: 'name', // the name of the dimension\r
- algorithm: 'absolute', // the id of the netdata algorithm\r
- multiplier: 1, // the multiplier\r
- divisor: 1, // the divisor\r
- hidden: false, // is hidden (boolean)\r
- }\r
- // add as many dimensions as needed\r
- }\r
-};\r
-*/\r
-\r
-var netdata = {\r
- options: {\r
- filename: __filename,\r
- DEBUG: false,\r
- update_every: 1,\r
- },\r
-\r
- chartAlgorithms: {\r
- incremental: 'incremental',\r
- absolute: 'absolute',\r
- percentage_of_absolute_row: 'percentage-of-absolute-row',\r
- percentage_of_incremental_row: 'percentage-of-incremental-row'\r
- },\r
-\r
- chartTypes: {\r
- line: 'line',\r
- area: 'area',\r
- stacked: 'stacked'\r
- },\r
-\r
- services: new Array(),\r
- modules_configuring: 0,\r
- charts: {},\r
-\r
-\r
- processors: {\r
- http: {\r
- name: 'http',\r
-\r
- process: function(service, callback) {\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request));\r
-\r
- var req = http.request(service.request, function(response) {\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...');\r
-\r
- var end = false;\r
- var data = '';\r
- response.setEncoding('utf8');\r
-\r
- if(response.statusCode !== 200) {\r
- if(end === false) {\r
- service.error('Got HTTP code ' + response.statusCode + ', failed to get data.');\r
- end = true;\r
- callback(null);\r
- }\r
- }\r
-\r
- response.on('data', function(chunk) {\r
- if(end === false) data += chunk;\r
- });\r
-\r
- response.on('error', function() {\r
- if(end === false) {\r
- service.error(': Read error, failed to get data.');\r
- end = true;\r
- callback(null);\r
- }\r
- });\r
-\r
- response.on('end', function() {\r
- if(end === false) {\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');\r
- end = true;\r
- callback(data);\r
- }\r
- });\r
- });\r
-\r
- req.on('error', function(e) {\r
- service.error('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);\r
- callback(null);\r
- });\r
-\r
- // write data to request body\r
- if(typeof service.postData !== 'undefined' && service.request.method === 'POST') {\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData);\r
- req.write(service.postData);\r
- }\r
-\r
- req.end();\r
- }\r
- }\r
- },\r
-\r
- stringify: function(obj) {\r
- return util.inspect(obj, {depth: 10});\r
- },\r
-\r
- // show debug info, if debug is enabled\r
- debug: function(msg) {\r
- if(this.options.DEBUG === true) {\r
- var now = new Date();\r
- console.error(now.toString() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());\r
- }\r
- },\r
-\r
- // log an error\r
- error: function(msg) {\r
- var now = new Date();\r
- console.error(now.toString() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());\r
- },\r
-\r
- // send data to netdata\r
- send: function(msg) {\r
- console.log(msg.toString());\r
- },\r
-\r
- service: function(service) {\r
- if(typeof service === 'undefined')\r
- service = {};\r
-\r
- service._current_chart = null; // the current chart we work on\r
- service._queue = ''; // data to be sent to netdata\r
-\r
- service.error_reported = false; // error log flood control\r
-\r
- service.added = false; // added to netdata.services\r
- service.enabled = true;\r
- service.updates = 0;\r
- service.running = false;\r
- service.started = 0;\r
- service.ended = 0;\r
- service.next_run = new Date().getTime();\r
-\r
- if(typeof service.update_every === 'undefined')\r
- service.update_every = service.module.update_every;\r
-\r
- if(typeof service.update_every === 'undefined')\r
- service.update_every = netdata.options.update_every;\r
-\r
- if(typeof service.processor === 'undefined')\r
- service.processor = netdata.processors.http;\r
-\r
- if(service.update_every < netdata.options.update_every)\r
- service.update_every = netdata.options.update_every;\r
-\r
- service.commit = function() {\r
- if(this.added !== true) {\r
- this.added = true;\r
- \r
- var now = new Date().getTime();\r
- while( this.next_run < now )\r
- this.next_run += (this.update_every * 1000);\r
-\r
- netdata.services.push(this);\r
- if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.');\r
- }\r
- };\r
-\r
- service.execute = function(callback) {\r
- this.module.active++;\r
- this.running = true;\r
- this.started = new Date().getTime();\r
- this.updates++;\r
-\r
- if(netdata.options.DEBUG === true)\r
- netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this));\r
-\r
- this.processor.process(this, function(response) {\r
- service.ended = new Date().getTime();\r
- service.duration = service.ended - service.started;\r
-\r
- if(typeof response === 'undefined')\r
- response = null;\r
-\r
- if(response !== null)\r
- service.errorClear();\r
-\r
- if(netdata.options.DEBUG === true)\r
- netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)');\r
-\r
- callback(service, response);\r
-\r
- service.running = false;\r
- service.module.active--;\r
- if(service.module.active < 0) {\r
- service.module.active = 0;\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': active module counter below zero.');\r
- }\r
-\r
- if(service.module.active === 0) {\r
- // check if we run under configure\r
- if(service.module.configure_callback !== null) {\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': configuration finish callback called from processResponse().');\r
- var ccallback = service.module.configure_callback;\r
- service.module.configure_callback = null;\r
- ccallback();\r
- }\r
- }\r
- });\r
- };\r
-\r
- service.update = function() {\r
- if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...');\r
-\r
- this.module.update(this, function() {\r
- if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.');\r
- });\r
- };\r
-\r
- service.error = function(message) {\r
- if(this.error_reported === false) {\r
- netdata.error(this.module.name + ': ' + this.name + ': ' + message);\r
- this.error_reported = true;\r
- }\r
- else if(netdata.options.DEBUG === true)\r
- netdata.debug(this.module.name + ': ' + this.name + ': ' + message);\r
- };\r
-\r
- service.errorClear = function() {\r
- this.error_reported = false;\r
- };\r
-\r
- service.queue = function(txt) {\r
- this._queue += txt + '\n';\r
- };\r
-\r
- service._send_chart_to_netdata = function(chart) {\r
- // internal function to send a chart to netdata\r
- this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.category + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString());\r
- \r
- for(var dim in chart.dimensions) {\r
- var d = chart.dimensions[dim];\r
-\r
- this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true)?'hidden':'').toString());\r
- d._created = true;\r
- d._updated = false;\r
- }\r
-\r
- chart._created = true;\r
- chart._updated = false;\r
- };\r
-\r
- // begin data collection for a chart\r
- service.begin = function(chart) {\r
- if(this._current_chart !== null && this._current_chart !== chart) {\r
- this.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.');\r
- this.end();\r
- }\r
-\r
- if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) {\r
- this.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.');\r
- return false;\r
- }\r
-\r
- if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id);\r
- this._current_chart = chart;\r
- this._current_chart._began = true;\r
-\r
- if(this._current_chart._dimensions_count !== 0) {\r
- if(this._current_chart._created === false || this._current_chart._updated === true)\r
- this._send_chart_to_netdata(this._current_chart);\r
-\r
- var now = this.ended;\r
- this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString());\r
- }\r
- // else this.error('Called begin() for chart ' + chart.id + ' which is empty.');\r
-\r
- this._current_chart._last_updated = now;\r
- this._current_chart._began = true;\r
- this._current_chart._counter++;\r
-\r
- return true;\r
- };\r
-\r
- // set a collected value for a chart\r
- // we do most things on the first value we attempt to set\r
- service.set = function(dimension, value) {\r
- if(this._current_chart === null) {\r
- this.error('Called set(' + dimension + ', ' + value + ') without an open chart.');\r
- return false;\r
- }\r
-\r
- if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') {\r
- this.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".');\r
- return false;\r
- }\r
-\r
- if(typeof value === 'undefined' || value === null)\r
- return false;\r
-\r
- if(this._current_chart._dimensions_count !== 0)\r
- this.queue('SET ' + dimension + ' = ' + value);\r
-\r
- return true;\r
- };\r
-\r
- // end data collection for the current chart - after calling begin()\r
- service.end = function() {\r
- if(this._current_chart !== null && this._current_chart._began === false) {\r
- this.error('Called end() without an open chart.');\r
- return false;\r
- }\r
-\r
- if(this._current_chart._dimensions_count !== 0) {\r
- this.queue('END');\r
- netdata.send(this._queue);\r
- }\r
-\r
- this._queue = '';\r
- this._current_chart._began = false;\r
- if(netdata.options.DEBUG === true) netdata.debug('sent chart ' + this._current_chart.id);\r
- this._current_chart = null;\r
- return true;\r
- };\r
-\r
- // discard the collected values for the current chart - after calling begin()\r
- service.flush = function() {\r
- if(this._current_chart === null || this._current_chart._began === false) {\r
- this.error('Called flush() without an open chart.');\r
- return false;\r
- }\r
-\r
- this._queue = '';\r
- this._current_chart._began = false;\r
- this._current_chart = null;\r
- return true;\r
- };\r
-\r
- // create a netdata chart\r
- service.chart = function(id, chart) {\r
- if(typeof(netdata.charts[id]) === 'undefined') {\r
- netdata.charts[id] = {\r
- _created: false,\r
- _updated: true,\r
- _began: false,\r
- _counter: 0,\r
- _last_updated: 0,\r
- _dimensions_count: 0,\r
- id: id,\r
- name: id,\r
- title: 'untitled chart',\r
- units: 'a unit',\r
- family: '',\r
- category: '',\r
- type: netdata.chartTypes.line,\r
- priority: 0,\r
- update_every: netdata.options.update_every,\r
- dimensions: {}\r
- };\r
- }\r
-\r
- var c = netdata.charts[id];\r
-\r
- if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its name');\r
- c.name = chart.name;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its title');\r
- c.title = chart.title;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its units');\r
- c.units = chart.units;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its family');\r
- c.family = chart.family;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.category) !== 'undefined' && chart.category !== c.category) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its category');\r
- c.category = chart.category;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its type');\r
- c.type = chart.type;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its priority');\r
- c.priority = chart.priority;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every);\r
- c.update_every = chart.update_every;\r
- c._updated = true;\r
- }\r
-\r
- if(typeof(chart.dimensions) !== 'undefined') {\r
- for(var x in chart.dimensions) {\r
- if(typeof(c.dimensions[x]) === 'undefined') {\r
- c._dimensions_count++;\r
-\r
- c.dimensions[x] = {\r
- _created: false,\r
- _updated: false,\r
- id: x, // the unique id of the dimension\r
- name: x, // the name of the dimension\r
- algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm\r
- multiplier: 1, // the multiplier\r
- divisor: 1, // the divisor\r
- hidden: false, // is hidden (boolean)\r
- };\r
-\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x);\r
- c._updated = true;\r
- }\r
-\r
- var dim = chart.dimensions[x];\r
- var d = c.dimensions[x];\r
-\r
- if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name');\r
- d.name = dim.name;\r
- d._updated = true;\r
- }\r
-\r
- if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm);\r
- d.algorithm = dim.algorithm;\r
- d._updated = true;\r
- }\r
-\r
- if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier');\r
- d.multiplier = dim.multiplier;\r
- d._updated = true;\r
- }\r
-\r
- if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor');\r
- d.divisor = dim.divisor;\r
- d._updated = true;\r
- }\r
-\r
- if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) {\r
- if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status');\r
- d.hidden = dim.hidden;\r
- d._updated = true;\r
- }\r
-\r
- if(d._updated) c._updated = true;\r
- }\r
- }\r
-\r
- //if(netdata.options.DEBUG === true) netdata.debug(netdata.charts);\r
- return netdata.charts[id];\r
- };\r
-\r
- return service;\r
- },\r
-\r
- runAllServices: function() {\r
- if(netdata.options.DEBUG === true) netdata.debug('runAllServices()');\r
-\r
- var now = new Date().getTime();\r
- var len = netdata.services.length;\r
- while(len--) {\r
- var service = netdata.services[len];\r
-\r
- if(service.enabled === false || service.running === true) continue;\r
- if(now <= service.next_run) continue;\r
-\r
- service.update();\r
-\r
- now = new Date().getTime();\r
- while(service.next_run < now)\r
- service.next_run += (service.update_every * 1000);\r
- }\r
-\r
- // 1/10th of update_every in pause\r
- setTimeout(netdata.runAllServices, netdata.options.update_every * 100);\r
- },\r
-\r
- start: function() {\r
- if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services));\r
-\r
- if(this.services.length === 0) {\r
- this.disableNodePlugin();\r
- process.exit(1);\r
- }\r
- else this.runAllServices();\r
- },\r
-\r
- // disable the whole node.js plugin\r
- disableNodePlugin: function() {\r
- this.send('DISABLE');\r
- process.exit(1);\r
- },\r
-\r
- requestFromParams: function(protocol, hostname, port, path, method) {\r
- return {\r
- protocol: protocol,\r
- hostname: hostname,\r
- port: port,\r
- path: path,\r
- //family: 4,\r
- method: method,\r
- headers: {\r
- 'Content-Type': 'application/x-www-form-urlencoded',\r
- 'Connection': 'keep-alive'\r
- },\r
- agent: new http.Agent({\r
- keepAlive: true,\r
- keepAliveMsecs: netdata.options.update_every * 1000,\r
- maxSockets: 2, // it must be 2 to work\r
- maxFreeSockets: 1\r
- })\r
- };\r
- },\r
-\r
- requestFromURL: function(a_url) {\r
- var u = url.parse(a_url);\r
- return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET');\r
- },\r
-\r
- configure: function(module, config, callback) {\r
- if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...');\r
-\r
- module.active = 0;\r
- module.update_every = this.options.update_every;\r
-\r
- if(typeof config.update_every !== 'undefined')\r
- module.update_every = config.update_every;\r
-\r
- module.enable_autodetect = (config.enable_autodetect)?true:false;\r
-\r
- if(typeof(callback) === 'function')\r
- module.configure_callback = callback;\r
- else\r
- module.configure_callback = null;\r
-\r
- var added = module.configure(config);\r
-\r
- if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.');\r
-\r
- if(module.configure_callback !== null && added === 0) {\r
- if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().');\r
- module.configure_callback = null;\r
- callback();\r
- }\r
-\r
- return added;\r
- }\r
-};\r
-\r
-if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename);\r
-module.exports = netdata;\r
+'use strict';
+
+var url = require('url');
+var http = require('http');
+var util = require('util');
+
+/*
+var netdata = require('netdata');
+
+var example_chart = {
+ id: 'id', // the unique id of the chart
+ name: 'name', // the name of the chart
+ title: 'title', // the title of the chart
+ units: 'units', // the units of the chart dimensions
+ family: 'family', // the family of the chart
+ context: 'context', // the context of the chart
+ type: netdata.chartTypes.line, // the type of the chart
+ priority: 0, // the priority relative to others in the same family
+ update_every: 1, // the expected update frequency of the chart
+ dimensions: {
+ 'dim1': {
+ id: 'dim1', // the unique id of the dimension
+ name: 'name', // the name of the dimension
+ algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm
+ multiplier: 1, // the multiplier
+ divisor: 1, // the divisor
+ hidden: false, // is hidden (boolean)
+ },
+ 'dim2': {
+ id: 'dim2', // the unique id of the dimension
+ name: 'name', // the name of the dimension
+ algorithm: 'absolute', // the id of the netdata algorithm
+ multiplier: 1, // the multiplier
+ divisor: 1, // the divisor
+ hidden: false, // is hidden (boolean)
+ }
+ // add as many dimensions as needed
+ }
+};
+*/
+
+var netdata = {
+ options: {
+ filename: __filename,
+ DEBUG: false,
+ update_every: 1,
+ },
+
+ chartAlgorithms: {
+ incremental: 'incremental',
+ absolute: 'absolute',
+ percentage_of_absolute_row: 'percentage-of-absolute-row',
+ percentage_of_incremental_row: 'percentage-of-incremental-row'
+ },
+
+ chartTypes: {
+ line: 'line',
+ area: 'area',
+ stacked: 'stacked'
+ },
+
+ services: new Array(),
+ modules_configuring: 0,
+ charts: {},
+
+
+ processors: {
+ http: {
+ name: 'http',
+
+ process: function(service, callback) {
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request));
+
+ var req = http.request(service.request, function(response) {
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...');
+
+ var end = false;
+ var data = '';
+ response.setEncoding('utf8');
+
+ if(response.statusCode !== 200) {
+ if(end === false) {
+ service.error('Got HTTP code ' + response.statusCode + ', failed to get data.');
+ end = true;
+ callback(null);
+ }
+ }
+
+ response.on('data', function(chunk) {
+ if(end === false) data += chunk;
+ });
+
+ response.on('error', function() {
+ if(end === false) {
+ service.error(': Read error, failed to get data.');
+ end = true;
+ callback(null);
+ }
+ });
+
+ response.on('end', function() {
+ if(end === false) {
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');
+ end = true;
+ callback(data);
+ }
+ });
+ });
+
+ req.on('error', function(e) {
+ service.error('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);
+ callback(null);
+ });
+
+ // write data to request body
+ if(typeof service.postData !== 'undefined' && service.request.method === 'POST') {
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData);
+ req.write(service.postData);
+ }
+
+ req.end();
+ }
+ }
+ },
+
+ stringify: function(obj) {
+ return util.inspect(obj, {depth: 10});
+ },
+
+ // show debug info, if debug is enabled
+ debug: function(msg) {
+ if(this.options.DEBUG === true) {
+ var now = new Date();
+ console.error(now.toString() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
+ }
+ },
+
+ // log an error
+ error: function(msg) {
+ var now = new Date();
+ console.error(now.toString() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
+ },
+
+ // send data to netdata
+ send: function(msg) {
+ console.log(msg.toString());
+ },
+
+ service: function(service) {
+ if(typeof service === 'undefined')
+ service = {};
+
+ var now = new Date().getTime();
+
+ service._current_chart = null; // the current chart we work on
+ service._queue = ''; // data to be sent to netdata
+
+ service.error_reported = false; // error log flood control
+
+ service.added = false; // added to netdata.services
+ service.enabled = true;
+ service.updates = 0;
+ service.running = false;
+ service.started = 0;
+ service.ended = 0;
+
+ if(typeof service.module === 'undefined') {
+ service.module = { name: 'not-defined-module' };
+ service.error('Attempted to create service without a module.');
+ service.enabled = false;
+ }
+
+ if(typeof service.name === 'undefined') {
+ service.name = 'unnamed@' + service.module.name + '/' + now;
+ }
+
+ if(typeof service.processor === 'undefined')
+ service.processor = netdata.processors.http;
+
+ if(typeof service.update_every === 'undefined')
+ service.update_every = service.module.update_every;
+
+ if(typeof service.update_every === 'undefined')
+ service.update_every = netdata.options.update_every;
+
+ if(service.update_every < netdata.options.update_every)
+ service.update_every = netdata.options.update_every;
+
+ // align the runs
+ service.next_run = now - (now % (service.update_every * 1000));
+
+ service.commit = function() {
+ if(this.added !== true) {
+ this.added = true;
+
+ var now = new Date().getTime();
+ while( this.next_run < now )
+ this.next_run += (this.update_every * 1000);
+
+ netdata.services.push(this);
+ if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.');
+ }
+ };
+
+ service.execute = function(callback) {
+ if(service.enabled === false) {
+ callback(null);
+ return;
+ }
+
+ this.module.active++;
+ this.running = true;
+ this.started = new Date().getTime();
+ this.updates++;
+
+ if(netdata.options.DEBUG === true)
+ netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this));
+
+ this.processor.process(this, function(response) {
+ service.ended = new Date().getTime();
+ service.duration = service.ended - service.started;
+
+ if(typeof response === 'undefined')
+ response = null;
+
+ if(response !== null)
+ service.errorClear();
+
+ if(netdata.options.DEBUG === true)
+ netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)');
+
+ callback(service, response);
+
+ service.running = false;
+ service.module.active--;
+ if(service.module.active < 0) {
+ service.module.active = 0;
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': active module counter below zero.');
+ }
+
+ if(service.module.active === 0) {
+ // check if we run under configure
+ if(service.module.configure_callback !== null) {
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': configuration finish callback called from processResponse().');
+ var ccallback = service.module.configure_callback;
+ service.module.configure_callback = null;
+ ccallback();
+ }
+ }
+ });
+ };
+
+ service.update = function() {
+ if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...');
+
+ this.module.update(this, function() {
+ if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.');
+ });
+ };
+
+ service.error = function(message) {
+ if(this.error_reported === false) {
+ netdata.error(this.module.name + ': ' + this.name + ': ' + message);
+ this.error_reported = true;
+ }
+ else if(netdata.options.DEBUG === true)
+ netdata.debug(this.module.name + ': ' + this.name + ': ' + message);
+ };
+
+ service.errorClear = function() {
+ this.error_reported = false;
+ };
+
+ service.queue = function(txt) {
+ this._queue += txt + '\n';
+ };
+
+ service._send_chart_to_netdata = function(chart) {
+ // internal function to send a chart to netdata
+ this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.context + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString());
+
+ for(var dim in chart.dimensions) {
+ var d = chart.dimensions[dim];
+
+ this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true)?'hidden':'').toString());
+ d._created = true;
+ d._updated = false;
+ }
+
+ chart._created = true;
+ chart._updated = false;
+ };
+
+ // begin data collection for a chart
+ service.begin = function(chart) {
+ if(this._current_chart !== null && this._current_chart !== chart) {
+ this.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.');
+ this.end();
+ }
+
+ if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) {
+ this.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.');
+ return false;
+ }
+
+ if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id);
+ this._current_chart = chart;
+ this._current_chart._began = true;
+
+ if(this._current_chart._dimensions_count !== 0) {
+ if(this._current_chart._created === false || this._current_chart._updated === true)
+ this._send_chart_to_netdata(this._current_chart);
+
+ var now = this.ended;
+ this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString());
+ }
+ // else this.error('Called begin() for chart ' + chart.id + ' which is empty.');
+
+ this._current_chart._last_updated = now;
+ this._current_chart._began = true;
+ this._current_chart._counter++;
+
+ return true;
+ };
+
+ // set a collected value for a chart
+ // we do most things on the first value we attempt to set
+ service.set = function(dimension, value) {
+ if(this._current_chart === null) {
+ this.error('Called set(' + dimension + ', ' + value + ') without an open chart.');
+ return false;
+ }
+
+ if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') {
+ this.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".');
+ return false;
+ }
+
+ if(typeof value === 'undefined' || value === null)
+ return false;
+
+ if(this._current_chart._dimensions_count !== 0) {
+ if (value instanceof Buffer)
+ this.queue('SET ' + dimension + ' = 0x' + value.toString('hex'));
+ else
+ this.queue('SET ' + dimension + ' = ' + value.toString());
+ }
+
+ return true;
+ };
+
+ // end data collection for the current chart - after calling begin()
+ service.end = function() {
+ if(this._current_chart !== null && this._current_chart._began === false) {
+ this.error('Called end() without an open chart.');
+ return false;
+ }
+
+ if(this._current_chart._dimensions_count !== 0) {
+ this.queue('END');
+ netdata.send(this._queue);
+ }
+
+ this._queue = '';
+ this._current_chart._began = false;
+ if(netdata.options.DEBUG === true) netdata.debug('sent chart ' + this._current_chart.id);
+ this._current_chart = null;
+ return true;
+ };
+
+ // discard the collected values for the current chart - after calling begin()
+ service.flush = function() {
+ if(this._current_chart === null || this._current_chart._began === false) {
+ this.error('Called flush() without an open chart.');
+ return false;
+ }
+
+ this._queue = '';
+ this._current_chart._began = false;
+ this._current_chart = null;
+ return true;
+ };
+
+ // create a netdata chart
+ service.chart = function(id, chart) {
+ if(typeof(netdata.charts[id]) === 'undefined') {
+ netdata.charts[id] = {
+ _created: false,
+ _updated: true,
+ _began: false,
+ _counter: 0,
+ _last_updated: 0,
+ _dimensions_count: 0,
+ id: id,
+ name: id,
+ title: 'untitled chart',
+ units: 'a unit',
+ family: '',
+ context: '',
+ type: netdata.chartTypes.line,
+ priority: 50000,
+ update_every: netdata.options.update_every,
+ dimensions: {}
+ };
+ }
+
+ var c = netdata.charts[id];
+
+ if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its name');
+ c.name = chart.name;
+ c._updated = true;
+ }
+
+ if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its title');
+ c.title = chart.title;
+ c._updated = true;
+ }
+
+ if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its units');
+ c.units = chart.units;
+ c._updated = true;
+ }
+
+ if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its family');
+ c.family = chart.family;
+ c._updated = true;
+ }
+
+ if(typeof(chart.context) !== 'undefined' && chart.context !== c.context) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its context');
+ c.context = chart.context;
+ c._updated = true;
+ }
+
+ if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its type');
+ c.type = chart.type;
+ c._updated = true;
+ }
+
+ if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its priority');
+ c.priority = chart.priority;
+ c._updated = true;
+ }
+
+ if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every);
+ c.update_every = chart.update_every;
+ c._updated = true;
+ }
+
+ if(typeof(chart.dimensions) !== 'undefined') {
+ for(var x in chart.dimensions) {
+ if(typeof(c.dimensions[x]) === 'undefined') {
+ c._dimensions_count++;
+
+ c.dimensions[x] = {
+ _created: false,
+ _updated: false,
+ id: x, // the unique id of the dimension
+ name: x, // the name of the dimension
+ algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm
+ multiplier: 1, // the multiplier
+ divisor: 1, // the divisor
+ hidden: false, // is hidden (boolean)
+ };
+
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x);
+ c._updated = true;
+ }
+
+ var dim = chart.dimensions[x];
+ var d = c.dimensions[x];
+
+ if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name');
+ d.name = dim.name;
+ d._updated = true;
+ }
+
+ if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm);
+ d.algorithm = dim.algorithm;
+ d._updated = true;
+ }
+
+ if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier');
+ d.multiplier = dim.multiplier;
+ d._updated = true;
+ }
+
+ if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor');
+ d.divisor = dim.divisor;
+ d._updated = true;
+ }
+
+ if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) {
+ if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status');
+ d.hidden = dim.hidden;
+ d._updated = true;
+ }
+
+ if(d._updated) c._updated = true;
+ }
+ }
+
+ //if(netdata.options.DEBUG === true) netdata.debug(netdata.charts);
+ return netdata.charts[id];
+ };
+
+ return service;
+ },
+
+ runAllServices: function() {
+ if(netdata.options.DEBUG === true) netdata.debug('runAllServices()');
+
+ var now = new Date().getTime();
+ var len = netdata.services.length;
+ while(len--) {
+ var service = netdata.services[len];
+
+ if(service.enabled === false || service.running === true) continue;
+ if(now <= service.next_run) continue;
+
+ service.update();
+
+ now = new Date().getTime();
+ while(service.next_run < now)
+ service.next_run += (service.update_every * 1000);
+ }
+
+ // 1/10th of update_every in pause
+ setTimeout(netdata.runAllServices, netdata.options.update_every * 100);
+ },
+
+ start: function() {
+ if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services));
+
+ if(this.services.length === 0) {
+ this.disableNodePlugin();
+ process.exit(1);
+ }
+ else this.runAllServices();
+ },
+
+ // disable the whole node.js plugin
+ disableNodePlugin: function() {
+ this.send('DISABLE');
+ process.exit(1);
+ },
+
+ requestFromParams: function(protocol, hostname, port, path, method) {
+ return {
+ protocol: protocol,
+ hostname: hostname,
+ port: port,
+ path: path,
+ //family: 4,
+ method: method,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Connection': 'keep-alive'
+ },
+ agent: new http.Agent({
+ keepAlive: true,
+ keepAliveMsecs: netdata.options.update_every * 1000,
+ maxSockets: 2, // it must be 2 to work
+ maxFreeSockets: 1
+ })
+ };
+ },
+
+ requestFromURL: function(a_url) {
+ var u = url.parse(a_url);
+ return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET');
+ },
+
+ configure: function(module, config, callback) {
+ if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...');
+
+ module.active = 0;
+ module.update_every = this.options.update_every;
+
+ if(typeof config.update_every !== 'undefined')
+ module.update_every = config.update_every;
+
+ module.enable_autodetect = (config.enable_autodetect)?true:false;
+
+ if(typeof(callback) === 'function')
+ module.configure_callback = callback;
+ else
+ module.configure_callback = null;
+
+ var added = module.configure(config);
+
+ if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.');
+
+ if(module.configure_callback !== null && added === 0) {
+ if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().');
+ module.configure_callback = null;
+ callback();
+ }
+
+ return added;
+ }
+};
+
+if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename);
+module.exports = netdata;