]> arthur.barton.de Git - netdata.git/commitdiff
added command line arguments in node.d.plugin to enabled selected modules; refactored...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Wed, 3 Feb 2016 23:29:29 +0000 (01:29 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Wed, 3 Feb 2016 23:29:29 +0000 (01:29 +0200)
node.d/named.node.js
node.d/node_modules/netdata.js
node.d/sma_webbox.node.js
plugins.d/node.d.plugin

index ad59f69bb76fcb0a1b9988d44f320d7a26df5cab..67426beadf0e395f0875cded3a59e64ee36d237d 100755 (executable)
-'use strict';\r
-\r
-// collect statistics from bind (named) v9.10+\r
-//\r
-// bind statistics documentation at:\r
-// http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/\r
-// https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics\r
-\r
-// example configuration in /etc/netdata/named.conf\r
-// the module supports auto-detection if bind is running in localhost\r
-\r
-/*\r
-{\r
-       "enable_autodetect": true,\r
-       "update_every": 5,\r
-       "servers": [\r
-               {\r
-                       "name": "bind1",\r
-                       "url": "http://127.0.0.1:8888/json/v1/server",\r
-                       "update_every": 1\r
-               },\r
-               {\r
-                       "name": "bind2",\r
-                       "url": "http://10.0.0.1:8888/xml/v3/server",\r
-                       "update_every": 2\r
-               }\r
-       ]\r
-}\r
-*/\r
-\r
-// the following is the bind named.conf configuration required\r
-\r
-/*\r
-statistics-channels {\r
-        inet 127.0.0.1 port 8888 allow { 127.0.0.1; };\r
-};\r
-*/\r
-\r
-var url = require('url');\r
-var http = require('http');\r
-var XML = require('pixl-xml');\r
-var netdata = require('netdata');\r
-\r
-if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');\r
-\r
-var named = {\r
-       name: __filename,\r
-       enable_autodetect: true,\r
-       update_every: 1000,\r
-\r
-       charts: {},\r
-\r
-       chartFromMembersCreate: function(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor) {\r
-               var chart = {\r
-                       id: id,                                                                                 // the unique id of the chart\r
-                       name: '',                                                                               // the unique name of the chart\r
-                       title: service.name + ' ' + title_suffix,               // the title of the chart\r
-                       units: units,                                                                   // the units of the chart dimensions\r
-                       family: family_prefix + '_' + service.name,             // the family of the chart\r
-                       category: category_prefix + '_' + service.name, // the category of the chart\r
-                       type: type,                                                                             // the type of the chart\r
-                       priority: priority,                                                             // the priority relative to others in the same family and category\r
-                       update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
-                       dimensions: {}\r
-               }\r
-\r
-               var found = 0;\r
-               for(var x in obj) {\r
-                       if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {\r
-                               found++;\r
-                               chart.dimensions[x] = {\r
-                                       id: x,                                  // the unique id of the dimension\r
-                                       name: x,                                // the name of the dimension\r
-                                       algorithm: algorithm,   // the id of the netdata algorithm\r
-                                       multiplier: multiplier, // the multiplier\r
-                                       divisor: divisor,               // the divisor\r
-                                       hidden: false                   // is hidden (boolean)\r
-                               }\r
-                       }\r
-               }\r
-\r
-               if(found === false)\r
-                       return null;\r
-\r
-               chart = service.chart(id, chart);\r
-               this.charts[id] = chart;\r
-               return chart;\r
-       },\r
-\r
-       chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor) {\r
-               var id = 'named_' + service.name + '.' + id_suffix;\r
-               var chart = this.charts[id];\r
-\r
-               if(typeof chart === 'undefined') {\r
-                       chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor);\r
-                       if(chart === null) return false;\r
-               }\r
-               else {\r
-                       // check if we need to re-generate the chart\r
-                       for(var x in obj) {\r
-                               if(typeof(chart.dimensions[x]) === 'undefined') {\r
-                                       chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor);\r
-                                       if(chart === null) return false;\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-\r
-               var found = 0;\r
-               service.begin(chart);\r
-               for(var x in obj) {\r
-                       if(typeof(chart.dimensions[x]) !== 'undefined') {\r
-                               found++;\r
-                               service.set(x, obj[x]);\r
-                       }\r
-               }\r
-               service.end();\r
-\r
-               if(found > 0) return true;\r
-               return false;\r
-       },\r
-\r
-       // an index to map values to different charts\r
-       lookups: {\r
-               nsstats: {},\r
-               resolver_stats: {},\r
-               numfetch: {}\r
-       },\r
-\r
-       // transform the XML response of bind\r
-       // to the JSON response of bind\r
-       xml2js: function(service, data_xml) {\r
-               var d = XML.parse(data_xml);\r
-               if(d === null) return null;\r
-\r
-               var data = {};\r
-               var len = d.server.counters.length;\r
-               while(len--) {\r
-                       var a = d.server.counters[len];\r
-                       if(typeof a.counter === 'undefined') continue;\r
-                       if(a.type === 'opcode') a.type = 'opcodes';\r
-                       else if(a.type === 'qtype') a.type = 'qtypes';\r
-                       else if(a.type === 'nsstat') a.type = 'nsstats';\r
-                       var aa = data[a.type] = {};\r
-                       var alen = 0\r
-                       var alen2 = a.counter.length;\r
-                       while(alen < alen2) {\r
-                               aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);\r
-                               alen++;\r
-                       }\r
-               }\r
-\r
-               data.views = {};\r
-               var vlen = d.views.view.length;\r
-               while(vlen--) {\r
-                       var vname = d.views.view[vlen].name;\r
-                       data.views[vname] = { resolver: {} };\r
-                       var len = d.views.view[vlen].counters.length;\r
-                       while(len--) {\r
-                               var a = d.views.view[vlen].counters[len];\r
-                               if(typeof a.counter === 'undefined') continue;\r
-                               if(a.type === 'resstats') a.type = 'stats';\r
-                               else if(a.type === 'resqtype') a.type = 'qtypes';\r
-                               else if(a.type === 'adbstat') a.type = 'adb';\r
-                               var aa = data.views[vname].resolver[a.type] = {};\r
-                               var alen = 0;\r
-                               var alen2 = a.counter.length;\r
-                               while(alen < alen2) {\r
-                                       aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);\r
-                                       alen++;\r
-                               }\r
-                       }\r
-               }\r
-\r
-               return data;\r
-       },\r
-\r
-       processResponse: function(service, data) {\r
-               if(data !== null) {\r
-                       var r;\r
-\r
-                       // parse XML or JSON\r
-                       // pepending on the URL given\r
-                       if(service.request.path.match(/^\/xml/) !== null)\r
-                               r = named.xml2js(service, data);\r
-                       else\r
-                               r = JSON.parse(data);\r
-\r
-                       if(typeof r === 'undefined' || r === null) {\r
-                               netdata.serviceError(service, "Cannot parse these data: " + data);\r
-                               return;\r
-                       }\r
-\r
-                       if(service.added !== true)\r
-                               netdata.serviceAdd(service);\r
-\r
-                       if(typeof r.nsstats !== 'undefined') {\r
-                               // we split the nsstats object to several others\r
-                               var global_requests = {}, global_requests_enable = false;\r
-                               var global_failures = {}, global_failures_enable = false;\r
-                               var global_failures_detail = {}, global_failures_detail_enable = false;\r
-                               var global_updates = {}, global_updates_enable = false;\r
-                               var protocol_queries = {}, protocol_queries_enable = false;\r
-                               var global_queries = {}, global_queries_enable = false;\r
-                               var global_queries_success = {}, global_queries_success_enable = false;\r
-                               var default_enable = false;\r
-                               var RecursClients = 0;\r
-\r
-                               // RecursClients is an absolute value\r
-                               if(typeof r.nsstats['RecursClients'] !== 'undefined') {\r
-                                       RecursClients = r.nsstats['RecursClients'];\r
-                                       delete r.nsstats['RecursClients'];\r
-                               }\r
-\r
-                               for( var x in r.nsstats ) {\r
-                                       // we maintain an index of the values found\r
-                                       // mapping them to objects splitted\r
-\r
-                                       var look = named.lookups.nsstats[x];\r
-                                       if(typeof look === 'undefined') {\r
-                                               // a new value, not found in the index\r
-                                               // index it:\r
-                                               if(x === 'Requestv4') {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: 'IPv4',\r
-                                                               type: 'global_requests'\r
-                                                       };\r
-                                               }\r
-                                               else if(x === 'Requestv6') {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: 'IPv6',\r
-                                                               type: 'global_requests'\r
-                                                       };\r
-                                               }\r
-                                               else if(x === 'QryFailure') {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: 'failures',\r
-                                                               type: 'global_failures'\r
-                                                       };\r
-                                               }\r
-                                               else if(x === 'QryUDP') {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: 'UDP',\r
-                                                               type: 'protocol_queries'\r
-                                                       };\r
-                                               }\r
-                                               else if(x === 'QryTCP') {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: 'TCP',\r
-                                                               type: 'protocol_queries'\r
-                                                       };\r
-                                               }\r
-                                               else if(x === 'QrySuccess') {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: 'queries',\r
-                                                               type: 'global_queries_success'\r
-                                                       };\r
-                                               }\r
-                                               else if(x.match(/QryRej$/) !== null) {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: x,\r
-                                                               type: 'global_failures_detail'\r
-                                                       };\r
-                                               }\r
-                                               else if(x.match(/^Qry/) !== null) {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: x,\r
-                                                               type: 'global_queries'\r
-                                                       };\r
-                                               }\r
-                                               else if(x.match(/^Update/) !== null) {\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: x,\r
-                                                               type: 'global_updates'\r
-                                                       };\r
-                                               }\r
-                                               else {\r
-                                                       // values not mapped, will remain\r
-                                                       // in the default map\r
-                                                       named.lookups.nsstats[x] = {\r
-                                                               name: x,\r
-                                                               type: 'default'\r
-                                                       };\r
-                                               }\r
-\r
-                                               look = named.lookups.nsstats[x];\r
-                                               // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);\r
-                                       }\r
-\r
-                                       switch(look.type) {\r
-                                               case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;\r
-                                               case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;\r
-                                               case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;\r
-                                               case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;\r
-                                               case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;\r
-                                               case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;\r
-                                               case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;\r
-                                               default: default_enable = true; break;\r
-                                       }\r
-                               }\r
-\r
-                               if(global_requests_enable == true)\r
-                                       service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 100, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(global_queries_success_enable == true)\r
-                                       service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'named', 'named', netdata.chartTypes.line, 150, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(protocol_queries_enable == true)\r
-                                       service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'named', 'named', netdata.chartTypes.stacked, 200, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(global_queries_enable == true)\r
-                                       service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'named', 'named', netdata.chartTypes.stacked, 300, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(global_updates_enable == true)\r
-                                       service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'named', 'named', netdata.chartTypes.stacked, 900, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(global_failures_enable == true)\r
-                                       service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'named', 'named', netdata.chartTypes.line, 950, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(global_failures_detail_enable == true)\r
-                                       service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'named', 'named', netdata.chartTypes.stacked, 960, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               if(default_enable === true)\r
-                                       service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'named', 'named', netdata.chartTypes.line, 999, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                               // RecursClients chart\r
-                               {\r
-                                       var id = 'named_' + service.name + '.recursive_clients';\r
-                                       var chart = named.charts[id];\r
-\r
-                                       if(typeof chart === 'undefined') {\r
-                                               chart = {\r
-                                                       id: id,                                                                                 // the unique id of the chart\r
-                                                       name: '',                                                                               // the unique name of the chart\r
-                                                       title: service.name + ' Bind, Current Recursive Clients',               // the title of the chart\r
-                                                       units: 'clients',                                                               // the units of the chart dimensions\r
-                                                       family: 'named',                                                                // the family of the chart\r
-                                                       category: 'named',                                                              // the category of the chart\r
-                                                       type: netdata.chartTypes.line,                                  // the type of the chart\r
-                                                       priority: 150,                                                                  // the priority relative to others in the same family and category\r
-                                                       update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
-                                                       dimensions: {\r
-                                                               'clients': {\r
-                                                                       id: 'clients',                                                          // the unique id of the dimension\r
-                                                                       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
-                                                       }\r
-                                               };\r
-\r
-                                               chart = service.chart(id, chart);\r
-                                               named.charts[id] = chart;\r
-                                       }\r
-\r
-                                       service.begin(chart);\r
-                                       service.set('clients', RecursClients);\r
-                                       service.end();\r
-                               }\r
-                       }\r
-\r
-                       if(typeof r.opcodes !== 'undefined')\r
-                               service.module.chartFromMembers(service, r.opcodes, 'in_opcodes', 'Bind, Global Incoming Requests by OpCode', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 1000, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                       if(typeof r.qtypes !== 'undefined')\r
-                               service.module.chartFromMembers(service, r.qtypes, 'in_qtypes', 'Bind, Global Incoming Requests by Query Type', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 2000, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                       if(typeof r.sockstats !== 'undefined')\r
-                               service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'named', 'named', netdata.chartTypes.line, 2500, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                       if(typeof r.views !== 'undefined') {\r
-                               for( var x in r.views ) {\r
-                                       var resolver = r.views[x].resolver;\r
-\r
-                                       if(typeof resolver !== 'undefined') {\r
-                                               if(typeof resolver.stats !== 'undefined') {\r
-                                                       var NumFetch = 0;\r
-                                                       var key = service.name + '.' + x;\r
-                                                       var default_enable = false;\r
-                                                       var rtt = {}, rtt_enable = false;\r
-\r
-                                                       // NumFetch is an absolute value\r
-                                                       if(typeof resolver.stats['NumFetch'] !== 'undefined') {\r
-                                                               named.lookups.numfetch[key] = true;\r
-                                                               NumFetch = resolver.stats['NumFetch'];\r
-                                                               delete resolver.stats['NumFetch'];\r
-                                                       }\r
-                                                       if(typeof resolver.stats['BucketSize'] !== 'undefined') {\r
-                                                               delete resolver.stats['BucketSize'];\r
-                                                       }\r
-\r
-                                                       // split the QryRTT* from the main chart\r
-                                                       for( var y in resolver.stats ) {\r
-                                                               // we maintain an index of the values found\r
-                                                               // mapping them to objects splitted\r
-\r
-                                                               var look = named.lookups.resolver_stats[y];\r
-                                                               if(typeof look === 'undefined') {\r
-                                                                       if(y.match(/^QryRTT/) !== null) {\r
-                                                                               named.lookups.resolver_stats[y] = {\r
-                                                                                       name: y,\r
-                                                                                       type: 'rtt'\r
-                                                                               };\r
-                                                                       }\r
-                                                                       else {\r
-                                                                               named.lookups.resolver_stats[y] = {\r
-                                                                                       name: y,\r
-                                                                                       type: 'default'\r
-                                                                               };\r
-                                                                       }\r
-\r
-                                                                       look = named.lookups.resolver_stats[y];\r
-                                                                       // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);\r
-                                                               }\r
-\r
-                                                               switch(look.type) {\r
-                                                                       case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;\r
-                                                                       default: default_enable = true; break;\r
-                                                               }\r
-                                                       }\r
-\r
-                                                       if(rtt_enable)\r
-                                                               service.module.chartFromMembers(service, rtt, 'view_resolver_rtt_' + x, 'Bind, ' + x + ' View, Resolver Round Trip Timings', 'queries/s', 'named', 'named', netdata.chartTypes.stacked, 5600, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                                                       if(default_enable)\r
-                                                               service.module.chartFromMembers(service, resolver.stats, 'view_resolver_stats_' + x, 'Bind, ' + x + ' View, Resolver Statistics', 'operations/s', 'named', 'named', netdata.chartTypes.line, 5500, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                                                       // NumFetch chart\r
-                                                       if(typeof named.lookups.numfetch[key] !== 'undefined') {\r
-                                                               var id = 'named_' + service.name + '.view_resolver_numfetch_' + x;\r
-                                                               var chart = named.charts[id];\r
-\r
-                                                               if(typeof chart === 'undefined') {\r
-                                                                       chart = {\r
-                                                                               id: id,                                                                                 // the unique id of the chart\r
-                                                                               name: '',                                                                               // the unique name of the chart\r
-                                                                               title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries',         // the title of the chart\r
-                                                                               units: 'queries',                                                               // the units of the chart dimensions\r
-                                                                               family: 'named',                                                                // the family of the chart\r
-                                                                               category: 'named',                                                              // the category of the chart\r
-                                                                               type: netdata.chartTypes.line,                                  // the type of the chart\r
-                                                                               priority: 5000,                                                                 // the priority relative to others in the same family and category\r
-                                                                               update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
-                                                                               dimensions: {\r
-                                                                                       'queries': {\r
-                                                                                               id: 'queries',                                                          // the unique id of the dimension\r
-                                                                                               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
-                                                                               }\r
-                                                                       };\r
-\r
-                                                                       chart = service.chart(id, chart);\r
-                                                                       named.charts[id] = chart;\r
-                                                               }\r
-\r
-                                                               service.begin(chart);\r
-                                                               service.set('queries', NumFetch);\r
-                                                               service.end();\r
-                                                       }\r
-                                               }\r
-                                       }\r
-\r
-                                       if(typeof resolver.qtypes !== 'undefined')\r
-                                               service.module.chartFromMembers(service, resolver.qtypes, 'view_resolver_qtypes_' + x, 'Bind, ' + x + ' View, Requests by Query Type', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 6000, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                                       //if(typeof resolver.cache !== 'undefined')\r
-                                       //      service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'named', 'named', netdata.chartTypes.stacked, 7000, netdata.chartAlgorithms.absolute, 1, 1);\r
-\r
-                                       if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) {\r
-                                               var id = 'named_' + service.name + '.view_resolver_cachehits_' + x;\r
-                                               var chart = named.charts[id];\r
-\r
-                                               if(typeof chart === 'undefined') {\r
-                                                       chart = {\r
-                                                               id: id,                                                                                 // the unique id of the chart\r
-                                                               name: '',                                                                               // the unique name of the chart\r
-                                                               title: service.name + ' Bind, ' + x + ' View, Resolver Cache Hits',             // the title of the chart\r
-                                                               units: 'operations/s',                                                          // the units of the chart dimensions\r
-                                                               family: 'named',                                                                // the family of the chart\r
-                                                               category: 'named',                                                              // the category of the chart\r
-                                                               type: netdata.chartTypes.area,                                  // the type of the chart\r
-                                                               priority: 8000,                                                                 // the priority relative to others in the same family and category\r
-                                                               update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
-                                                               dimensions: {\r
-                                                                       'CacheHits': {\r
-                                                                               id: 'CacheHits',                                                        // the unique id of the dimension\r
-                                                                               name: 'hits',                                                           // the name of the dimension\r
-                                                                               algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm\r
-                                                                               multiplier: 1,                                                          // the multiplier\r
-                                                                               divisor: 1,                                                                     // the divisor\r
-                                                                               hidden: false                                                           // is hidden (boolean)\r
-                                                                       },\r
-                                                                       'CacheMisses': {\r
-                                                                               id: 'CacheMisses',                                                      // the unique id of the dimension\r
-                                                                               name: 'misses',                                                         // the name of the dimension\r
-                                                                               algorithm: netdata.chartAlgorithms.incremental,// 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
-                                                       };\r
-\r
-                                                       chart = service.chart(id, chart);\r
-                                                       named.charts[id] = chart;\r
-                                               }\r
-\r
-                                               service.begin(chart);\r
-                                               service.set('CacheHits', resolver.cachestats['CacheHits']);\r
-                                               service.set('CacheMisses', resolver.cachestats['CacheMisses']);\r
-                                               service.end();\r
-                                       }\r
-\r
-                                       // this is wrong, it contains many types of info:\r
-                                       // 1. CacheHits, CacheMisses - incremental (added above)\r
-                                       // 2. QueryHits, QueryMisses - incremental\r
-                                       // 3. DeleteLRU, DeleteTTL - incremental\r
-                                       // 4. CacheNodes, CacheBuckets - absolute\r
-                                       // 5. TreeMemTotal, TreeMemInUse - absolute\r
-                                       // 6. HeapMemMax, HeapMemTotal, HeapMemInUse - absolute\r
-                                       //if(typeof resolver.cachestats !== 'undefined')\r
-                                       //      service.module.chartFromMembers(service, resolver.cachestats, 'view_resolver_cachestats_' + x, 'Bind, ' + x + ' View, Cache Statistics', 'requests/s', 'named', 'named', netdata.chartTypes.line, 8000, netdata.chartAlgorithms.incremental, 1, 1);\r
-\r
-                                       //if(typeof resolver.adb !== 'undefined')\r
-                                       //      service.module.chartFromMembers(service, resolver.adb, 'view_resolver_adb_' + x, 'Bind, ' + x + ' View, ADB Statistics', 'entries', 'named', 'named', netdata.chartTypes.line, 8500, netdata.chartAlgorithms.absolute, 1, 1);\r
-                               }\r
-                       }\r
-               }\r
-       },\r
-\r
-       // module.serviceExecute()\r
-       // this function is called only from this module\r
-       // its purpose is to prepare the request and call\r
-       // netdata.serviceExecute()\r
-       serviceExecute: function(name, a_url, update_every) {\r
-               if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);\r
-               netdata.serviceExecute({\r
-                       name: name,\r
-                       request: netdata.requestFromURL(a_url),\r
-                       update_every: update_every,\r
-                       module: this\r
-               }, this.processResponse);\r
-       },\r
-\r
-       configure: function(config) {\r
-               var added = 0;\r
-\r
-               if(this.enable_autodetect === true) {\r
-                       this.serviceExecute('local', 'http://localhost:8888/json/v1/server', this.update_every);\r
-                       added++;\r
-               }\r
-               \r
-               if(typeof(config.servers) !== 'undefined') {\r
-                       var len = config.servers.length;\r
-                       while(len--) {\r
-                               if(typeof config.servers[len].update_every === 'undefined')\r
-                                       config.servers[len].update_every = this.update_every;\r
-                               else\r
-                                       config.servers[len].update_every = config.servers[len].update_every * 1000;\r
-\r
-                               this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);\r
-                               added++;\r
-                       }\r
-               }\r
-\r
-               return added;\r
-       },\r
-\r
-       // module.update()\r
-       // this is called repeatidly to collect data, by calling\r
-       // netdata.serviceExecute()\r
-       update: function(service, callback) {\r
-               netdata.serviceExecute(service, function(serv, data) {\r
-                       service.module.processResponse(serv, data);\r
-                       callback();\r
-               });\r
-       },\r
-};\r
-\r
-module.exports = named;\r
+'use strict';
+
+// collect statistics from bind (named) v9.10+
+//
+// bind statistics documentation at:
+// http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/
+// https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics
+
+// example configuration in /etc/netdata/named.conf
+// the module supports auto-detection if bind is running in localhost
+
+/*
+{
+       "enable_autodetect": true,
+       "update_every": 5,
+       "servers": [
+               {
+                       "name": "bind1",
+                       "url": "http://127.0.0.1:8888/json/v1/server",
+                       "update_every": 1
+               },
+               {
+                       "name": "bind2",
+                       "url": "http://10.0.0.1:8888/xml/v3/server",
+                       "update_every": 2
+               }
+       ]
+}
+*/
+
+// the following is the bind named.conf configuration required
+
+/*
+statistics-channels {
+        inet 127.0.0.1 port 8888 allow { 127.0.0.1; };
+};
+*/
+
+var url = require('url');
+var http = require('http');
+var XML = require('pixl-xml');
+var netdata = require('netdata');
+
+if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');
+
+var named = {
+       name: __filename,
+       enable_autodetect: true,
+       update_every: 1000,
+
+       charts: {},
+
+       chartFromMembersCreate: function(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor) {
+               var chart = {
+                       id: id,                                                                                 // the unique id of the chart
+                       name: '',                                                                               // the unique name of the chart
+                       title: service.name + ' ' + title_suffix,               // the title of the chart
+                       units: units,                                                                   // the units of the chart dimensions
+                       family: family_prefix + '_' + service.name,             // the family of the chart
+                       category: category_prefix + '_' + service.name, // the category of the chart
+                       type: type,                                                                             // the type of the chart
+                       priority: priority,                                                             // the priority relative to others in the same family and category
+                       update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart
+                       dimensions: {}
+               }
+
+               var found = 0;
+               for(var x in obj) {
+                       if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {
+                               found++;
+                               chart.dimensions[x] = {
+                                       id: x,                                  // the unique id of the dimension
+                                       name: x,                                // the name of the dimension
+                                       algorithm: algorithm,   // the id of the netdata algorithm
+                                       multiplier: multiplier, // the multiplier
+                                       divisor: divisor,               // the divisor
+                                       hidden: false                   // is hidden (boolean)
+                               }
+                       }
+               }
+
+               if(found === false)
+                       return null;
+
+               chart = service.chart(id, chart);
+               this.charts[id] = chart;
+               return chart;
+       },
+
+       chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor) {
+               var id = 'named_' + service.name + '.' + id_suffix;
+               var chart = this.charts[id];
+
+               if(typeof chart === 'undefined') {
+                       chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor);
+                       if(chart === null) return false;
+               }
+               else {
+                       // check if we need to re-generate the chart
+                       for(var x in obj) {
+                               if(typeof(chart.dimensions[x]) === 'undefined') {
+                                       chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor);
+                                       if(chart === null) return false;
+                                       break;
+                               }
+                       }
+               }
+
+               var found = 0;
+               service.begin(chart);
+               for(var x in obj) {
+                       if(typeof(chart.dimensions[x]) !== 'undefined') {
+                               found++;
+                               service.set(x, obj[x]);
+                       }
+               }
+               service.end();
+
+               if(found > 0) return true;
+               return false;
+       },
+
+       // an index to map values to different charts
+       lookups: {
+               nsstats: {},
+               resolver_stats: {},
+               numfetch: {}
+       },
+
+       // transform the XML response of bind
+       // to the JSON response of bind
+       xml2js: function(service, data_xml) {
+               var d = XML.parse(data_xml);
+               if(d === null) return null;
+
+               var data = {};
+               var len = d.server.counters.length;
+               while(len--) {
+                       var a = d.server.counters[len];
+                       if(typeof a.counter === 'undefined') continue;
+                       if(a.type === 'opcode') a.type = 'opcodes';
+                       else if(a.type === 'qtype') a.type = 'qtypes';
+                       else if(a.type === 'nsstat') a.type = 'nsstats';
+                       var aa = data[a.type] = {};
+                       var alen = 0
+                       var alen2 = a.counter.length;
+                       while(alen < alen2) {
+                               aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);
+                               alen++;
+                       }
+               }
+
+               data.views = {};
+               var vlen = d.views.view.length;
+               while(vlen--) {
+                       var vname = d.views.view[vlen].name;
+                       data.views[vname] = { resolver: {} };
+                       var len = d.views.view[vlen].counters.length;
+                       while(len--) {
+                               var a = d.views.view[vlen].counters[len];
+                               if(typeof a.counter === 'undefined') continue;
+                               if(a.type === 'resstats') a.type = 'stats';
+                               else if(a.type === 'resqtype') a.type = 'qtypes';
+                               else if(a.type === 'adbstat') a.type = 'adb';
+                               var aa = data.views[vname].resolver[a.type] = {};
+                               var alen = 0;
+                               var alen2 = a.counter.length;
+                               while(alen < alen2) {
+                                       aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);
+                                       alen++;
+                               }
+                       }
+               }
+
+               return data;
+       },
+
+       processResponse: function(service, data) {
+               if(data !== null) {
+                       var r;
+
+                       // parse XML or JSON
+                       // pepending on the URL given
+                       if(service.request.path.match(/^\/xml/) !== null)
+                               r = named.xml2js(service, data);
+                       else
+                               r = JSON.parse(data);
+
+                       if(typeof r === 'undefined' || r === null) {
+                               netdata.serviceError(service, "Cannot parse these data: " + data);
+                               return;
+                       }
+
+                       if(service.added !== true)
+                               service.commit();
+
+                       if(typeof r.nsstats !== 'undefined') {
+                               // we split the nsstats object to several others
+                               var global_requests = {}, global_requests_enable = false;
+                               var global_failures = {}, global_failures_enable = false;
+                               var global_failures_detail = {}, global_failures_detail_enable = false;
+                               var global_updates = {}, global_updates_enable = false;
+                               var protocol_queries = {}, protocol_queries_enable = false;
+                               var global_queries = {}, global_queries_enable = false;
+                               var global_queries_success = {}, global_queries_success_enable = false;
+                               var default_enable = false;
+                               var RecursClients = 0;
+
+                               // RecursClients is an absolute value
+                               if(typeof r.nsstats['RecursClients'] !== 'undefined') {
+                                       RecursClients = r.nsstats['RecursClients'];
+                                       delete r.nsstats['RecursClients'];
+                               }
+
+                               for( var x in r.nsstats ) {
+                                       // we maintain an index of the values found
+                                       // mapping them to objects splitted
+
+                                       var look = named.lookups.nsstats[x];
+                                       if(typeof look === 'undefined') {
+                                               // a new value, not found in the index
+                                               // index it:
+                                               if(x === 'Requestv4') {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: 'IPv4',
+                                                               type: 'global_requests'
+                                                       };
+                                               }
+                                               else if(x === 'Requestv6') {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: 'IPv6',
+                                                               type: 'global_requests'
+                                                       };
+                                               }
+                                               else if(x === 'QryFailure') {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: 'failures',
+                                                               type: 'global_failures'
+                                                       };
+                                               }
+                                               else if(x === 'QryUDP') {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: 'UDP',
+                                                               type: 'protocol_queries'
+                                                       };
+                                               }
+                                               else if(x === 'QryTCP') {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: 'TCP',
+                                                               type: 'protocol_queries'
+                                                       };
+                                               }
+                                               else if(x === 'QrySuccess') {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: 'queries',
+                                                               type: 'global_queries_success'
+                                                       };
+                                               }
+                                               else if(x.match(/QryRej$/) !== null) {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: x,
+                                                               type: 'global_failures_detail'
+                                                       };
+                                               }
+                                               else if(x.match(/^Qry/) !== null) {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: x,
+                                                               type: 'global_queries'
+                                                       };
+                                               }
+                                               else if(x.match(/^Update/) !== null) {
+                                                       named.lookups.nsstats[x] = {
+                                                               name: x,
+                                                               type: 'global_updates'
+                                                       };
+                                               }
+                                               else {
+                                                       // values not mapped, will remain
+                                                       // in the default map
+                                                       named.lookups.nsstats[x] = {
+                                                               name: x,
+                                                               type: 'default'
+                                                       };
+                                               }
+
+                                               look = named.lookups.nsstats[x];
+                                               // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);
+                                       }
+
+                                       switch(look.type) {
+                                               case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;
+                                               case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;
+                                               case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;
+                                               case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;
+                                               case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;
+                                               case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;
+                                               case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;
+                                               default: default_enable = true; break;
+                                       }
+                               }
+
+                               if(global_requests_enable == true)
+                                       service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 100, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(global_queries_success_enable == true)
+                                       service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'named', 'named', netdata.chartTypes.line, 150, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(protocol_queries_enable == true)
+                                       service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'named', 'named', netdata.chartTypes.stacked, 200, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(global_queries_enable == true)
+                                       service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'named', 'named', netdata.chartTypes.stacked, 300, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(global_updates_enable == true)
+                                       service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'named', 'named', netdata.chartTypes.stacked, 900, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(global_failures_enable == true)
+                                       service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'named', 'named', netdata.chartTypes.line, 950, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(global_failures_detail_enable == true)
+                                       service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'named', 'named', netdata.chartTypes.stacked, 960, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               if(default_enable === true)
+                                       service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'named', 'named', netdata.chartTypes.line, 999, netdata.chartAlgorithms.incremental, 1, 1);
+
+                               // RecursClients chart
+                               {
+                                       var id = 'named_' + service.name + '.recursive_clients';
+                                       var chart = named.charts[id];
+
+                                       if(typeof chart === 'undefined') {
+                                               chart = {
+                                                       id: id,                                                                                 // the unique id of the chart
+                                                       name: '',                                                                               // the unique name of the chart
+                                                       title: service.name + ' Bind, Current Recursive Clients',               // the title of the chart
+                                                       units: 'clients',                                                               // the units of the chart dimensions
+                                                       family: 'named',                                                                // the family of the chart
+                                                       category: 'named',                                                              // the category of the chart
+                                                       type: netdata.chartTypes.line,                                  // the type of the chart
+                                                       priority: 150,                                                                  // the priority relative to others in the same family and category
+                                                       update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart
+                                                       dimensions: {
+                                                               'clients': {
+                                                                       id: 'clients',                                                          // the unique id of the dimension
+                                                                       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)
+                                                               }
+                                                       }
+                                               };
+
+                                               chart = service.chart(id, chart);
+                                               named.charts[id] = chart;
+                                       }
+
+                                       service.begin(chart);
+                                       service.set('clients', RecursClients);
+                                       service.end();
+                               }
+                       }
+
+                       if(typeof r.opcodes !== 'undefined')
+                               service.module.chartFromMembers(service, r.opcodes, 'in_opcodes', 'Bind, Global Incoming Requests by OpCode', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 1000, netdata.chartAlgorithms.incremental, 1, 1);
+
+                       if(typeof r.qtypes !== 'undefined')
+                               service.module.chartFromMembers(service, r.qtypes, 'in_qtypes', 'Bind, Global Incoming Requests by Query Type', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 2000, netdata.chartAlgorithms.incremental, 1, 1);
+
+                       if(typeof r.sockstats !== 'undefined')
+                               service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'named', 'named', netdata.chartTypes.line, 2500, netdata.chartAlgorithms.incremental, 1, 1);
+
+                       if(typeof r.views !== 'undefined') {
+                               for( var x in r.views ) {
+                                       var resolver = r.views[x].resolver;
+
+                                       if(typeof resolver !== 'undefined') {
+                                               if(typeof resolver.stats !== 'undefined') {
+                                                       var NumFetch = 0;
+                                                       var key = service.name + '.' + x;
+                                                       var default_enable = false;
+                                                       var rtt = {}, rtt_enable = false;
+
+                                                       // NumFetch is an absolute value
+                                                       if(typeof resolver.stats['NumFetch'] !== 'undefined') {
+                                                               named.lookups.numfetch[key] = true;
+                                                               NumFetch = resolver.stats['NumFetch'];
+                                                               delete resolver.stats['NumFetch'];
+                                                       }
+                                                       if(typeof resolver.stats['BucketSize'] !== 'undefined') {
+                                                               delete resolver.stats['BucketSize'];
+                                                       }
+
+                                                       // split the QryRTT* from the main chart
+                                                       for( var y in resolver.stats ) {
+                                                               // we maintain an index of the values found
+                                                               // mapping them to objects splitted
+
+                                                               var look = named.lookups.resolver_stats[y];
+                                                               if(typeof look === 'undefined') {
+                                                                       if(y.match(/^QryRTT/) !== null) {
+                                                                               named.lookups.resolver_stats[y] = {
+                                                                                       name: y,
+                                                                                       type: 'rtt'
+                                                                               };
+                                                                       }
+                                                                       else {
+                                                                               named.lookups.resolver_stats[y] = {
+                                                                                       name: y,
+                                                                                       type: 'default'
+                                                                               };
+                                                                       }
+
+                                                                       look = named.lookups.resolver_stats[y];
+                                                                       // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);
+                                                               }
+
+                                                               switch(look.type) {
+                                                                       case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;
+                                                                       default: default_enable = true; break;
+                                                               }
+                                                       }
+
+                                                       if(rtt_enable)
+                                                               service.module.chartFromMembers(service, rtt, 'view_resolver_rtt_' + x, 'Bind, ' + x + ' View, Resolver Round Trip Timings', 'queries/s', 'named', 'named', netdata.chartTypes.stacked, 5600, netdata.chartAlgorithms.incremental, 1, 1);
+
+                                                       if(default_enable)
+                                                               service.module.chartFromMembers(service, resolver.stats, 'view_resolver_stats_' + x, 'Bind, ' + x + ' View, Resolver Statistics', 'operations/s', 'named', 'named', netdata.chartTypes.line, 5500, netdata.chartAlgorithms.incremental, 1, 1);
+
+                                                       // NumFetch chart
+                                                       if(typeof named.lookups.numfetch[key] !== 'undefined') {
+                                                               var id = 'named_' + service.name + '.view_resolver_numfetch_' + x;
+                                                               var chart = named.charts[id];
+
+                                                               if(typeof chart === 'undefined') {
+                                                                       chart = {
+                                                                               id: id,                                                                                 // the unique id of the chart
+                                                                               name: '',                                                                               // the unique name of the chart
+                                                                               title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries',         // the title of the chart
+                                                                               units: 'queries',                                                               // the units of the chart dimensions
+                                                                               family: 'named',                                                                // the family of the chart
+                                                                               category: 'named',                                                              // the category of the chart
+                                                                               type: netdata.chartTypes.line,                                  // the type of the chart
+                                                                               priority: 5000,                                                                 // the priority relative to others in the same family and category
+                                                                               update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart
+                                                                               dimensions: {
+                                                                                       'queries': {
+                                                                                               id: 'queries',                                                          // the unique id of the dimension
+                                                                                               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)
+                                                                                       }
+                                                                               }
+                                                                       };
+
+                                                                       chart = service.chart(id, chart);
+                                                                       named.charts[id] = chart;
+                                                               }
+
+                                                               service.begin(chart);
+                                                               service.set('queries', NumFetch);
+                                                               service.end();
+                                                       }
+                                               }
+                                       }
+
+                                       if(typeof resolver.qtypes !== 'undefined')
+                                               service.module.chartFromMembers(service, resolver.qtypes, 'view_resolver_qtypes_' + x, 'Bind, ' + x + ' View, Requests by Query Type', 'requests/s', 'named', 'named', netdata.chartTypes.stacked, 6000, netdata.chartAlgorithms.incremental, 1, 1);
+
+                                       //if(typeof resolver.cache !== 'undefined')
+                                       //      service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'named', 'named', netdata.chartTypes.stacked, 7000, netdata.chartAlgorithms.absolute, 1, 1);
+
+                                       if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) {
+                                               var id = 'named_' + service.name + '.view_resolver_cachehits_' + x;
+                                               var chart = named.charts[id];
+
+                                               if(typeof chart === 'undefined') {
+                                                       chart = {
+                                                               id: id,                                                                                 // the unique id of the chart
+                                                               name: '',                                                                               // the unique name of the chart
+                                                               title: service.name + ' Bind, ' + x + ' View, Resolver Cache Hits',             // the title of the chart
+                                                               units: 'operations/s',                                                          // the units of the chart dimensions
+                                                               family: 'named',                                                                // the family of the chart
+                                                               category: 'named',                                                              // the category of the chart
+                                                               type: netdata.chartTypes.area,                                  // the type of the chart
+                                                               priority: 8000,                                                                 // the priority relative to others in the same family and category
+                                                               update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart
+                                                               dimensions: {
+                                                                       'CacheHits': {
+                                                                               id: 'CacheHits',                                                        // the unique id of the dimension
+                                                                               name: 'hits',                                                           // the name of the dimension
+                                                                               algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
+                                                                               multiplier: 1,                                                          // the multiplier
+                                                                               divisor: 1,                                                                     // the divisor
+                                                                               hidden: false                                                           // is hidden (boolean)
+                                                                       },
+                                                                       'CacheMisses': {
+                                                                               id: 'CacheMisses',                                                      // the unique id of the dimension
+                                                                               name: 'misses',                                                         // the name of the dimension
+                                                                               algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
+                                                                               multiplier: -1,                                                         // the multiplier
+                                                                               divisor: 1,                                                                     // the divisor
+                                                                               hidden: false                                                           // is hidden (boolean)
+                                                                       }
+                                                               }
+                                                       };
+
+                                                       chart = service.chart(id, chart);
+                                                       named.charts[id] = chart;
+                                               }
+
+                                               service.begin(chart);
+                                               service.set('CacheHits', resolver.cachestats['CacheHits']);
+                                               service.set('CacheMisses', resolver.cachestats['CacheMisses']);
+                                               service.end();
+                                       }
+
+                                       // this is wrong, it contains many types of info:
+                                       // 1. CacheHits, CacheMisses - incremental (added above)
+                                       // 2. QueryHits, QueryMisses - incremental
+                                       // 3. DeleteLRU, DeleteTTL - incremental
+                                       // 4. CacheNodes, CacheBuckets - absolute
+                                       // 5. TreeMemTotal, TreeMemInUse - absolute
+                                       // 6. HeapMemMax, HeapMemTotal, HeapMemInUse - absolute
+                                       //if(typeof resolver.cachestats !== 'undefined')
+                                       //      service.module.chartFromMembers(service, resolver.cachestats, 'view_resolver_cachestats_' + x, 'Bind, ' + x + ' View, Cache Statistics', 'requests/s', 'named', 'named', netdata.chartTypes.line, 8000, netdata.chartAlgorithms.incremental, 1, 1);
+
+                                       //if(typeof resolver.adb !== 'undefined')
+                                       //      service.module.chartFromMembers(service, resolver.adb, 'view_resolver_adb_' + x, 'Bind, ' + x + ' View, ADB Statistics', 'entries', 'named', 'named', netdata.chartTypes.line, 8500, netdata.chartAlgorithms.absolute, 1, 1);
+                               }
+                       }
+               }
+       },
+
+       // module.serviceExecute()
+       // this function is called only from this module
+       // its purpose is to prepare the request and call
+       // netdata.serviceExecute()
+       serviceExecute: function(name, a_url, update_every) {
+               if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);
+               var service = netdata.service({
+                       name: name,
+                       request: netdata.requestFromURL(a_url),
+                       update_every: update_every,
+                       module: this
+               });
+
+               service.execute(this.processResponse);
+       },
+
+       configure: function(config) {
+               var added = 0;
+
+               if(this.enable_autodetect === true) {
+                       this.serviceExecute('local', 'http://localhost:8888/json/v1/server', this.update_every);
+                       added++;
+               }
+               
+               if(typeof(config.servers) !== 'undefined') {
+                       var len = config.servers.length;
+                       while(len--) {
+                               if(typeof config.servers[len].update_every === 'undefined')
+                                       config.servers[len].update_every = this.update_every;
+                               else
+                                       config.servers[len].update_every = config.servers[len].update_every * 1000;
+
+                               this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);
+                               added++;
+                       }
+               }
+
+               return added;
+       },
+
+       // module.update()
+       // this is called repeatidly to collect data, by calling
+       // netdata.serviceExecute()
+       update: function(service, callback) {
+               service.execute(function(serv, data) {
+                       service.module.processResponse(serv, data);
+                       callback();
+               });
+       },
+};
+
+module.exports = named;
index 52d3b2bcc7e9bdbb105300fb65a43246bd320c22..eeccdf7decc021d127bef496d27bf91679ad21f5 100755 (executable)
@@ -63,6 +63,13 @@ var netdata = {
        modules_configuring: 0,\r
        charts: {},\r
 \r
+\r
+       processors: {\r
+               http: {\r
+\r
+               },\r
+       },\r
+\r
        stringify: function(obj) {\r
                return util.inspect(obj, {depth: 10});\r
        },\r
@@ -86,276 +93,388 @@ var netdata = {
                console.log(msg.toString());\r
        },\r
 \r
-       serviceAdd: function(service) {\r
-               if(netdata.serviceIsInitialized(service) === false)\r
-                       netdata.serviceInit(service);\r
-\r
-               if(service.added !== true) {\r
-                       service.updates = 0;\r
-                       service.enabled = true;\r
-                       service.added = true;\r
-                       service.running = false;\r
-                       service.started = 0;\r
-                       service.ended = 0;\r
-                       service._current_chart = null; // the current chart we work on\r
-                       service._queue = '';\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
+       service: function(service) {\r
+               if(typeof service === 'undefined')\r
+                       service = {};\r
 \r
-                               chart._created = true;\r
-                               chart._updated = false;\r
-                       };\r
+               service._current_chart = null;  // the current chart we work on\r
+               service._queue = '';                    // data to be sent to netdata\r
 \r
-                       // begin data collection for a chart\r
-                       service.begin = function(chart) {\r
-                               if(this._current_chart !== null && this._current_chart !== chart) {\r
-                                       netdata.serviceError(this, 'Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.');\r
-                                       this.end();\r
-                               }\r
+               service.error_reported = false; // error log flood control\r
 \r
-                               if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) {\r
-                                       netdata.serviceError(this, 'Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.');\r
-                                       return false;\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
 \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
+               if(typeof service.update_every === 'undefined')\r
+                       service.update_every = service.module.update_every;\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
+               if(typeof service.update_every === 'undefined')\r
+                       service.update_every = netdata.options.update_every;\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 netdata.serviceError(this, 'Called begin() for chart ' + chart.id + ' which is empty.');\r
+               if(typeof service.processor === 'undefined')\r
+                       service.processor = netdata.processors.http;\r
 \r
-                               this._current_chart._last_updated = now;\r
-                               this._current_chart._began = true;\r
-                               this._current_chart._counter++;\r
+               service.commit = function() {\r
+                       if(this.added !== true) {\r
+                               this.added = true;\r
+                               netdata.services.push(this);\r
+                               if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.');\r
+                       }\r
+               };\r
 \r
-                               return true;\r
-                       };\r
+               service._processResponse = function(response, callback) {\r
+                       this.running = false;\r
+                       this.ended = new Date().getTime();\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
-                                       netdata.serviceError(this, 'Called set(' + dimension + ', ' + value + ') without an open chart.');\r
-                                       return false;\r
-                               }\r
+                       if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': processing response (received it in ' + (this.ended - this.started).toString() + ' ms)');\r
 \r
-                               if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') {\r
-                                       netdata.serviceError(this, 'Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".');\r
-                                       return false;\r
+                       callback(this, response);\r
+\r
+                       this.module.active--;\r
+                       if(this.module.active < 0) {\r
+                               this.module.active = 0;\r
+                               if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': active module counter below zero.');\r
+                       }\r
+\r
+                       if(this.module.active === 0) {\r
+                               // check if we run under configure\r
+                               if(this.module.configure_callback !== null) {\r
+                                       if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': configuration finish callback called from processResponse().');\r
+                                       var ccallback = this.module.configure_callback;\r
+                                       this.module.configure_callback = null;\r
+                                       ccallback();\r
                                }\r
+                       }\r
+               };\r
 \r
-                               if(typeof value === 'undefined' || value === null)\r
-                                       return false;\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(this._current_chart._dimensions_count !== 0)\r
-                                       this.queue('SET ' + dimension + ' = ' + value);\r
+                       if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': making request: ' + netdata.stringify(this.request));\r
 \r
-                               return true;\r
-                       };\r
+                       var req = http.request(this.request, function(response) {\r
+                               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...');\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
-                                       netdata.serviceError(this, 'Called end() without an open chart.');\r
-                                       return false;\r
-                               }\r
+                               var end = false;\r
+                               var data = '';\r
+                               response.setEncoding('utf8');\r
 \r
-                               if(this._current_chart._dimensions_count !== 0) {\r
-                                       this.queue('END');\r
-                                       netdata.send(this._queue);\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
+                                               service._processResponse(null, callback);\r
+                                       }\r
                                }\r
 \r
-                               this._queue = '';\r
-                               this._current_chart._began = false;\r
-                               if(netdata.options.DEBUG === true) netdata.debug('committed 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
-                                       netdata.serviceError(this, 'Called flush() without an open chart.');\r
-                                       return false;\r
-                               }\r
+                               response.on('data', function(chunk) {\r
+                                       if(end === false) data += chunk;\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: false,\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: id,\r
-                                               category: id,\r
-                                               type: netdata.chartTypes.line,\r
-                                               priority: 0,\r
-                                               update_every: netdata.options.update_every,\r
-                                               dimensions: {}\r
-                                       };\r
-                               }\r
+                               response.on('error', function() {\r
+                                       if(end === false) {\r
+                                               service.error(': Read error, failed to get data.');\r
+                                               end = true;\r
+                                               service._processResponse(null, callback);\r
+                                       }\r
+                               });\r
+\r
+                               response.on('end', function() {\r
+                                       if(end === false) {\r
+                                               service.errorClear();\r
+                                               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');\r
+                                               end = true;\r
+                                               service._processResponse(data, callback);\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
+                               service._processResponse(null, callback);\r
+                       });\r
+\r
+                       // write data to request body\r
+                       if(typeof this.postData !== 'undefined' && this.request.method === 'POST') {\r
+                               if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': posting data: ' + this.postData);\r
+                               req.write(this.postData);\r
+                       }\r
 \r
-                               var c = netdata.charts[id];\r
+                       req.end();\r
+               };\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
+               service.update = function() {\r
+                       if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...');\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
+                       this.module.update(this, function() {\r
+                               service.ended = new Date().getTime();\r
+                               service.duration = service.ended - service.started;\r
+                               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.');\r
+                               service.running = false;\r
+                       });\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
+               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
-                               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
+               service.errorClear = function() {\r
+                       this.error_reported = false;\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
+               service.queue = function(txt) {\r
+                       this._queue += txt + '\n';\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
+               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
-                               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
+                               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
-                               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');\r
-                                       c.update_every = chart.update_every;\r
-                                       c._updated = true;\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: false,\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: id,\r
+                                       category: id,\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.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
+                       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');\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
 \r
-                               if(netdata.options.DEBUG === true) netdata.debug(netdata.charts);\r
-                               return netdata.charts[id];\r
-                       };\r
+                                       var dim = chart.dimensions[x];\r
+                                       var d = c.dimensions[x];\r
 \r
-                       this.services.push(service);\r
-                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': service added.');\r
-               }\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
-       serviceRun: function(service) {\r
-               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': starting data collection...');\r
-               service.running = true;\r
-               service.started = new Date().getTime();\r
-               service.updates++;\r
-\r
-               service.module.update(service, function() {\r
-                       service.ended = new Date().getTime();\r
-                       service.duration = service.ended - service.started;\r
-                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.');\r
-                       service.running = false;\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
@@ -369,7 +488,7 @@ var netdata = {
                        if(service.enabled === false || service.running === true) continue;\r
                        if(now - service.ended < service.update_every) continue;\r
 \r
-                       netdata.serviceRun(service);\r
+                       service.update();\r
                }\r
 \r
                setTimeout(netdata.runAllServices, 100);\r
@@ -417,118 +536,10 @@ var netdata = {
                return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET');\r
        },\r
 \r
-       processResponse: function(service, data, callback) {\r
-               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': processing response...');\r
-\r
-               callback(service, data);\r
-\r
-               service.module.running--;\r
-               if(service.module.running <= 0) {\r
-                       service.module.running = 0;\r
-\r
-                       // check if we run under configure\r
-                       if(service.module.configure_callback !== null) {\r
-                               if(netdata.options.DEBUG === true) this.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
-       serviceError: function(service, message) {\r
-               if(service.error_reported === false) {\r
-                       netdata.error(service.module.name + ': ' + service.name + ': ' + message);\r
-                       service.error_reported = true;\r
-               }\r
-               else if(netdata.options.DEBUG === true)\r
-                       netdata.debug(service.module.name + ': ' + service.name + ': ' + message);\r
-       },\r
-\r
-       serviceErrorClear: function(service) {\r
-               service.error_reported = false;\r
-       },\r
-\r
-       serviceInit: function(service) {\r
-               service.error_reported = false;\r
-               service.added = false;\r
-               service.enabled = true;\r
-       },\r
-\r
-       serviceIsInitialized: function(service) {\r
-               if(typeof service.error_reported === 'undefined')\r
-                       return false;\r
-\r
-               return true;\r
-       },\r
-\r
-       getResponse: function(service, response, callback) {\r
-               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got response...');\r
-\r
-               var end = false;\r
-               var data = '';\r
-               response.setEncoding('utf8');\r
-\r
-               if(response.statusCode !== 200) {\r
-                       if(end === false) {\r
-                               netdata.serviceError(service, ': got HTTP code ' + response.statusCode + ', failed to get data.');\r
-                               end = true;\r
-                               netdata.processResponse(service, null, callback);\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
-                               netdata.serviceError(service, ': Read error, failed to get data.');\r
-                               end = true;\r
-                               netdata.processResponse(service, null, callback);\r
-                       }\r
-               });\r
-\r
-               response.on('end', function() {\r
-                       if(end === false) {\r
-                               netdata.serviceErrorClear(service);\r
-                               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');\r
-                               end = true;\r
-                               netdata.processResponse(service, data, callback);\r
-                       }\r
-               });\r
-       },\r
-\r
-       serviceExecute: function(service, callback) {\r
-               if(netdata.serviceIsInitialized(service) === false)\r
-                       netdata.serviceInit(service);\r
-\r
-               service.module.running++;\r
-\r
-               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making request: ' + netdata.stringify(service.request));\r
-               var req = http.request(service.request, function(response) {\r
-                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': request done.');\r
-                       netdata.getResponse(service, response, callback);\r
-               });\r
-\r
-               req.on('error', function(e) {\r
-                       netdata.serviceError(service, ': failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);\r
-                       netdata.processResponse(service, null, callback);\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
        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.running = 0;\r
+               module.active = 0;\r
                module.update_every = this.options.update_every;\r
 \r
                if(typeof config.update_every !== 'undefined')\r
@@ -553,24 +564,7 @@ var netdata = {
 \r
                return added;\r
        }\r
-\r
 };\r
 \r
 if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename);\r
 module.exports = netdata;\r
-\r
-/*\r
-var test1 = netdata.chart('test1', { name: 'test name', dimensions: { dim1: {}}});\r
-netdata.begin(test1);\r
-netdata.set('dim1', 1);\r
-netdata.end();\r
-netdata.begin(test1);\r
-netdata.set('dim1', 2);\r
-netdata.end();\r
-netdata.begin(test1);\r
-netdata.set('dim1', 3);\r
-netdata.end();\r
-netdata.begin(test1);\r
-netdata.set('dim1', 4);\r
-netdata.end();\r
-*/\r
index 556c39db6686af35f9bb71762f7efacee2868153..72f998b4b016dfa85fc4488ac63042710c3fa89a 100755 (executable)
@@ -70,7 +70,7 @@ var webbox = {
 
                        // add the service
                        if(found > 0 && service.added !== true)
-                               netdata.serviceAdd(service);
+                               service.commit();
 
                        // Grid Current Power Chart
                        if(d['GriPwr'].value !== null) {
@@ -190,17 +190,17 @@ var webbox = {
        serviceExecute: function(name, hostname, update_every) {
                if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': hostname: ' + hostname + ', update_every: ' + update_every);
 
-               var service = {
+               var service = netdata.service({
                        name: name,
                        request: netdata.requestFromURL('http://' + hostname + '/rpc'),
                        update_every: update_every,
                        module: this
-               };
+               });
                service.postData = 'RPC={"proc":"GetPlantOverview","format":"JSON","version":"1.0","id":"1"}';
                service.request.method = 'POST';
                service.request.headers['Content-Length'] = service.postData.length;
 
-               netdata.serviceExecute(service, this.processResponse);
+               service.execute(this.processResponse);
        },
 
        configure: function(config) {
@@ -229,7 +229,7 @@ var webbox = {
        // this is called repeatidly to collect data, by calling
        // netdata.serviceExecute()
        update: function(service, callback) {
-               netdata.serviceExecute(service, function(serv, data) {
+               service.execute(function(serv, data) {
                        service.module.processResponse(serv, data);
                        callback();
                });
index 304b7aeb3317f0e726f79cd1bba5be115265d979..13ac7aa8824394ea593c5b2773904a39d14fcc51 100755 (executable)
@@ -97,43 +97,63 @@ function dumpError(err) {
 
 // --------------------------------------------------------------------------------------------------------------------
 // get command line arguments
+{
+       var found_myself = false;
+       var found_number = false;
+       var found_modules = false;
+       process.argv.forEach(function (val, index, array) {
+               netdata.debug('PARAM: ' + val);
+
+               if(!found_myself) {
+                       if(val === __filename)
+                               found_myself = true;
+               }
+               else {
+                       switch(val) {
+                               case 'debug':
+                                       netdata.options.DEBUG = true;
+                                       netdata.debug('DEBUG enabled');
+                                       break;
+
+                               default:
+                                       if(found_number === true) {
+                                               if(found_modules === false) {
+                                                       for(var i in netdata.options.modules)
+                                                               netdata.options.modules[i].enabled = false;
+                                               }
 
-var found_myself = false;
-process.argv.forEach(function (val, index, array) {
-       netdata.debug('PARAM: ' + val);
+                                               if(typeof netdata.options.modules[val] === 'undefined')
+                                                       netdata.options.modules[val] = {};
 
-       if(!found_myself) {
-               if(val === __filename)
-                       found_myself = true;
-       }
-       else {
-               switch(val) {
-                       case 'debug':
-                               netdata.options.DEBUG = true;
-                               netdata.debug('DEBUG enabled');
-                               break;
-
-                       default:
-                               try {
-                                       var x = parseInt(val);
-                                       if(x > 0) {
-                                               netdata.options.update_every = x * 1000;
-                                               if(netdata.options.update_every < NETDATA_UPDATE_EVERY) {
-                                                       netdata.options.update_every = NETDATA_UPDATE_EVERY;
-                                                       netdata.debug('Update frequency ' + x + 's is too low');
+                                               netdata.options.modules[val].enabled = true;
+                                               netdata.options.modules_enable_all = false;
+                                               netdata.debug('enabled module ' + val);
+                                       }
+                                       else {
+                                               try {
+                                                       var x = parseInt(val);
+                                                       if(x > 0) {
+                                                               netdata.options.update_every = x * 1000;
+                                                               if(netdata.options.update_every < NETDATA_UPDATE_EVERY) {
+                                                                       netdata.options.update_every = NETDATA_UPDATE_EVERY;
+                                                                       netdata.debug('Update frequency ' + x + 's is too low');
+                                                               }
+
+                                                               found_number = true;
+                                                               netdata.debug('Update frequency set to ' + netdata.options.update_every + ' ms');
+                                                       }
+                                                       else netdata.error('Ignoring parameter: ' + val);
+                                               }
+                                               catch(e) {
+                                                       netdata.error('Cannot get value of parameter: ' + val);
+                                                       dumpError(e);
                                                }
-
-                                               netdata.debug('Update frequency set to ' + netdata.options.update_every + ' ms');
                                        }
-                                       else netdata.error('Ignoring parameter: ' + val);
-                               }
-                               catch(e) {
-                                       netdata.error('Cannot get value of parameter: ' + val);
-                                       dumpError(e);
-                               }
+                                       break;
+                       }
                }
-       }
-});
+       });
+}
 
 if(netdata.options.update_every < 1000) {
        netdata.debug('Adjusting update frequency to 1 second');