]> arthur.barton.de Git - netdata.git/blob - node.d/named.node.js
added node.js plugin manager, implemented bind (named) 9.10+ JSON plugin, refactored...
[netdata.git] / node.d / named.node.js
1 'use strict';\r
2 \r
3 // collect statistics from bind (named) v9.10+\r
4 //\r
5 // bind statistics documentation at:\r
6 // https://ftp.isc.org/isc/bind/9.10.3/doc/arm/Bv9ARM.ch06.html#statistics\r
7 \r
8 // example configuration in /etc/netdata/named.conf\r
9 // the module supports auto-detection if bind is running in localhost\r
10 \r
11 /*\r
12 {\r
13         "enable_autodetect": true,\r
14         "update_every": 5,\r
15         "servers": [\r
16                 {\r
17                         "name": "bind1",\r
18                         "url": "http://127.0.0.1:8888/json/v1",\r
19                         "update_every": 1\r
20                 },\r
21                 {\r
22                         "name": "bind2",\r
23                         "url": "http://10.1.2.3:8888/json/v1",\r
24                         "update_every": 2\r
25                 }\r
26         ]\r
27 }\r
28 */\r
29 \r
30 // the following is the bind named.conf configuration required\r
31 \r
32 /*\r
33 statistics-channels {\r
34         inet 127.0.0.1 port 8888 allow { 127.0.0.1; };\r
35 };\r
36 */\r
37 \r
38 var url = require('url');\r
39 var http = require('http');\r
40 var netdata = require('netdata');\r
41 \r
42 if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');\r
43 \r
44 var named = {\r
45         name: __filename,\r
46         enable_autodetect: true,\r
47         update_every: 1000,\r
48 \r
49         charts: {},\r
50 \r
51         chartFromMembersCreate: function(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor) {\r
52                 var chart = {\r
53                         id: id,                                                                                 // the unique id of the chart\r
54                         name: '',                                                                               // the unique name of the chart\r
55                         title: service.name + ' ' + title_suffix,               // the title of the chart\r
56                         units: units,                                                                   // the units of the chart dimensions\r
57                         family: family_prefix + '_' + service.name,             // the family of the chart\r
58                         category: category_prefix + '_' + service.name, // the category of the chart\r
59                         type: type,                                                                             // the type of the chart\r
60                         priority: priority,                                                             // the priority relative to others in the same family and category\r
61                         update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
62                         dimensions: {}\r
63                 }\r
64 \r
65                 var found = 0;\r
66                 for(var x in obj) {\r
67                         if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {\r
68                                 found++;\r
69                                 chart.dimensions[x] = {\r
70                                         id: x,                                  // the unique id of the dimension\r
71                                         name: x,                                // the name of the dimension\r
72                                         algorithm: algorithm,   // the id of the netdata algorithm\r
73                                         multiplier: multiplier, // the multiplier\r
74                                         divisor: divisor,               // the divisor\r
75                                         hidden: false                   // is hidden (boolean)\r
76                                 }\r
77                         }\r
78                 }\r
79 \r
80                 if(found === false)\r
81                         return null;\r
82 \r
83                 chart = service.chart(id, chart);\r
84                 this.charts[id] = chart;\r
85                 return chart;\r
86         },\r
87 \r
88         chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor) {\r
89                 var id = 'named_' + service.name + '.' + id_suffix;\r
90                 var chart = this.charts[id];\r
91 \r
92                 if(typeof chart === 'undefined') {\r
93                         chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor);\r
94                         if(chart === null) return false;\r
95                 }\r
96                 else {\r
97                         // check if we need to re-generate the chart\r
98                         for(var x in obj) {\r
99                                 if(typeof(chart.dimensions[x]) === 'undefined') {\r
100                                         chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family_prefix, category_prefix, type, priority, algorithm, multiplier, divisor);\r
101                                         if(chart === null) return false;\r
102                                         break;\r
103                                 }\r
104                         }\r
105                 }\r
106 \r
107                 var found = 0;\r
108                 service.begin(chart);\r
109                 for(var x in obj) {\r
110                         if(typeof(chart.dimensions[x]) !== 'undefined') {\r
111                                 found++;\r
112                                 service.set(x, obj[x]);\r
113                         }\r
114                 }\r
115                 service.end();\r
116 \r
117                 if(found > 0) return true;\r
118                 return false;\r
119         },\r
120 \r
121         // an index to map values to different charts\r
122         lookups: {\r
123                 nsstats: {},\r
124                 resolver_stats: {},\r
125                 numfetch: {}\r
126         },\r
127 \r
128         processResponse: function(service, data) {\r
129                 if(data !== null) {\r
130                         var r = JSON.parse(data);\r
131 \r
132                         if(service.added !== true)\r
133                                 netdata.serviceAdd(service);\r
134 \r
135                         if(typeof r.nsstats !== 'undefined') {\r
136                                 // we split the nsstats object to several others\r
137                                 var global_requests = {}, global_requests_enable = false;\r
138                                 var global_failures = {}, global_failures_enable = false;\r
139                                 var global_failures_detail = {}, global_failures_detail_enable = false;\r
140                                 var global_updates = {}, global_updates_enable = false;\r
141                                 var protocol_queries = {}, protocol_queries_enable = false;\r
142                                 var global_queries = {}, global_queries_enable = false;\r
143                                 var global_queries_success = {}, global_queries_success_enable = false;\r
144                                 var default_enable = false;\r
145                                 var RecursClients = 0;\r
146 \r
147                                 // RecursClients is an absolute value\r
148                                 if(typeof r.nsstats['RecursClients'] !== 'undefined') {\r
149                                         RecursClients = r.nsstats['RecursClients'];\r
150                                         delete r.nsstats['RecursClients'];\r
151                                 }\r
152 \r
153                                 for( var x in r.nsstats ) {\r
154                                         // we maintain an index of the values found\r
155                                         // mapping them to objects splitted\r
156 \r
157                                         var look = named.lookups.nsstats[x];\r
158                                         if(typeof look === 'undefined') {\r
159                                                 // a new value, not found in the index\r
160                                                 // index it:\r
161                                                 if(x === 'Requestv4') {\r
162                                                         named.lookups.nsstats[x] = {\r
163                                                                 name: 'IPv4',\r
164                                                                 type: 'global_requests'\r
165                                                         };\r
166                                                 }\r
167                                                 else if(x === 'Requestv6') {\r
168                                                         named.lookups.nsstats[x] = {\r
169                                                                 name: 'IPv6',\r
170                                                                 type: 'global_requests'\r
171                                                         };\r
172                                                 }\r
173                                                 else if(x === 'QryFailure') {\r
174                                                         named.lookups.nsstats[x] = {\r
175                                                                 name: 'failures',\r
176                                                                 type: 'global_failures'\r
177                                                         };\r
178                                                 }\r
179                                                 else if(x === 'QryUDP') {\r
180                                                         named.lookups.nsstats[x] = {\r
181                                                                 name: 'UDP',\r
182                                                                 type: 'protocol_queries'\r
183                                                         };\r
184                                                 }\r
185                                                 else if(x === 'QryTCP') {\r
186                                                         named.lookups.nsstats[x] = {\r
187                                                                 name: 'TCP',\r
188                                                                 type: 'protocol_queries'\r
189                                                         };\r
190                                                 }\r
191                                                 else if(x === 'QrySuccess') {\r
192                                                         named.lookups.nsstats[x] = {\r
193                                                                 name: 'queries',\r
194                                                                 type: 'global_queries_success'\r
195                                                         };\r
196                                                 }\r
197                                                 else if(x.match(/QryRej$/) !== null) {\r
198                                                         named.lookups.nsstats[x] = {\r
199                                                                 name: x,\r
200                                                                 type: 'global_failures_detail'\r
201                                                         };\r
202                                                 }\r
203                                                 else if(x.match(/^Qry/) !== null) {\r
204                                                         named.lookups.nsstats[x] = {\r
205                                                                 name: x,\r
206                                                                 type: 'global_queries'\r
207                                                         };\r
208                                                 }\r
209                                                 else if(x.match(/^Update/) !== null) {\r
210                                                         named.lookups.nsstats[x] = {\r
211                                                                 name: x,\r
212                                                                 type: 'global_updates'\r
213                                                         };\r
214                                                 }\r
215                                                 else {\r
216                                                         // values not mapped, will remain\r
217                                                         // in the default map\r
218                                                         named.lookups.nsstats[x] = {\r
219                                                                 name: x,\r
220                                                                 type: 'default'\r
221                                                         };\r
222                                                 }\r
223 \r
224                                                 look = named.lookups.nsstats[x];\r
225                                                 // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);\r
226                                         }\r
227 \r
228                                         switch(look.type) {\r
229                                                 case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;\r
230                                                 case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;\r
231                                                 case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;\r
232                                                 case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;\r
233                                                 case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;\r
234                                                 case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;\r
235                                                 case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;\r
236                                                 default: default_enable = true; break;\r
237                                         }\r
238                                 }\r
239 \r
240                                 if(global_requests_enable == true)\r
241                                         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
242 \r
243                                 if(global_queries_success_enable == true)\r
244                                         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
245 \r
246                                 if(protocol_queries_enable == true)\r
247                                         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
248 \r
249                                 if(global_queries_enable == true)\r
250                                         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
251 \r
252                                 if(global_updates_enable == true)\r
253                                         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
254 \r
255                                 if(global_failures_enable == true)\r
256                                         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
257 \r
258                                 if(global_failures_detail_enable == true)\r
259                                         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
260 \r
261                                 if(default_enable === true)\r
262                                         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
263 \r
264                                 // RecursClients chart\r
265                                 {\r
266                                         var id = 'named_' + service.name + '.recursive_clients';\r
267                                         var chart = named.charts[id];\r
268 \r
269                                         if(typeof chart === 'undefined') {\r
270                                                 chart = {\r
271                                                         id: id,                                                                                 // the unique id of the chart\r
272                                                         name: '',                                                                               // the unique name of the chart\r
273                                                         title: service.name + ' Bind, Current Recursive Clients',               // the title of the chart\r
274                                                         units: 'clients',                                                               // the units of the chart dimensions\r
275                                                         family: 'named',                                                                // the family of the chart\r
276                                                         category: 'named',                                                              // the category of the chart\r
277                                                         type: netdata.chartTypes.line,                                  // the type of the chart\r
278                                                         priority: 150,                                                                  // the priority relative to others in the same family and category\r
279                                                         update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
280                                                         dimensions: {\r
281                                                                 'clients': {\r
282                                                                         id: 'clients',                                                          // the unique id of the dimension\r
283                                                                         name: '',                                                                       // the name of the dimension\r
284                                                                         algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm\r
285                                                                         multiplier: 1,                                                          // the multiplier\r
286                                                                         divisor: 1,                                                                     // the divisor\r
287                                                                         hidden: false                                                           // is hidden (boolean)\r
288                                                                 }\r
289                                                         }\r
290                                                 };\r
291 \r
292                                                 chart = service.chart(id, chart);\r
293                                                 named.charts[id] = chart;\r
294                                         }\r
295 \r
296                                         service.begin(chart);\r
297                                         service.set('clients', RecursClients);\r
298                                         service.end();\r
299                                 }\r
300                         }\r
301 \r
302                         if(typeof r.opcodes !== 'undefined')\r
303                                 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
304 \r
305                         if(typeof r.qtypes !== 'undefined')\r
306                                 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
307 \r
308                         if(typeof r.views !== 'undefined') {\r
309                                 for( var x in r.views ) {\r
310                                         var resolver = r.views[x].resolver;\r
311 \r
312                                         if(typeof resolver !== 'undefined') {\r
313                                                 if(typeof resolver.stats !== 'undefined') {\r
314                                                         var NumFetch = 0;\r
315                                                         var key = service.name + '.' + x;\r
316                                                         var default_enable = false;\r
317                                                         var rtt = {}, rtt_enable = false;\r
318 \r
319                                                         // NumFetch is an absolute value\r
320                                                         if(typeof resolver.stats['NumFetch'] !== 'undefined') {\r
321                                                                 named.lookups.numfetch[key] = true;\r
322                                                                 NumFetch = resolver.stats['NumFetch'];\r
323                                                                 delete resolver.stats['NumFetch'];\r
324                                                         }\r
325                                                         if(typeof resolver.stats['BucketSize'] !== 'undefined') {\r
326                                                                 delete resolver.stats['BucketSize'];\r
327                                                         }\r
328 \r
329                                                         // split the QryRTT* from the main chart\r
330                                                         for( var y in resolver.stats ) {\r
331                                                                 // we maintain an index of the values found\r
332                                                                 // mapping them to objects splitted\r
333 \r
334                                                                 var look = named.lookups.resolver_stats[y];\r
335                                                                 if(typeof look === 'undefined') {\r
336                                                                         if(y.match(/^QryRTT/) !== null) {\r
337                                                                                 named.lookups.resolver_stats[y] = {\r
338                                                                                         name: y,\r
339                                                                                         type: 'rtt'\r
340                                                                                 };\r
341                                                                         }\r
342                                                                         else {\r
343                                                                                 named.lookups.resolver_stats[y] = {\r
344                                                                                         name: y,\r
345                                                                                         type: 'default'\r
346                                                                                 };\r
347                                                                         }\r
348 \r
349                                                                         look = named.lookups.resolver_stats[y];\r
350                                                                         // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);\r
351                                                                 }\r
352 \r
353                                                                 switch(look.type) {\r
354                                                                         case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;\r
355                                                                         default: default_enable = true; break;\r
356                                                                 }\r
357                                                         }\r
358 \r
359                                                         if(rtt_enable)\r
360                                                                 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
361 \r
362                                                         if(default_enable)\r
363                                                                 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
364 \r
365                                                         // NumFetch chart\r
366                                                         if(typeof named.lookups.numfetch[key] !== 'undefined') {\r
367                                                                 var id = 'named_' + service.name + '.view_resolver_numfetch_' + x;\r
368                                                                 var chart = named.charts[id];\r
369 \r
370                                                                 if(typeof chart === 'undefined') {\r
371                                                                         chart = {\r
372                                                                                 id: id,                                                                                 // the unique id of the chart\r
373                                                                                 name: '',                                                                               // the unique name of the chart\r
374                                                                                 title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries',         // the title of the chart\r
375                                                                                 units: 'queries',                                                               // the units of the chart dimensions\r
376                                                                                 family: 'named',                                                                // the family of the chart\r
377                                                                                 category: 'named',                                                              // the category of the chart\r
378                                                                                 type: netdata.chartTypes.line,                                  // the type of the chart\r
379                                                                                 priority: 5000,                                                                 // the priority relative to others in the same family and category\r
380                                                                                 update_every: Math.round(service.update_every / 1000), // the expected update frequency of the chart\r
381                                                                                 dimensions: {\r
382                                                                                         'queries': {\r
383                                                                                                 id: 'queries',                                                          // the unique id of the dimension\r
384                                                                                                 name: '',                                                                       // the name of the dimension\r
385                                                                                                 algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm\r
386                                                                                                 multiplier: 1,                                                          // the multiplier\r
387                                                                                                 divisor: 1,                                                                     // the divisor\r
388                                                                                                 hidden: false                                                           // is hidden (boolean)\r
389                                                                                         }\r
390                                                                                 }\r
391                                                                         };\r
392 \r
393                                                                         chart = service.chart(id, chart);\r
394                                                                         named.charts[id] = chart;\r
395                                                                 }\r
396 \r
397                                                                 service.begin(chart);\r
398                                                                 service.set('queries', NumFetch);\r
399                                                                 service.end();\r
400                                                         }\r
401                                                 }\r
402                                                 \r
403                                                 if(typeof resolver.qtypes !== 'undefined')\r
404                                                         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
405                                         }\r
406                                 }\r
407                         }\r
408                 }\r
409         },\r
410 \r
411         // module.serviceExecute()\r
412         // this function is called only from this module\r
413         // its purpose is to prepare the request and call\r
414         // netdata.serviceExecute()\r
415         serviceExecute: function(name, a_url, update_every) {\r
416                 if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);\r
417                 netdata.serviceExecute({\r
418                         name: name,\r
419                         request: netdata.requestFromURL(a_url),\r
420                         update_every: update_every,\r
421                         added: false,\r
422                         enabled: true,\r
423                         module: this\r
424                 }, this.processResponse);\r
425         },\r
426 \r
427         configure: function(config) {\r
428                 var added = 0;\r
429 \r
430                 if(this.enable_autodetect === true) {\r
431                         this.serviceExecute('local', 'http://localhost:8888/json/v1', this.update_every);\r
432                         added++;\r
433                 }\r
434                 \r
435                 if(typeof(config.servers) !== 'undefined') {\r
436                         var len = config.servers.length;\r
437                         while(len--) {\r
438                                 if(typeof config.servers[len].update_every === 'undefined')\r
439                                         config.servers[len].update_every = this.update_every;\r
440                                 else\r
441                                         config.servers[len].update_every = config.servers[len].update_every * 1000;\r
442 \r
443                                 this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);\r
444                                 added++;\r
445                         }\r
446                 }\r
447 \r
448                 return added;\r
449         },\r
450 \r
451         // module.update()\r
452         // this is called repeatidly to collect data, by calling\r
453         // netdata.serviceExecute()\r
454         update: function(service, callback) {\r
455                 netdata.serviceExecute(service, function(serv, data) {\r
456                         service.module.processResponse(serv, data);\r
457                         callback();\r
458                 });\r
459         },\r
460 };\r
461 \r
462 module.exports = named;\r