]> arthur.barton.de Git - netdata.git/blob - node.d/snmp.node.js
updated configs.signatures
[netdata.git] / node.d / snmp.node.js
1 'use strict';
2 // netdata snmp module
3 // This program will connect to one or more SNMP Agents
4 //
5
6 // example configuration in /etc/netdata/node.d/snmp.conf
7 /*
8 {
9     "enable_autodetect": false,
10     "update_every": 5,
11     "max_request_size": 50,
12     "servers": [
13         {
14             "hostname": "10.11.12.8",
15             "community": "public",
16             "update_every": 10,
17             "max_request_size": 50,
18             "options": { "timeout": 10000 },
19             "charts": {
20                 "snmp_switch.bandwidth_port1": {
21                     "title": "Switch Bandwidth for port 1",
22                     "units": "kilobits/s",
23                     "type": "area",
24                     "priority": 1,
25                     "dimensions": {
26                         "in": {
27                             "oid": ".1.3.6.1.2.1.2.2.1.10.1",
28                             "algorithm": "incremental",
29                             "multiplier": 8,
30                             "divisor": 1024,
31                             "offset": 0
32                         },
33                         "out": {
34                             "oid": ".1.3.6.1.2.1.2.2.1.16.1",
35                             "algorithm": "incremental",
36                             "multiplier": -8,
37                             "divisor": 1024,
38                             "offset": 0
39                         }
40                     }
41                 },
42                 "snmp_switch.bandwidth_port2": {
43                     "title": "Switch Bandwidth for port 2",
44                     "units": "kilobits/s",
45                     "type": "area",
46                     "priority": 1,
47                     "dimensions": {
48                         "in": {
49                             "oid": ".1.3.6.1.2.1.2.2.1.10.2",
50                             "algorithm": "incremental",
51                             "multiplier": 8,
52                             "divisor": 1024,
53                              "offset": 0
54                         },
55                         "out": {
56                             "oid": ".1.3.6.1.2.1.2.2.1.16.2",
57                             "algorithm": "incremental",
58                             "multiplier": -8,
59                             "divisor": 1024,
60                             "offset": 0
61                         }
62                     }
63                 }
64             }
65         }
66     ]
67 }
68 */
69
70 // You can also give ranges of charts like the following.
71 // This will append 1-24 to id, title, oid (on each dimension)
72 // so that 24 charts will be created.
73 /*
74 {
75     "enable_autodetect": false,
76     "update_every": 10,
77     "max_request_size": 50,
78     "servers": [
79         {
80             "hostname": "10.11.12.8",
81             "community": "public",
82             "update_every": 10,
83             "max_request_size": 50,
84             "options": { "timeout": 20000 },
85             "charts": {
86                 "snmp_switch.bandwidth_port": {
87                     "title": "Switch Bandwidth for port ",
88                     "units": "kilobits/s",
89                     "type": "area",
90                     "priority": 1,
91                     "multiply_range": [ 1, 24 ],
92                     "dimensions": {
93                         "in": {
94                             "oid": ".1.3.6.1.2.1.2.2.1.10.",
95                             "algorithm": "incremental",
96                             "multiplier": 8,
97                             "divisor": 1024,
98                             "offset": 0
99                         },
100                         "out": {
101                             "oid": ".1.3.6.1.2.1.2.2.1.16.",
102                             "algorithm": "incremental",
103                             "multiplier": -8,
104                             "divisor": 1024,
105                             "offset": 0
106                         }
107                     }
108                 }
109             }
110         }
111     ]
112 }
113 */
114
115 var net_snmp = require('net-snmp');
116 var extend = require('extend');
117 var netdata = require('netdata');
118
119 if(netdata.options.DEBUG === true) netdata.debug('loaded', __filename, ' plugin');
120
121 netdata.processors.snmp = {
122     name: 'snmp',
123
124     fixoid: function(oid) {
125         if(typeof oid !== 'string')
126             return oid;
127
128         if(oid.charAt(0) === '.')
129             return oid.substring(1, oid.length);
130
131         return oid;
132     },
133
134     prepare: function(service) {
135         var __DEBUG = netdata.options.DEBUG;
136
137         if(typeof service.snmp_oids === 'undefined' || service.snmp_oids === null || service.snmp_oids.length === 0) {
138             // this is the first time we see this service
139
140             if(__DEBUG === true)
141                 netdata.debug(service.module.name + ': ' + service.name + ': preparing ' + this.name + ' OIDs');
142
143             // build an index of all OIDs
144             service.snmp_oids_index = {};
145             var chart_keys = Object.keys(service.request.charts);
146             var chart_keys_len = chart_keys.length;
147             while(chart_keys_len--) {
148                 var c = chart_keys[chart_keys_len];
149                 var chart = service.request.charts[c];
150
151                 // for each chart
152
153                 if(__DEBUG === true)
154                     netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c);
155
156                 if(typeof chart.titleoid !== 'undefined') {
157                         service.snmp_oids_index[this.fixoid(chart.titleoid)] = {
158                             type: 'title',
159                             link: chart
160                         };
161                     }
162
163                 var dim_keys = Object.keys(chart.dimensions);
164                 var dim_keys_len = dim_keys.length;
165                 while(dim_keys_len--) {
166                     var d = dim_keys[dim_keys_len];
167                     var dim = chart.dimensions[d];
168
169                     // for each dimension in the chart
170
171                     var oid = this.fixoid(dim.oid);
172                     var oidname = this.fixoid(dim.oidname);
173                     
174                     if(__DEBUG === true)
175                         netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c + ', dimension: ' + d + ', OID: ' + oid + ", OID name: " + oidname);
176
177                     // link it to the point we need to set the value to
178                     service.snmp_oids_index[oid] = {
179                         type: 'value',
180                         link: dim
181                     };
182
183                     if(typeof oidname !== 'undefined')
184                         service.snmp_oids_index[oidname] = {
185                             type: 'name',
186                             link: dim
187                         };
188
189                     // and set the value to null
190                     dim.value = null;
191                 }
192             }
193
194             if(__DEBUG === true)
195                 netdata.debug(service.module.name + ': ' + service.name + ': indexed ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids_index));
196
197             // now create the array of OIDs needed by net-snmp
198             service.snmp_oids = Object.keys(service.snmp_oids_index);
199
200             if(__DEBUG === true)
201                 netdata.debug(service.module.name + ': ' + service.name + ': final list of ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids));
202
203             service.snmp_oids_cleaned = 0;
204         }
205         else if(service.snmp_oids_cleaned === 0) {
206             service.snmp_oids_cleaned = 1;
207
208             // the second time, keep only values
209
210             service.snmp_oids = new Array();
211             var oid_keys = Object.keys(service.snmp_oids_index);
212             var oid_keys_len = oid_keys.length;
213             while(oid_keys_len--) {
214                 if (service.snmp_oids_index[oid_keys[oid_keys_len]].type === 'value')
215                     service.snmp_oids.push(oid_keys[oid_keys_len]);
216             }
217         }
218     },
219
220     getdata: function(service, index, ok, failed, callback) {
221         var __DEBUG = netdata.options.DEBUG;
222         var that = this;
223
224         if(index >= service.snmp_oids.length) {
225             callback((ok > 0)?{ ok: ok, failed: failed }:null);
226             return;
227         }
228
229         var slice;
230         if(service.snmp_oids.length <= service.request.max_request_size) {
231             slice = service.snmp_oids;
232             index = service.snmp_oids.length;
233         }
234         else if(service.snmp_oids.length - index <= service.request.max_request_size) {
235             slice = service.snmp_oids.slice(index, service.snmp_oids.length);
236             index = service.snmp_oids.length;
237         }
238         else {
239             slice = service.snmp_oids.slice(index, index + service.request.max_request_size);
240             index += service.request.max_request_size;
241         }
242
243         if(__DEBUG === true)
244             netdata.debug(service.module.name + ': ' + service.name + ': making ' + slice.length + ' entries request, max is: ' + service.request.max_request_size);
245
246         service.snmp_session.get(slice, function(error, varbinds) {
247             if(error) {
248                 service.error('Received error = ' + netdata.stringify(error) + ' varbinds = ' + netdata.stringify(varbinds));
249
250                 // make all values null
251                 var len = slice.length;
252                 while(len--)
253                     service.snmp_oids_index[slice[len]].value = null;
254             }
255             else {
256                 if(__DEBUG === true)
257                     netdata.debug(service.module.name + ': ' + service.name + ': got valid ' + service.module.name + ' response: ' + netdata.stringify(varbinds));
258
259                 var varbinds_len = varbinds.length;
260                 for(var i = 0; i < varbinds_len ; i++) {
261                     var value = null;
262
263                     if(net_snmp.isVarbindError(varbinds[i])) {
264                         if(__DEBUG === true)
265                             netdata.debug(service.module.name + ': ' + service.name + ': failed ' + service.module.name + ' get for OIDs ' + varbinds[i].oid);
266
267                         service.error('OID ' + varbinds[i].oid + ' gave error: ' + snmp.varbindError(varbinds[i]));
268                         value = null;
269                         failed++;
270                     }
271                     else {
272                         if(__DEBUG === true)
273                             netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value);
274
275                         if(varbinds[i].type === net_snmp.ObjectType.OctetString && service.snmp_oids_index[varbinds[i].oid].type !== 'title')
276                             value = parseFloat(varbinds[i].value) * 1000;
277                         else
278                             value = varbinds[i].value;
279
280                         ok++;
281                     }
282
283                     if(value !== null) {
284                         switch(service.snmp_oids_index[varbinds[i].oid].type) {
285                             case 'title': service.snmp_oids_index[varbinds[i].oid].link.title += ' ' + value; break;
286                             case 'name' : service.snmp_oids_index[varbinds[i].oid].link.name = value; break;
287                             case 'value': service.snmp_oids_index[varbinds[i].oid].link.value = value; break;
288                         }
289                     }
290                 }
291
292                 if(__DEBUG === true)
293                     netdata.debug(service.module.name + ': ' + service.name + ': finished ' + service.module.name + ' with ' + ok + ' successful and ' + failed + ' failed values');
294             }
295             that.getdata(service, index, ok, failed, callback);
296         });
297     },
298
299     process: function(service, callback) {
300         var __DEBUG = netdata.options.DEBUG;
301
302         this.prepare(service);
303
304         if(service.snmp_oids.length === 0) {
305             // no OIDs found for this service
306
307             if(__DEBUG === true)
308                 service.error('no OIDs to process.');
309
310             callback(null);
311             return;
312         }
313
314         if(typeof service.snmp_session === 'undefined' || service.snmp_session === null) {
315             // no SNMP session has been created for this service
316             // the SNMP session is just the initialization of NET-SNMP
317
318             if(__DEBUG === true)
319                 netdata.debug(service.module.name + ': ' + service.name + ': opening ' + this.name + ' session on ' + service.request.hostname + ' community ' + service.request.community + ' options ' + netdata.stringify(service.request.options));
320
321             // create the SNMP session
322             service.snmp_session = net_snmp.createSession (service.request.hostname, service.request.community, service.request.options);
323
324             if(__DEBUG === true)
325                 netdata.debug(service.module.name + ': ' + service.name + ': got ' + this.name + ' session: ' + netdata.stringify(service.snmp_session));
326
327             // if we later need traps, this is how to do it:
328             //service.snmp_session.trap(net_snmp.TrapType.LinkDown, function(error) {
329             //  if(error) console.error('trap error: ' + netdata.stringify(error));
330             //});
331         }
332
333         // do it, get the SNMP values for the sessions we need
334         this.getdata(service, 0, 0, 0, callback);
335     }
336 };
337
338 var snmp = {
339     name: __filename,
340     enable_autodetect: true,
341     update_every: 1,
342     base_priority: 50000,
343
344     charts: {},
345
346     processResponse: function(service, data) {
347         if(data !== null) {
348             if(service.added !== true)
349                 service.commit();
350
351             var chart_keys = Object.keys(service.request.charts);
352             var chart_keys_len = chart_keys.length;
353             for(var i = 0; i < chart_keys_len; i++) {
354                 var c = chart_keys[i];
355
356                 var chart = snmp.charts[c];
357                 if(typeof chart === 'undefined') {
358                     chart = service.chart(c, service.request.charts[c]);
359                     snmp.charts[c] = chart;
360                 }
361
362                 service.begin(chart);
363
364                 var dimensions = service.request.charts[c].dimensions;
365                 var dim_keys = Object.keys(dimensions);
366                 var dim_keys_len = dim_keys.length;
367                 for(var j = 0; j < dim_keys_len ; j++) {
368                     var d = dim_keys[j];
369
370                     if (dimensions[d].value !== null) {
371                         if(typeof dimensions[d].offset === 'number')
372                             service.set(d, dimensions[d].value + dimensions[d].offset);
373                         else
374                             service.set(d, dimensions[d].value);
375                     }
376                 }
377
378                 service.end();
379             }
380         }
381     },
382
383     // module.serviceExecute()
384     // this function is called only from this module
385     // its purpose is to prepare the request and call
386     // netdata.serviceExecute()
387     serviceExecute: function(conf) {
388         var __DEBUG = netdata.options.DEBUG;
389
390         if(__DEBUG === true)
391             netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', update_every: ' + conf.update_every);
392
393         var service = netdata.service({
394             name: conf.hostname,
395             request: conf,
396             update_every: conf.update_every,
397             module: this,
398             processor: netdata.processors.snmp
399         });
400
401         // multiply the charts, if required
402         var chart_keys = Object.keys(service.request.charts);
403         var chart_keys_len = chart_keys.length;
404         for( var i = 0; i < chart_keys_len ; i++ ) {
405             var c = chart_keys[i];
406             var service_request_chart = service.request.charts[c];
407
408             if(__DEBUG === true)
409                 netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', examining chart: ' + c);
410
411             if(typeof service_request_chart.update_every === 'undefined')
412                 service_request_chart.update_every = service.update_every;
413
414             if(typeof service_request_chart.multiply_range !== 'undefined') {
415                 var from = service_request_chart.multiply_range[0];
416                 var to = service_request_chart.multiply_range[1];
417                 var prio = service_request_chart.priority || 1;
418
419                 if(prio < snmp.base_priority) prio += snmp.base_priority;
420
421                 while(from <= to) {
422                     var id = c + from.toString();
423                     var chart = extend(true, {}, service_request_chart);
424                     chart.title += from.toString();
425                     
426                     if(typeof chart.titleoid !== 'undefined')
427                         chart.titleoid += from.toString();
428
429                     chart.priority = prio++;
430
431                     var dim_keys = Object.keys(chart.dimensions);
432                     var dim_keys_len = dim_keys.length;
433                     for(var j = 0; j < dim_keys_len ; j++) {
434                         var d = dim_keys[j];
435
436                         chart.dimensions[d].oid += from.toString();
437
438                         if(typeof chart.dimensions[d].oidname !== 'undefined')
439                             chart.dimensions[d].oidname += from.toString();
440                     }
441                     service.request.charts[id] = chart;
442                     from++;
443                 }
444
445                 delete service.request.charts[c];
446             }
447             else {
448                 if(service.request.charts[c].priority < snmp.base_priority)
449                     service.request.charts[c].priority += snmp.base_priority;
450             }
451         }
452
453         service.execute(this.processResponse);
454     },
455
456     configure: function(config) {
457         var added = 0;
458
459         if(typeof config.max_request_size === 'undefined')
460             config.max_request_size = 50;
461
462         if(typeof(config.servers) !== 'undefined') {
463             var len = config.servers.length;
464             while(len--) {
465                 if(typeof config.servers[len].update_every === 'undefined')
466                     config.servers[len].update_every = this.update_every;
467
468                 if(typeof config.servers[len].max_request_size === 'undefined')
469                     config.servers[len].max_request_size = config.max_request_size;
470
471                 this.serviceExecute(config.servers[len]);
472                 added++;
473             }
474         }
475
476         return added;
477     },
478
479     // module.update()
480     // this is called repeatidly to collect data, by calling
481     // service.execute()
482     update: function(service, callback) {
483         service.execute(function(serv, data) {
484             service.module.processResponse(serv, data);
485             callback();
486         });
487     }
488 };
489
490 module.exports = snmp;