]> arthur.barton.de Git - netdata.git/blob - node.d/named.node.js
updated configs.signatures
[netdata.git] / node.d / named.node.js
1 'use strict';
2
3 // collect statistics from bind (named) v9.10+
4 //
5 // bind statistics documentation at:
6 // http://jpmens.net/2013/03/18/json-in-bind-9-s-statistics-server/
7 // https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics
8
9 // example configuration in /etc/netdata/node.d/named.conf
10 // the module supports auto-detection if bind is running at localhost
11
12 /*
13 {
14     "enable_autodetect": true,
15     "update_every": 5,
16     "servers": [
17         {
18             "name": "bind1",
19             "url": "http://127.0.0.1:8888/json/v1/server",
20             "update_every": 1
21         },
22         {
23             "name": "bind2",
24             "url": "http://10.0.0.1:8888/xml/v3/server",
25             "update_every": 2
26         }
27     ]
28 }
29 */
30
31 // the following is the bind named.conf configuration required
32
33 /*
34 statistics-channels {
35         inet 127.0.0.1 port 8888 allow { 127.0.0.1; };
36 };
37 */
38
39 var url = require('url');
40 var http = require('http');
41 var XML = require('pixl-xml');
42 var netdata = require('netdata');
43
44 if(netdata.options.DEBUG === true) netdata.debug('loaded', __filename, 'plugin');
45
46 var named = {
47     name: __filename,
48     enable_autodetect: true,
49     update_every: 1,
50     base_priority: 60000,
51     charts: {},
52
53     chartFromMembersCreate: function(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
54         var chart = {
55             id: id,                                         // the unique id of the chart
56             name: '',                                       // the unique name of the chart
57             title: service.name + ' ' + title_suffix,       // the title of the chart
58             units: units,                                   // the units of the chart dimensions
59             family: family,                                 // the family of the chart
60             context: context,                               // the context of the chart
61             type: type,                                     // the type of the chart
62             priority: priority,                             // the priority relative to others in the same family
63             update_every: service.update_every,             // the expected update frequency of the chart
64             dimensions: {}
65         };
66
67         var found = 0;
68         var dims = Object.keys(obj);
69         var len = dims.length;
70         for(var i = 0; i < len ;i++) {
71             var x = dims[i];
72
73             if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {
74                 found++;
75                 chart.dimensions[x] = {
76                     id: x,                  // the unique id of the dimension
77                     name: x,                // the name of the dimension
78                     algorithm: algorithm,   // the id of the netdata algorithm
79                     multiplier: multiplier, // the multiplier
80                     divisor: divisor,       // the divisor
81                     hidden: false           // is hidden (boolean)
82                 }
83             }
84         }
85
86         if(found === false)
87             return null;
88
89         chart = service.chart(id, chart);
90         this.charts[id] = chart;
91         return chart;
92     },
93
94     chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
95         var id = 'named_' + service.name + '.' + id_suffix;
96         var chart = this.charts[id];
97         var dims, len, x, i;
98
99         if(typeof chart === 'undefined') {
100             chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
101             if(chart === null) return false;
102         }
103         else {
104             // check if we need to re-generate the chart
105             dims = Object.keys(obj);
106             len = dims.length;
107             for(i = 0; i < len ;i++) {
108                 x = dims[i];
109                 if(typeof(chart.dimensions[x]) === 'undefined') {
110                     chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
111                     if(chart === null) return false;
112                     break;
113                 }
114             }
115         }
116
117         service.begin(chart);
118
119         var found = 0;
120         dims = Object.keys(obj);
121         len = dims.length;
122         for(i = 0; i < len ;i++) {
123             x = dims[i];
124             if(typeof(chart.dimensions[x]) !== 'undefined') {
125                 found++;
126                 service.set(x, obj[x]);
127             }
128         }
129
130         service.end();
131
132         return (found > 0);
133     },
134
135     // an index to map values to different charts
136     lookups: {
137         nsstats: {},
138         resolver_stats: {},
139         numfetch: {}
140     },
141
142     // transform the XML response of bind
143     // to the JSON response of bind
144     xml2js: function(service, data_xml) {
145         var d = XML.parse(data_xml);
146         if(d === null) return null;
147
148         var a, aa, alen, alen2;
149
150         var data = {};
151         var len = d.server.counters.length;
152         while(len--) {
153             a = d.server.counters[len];
154             if(typeof a.counter === 'undefined') continue;
155             if(a.type === 'opcode') a.type = 'opcodes';
156             else if(a.type === 'qtype') a.type = 'qtypes';
157             else if(a.type === 'nsstat') a.type = 'nsstats';
158             aa = data[a.type] = {};
159             alen = 0;
160             alen2 = a.counter.length;
161             while(alen < alen2) {
162                 aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data, 10);
163                 alen++;
164             }
165         }
166
167         data.views = {};
168         var vlen = d.views.view.length;
169         while(vlen--) {
170             var vname = d.views.view[vlen].name;
171             data.views[vname] = { resolver: {} };
172             len = d.views.view[vlen].counters.length;
173             while(len--) {
174                 a = d.views.view[vlen].counters[len];
175                 if(typeof a.counter === 'undefined') continue;
176                 if(a.type === 'resstats') a.type = 'stats';
177                 else if(a.type === 'resqtype') a.type = 'qtypes';
178                 else if(a.type === 'adbstat') a.type = 'adb';
179                 aa = data.views[vname].resolver[a.type] = {};
180                 alen = 0;
181                 alen2 = a.counter.length;
182                 while(alen < alen2) {
183                     aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data, 10);
184                     alen++;
185                 }
186             }
187         }
188
189         return data;
190     },
191
192     processResponse: function(service, data) {
193         if(data !== null) {
194             var r, x, look, id, chart, keys, len;
195
196             // parse XML or JSON
197             // pepending on the URL given
198             if(service.request.path.match(/^\/xml/) !== null)
199                 r = named.xml2js(service, data);
200             else
201                 r = JSON.parse(data);
202
203             if(typeof r === 'undefined' || r === null) {
204                 netdata.serviceError(service, "Cannot parse these data: " + data);
205                 return;
206             }
207
208             if(service.added !== true)
209                 service.commit();
210
211             if(typeof r.nsstats !== 'undefined') {
212                 // we split the nsstats object to several others
213                 var global_requests = {}, global_requests_enable = false;
214                 var global_failures = {}, global_failures_enable = false;
215                 var global_failures_detail = {}, global_failures_detail_enable = false;
216                 var global_updates = {}, global_updates_enable = false;
217                 var protocol_queries = {}, protocol_queries_enable = false;
218                 var global_queries = {}, global_queries_enable = false;
219                 var global_queries_success = {}, global_queries_success_enable = false;
220                 var default_enable = false;
221                 var RecursClients = 0;
222
223                 // RecursClients is an absolute value
224                 if(typeof r.nsstats['RecursClients'] !== 'undefined') {
225                     RecursClients = r.nsstats['RecursClients'];
226                     delete r.nsstats['RecursClients'];
227                 }
228
229                 keys = Object.keys(r.nsstats);
230                 len = keys.length;
231                 while(len--) {
232                     x = keys[len];
233
234                     // we maintain an index of the values found
235                     // mapping them to objects splitted
236
237                     look = named.lookups.nsstats[x];
238                     if(typeof look === 'undefined') {
239                         // a new value, not found in the index
240                         // index it:
241                         if(x === 'Requestv4') {
242                             named.lookups.nsstats[x] = {
243                                 name: 'IPv4',
244                                 type: 'global_requests'
245                             };
246                         }
247                         else if(x === 'Requestv6') {
248                             named.lookups.nsstats[x] = {
249                                 name: 'IPv6',
250                                 type: 'global_requests'
251                             };
252                         }
253                         else if(x === 'QryFailure') {
254                             named.lookups.nsstats[x] = {
255                                 name: 'failures',
256                                 type: 'global_failures'
257                             };
258                         }
259                         else if(x === 'QryUDP') {
260                             named.lookups.nsstats[x] = {
261                                 name: 'UDP',
262                                 type: 'protocol_queries'
263                             };
264                         }
265                         else if(x === 'QryTCP') {
266                             named.lookups.nsstats[x] = {
267                                 name: 'TCP',
268                                 type: 'protocol_queries'
269                             };
270                         }
271                         else if(x === 'QrySuccess') {
272                             named.lookups.nsstats[x] = {
273                                 name: 'queries',
274                                 type: 'global_queries_success'
275                             };
276                         }
277                         else if(x.match(/QryRej$/) !== null) {
278                             named.lookups.nsstats[x] = {
279                                 name: x,
280                                 type: 'global_failures_detail'
281                             };
282                         }
283                         else if(x.match(/^Qry/) !== null) {
284                             named.lookups.nsstats[x] = {
285                                 name: x,
286                                 type: 'global_queries'
287                             };
288                         }
289                         else if(x.match(/^Update/) !== null) {
290                             named.lookups.nsstats[x] = {
291                                 name: x,
292                                 type: 'global_updates'
293                             };
294                         }
295                         else {
296                             // values not mapped, will remain
297                             // in the default map
298                             named.lookups.nsstats[x] = {
299                                 name: x,
300                                 type: 'default'
301                             };
302                         }
303
304                         look = named.lookups.nsstats[x];
305                         // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);
306                     }
307
308                     switch(look.type) {
309                         case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;
310                         case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;
311                         case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;
312                         case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;
313                         case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;
314                         case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;
315                         case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;
316                         default: default_enable = true; break;
317                     }
318                 }
319
320                 if(global_requests_enable === true)
321                     service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'requests', 'named.requests', netdata.chartTypes.stacked, named.base_priority + 1, netdata.chartAlgorithms.incremental, 1, 1);
322
323                 if(global_queries_success_enable === true)
324                     service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'queries', 'named.queries_succcess', netdata.chartTypes.line, named.base_priority + 2, netdata.chartAlgorithms.incremental, 1, 1);
325
326                 if(protocol_queries_enable === true)
327                     service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'queries', 'named.protocol_queries', netdata.chartTypes.stacked, named.base_priority + 3, netdata.chartAlgorithms.incremental, 1, 1);
328
329                 if(global_queries_enable === true)
330                     service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'queries', 'named.global_queries', netdata.chartTypes.stacked, named.base_priority + 4, netdata.chartAlgorithms.incremental, 1, 1);
331
332                 if(global_updates_enable === true)
333                     service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'updates', 'named.global_updates', netdata.chartTypes.stacked, named.base_priority + 5, netdata.chartAlgorithms.incremental, 1, 1);
334
335                 if(global_failures_enable === true)
336                     service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'failures', 'named.global_failures', netdata.chartTypes.line, named.base_priority + 6, netdata.chartAlgorithms.incremental, 1, 1);
337
338                 if(global_failures_detail_enable === true)
339                     service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'failures', 'named.global_failures_detail', netdata.chartTypes.stacked, named.base_priority + 7, netdata.chartAlgorithms.incremental, 1, 1);
340
341                 if(default_enable === true)
342                     service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'other', 'named.nsstats', netdata.chartTypes.line, named.base_priority + 8, netdata.chartAlgorithms.incremental, 1, 1);
343
344                 // RecursClients chart
345                 id = 'named_' + service.name + '.recursive_clients';
346                 chart = named.charts[id];
347
348                 if(typeof chart === 'undefined') {
349                     chart = {
350                         id: id,                                         // the unique id of the chart
351                         name: '',                                       // the unique name of the chart
352                         title: service.name + ' Bind, Current Recursive Clients',       // the title of the chart
353                         units: 'clients',                               // the units of the chart dimensions
354                         family: 'clients',                              // the family of the chart
355                         context: 'named.recursive_clients',             // the context of the chart
356                         type: netdata.chartTypes.line,                  // the type of the chart
357                         priority: named.base_priority + 1,              // the priority relative to others in the same family
358                         update_every: service.update_every,             // the expected update frequency of the chart
359                         dimensions: {
360                             'clients': {
361                                 id: 'clients',                              // the unique id of the dimension
362                                 name: '',                                   // the name of the dimension
363                                 algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
364                                 multiplier: 1,                              // the multiplier
365                                 divisor: 1,                                 // the divisor
366                                 hidden: false                               // is hidden (boolean)
367                             }
368                         }
369                     };
370
371                     chart = service.chart(id, chart);
372                     named.charts[id] = chart;
373                 }
374
375                 service.begin(chart);
376                 service.set('clients', RecursClients);
377                 service.end();
378             }
379
380             if(typeof r.opcodes !== 'undefined')
381                 service.module.chartFromMembers(service, r.opcodes, 'in_opcodes', 'Bind, Global Incoming Requests by OpCode', 'requests/s', 'requests', 'named.in_opcodes', netdata.chartTypes.stacked, named.base_priority + 9, netdata.chartAlgorithms.incremental, 1, 1);
382
383             if(typeof r.qtypes !== 'undefined')
384                 service.module.chartFromMembers(service, r.qtypes, 'in_qtypes', 'Bind, Global Incoming Requests by Query Type', 'requests/s', 'requests', 'named.in_qtypes', netdata.chartTypes.stacked, named.base_priority + 10, netdata.chartAlgorithms.incremental, 1, 1);
385
386             if(typeof r.sockstats !== 'undefined')
387                 service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'sockets', 'named.in_sockstats', netdata.chartTypes.line, named.base_priority + 11, netdata.chartAlgorithms.incremental, 1, 1);
388
389             if(typeof r.views !== 'undefined') {
390                 keys = Object.keys(r.views);
391                 len = keys.length;
392                 while(len--) {
393                     x = keys[len];
394                     var resolver = r.views[x].resolver;
395
396                     if(typeof resolver !== 'undefined') {
397                         if(typeof resolver.stats !== 'undefined') {
398                             var NumFetch = 0;
399                             var key = service.name + '.' + x;
400                             var rtt = {}, rtt_enable = false;
401                             default_enable = false;
402
403                             // NumFetch is an absolute value
404                             if(typeof resolver.stats['NumFetch'] !== 'undefined') {
405                                 named.lookups.numfetch[key] = true;
406                                 NumFetch = resolver.stats['NumFetch'];
407                                 delete resolver.stats['NumFetch'];
408                             }
409                             if(typeof resolver.stats['BucketSize'] !== 'undefined') {
410                                 delete resolver.stats['BucketSize'];
411                             }
412
413                             // split the QryRTT* from the main chart
414                             var ykeys = Object.keys(resolver.stats);
415                             var ylen = ykeys.length;
416                             while(ylen--) {
417                                 var y = ykeys[ylen];
418
419                                 // we maintain an index of the values found
420                                 // mapping them to objects splitted
421
422                                 look = named.lookups.resolver_stats[y];
423                                 if(typeof look === 'undefined') {
424                                     if(y.match(/^QryRTT/) !== null) {
425                                         named.lookups.resolver_stats[y] = {
426                                             name: y,
427                                             type: 'rtt'
428                                         };
429                                     }
430                                     else {
431                                         named.lookups.resolver_stats[y] = {
432                                             name: y,
433                                             type: 'default'
434                                         };
435                                     }
436
437                                     look = named.lookups.resolver_stats[y];
438                                     // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);
439                                 }
440
441                                 switch(look.type) {
442                                     case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;
443                                     default: default_enable = true; break;
444                                 }
445                             }
446
447                             if(rtt_enable)
448                                 service.module.chartFromMembers(service, rtt, 'view_resolver_rtt_' + x, 'Bind, ' + x + ' View, Resolver Round Trip Timings', 'queries/s', 'view_' + x, 'named.resolver_rtt', netdata.chartTypes.stacked, named.base_priority + 12, netdata.chartAlgorithms.incremental, 1, 1);
449
450                             if(default_enable)
451                                 service.module.chartFromMembers(service, resolver.stats, 'view_resolver_stats_' + x, 'Bind, ' + x + ' View, Resolver Statistics', 'operations/s', 'view_' + x, 'named.resolver_stats', netdata.chartTypes.line, named.base_priority + 13, netdata.chartAlgorithms.incremental, 1, 1);
452
453                             // NumFetch chart
454                             if(typeof named.lookups.numfetch[key] !== 'undefined') {
455                                 id = 'named_' + service.name + '.view_resolver_numfetch_' + x;
456                                 chart = named.charts[id];
457
458                                 if(typeof chart === 'undefined') {
459                                     chart = {
460                                         id: id,                                         // the unique id of the chart
461                                         name: '',                                       // the unique name of the chart
462                                         title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries',     // the title of the chart
463                                         units: 'queries',                               // the units of the chart dimensions
464                                         family: 'view_' + x,                            // the family of the chart
465                                         context: 'named.resolver_active_queries',       // the context of the chart
466                                         type: netdata.chartTypes.line,                  // the type of the chart
467                                         priority: named.base_priority + 1001,           // the priority relative to others in the same family
468                                         update_every: service.update_every,             // the expected update frequency of the chart
469                                         dimensions: {
470                                             'queries': {
471                                                 id: 'queries',                              // the unique id of the dimension
472                                                 name: '',                                   // the name of the dimension
473                                                 algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
474                                                 multiplier: 1,                              // the multiplier
475                                                 divisor: 1,                                 // the divisor
476                                                 hidden: false                               // is hidden (boolean)
477                                             }
478                                         }
479                                     };
480
481                                     chart = service.chart(id, chart);
482                                     named.charts[id] = chart;
483                                 }
484
485                                 service.begin(chart);
486                                 service.set('queries', NumFetch);
487                                 service.end();
488                             }
489                         }
490                     }
491
492                     if(typeof resolver.qtypes !== 'undefined')
493                         service.module.chartFromMembers(service, resolver.qtypes, 'view_resolver_qtypes_' + x, 'Bind, ' + x + ' View, Requests by Query Type', 'requests/s', 'view_' + x, 'named.resolver_qtypes', netdata.chartTypes.stacked, named.base_priority + 14, netdata.chartAlgorithms.incremental, 1, 1);
494
495                     //if(typeof resolver.cache !== 'undefined')
496                     //  service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'view_' + x, 'named.resolver_cache', netdata.chartTypes.stacked, named.base_priority + 15, netdata.chartAlgorithms.absolute, 1, 1);
497
498                     if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) {
499                         id = 'named_' + service.name + '.view_resolver_cachehits_' + x;
500                         chart = named.charts[id];
501
502                         if(typeof chart === 'undefined') {
503                             chart = {
504                                 id: id,                                         // the unique id of the chart
505                                 name: '',                                       // the unique name of the chart
506                                 title: service.name + ' Bind, ' + x + ' View, Resolver Cache Hits',     // the title of the chart
507                                 units: 'operations/s',                          // the units of the chart dimensions
508                                 family: 'view_' + x,                            // the family of the chart
509                                 context: 'named.resolver_cache_hits',           // the context of the chart
510                                 type: netdata.chartTypes.area,                  // the type of the chart
511                                 priority: named.base_priority + 1100,           // the priority relative to others in the same family
512                                 update_every: service.update_every,             // the expected update frequency of the chart
513                                 dimensions: {
514                                     'CacheHits': {
515                                         id: 'CacheHits',                            // the unique id of the dimension
516                                         name: 'hits',                               // the name of the dimension
517                                         algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
518                                         multiplier: 1,                              // the multiplier
519                                         divisor: 1,                                 // the divisor
520                                         hidden: false                               // is hidden (boolean)
521                                     },
522                                     'CacheMisses': {
523                                         id: 'CacheMisses',                          // the unique id of the dimension
524                                         name: 'misses',                             // the name of the dimension
525                                         algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
526                                         multiplier: -1,                             // the multiplier
527                                         divisor: 1,                                 // the divisor
528                                         hidden: false                               // is hidden (boolean)
529                                     }
530                                 }
531                             };
532
533                             chart = service.chart(id, chart);
534                             named.charts[id] = chart;
535                         }
536
537                         service.begin(chart);
538                         service.set('CacheHits', resolver.cachestats['CacheHits']);
539                         service.set('CacheMisses', resolver.cachestats['CacheMisses']);
540                         service.end();
541                     }
542
543                     // this is wrong, it contains many types of info:
544                     // 1. CacheHits, CacheMisses - incremental (added above)
545                     // 2. QueryHits, QueryMisses - incremental
546                     // 3. DeleteLRU, DeleteTTL - incremental
547                     // 4. CacheNodes, CacheBuckets - absolute
548                     // 5. TreeMemTotal, TreeMemInUse - absolute
549                     // 6. HeapMemMax, HeapMemTotal, HeapMemInUse - absolute
550                     //if(typeof resolver.cachestats !== 'undefined')
551                     //  service.module.chartFromMembers(service, resolver.cachestats, 'view_resolver_cachestats_' + x, 'Bind, ' + x + ' View, Cache Statistics', 'requests/s', 'view_' + x, 'named.resolver_cache_stats', netdata.chartTypes.line, named.base_priority + 1001, netdata.chartAlgorithms.incremental, 1, 1);
552
553                     //if(typeof resolver.adb !== 'undefined')
554                     //  service.module.chartFromMembers(service, resolver.adb, 'view_resolver_adb_' + x, 'Bind, ' + x + ' View, ADB Statistics', 'entries', 'view_' + x, 'named.resolver_adb', netdata.chartTypes.line, named.base_priority + 1002, netdata.chartAlgorithms.absolute, 1, 1);
555                 }
556             }
557         }
558     },
559
560     // module.serviceExecute()
561     // this function is called only from this module
562     // its purpose is to prepare the request and call
563     // netdata.serviceExecute()
564     serviceExecute: function(name, a_url, update_every) {
565         if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);
566         var service = netdata.service({
567             name: name,
568             request: netdata.requestFromURL(a_url),
569             update_every: update_every,
570             module: this
571         });
572
573         service.execute(this.processResponse);
574     },
575
576     configure: function(config) {
577         var added = 0;
578
579         if(this.enable_autodetect === true) {
580             this.serviceExecute('local', 'http://localhost:8888/json/v1/server', this.update_every);
581             added++;
582         }
583         
584         if(typeof(config.servers) !== 'undefined') {
585             var len = config.servers.length;
586             while(len--) {
587                 if(typeof config.servers[len].update_every === 'undefined')
588                     config.servers[len].update_every = this.update_every;
589
590                 this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);
591                 added++;
592             }
593         }
594
595         return added;
596     },
597
598     // module.update()
599     // this is called repeatidly to collect data, by calling
600     // netdata.serviceExecute()
601     update: function(service, callback) {
602         service.execute(function(serv, data) {
603             service.module.processResponse(serv, data);
604             callback();
605         });
606     }
607 };
608
609 module.exports = named;