]> arthur.barton.de Git - netdata.git/commitdiff
added SNMP node.js data collector; node.d.plugin now supports multiple processors...
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Fri, 5 Feb 2016 00:02:12 +0000 (02:02 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Fri, 5 Feb 2016 00:02:12 +0000 (02:02 +0200)
14 files changed:
LICENSE.md
node.d/Makefile.am
node.d/named.node.js
node.d/node_modules/asn1.js [new file with mode: 0644]
node.d/node_modules/ber/errors.js [new file with mode: 0644]
node.d/node_modules/ber/index.js [new file with mode: 0644]
node.d/node_modules/ber/reader.js [new file with mode: 0644]
node.d/node_modules/ber/types.js [new file with mode: 0644]
node.d/node_modules/ber/writer.js [new file with mode: 0644]
node.d/node_modules/net-snmp.js [new file with mode: 0644]
node.d/node_modules/netdata.js
node.d/sma_webbox.node.js
node.d/snmp.node.js [new file with mode: 0755]
plugins.d/node.d.plugin

index 0483736736259da3f88c425b0c61d980a7747f5d..9666af975ddc8380b97f1edcba8f12e9eb05f954 100644 (file)
@@ -114,4 +114,27 @@ connectivity is not available.
     Copyright 2015, Mike Bostock
     [BSD License](http://opensource.org/licenses/BSD-3-Clause)
     
+    
+- [node-extend](https://github.com/justmoon/node-extend)
+
+    Copyright 2014, Stefan Thomas
+    [MIT License](https://github.com/justmoon/node-extend/blob/master/LICENSE)
+    
+
+- [node-net-snmp](https://github.com/stephenwvickers/node-net-snmp)
+
+    Copyright 2013, Stephen Vickers
+    [MIT License](https://github.com/stephenwvickers/node-net-snmp)
+    
 
+- [node-asn1](https://github.com/mcavage/node-asn1)
+
+    Copyright 2011, Mark Cavage
+    [MIT License](https://github.com/mcavage/node-asn1)
+    
+
+- [pixl-xml](https://github.com/jhuckaby/pixl-xml)
+
+    Copyright 2015, Joseph Huckaby
+    [MIT License](https://github.com/jhuckaby/pixl-xml)
+    
index 5e8ec6ad7f4336ccc10bf8626a272a229a1491b2..ae7eeac52293ed70ebfb9ade89e9352f422d8982 100644 (file)
@@ -4,6 +4,7 @@ dist_node_SCRIPTS = \
        README.md \
        named.node.js \
        sma_webbox.node.js \
+       snmp.node.js \
        $(NULL)
 
 nodemodulesdir=$(nodedir)/node_modules
@@ -11,4 +12,15 @@ dist_nodemodules_DATA = \
        node_modules/netdata.js \
        node_modules/extend.js \
        node_modules/pixl-xml.js \
+       node_modules/net-snmp.js \
+       node_modules/asn1.js \
+       $(NULL)
+
+nodemodulesberdir=$(nodedir)/node_modules/ber
+dist_nodemodulesber_DATA = \
+       node_modules/ber/index.js \
+       node_modules/ber/errors.js \
+       node_modules/ber/reader.js \
+       node_modules/ber/types.js \
+       node_modules/ber/writer.js \
        $(NULL)
index 67426beadf0e395f0875cded3a59e64ee36d237d..4348fbaa6fc57b332aba7290254d6219cbce6a62 100755 (executable)
@@ -46,7 +46,7 @@ if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plug
 var named = {
        name: __filename,
        enable_autodetect: true,
-       update_every: 1000,
+       update_every: 1,
 
        charts: {},
 
@@ -60,7 +60,7 @@ var named = {
                        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
+                       update_every: service.update_every,                     // the expected update frequency of the chart
                        dimensions: {}
                }
 
@@ -338,7 +338,7 @@ var named = {
                                                        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
+                                                       update_every: service.update_every,                             // the expected update frequency of the chart
                                                        dimensions: {
                                                                'clients': {
                                                                        id: 'clients',                                                          // the unique id of the dimension
@@ -442,7 +442,7 @@ var named = {
                                                                                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
+                                                                               update_every: service.update_every,                             // the expected update frequency of the chart
                                                                                dimensions: {
                                                                                        'queries': {
                                                                                                id: 'queries',                                                          // the unique id of the dimension
@@ -486,7 +486,7 @@ var named = {
                                                                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
+                                                               update_every: service.update_every,                             // the expected update frequency of the chart
                                                                dimensions: {
                                                                        'CacheHits': {
                                                                                id: 'CacheHits',                                                        // the unique id of the dimension
@@ -563,8 +563,6 @@ var named = {
                        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++;
diff --git a/node.d/node_modules/asn1.js b/node.d/node_modules/asn1.js
new file mode 100644 (file)
index 0000000..d1766e7
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+// If you have no idea what ASN.1 or BER is, see this:
+// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc
+
+var Ber = require('./ber/index');
+
+
+
+///--- Exported API
+
+module.exports = {
+
+  Ber: Ber,
+
+  BerReader: Ber.Reader,
+
+  BerWriter: Ber.Writer
+
+};
diff --git a/node.d/node_modules/ber/errors.js b/node.d/node_modules/ber/errors.js
new file mode 100644 (file)
index 0000000..ff21d4f
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+
+module.exports = {
+
+  newInvalidAsn1Error: function(msg) {
+    var e = new Error();
+    e.name = 'InvalidAsn1Error';
+    e.message = msg || '';
+    return e;
+  }
+
+};
diff --git a/node.d/node_modules/ber/index.js b/node.d/node_modules/ber/index.js
new file mode 100644 (file)
index 0000000..4fb90ae
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var errors = require('./errors');
+var types = require('./types');
+
+var Reader = require('./reader');
+var Writer = require('./writer');
+
+
+///--- Exports
+
+module.exports = {
+
+  Reader: Reader,
+
+  Writer: Writer
+
+};
+
+for (var t in types) {
+  if (types.hasOwnProperty(t))
+    module.exports[t] = types[t];
+}
+for (var e in errors) {
+  if (errors.hasOwnProperty(e))
+    module.exports[e] = errors[e];
+}
diff --git a/node.d/node_modules/ber/reader.js b/node.d/node_modules/ber/reader.js
new file mode 100644 (file)
index 0000000..0a00e98
--- /dev/null
@@ -0,0 +1,261 @@
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var assert = require('assert');
+
+var ASN1 = require('./types');
+var errors = require('./errors');
+
+
+///--- Globals
+
+var newInvalidAsn1Error = errors.newInvalidAsn1Error;
+
+
+
+///--- API
+
+function Reader(data) {
+  if (!data || !Buffer.isBuffer(data))
+    throw new TypeError('data must be a node Buffer');
+
+  this._buf = data;
+  this._size = data.length;
+
+  // These hold the "current" state
+  this._len = 0;
+  this._offset = 0;
+}
+
+Object.defineProperty(Reader.prototype, 'length', {
+  enumerable: true,
+  get: function () { return (this._len); }
+});
+
+Object.defineProperty(Reader.prototype, 'offset', {
+  enumerable: true,
+  get: function () { return (this._offset); }
+});
+
+Object.defineProperty(Reader.prototype, 'remain', {
+  get: function () { return (this._size - this._offset); }
+});
+
+Object.defineProperty(Reader.prototype, 'buffer', {
+  get: function () { return (this._buf.slice(this._offset)); }
+});
+
+
+/**
+ * Reads a single byte and advances offset; you can pass in `true` to make this
+ * a "peek" operation (i.e., get the byte, but don't advance the offset).
+ *
+ * @param {Boolean} peek true means don't move offset.
+ * @return {Number} the next byte, null if not enough data.
+ */
+Reader.prototype.readByte = function(peek) {
+  if (this._size - this._offset < 1)
+    return null;
+
+  var b = this._buf[this._offset] & 0xff;
+
+  if (!peek)
+    this._offset += 1;
+
+  return b;
+};
+
+
+Reader.prototype.peek = function() {
+  return this.readByte(true);
+};
+
+
+/**
+ * Reads a (potentially) variable length off the BER buffer.  This call is
+ * not really meant to be called directly, as callers have to manipulate
+ * the internal buffer afterwards.
+ *
+ * As a result of this call, you can call `Reader.length`, until the
+ * next thing called that does a readLength.
+ *
+ * @return {Number} the amount of offset to advance the buffer.
+ * @throws {InvalidAsn1Error} on bad ASN.1
+ */
+Reader.prototype.readLength = function(offset) {
+  if (offset === undefined)
+    offset = this._offset;
+
+  if (offset >= this._size)
+    return null;
+
+  var lenB = this._buf[offset++] & 0xff;
+  if (lenB === null)
+    return null;
+
+  if ((lenB & 0x80) == 0x80) {
+    lenB &= 0x7f;
+
+    if (lenB == 0)
+      throw newInvalidAsn1Error('Indefinite length not supported');
+
+    if (lenB > 4)
+      throw newInvalidAsn1Error('encoding too long');
+
+    if (this._size - offset < lenB)
+      return null;
+
+    this._len = 0;
+    for (var i = 0; i < lenB; i++)
+      this._len = (this._len << 8) + (this._buf[offset++] & 0xff);
+
+  } else {
+    // Wasn't a variable length
+    this._len = lenB;
+  }
+
+  return offset;
+};
+
+
+/**
+ * Parses the next sequence in this BER buffer.
+ *
+ * To get the length of the sequence, call `Reader.length`.
+ *
+ * @return {Number} the sequence's tag.
+ */
+Reader.prototype.readSequence = function(tag) {
+  var seq = this.peek();
+  if (seq === null)
+    return null;
+  if (tag !== undefined && tag !== seq)
+    throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+                              ': got 0x' + seq.toString(16));
+
+  var o = this.readLength(this._offset + 1); // stored in `length`
+  if (o === null)
+    return null;
+
+  this._offset = o;
+  return seq;
+};
+
+
+Reader.prototype.readInt = function() {
+  return this._readTag(ASN1.Integer);
+};
+
+
+Reader.prototype.readBoolean = function() {
+  return (this._readTag(ASN1.Boolean) === 0 ? false : true);
+};
+
+
+Reader.prototype.readEnumeration = function() {
+  return this._readTag(ASN1.Enumeration);
+};
+
+
+Reader.prototype.readString = function(tag, retbuf) {
+  if (!tag)
+    tag = ASN1.OctetString;
+
+  var b = this.peek();
+  if (b === null)
+    return null;
+
+  if (b !== tag)
+    throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+                              ': got 0x' + b.toString(16));
+
+  var o = this.readLength(this._offset + 1); // stored in `length`
+
+  if (o === null)
+    return null;
+
+  if (this.length > this._size - o)
+    return null;
+
+  this._offset = o;
+
+  if (this.length === 0)
+    return retbuf ? new Buffer(0) : '';
+
+  var str = this._buf.slice(this._offset, this._offset + this.length);
+  this._offset += this.length;
+
+  return retbuf ? str : str.toString('utf8');
+};
+
+Reader.prototype.readOID = function(tag) {
+  if (!tag)
+    tag = ASN1.OID;
+
+  var b = this.readString(tag, true);
+  if (b === null)
+    return null;
+
+  var values = [];
+  var value = 0;
+
+  for (var i = 0; i < b.length; i++) {
+    var byte = b[i] & 0xff;
+
+    value <<= 7;
+    value += byte & 0x7f;
+    if ((byte & 0x80) == 0) {
+      values.push(value);
+      value = 0;
+    }
+  }
+
+  value = values.shift();
+  values.unshift(value % 40);
+  values.unshift((value / 40) >> 0);
+
+  return values.join('.');
+};
+
+
+Reader.prototype._readTag = function(tag) {
+  assert.ok(tag !== undefined);
+
+  var b = this.peek();
+
+  if (b === null)
+    return null;
+
+  if (b !== tag)
+    throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) +
+                              ': got 0x' + b.toString(16));
+
+  var o = this.readLength(this._offset + 1); // stored in `length`
+  if (o === null)
+    return null;
+
+  if (this.length > 4)
+    throw newInvalidAsn1Error('Integer too long: ' + this.length);
+
+  if (this.length > this._size - o)
+    return null;
+  this._offset = o;
+
+  var fb = this._buf[this._offset];
+  var value = 0;
+
+  for (var i = 0; i < this.length; i++) {
+    value <<= 8;
+    value |= (this._buf[this._offset++] & 0xff);
+  }
+
+  if ((fb & 0x80) == 0x80 && i !== 4)
+    value -= (1 << (i * 8));
+
+  return value >> 0;
+};
+
+
+
+///--- Exported API
+
+module.exports = Reader;
diff --git a/node.d/node_modules/ber/types.js b/node.d/node_modules/ber/types.js
new file mode 100644 (file)
index 0000000..8aea000
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+
+module.exports = {
+  EOC: 0,
+  Boolean: 1,
+  Integer: 2,
+  BitString: 3,
+  OctetString: 4,
+  Null: 5,
+  OID: 6,
+  ObjectDescriptor: 7,
+  External: 8,
+  Real: 9, // float
+  Enumeration: 10,
+  PDV: 11,
+  Utf8String: 12,
+  RelativeOID: 13,
+  Sequence: 16,
+  Set: 17,
+  NumericString: 18,
+  PrintableString: 19,
+  T61String: 20,
+  VideotexString: 21,
+  IA5String: 22,
+  UTCTime: 23,
+  GeneralizedTime: 24,
+  GraphicString: 25,
+  VisibleString: 26,
+  GeneralString: 28,
+  UniversalString: 29,
+  CharacterString: 30,
+  BMPString: 31,
+  Constructor: 32,
+  Context: 128
+};
diff --git a/node.d/node_modules/ber/writer.js b/node.d/node_modules/ber/writer.js
new file mode 100644 (file)
index 0000000..d9d99af
--- /dev/null
@@ -0,0 +1,316 @@
+// Copyright 2011 Mark Cavage <mcavage@gmail.com> All rights reserved.
+
+var assert = require('assert');
+var ASN1 = require('./types');
+var errors = require('./errors');
+
+
+///--- Globals
+
+var newInvalidAsn1Error = errors.newInvalidAsn1Error;
+
+var DEFAULT_OPTS = {
+  size: 1024,
+  growthFactor: 8
+};
+
+
+///--- Helpers
+
+function merge(from, to) {
+  assert.ok(from);
+  assert.equal(typeof(from), 'object');
+  assert.ok(to);
+  assert.equal(typeof(to), 'object');
+
+  var keys = Object.getOwnPropertyNames(from);
+  keys.forEach(function(key) {
+    if (to[key])
+      return;
+
+    var value = Object.getOwnPropertyDescriptor(from, key);
+    Object.defineProperty(to, key, value);
+  });
+
+  return to;
+}
+
+
+
+///--- API
+
+function Writer(options) {
+  options = merge(DEFAULT_OPTS, options || {});
+
+  this._buf = new Buffer(options.size || 1024);
+  this._size = this._buf.length;
+  this._offset = 0;
+  this._options = options;
+
+  // A list of offsets in the buffer where we need to insert
+  // sequence tag/len pairs.
+  this._seq = [];
+}
+
+Object.defineProperty(Writer.prototype, 'buffer', {
+  get: function () {
+    if (this._seq.length)
+      throw new InvalidAsn1Error(this._seq.length + ' unended sequence(s)');
+
+    return (this._buf.slice(0, this._offset));
+  }
+});
+
+Writer.prototype.writeByte = function(b) {
+  if (typeof(b) !== 'number')
+    throw new TypeError('argument must be a Number');
+
+  this._ensure(1);
+  this._buf[this._offset++] = b;
+};
+
+
+Writer.prototype.writeInt = function(i, tag) {
+  if (typeof(i) !== 'number')
+    throw new TypeError('argument must be a Number');
+  if (typeof(tag) !== 'number')
+    tag = ASN1.Integer;
+
+  var sz = 4;
+
+  while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000 >> 0)) &&
+         (sz > 1)) {
+    sz--;
+    i <<= 8;
+  }
+
+  if (sz > 4)
+    throw new InvalidAsn1Error('BER ints cannot be > 0xffffffff');
+
+  this._ensure(2 + sz);
+  this._buf[this._offset++] = tag;
+  this._buf[this._offset++] = sz;
+
+  while (sz-- > 0) {
+    this._buf[this._offset++] = ((i & 0xff000000) >>> 24);
+    i <<= 8;
+  }
+
+};
+
+
+Writer.prototype.writeNull = function() {
+  this.writeByte(ASN1.Null);
+  this.writeByte(0x00);
+};
+
+
+Writer.prototype.writeEnumeration = function(i, tag) {
+  if (typeof(i) !== 'number')
+    throw new TypeError('argument must be a Number');
+  if (typeof(tag) !== 'number')
+    tag = ASN1.Enumeration;
+
+  return this.writeInt(i, tag);
+};
+
+
+Writer.prototype.writeBoolean = function(b, tag) {
+  if (typeof(b) !== 'boolean')
+    throw new TypeError('argument must be a Boolean');
+  if (typeof(tag) !== 'number')
+    tag = ASN1.Boolean;
+
+  this._ensure(3);
+  this._buf[this._offset++] = tag;
+  this._buf[this._offset++] = 0x01;
+  this._buf[this._offset++] = b ? 0xff : 0x00;
+};
+
+
+Writer.prototype.writeString = function(s, tag) {
+  if (typeof(s) !== 'string')
+    throw new TypeError('argument must be a string (was: ' + typeof(s) + ')');
+  if (typeof(tag) !== 'number')
+    tag = ASN1.OctetString;
+
+  var len = Buffer.byteLength(s);
+  this.writeByte(tag);
+  this.writeLength(len);
+  if (len) {
+    this._ensure(len);
+    this._buf.write(s, this._offset);
+    this._offset += len;
+  }
+};
+
+
+Writer.prototype.writeBuffer = function(buf, tag) {
+  if (typeof(tag) !== 'number')
+    throw new TypeError('tag must be a number');
+  if (!Buffer.isBuffer(buf))
+    throw new TypeError('argument must be a buffer');
+
+  this.writeByte(tag);
+  this.writeLength(buf.length);
+  this._ensure(buf.length);
+  buf.copy(this._buf, this._offset, 0, buf.length);
+  this._offset += buf.length;
+};
+
+
+Writer.prototype.writeStringArray = function(strings) {
+  if ((!strings instanceof Array))
+    throw new TypeError('argument must be an Array[String]');
+
+  var self = this;
+  strings.forEach(function(s) {
+    self.writeString(s);
+  });
+};
+
+// This is really to solve DER cases, but whatever for now
+Writer.prototype.writeOID = function(s, tag) {
+  if (typeof(s) !== 'string')
+    throw new TypeError('argument must be a string');
+  if (typeof(tag) !== 'number')
+    tag = ASN1.OID;
+
+  if (!/^([0-9]+\.){3,}[0-9]+$/.test(s))
+    throw new Error('argument is not a valid OID string');
+
+  function encodeOctet(bytes, octet) {
+    if (octet < 128) {
+        bytes.push(octet);
+    } else if (octet < 16384) {
+        bytes.push((octet >>> 7) | 0x80);
+        bytes.push(octet & 0x7F);
+    } else if (octet < 2097152) {
+      bytes.push((octet >>> 14) | 0x80);
+      bytes.push(((octet >>> 7) | 0x80) & 0xFF);
+      bytes.push(octet & 0x7F);
+    } else if (octet < 268435456) {
+      bytes.push((octet >>> 21) | 0x80);
+      bytes.push(((octet >>> 14) | 0x80) & 0xFF);
+      bytes.push(((octet >>> 7) | 0x80) & 0xFF);
+      bytes.push(octet & 0x7F);
+    } else {
+      bytes.push(((octet >>> 28) | 0x80) & 0xFF);
+      bytes.push(((octet >>> 21) | 0x80) & 0xFF);
+      bytes.push(((octet >>> 14) | 0x80) & 0xFF);
+      bytes.push(((octet >>> 7) | 0x80) & 0xFF);
+      bytes.push(octet & 0x7F);
+    }
+  }
+
+  var tmp = s.split('.');
+  var bytes = [];
+  bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10));
+  tmp.slice(2).forEach(function(b) {
+    encodeOctet(bytes, parseInt(b, 10));
+  });
+
+  var self = this;
+  this._ensure(2 + bytes.length);
+  this.writeByte(tag);
+  this.writeLength(bytes.length);
+  bytes.forEach(function(b) {
+    self.writeByte(b);
+  });
+};
+
+
+Writer.prototype.writeLength = function(len) {
+  if (typeof(len) !== 'number')
+    throw new TypeError('argument must be a Number');
+
+  this._ensure(4);
+
+  if (len <= 0x7f) {
+    this._buf[this._offset++] = len;
+  } else if (len <= 0xff) {
+    this._buf[this._offset++] = 0x81;
+    this._buf[this._offset++] = len;
+  } else if (len <= 0xffff) {
+    this._buf[this._offset++] = 0x82;
+    this._buf[this._offset++] = len >> 8;
+    this._buf[this._offset++] = len;
+  } else if (len <= 0xffffff) {
+    this._buf[this._offset++] = 0x83;
+    this._buf[this._offset++] = len >> 16;
+    this._buf[this._offset++] = len >> 8;
+    this._buf[this._offset++] = len;
+  } else {
+    throw new InvalidAsn1ERror('Length too long (> 4 bytes)');
+  }
+};
+
+Writer.prototype.startSequence = function(tag) {
+  if (typeof(tag) !== 'number')
+    tag = ASN1.Sequence | ASN1.Constructor;
+
+  this.writeByte(tag);
+  this._seq.push(this._offset);
+  this._ensure(3);
+  this._offset += 3;
+};
+
+
+Writer.prototype.endSequence = function() {
+  var seq = this._seq.pop();
+  var start = seq + 3;
+  var len = this._offset - start;
+
+  if (len <= 0x7f) {
+    this._shift(start, len, -2);
+    this._buf[seq] = len;
+  } else if (len <= 0xff) {
+    this._shift(start, len, -1);
+    this._buf[seq] = 0x81;
+    this._buf[seq + 1] = len;
+  } else if (len <= 0xffff) {
+    this._buf[seq] = 0x82;
+    this._buf[seq + 1] = len >> 8;
+    this._buf[seq + 2] = len;
+  } else if (len <= 0xffffff) {
+    this._shift(start, len, 1);
+    this._buf[seq] = 0x83;
+    this._buf[seq + 1] = len >> 16;
+    this._buf[seq + 2] = len >> 8;
+    this._buf[seq + 3] = len;
+  } else {
+    throw new InvalidAsn1Error('Sequence too long');
+  }
+};
+
+
+Writer.prototype._shift = function(start, len, shift) {
+  assert.ok(start !== undefined);
+  assert.ok(len !== undefined);
+  assert.ok(shift);
+
+  this._buf.copy(this._buf, start + shift, start, start + len);
+  this._offset += shift;
+};
+
+Writer.prototype._ensure = function(len) {
+  assert.ok(len);
+
+  if (this._size - this._offset < len) {
+    var sz = this._size * this._options.growthFactor;
+    if (sz - this._offset < len)
+      sz += len;
+
+    var buf = new Buffer(sz);
+
+    this._buf.copy(buf, 0, 0, this._offset);
+    this._buf = buf;
+    this._size = sz;
+  }
+};
+
+
+
+///--- Exported API
+
+module.exports = Writer;
diff --git a/node.d/node_modules/net-snmp.js b/node.d/node_modules/net-snmp.js
new file mode 100644 (file)
index 0000000..95692c1
--- /dev/null
@@ -0,0 +1,1466 @@
+
+// Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com>
+
+var ber = require ("asn1").Ber;
+var dgram = require ("dgram");
+var events = require ("events");
+var util = require ("util");
+
+/*****************************************************************************
+ ** Constants
+ **/
+
+function _expandConstantObject (object) {
+       var keys = [];
+       for (key in object)
+               keys.push (key);
+       for (var i = 0; i < keys.length; i++)
+               object[object[keys[i]]] = parseInt (keys[i]);
+}
+
+var ErrorStatus = {
+       0: "NoError",
+       1: "TooBig",
+       2: "NoSuchName",
+       3: "BadValue",
+       4: "ReadOnly",
+       5: "GeneralError",
+       6: "NoAccess",
+       7: "WrongType",
+       8: "WrongLength",
+       9: "WrongEncoding",
+       10: "WrongValue",
+       11: "NoCreation",
+       12: "InconsistentValue",
+       13: "ResourceUnavailable",
+       14: "CommitFailed",
+       15: "UndoFailed",
+       16: "AuthorizationError",
+       17: "NotWritable",
+       18: "InconsistentName"
+};
+
+_expandConstantObject (ErrorStatus);
+
+var ObjectType = {
+       1: "Boolean",
+       2: "Integer",
+       4: "OctetString",
+       5: "Null",
+       6: "OID",
+       64: "IpAddress",
+       65: "Counter",
+       66: "Gauge",
+       67: "TimeTicks",
+       68: "Opaque",
+       70: "Counter64",
+       128: "NoSuchObject",
+       129: "NoSuchInstance",
+       130: "EndOfMibView"
+};
+
+_expandConstantObject (ObjectType);
+
+ObjectType.Integer32 = ObjectType.Integer;
+ObjectType.Counter32 = ObjectType.Counter;
+ObjectType.Gauge32 = ObjectType.Gauge;
+ObjectType.Unsigned32 = ObjectType.Gauge32;
+
+var PduType = {
+       160: "GetRequest",
+       161: "GetNextRequest",
+       162: "GetResponse",
+       163: "SetRequest",
+       164: "Trap",
+       165: "GetBulkRequest",
+       166: "InformRequest",
+       167: "TrapV2",
+       168: "Report"
+};
+
+_expandConstantObject (PduType);
+
+var TrapType = {
+       0: "ColdStart",
+       1: "WarmStart",
+       2: "LinkDown",
+       3: "LinkUp",
+       4: "AuthenticationFailure",
+       5: "EgpNeighborLoss",
+       6: "EnterpriseSpecific"
+};
+
+_expandConstantObject (TrapType);
+
+var Version1 = 0;
+var Version2c = 1;
+
+/*****************************************************************************
+ ** Exception class definitions
+ **/
+
+function ResponseInvalidError (message) {
+       this.name = "ResponseInvalidError";
+       this.message = message;
+       Error.captureStackTrace(this, ResponseInvalidError);
+}
+util.inherits (ResponseInvalidError, Error);
+
+function RequestInvalidError (message) {
+       this.name = "RequestInvalidError";
+       this.message = message;
+       Error.captureStackTrace(this, RequestInvalidError);
+}
+util.inherits (RequestInvalidError, Error);
+
+function RequestFailedError (message, status) {
+       this.name = "RequestFailedError";
+       this.message = message;
+       this.status = status;
+       Error.captureStackTrace(this, RequestFailedError);
+}
+util.inherits (RequestFailedError, Error);
+
+function RequestTimedOutError (message) {
+       this.name = "RequestTimedOutError";
+       this.message = message;
+       Error.captureStackTrace(this, RequestTimedOutError);
+}
+util.inherits (RequestTimedOutError, Error);
+
+/*****************************************************************************
+ ** OID and varbind helper functions
+ **/
+
+function isVarbindError (varbind) {
+       if (varbind.type == ObjectType.NoSuchObject
+                       || varbind.type == ObjectType.NoSuchInstance
+                       || varbind.type == ObjectType.EndOfMibView)
+               return true;
+       else
+               return false;
+}
+
+function varbindError (varbind) {
+       return (ObjectType[varbind.type] || "NotAnError") + ": " + varbind.oid;
+}
+
+function oidFollowsOid (oidString, nextString) {
+       var oid = {str: oidString, len: oidString.length, idx: 0};
+       var next = {str: nextString, len: nextString.length, idx: 0};
+       var dotCharCode = ".".charCodeAt (0);
+
+       function getNumber (item) {
+               var n = 0;
+               if (item.idx >= item.len)
+                       return null;
+               while (item.idx < item.len) {
+                       var charCode = item.str.charCodeAt (item.idx++);
+                       if (charCode == dotCharCode)
+                               return n;
+                       n = (n ? (n * 10) : n) + (charCode - 48);
+               }
+               return n;
+       }
+
+       while (1) {
+               var oidNumber = getNumber (oid);
+               var nextNumber = getNumber (next);
+
+               if (oidNumber !== null) {
+                       if (nextNumber !== null) {
+                               if (nextNumber > oidNumber) {
+                                       return true;
+                               } else if (nextNumber < oidNumber) {
+                                       return false;
+                               }
+                       } else {
+                               return true;
+                       }
+               } else {
+                       return true;
+               }
+       }
+}
+
+function oidInSubtree (oidString, nextString) {
+       var oid = oidString.split (".");
+       var next = nextString.split (".");
+
+       if (oid.length > next.length)
+               return false;
+
+       for (var i = 0; i < oid.length; i++) {
+               if (next[i] != oid[i])
+                       return false;
+       }
+
+       return true;
+}
+
+/**
+ ** Some SNMP agents produce integers on the wire such as 00 ff ff ff ff.
+ ** The ASN.1 BER parser we use throws an error when parsing this, which we
+ ** believe is correct.  So, we decided not to bother the "asn1" developer(s)
+ ** with this, instead opting to work around it here.
+ **
+ ** If an integer is 5 bytes in length we check if the first byte is 0, and if so
+ ** simply drop it and parse it like it was a 4 byte integer, otherwise throw
+ ** an error since the integer is too large.
+ **/
+
+function readInt (buffer) {
+       return readUint (buffer, true);
+}
+
+function readUint (buffer, isSigned) {
+       buffer.readByte ();
+       var length = buffer.readByte ();
+
+       if (length > 5) {
+                throw new RangeError ("Integer too long '" + length + "'");
+       } else if (length == 5) {
+               if (buffer.readByte () !== 0)
+                       throw new RangeError ("Integer too long '" + length + "'");
+               length = 4;
+       }
+
+       value = 0, signedBitSet = false;
+       
+       for (var i = 0; i < length; i++) {
+               value *= 256;
+               value += buffer.readByte ();
+
+               if (isSigned && i <= 0) {
+                       if ((value & 0x80) == 0x80)
+                               signedBitSet = true;
+               }
+       }
+       
+       if (signedBitSet)
+               value -= (1 << (i * 8));
+
+       return value;
+}
+
+function readUint64 (buffer) {
+       var value = buffer.readString (ObjectType.Counter64, true);
+
+       if (value.length > 8)
+               throw new RequestInvalidError ("64 bit unsigned integer too long '"
+                               + value.length + "'")
+
+       return value;
+}
+
+function readVarbinds (buffer, varbinds) {
+       buffer.readSequence ();
+
+       while (1) {
+               buffer.readSequence ();
+               var oid = buffer.readOID ();
+               var type = buffer.peek ();
+
+               if (type == null)
+                       break;
+
+               var value;
+
+               if (type == ObjectType.Boolean) {
+                       value = buffer.readBoolean ();
+               } else if (type == ObjectType.Integer) {
+                       value = readInt (buffer);
+               } else if (type == ObjectType.OctetString) {
+                       value = buffer.readString (null, true);
+               } else if (type == ObjectType.Null) {
+                       buffer.readByte ();
+                       buffer.readByte ();
+                       value = null;
+               } else if (type == ObjectType.OID) {
+                       value = buffer.readOID ();
+               } else if (type == ObjectType.IpAddress) {
+                       var bytes = buffer.readString (ObjectType.IpAddress, true);
+                       if (bytes.length != 4)
+                               throw new ResponseInvalidError ("Length '" + bytes.length
+                                               + "' of IP address '" + bytes.toString ("hex")
+                                               + "' is not 4");
+                       value = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3];
+               } else if (type == ObjectType.Counter) {
+                       value = readUint (buffer);
+               } else if (type == ObjectType.Gauge) {
+                       value = readUint (buffer);
+               } else if (type == ObjectType.TimeTicks) {
+                       value = readUint (buffer);
+               } else if (type == ObjectType.Opaque) {
+                       value = buffer.readString (ObjectType.Opaque, true);
+               } else if (type == ObjectType.Counter64) {
+                       value = readUint64 (buffer);
+               } else if (type == ObjectType.NoSuchObject) {
+                       buffer.readByte ();
+                       buffer.readByte ();
+                       value = null;
+               } else if (type == ObjectType.NoSuchInstance) {
+                       buffer.readByte ();
+                       buffer.readByte ();
+                       value = null;
+               } else if (type == ObjectType.EndOfMibView) {
+                       buffer.readByte ();
+                       buffer.readByte ();
+                       value = null;
+               } else {
+                       throw new ResponseInvalidError ("Unknown type '" + type
+                                       + "' in response");
+               }
+
+               varbinds.push ({
+                       oid: oid,
+                       type: type,
+                       value: value
+               });
+       }
+}
+
+function writeUint (buffer, type, value) {
+       var b = new Buffer (4);
+       b.writeUInt32BE (value, 0);
+       buffer.writeBuffer (b, type);
+}
+
+function writeUint64 (buffer, value) {
+       if (value.length > 8)
+               throw new RequestInvalidError ("64 bit unsigned integer too long '"
+                               + value.length + "'")
+       buffer.writeBuffer (value, ObjectType.Counter64);
+}
+
+function writeVarbinds (buffer, varbinds) {
+       buffer.startSequence ();
+       for (var i = 0; i < varbinds.length; i++) {
+               buffer.startSequence ();
+               buffer.writeOID (varbinds[i].oid);
+
+               if (varbinds[i].type && varbinds[i].hasOwnProperty("value")) {
+                       var type = varbinds[i].type;
+                       var value = varbinds[i].value;
+
+                       if (type == ObjectType.Boolean) {
+                               buffer.writeBoolean (value ? true : false);
+                       } else if (type == ObjectType.Integer) { // also Integer32
+                               buffer.writeInt (value);
+                       } else if (type == ObjectType.OctetString) {
+                               if (typeof value == "string")
+                                       buffer.writeString (value);
+                               else
+                                       buffer.writeBuffer (value, ObjectType.OctetString);
+                       } else if (type == ObjectType.Null) {
+                               buffer.writeNull ();
+                       } else if (type == ObjectType.OID) {
+                               buffer.writeOID (value);
+                       } else if (type == ObjectType.IpAddress) {
+                               var bytes = value.split (".");
+                               if (bytes.length != 4)
+                                       throw new RequestInvalidError ("Invalid IP address '"
+                                                       + value + "'");
+                               buffer.writeBuffer (new Buffer (bytes), 64);
+                       } else if (type == ObjectType.Counter) { // also Counter32
+                               writeUint (buffer, ObjectType.Counter, value);
+                       } else if (type == ObjectType.Gauge) { // also Gauge32 & Unsigned32
+                               writeUint (buffer, ObjectType.Gauge, value);
+                       } else if (type == ObjectType.TimeTicks) {
+                               writeUint (buffer, ObjectType.TimeTicks, value);
+                       } else if (type == ObjectType.Opaque) {
+                               buffer.writeBuffer (value, ObjectType.Opaque);
+                       } else if (type == ObjectType.Counter64) {
+                               writeUint64 (buffer, value);
+                       } else {
+                               throw new RequestInvalidError ("Unknown type '" + type
+                                               + "' in request");
+                       }
+               } else {
+                       buffer.writeNull ();
+               }
+
+               buffer.endSequence ();
+       };
+       buffer.endSequence ();
+}
+
+/*****************************************************************************
+ ** PDU class definitions
+ **/
+
+var SimplePdu = function (id, varbinds, options) {
+       this.id = id;
+       this.varbinds = varbinds;
+       this.options = options || {};
+};
+
+SimplePdu.prototype.toBuffer = function (buffer) {
+       buffer.startSequence (this.type);
+
+       buffer.writeInt (this.id);
+       buffer.writeInt ((this.type == PduType.GetBulkRequest)
+                       ? (this.options.nonRepeaters || 0)
+                       : 0);
+       buffer.writeInt ((this.type == PduType.GetBulkRequest)
+                       ? (this.options.maxRepetitions || 0)
+                       : 0);
+
+       writeVarbinds (buffer, this.varbinds);
+
+       buffer.endSequence ();
+};
+
+var GetBulkRequestPdu = function () {
+       this.type = PduType.GetBulkRequest;
+       GetBulkRequestPdu.super_.apply (this, arguments);
+};
+
+util.inherits (GetBulkRequestPdu, SimplePdu);
+
+var GetNextRequestPdu = function () {
+       this.type = PduType.GetNextRequest;
+       GetNextRequestPdu.super_.apply (this, arguments);
+};
+
+util.inherits (GetNextRequestPdu, SimplePdu);
+
+var GetResponsePdu = function (buffer) {
+       this.type = PduType.GetResponse;
+
+       buffer.readSequence (this.type);
+
+       this.id = buffer.readInt ();
+
+       this.errorStatus = buffer.readInt ();
+       this.errorIndex = buffer.readInt ();
+
+       this.varbinds = [];
+
+       readVarbinds (buffer, this.varbinds);
+};
+
+var GetRequestPdu = function () {
+       this.type = PduType.GetRequest;
+       GetRequestPdu.super_.apply (this, arguments);
+};
+
+util.inherits (GetRequestPdu, SimplePdu);
+
+var InformRequestPdu = function () {
+       this.type = PduType.InformRequest;
+       InformRequestPdu.super_.apply (this, arguments);
+};
+
+util.inherits (InformRequestPdu, SimplePdu);
+
+var SetRequestPdu = function () {
+       this.type = PduType.SetRequest;
+       SetRequestPdu.super_.apply (this, arguments);
+};
+
+util.inherits (SetRequestPdu, SimplePdu);
+
+var TrapPdu = function (typeOrOid, varbinds, options) {
+       this.type = PduType.Trap;
+
+       this.agentAddr = options.agentAddr || "127.0.0.1";
+       this.upTime = options.upTime;
+
+       if (typeof typeOrOid == "string") {
+               this.generic = TrapType.EnterpriseSpecific;
+               this.specific = parseInt (typeOrOid.match (/\.(\d+)$/)[1]);
+               this.enterprise = typeOrOid.replace (/\.(\d+)$/, "");
+       } else {
+               this.generic = typeOrOid;
+               this.specific = 0;
+               this.enterprise = "1.3.6.1.4.1";
+       }
+
+       this.varbinds = varbinds;
+};
+
+TrapPdu.prototype.toBuffer = function (buffer) {
+       buffer.startSequence (this.type);
+
+       buffer.writeOID (this.enterprise);
+       buffer.writeBuffer (new Buffer (this.agentAddr.split (".")),
+                       ObjectType.IpAddress);
+       buffer.writeInt (this.generic);
+       buffer.writeInt (this.specific);
+       writeUint (buffer, ObjectType.TimeTicks,
+                       this.upTime || Math.floor (process.uptime () * 100));
+
+       writeVarbinds (buffer, this.varbinds);
+
+       buffer.endSequence ();
+};
+
+var TrapV2Pdu = function () {
+       this.type = PduType.TrapV2;
+       TrapV2Pdu.super_.apply (this, arguments);
+};
+
+util.inherits (TrapV2Pdu, SimplePdu);
+
+/*****************************************************************************
+ ** Message class definitions
+ **/
+
+var RequestMessage = function (version, community, pdu) {
+       this.version = version;
+       this.community = community;
+       this.pdu = pdu;
+};
+
+RequestMessage.prototype.toBuffer = function () {
+       if (this.buffer)
+               return this.buffer;
+
+       var writer = new ber.Writer ();
+
+       writer.startSequence ();
+
+       writer.writeInt (this.version);
+       writer.writeString (this.community);
+
+       this.pdu.toBuffer (writer);
+
+       writer.endSequence ();
+
+       this.buffer = writer.buffer;
+
+       return this.buffer;
+};
+
+var ResponseMessage = function (buffer) {
+       var reader = new ber.Reader (buffer);
+
+       reader.readSequence ();
+
+       this.version = reader.readInt ();
+       this.community = reader.readString ();
+
+       var type = reader.peek ();
+
+       if (type == PduType.GetResponse) {
+               this.pdu = new GetResponsePdu (reader);
+       } else {
+               throw new ResponseInvalidError ("Unknown PDU type '" + type
+                               + "' in response");
+       }
+}
+
+/*****************************************************************************
+ ** Session class definition
+ **/
+
+var Session = function (target, community, options) {
+       this.target = target || "127.0.0.1";
+       this.community = community || "public";
+
+       this.version = (options && options.version)
+                       ? options.version
+                       : Version1;
+
+       this.transport = (options && options.transport)
+                       ? options.transport
+                       : "udp4";
+       this.port = (options && options.port )
+                       ? options.port
+                       : 161;
+       this.trapPort = (options && options.trapPort )
+                       ? options.trapPort
+                       : 162;
+
+       this.retries = (options && (options.retries || options.retries == 0))
+                       ? options.retries
+                       : 1;
+       this.timeout = (options && options.timeout)
+                       ? options.timeout
+                       : 5000;
+
+       this.sourceAddress = (options && options.sourceAddress )
+                       ? options.sourceAddress
+                       : undefined;
+       this.sourcePort = (options && options.sourcePort )
+                       ? parseInt(options.sourcePort)
+                       : undefined;
+
+       this.reqs = {};
+       this.reqCount = 0;
+
+       this.dgram = dgram.createSocket (this.transport);
+       this.dgram.unref();
+       
+       var me = this;
+       this.dgram.on ("message", me.onMsg.bind (me));
+       this.dgram.on ("close", me.onClose.bind (me));
+       this.dgram.on ("error", me.onError.bind (me));
+
+       if (this.sourceAddress || this.sourcePort)
+               req.dgram.bind (this.sourcePort, this.sourceAddress);
+};
+
+util.inherits (Session, events.EventEmitter);
+
+Session.prototype.close = function () {
+       this.dgram.close ();
+       return this;
+}
+
+Session.prototype.cancelRequests = function (error) {
+       for (id in this.reqs) {
+               var req = this.reqs[id];
+               this.unregisterRequest (req.id);
+               req.responseCb (error);
+       }
+}
+
+function _generateId () {
+       return Math.floor (Math.random () + Math.random () * 10000000)
+}
+
+Session.prototype.get = function (oids, responseCb) {
+       function feedCb (req, message) {
+               var pdu = message.pdu;
+               var varbinds = [];
+
+               if (req.message.pdu.varbinds.length != pdu.varbinds.length) {
+                       req.responseCb (new ResponseInvalidError ("Requested OIDs do not "
+                                       + "match response OIDs"));
+               } else {
+                       for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
+                               if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) {
+                                       req.responseCb (new ResponseInvalidError ("OID '"
+                                                       + req.message.pdu.varbinds[i].oid
+                                                       + "' in request at positiion '" + i + "' does not "
+                                                       + "match OID '" + pdu.varbinds[i].oid + "' in response "
+                                                       + "at position '" + i + "'"));
+                                       return;
+                               } else {
+                                       varbinds.push (pdu.varbinds[i]);
+                               }
+                       }
+
+                       req.responseCb (null, varbinds);
+               }
+       };
+
+       var pduVarbinds = [];
+
+       for (var i = 0; i < oids.length; i++) {
+               var varbind = {
+                       oid: oids[i]
+               };
+               pduVarbinds.push (varbind);
+       }
+
+       this.simpleGet (GetRequestPdu, feedCb, pduVarbinds, responseCb);
+
+       return this;
+};
+
+Session.prototype.getBulk = function () {
+       var oids, nonRepeaters, maxRepetitions, responseCb;
+
+       if (arguments.length >= 4) {
+               oids = arguments[0];
+               nonRepeaters = arguments[1];
+               maxRepetitions = arguments[2];
+               responseCb = arguments[3];
+       } else if (arguments.length >= 3) {
+               oids = arguments[0];
+               nonRepeaters = arguments[1];
+               maxRepetitions = 10;
+               responseCb = arguments[2];
+       } else {
+               oids = arguments[0];
+               nonRepeaters = 0;
+               maxRepetitions = 10;
+               responseCb = arguments[1];
+       }
+
+       function feedCb (req, message) {
+               var pdu = message.pdu;
+               var varbinds = [];
+               var i = 0;
+
+               // first walk through and grab non-repeaters
+               if (pdu.varbinds.length < nonRepeaters) {
+                       req.responseCb (new ResponseInvalidError ("Varbind count in "
+                                       + "response '" + pdu.varbinds.length + "' is less than "
+                                       + "non-repeaters '" + nonRepeaters + "' in request"));
+               } else {
+                       for ( ; i < nonRepeaters; i++) {
+                               if (isVarbindError (pdu.varbinds[i])) {
+                                       varbinds.push (pdu.varbinds[i]);
+                               } else if (! oidFollowsOid (req.message.pdu.varbinds[i].oid,
+                                               pdu.varbinds[i].oid)) {
+                                       req.responseCb (new ResponseInvalidError ("OID '"
+                                                       + req.message.pdu.varbinds[i].oid + "' in request at "
+                                                       + "positiion '" + i + "' does not precede "
+                                                       + "OID '" + pdu.varbinds[i].oid + "' in response "
+                                                       + "at position '" + i + "'"));
+                                       return;
+                               } else {
+                                       varbinds.push (pdu.varbinds[i]);
+                               }
+                       }
+               }
+
+               var repeaters = req.message.pdu.varbinds.length - nonRepeaters;
+
+               // secondly walk through and grab repeaters
+               if (pdu.varbinds.length % (repeaters)) {
+                       req.responseCb (new ResponseInvalidError ("Varbind count in "
+                                       + "response '" + pdu.varbinds.length + "' is not a "
+                                       + "multiple of repeaters '" + repeaters
+                                       + "' plus non-repeaters '" + nonRepeaters + "' in request"));
+               } else {
+                       while (i < pdu.varbinds.length) {
+                               for (var j = 0; j < repeaters; j++, i++) {
+                                       var reqIndex = nonRepeaters + j;
+                                       var respIndex = i;
+
+                                       if (isVarbindError (pdu.varbinds[respIndex])) {
+                                               if (! varbinds[reqIndex])
+                                                       varbinds[reqIndex] = [];
+                                               varbinds[reqIndex].push (pdu.varbinds[respIndex]);
+                                       } else if (! oidFollowsOid (
+                                                       req.message.pdu.varbinds[reqIndex].oid,
+                                                       pdu.varbinds[respIndex].oid)) {
+                                               req.responseCb (new ResponseInvalidError ("OID '"
+                                                               + req.message.pdu.varbinds[reqIndex].oid
+                                                               + "' in request at positiion '" + (reqIndex)
+                                                               + "' does not precede OID '"
+                                                               + pdu.varbinds[respIndex].oid
+                                                               + "' in response at position '" + (respIndex) + "'"));
+                                               return;
+                                       } else {
+                                               if (! varbinds[reqIndex])
+                                                       varbinds[reqIndex] = [];
+                                               varbinds[reqIndex].push (pdu.varbinds[respIndex]);
+                                       }
+                               }
+                       }
+               }
+
+               req.responseCb (null, varbinds);
+       };
+
+       var pduVarbinds = [];
+
+       for (var i = 0; i < oids.length; i++) {
+               var varbind = {
+                       oid: oids[i]
+               };
+               pduVarbinds.push (varbind);
+       }
+
+       var options = {
+               nonRepeaters: nonRepeaters,
+               maxRepetitions: maxRepetitions
+       };
+
+       this.simpleGet (GetBulkRequestPdu, feedCb, pduVarbinds, responseCb,
+                       options);
+
+       return this;
+};
+
+Session.prototype.getNext = function (oids, responseCb) {
+       function feedCb (req, message) {
+               var pdu = message.pdu;
+               var varbinds = [];
+
+               if (req.message.pdu.varbinds.length != pdu.varbinds.length) {
+                       req.responseCb (new ResponseInvalidError ("Requested OIDs do not "
+                                       + "match response OIDs"));
+               } else {
+                       for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
+                               if (isVarbindError (pdu.varbinds[i])) {
+                                       varbinds.push (pdu.varbinds[i]);
+                               } else if (! oidFollowsOid (req.message.pdu.varbinds[i].oid,
+                                               pdu.varbinds[i].oid)) {
+                                       req.responseCb (new ResponseInvalidError ("OID '"
+                                                       + req.message.pdu.varbinds[i].oid + "' in request at "
+                                                       + "positiion '" + i + "' does not precede "
+                                                       + "OID '" + pdu.varbinds[i].oid + "' in response "
+                                                       + "at position '" + i + "'"));
+                                       return;
+                               } else {
+                                       varbinds.push (pdu.varbinds[i]);
+                               }
+                       }
+
+                       req.responseCb (null, varbinds);
+               }
+       };
+
+       var pduVarbinds = [];
+
+       for (var i = 0; i < oids.length; i++) {
+               var varbind = {
+                       oid: oids[i]
+               };
+               pduVarbinds.push (varbind);
+       }
+
+       this.simpleGet (GetNextRequestPdu, feedCb, pduVarbinds, responseCb);
+
+       return this;
+};
+
+Session.prototype.inform = function () {
+       var typeOrOid = arguments[0];;
+       var varbinds, options = {}, responseCb;
+
+       /**
+        ** Support the following signatures:
+        ** 
+        **    typeOrOid, varbinds, options, callback
+        **    typeOrOid, varbinds, callback
+        **    typeOrOid, options, callback
+        **    typeOrOid, callback
+        **/
+       if (arguments.length >= 4) {
+               varbinds = arguments[1];
+               options = arguments[2];
+               responseCb = arguments[3];
+       } else if (arguments.length >= 3) {
+               if (arguments[1].constructor != Array) {
+                       varbinds = [];
+                       options = arguments[1];
+                       responseCb = arguments[2];
+               } else {
+                       varbinds = arguments[1];
+                       responseCb = arguments[2];
+               }
+       } else {
+               varbinds = [];
+               responseCb = arguments[1];
+       }
+
+       function feedCb (req, message) {
+               var pdu = message.pdu;
+               var varbinds = [];
+
+               if (req.message.pdu.varbinds.length != pdu.varbinds.length) {
+                       req.responseCb (new ResponseInvalidError ("Inform OIDs do not "
+                                       + "match response OIDs"));
+               } else {
+                       for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
+                               if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) {
+                                       req.responseCb (new ResponseInvalidError ("OID '"
+                                                       + req.message.pdu.varbinds[i].oid
+                                                       + "' in inform at positiion '" + i + "' does not "
+                                                       + "match OID '" + pdu.varbinds[i].oid + "' in response "
+                                                       + "at position '" + i + "'"));
+                                       return;
+                               } else {
+                                       varbinds.push (pdu.varbinds[i]);
+                               }
+                       }
+
+                       req.responseCb (null, varbinds);
+               }
+       };
+
+       if (typeof typeOrOid != "string")
+               typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1);
+
+       var pduVarbinds = [
+               {
+                       oid: "1.3.6.1.2.1.1.3.0",
+                       type: ObjectType.TimeTicks,
+                       value: options.upTime || Math.floor (process.uptime () * 100)
+               },
+               {
+                       oid: "1.3.6.1.6.3.1.1.4.1.0",
+                       type: ObjectType.OID,
+                       value: typeOrOid
+               }
+       ];
+
+       for (var i = 0; i < varbinds.length; i++) {
+               var varbind = {
+                       oid: varbinds[i].oid,
+                       type: varbinds[i].type,
+                       value: varbinds[i].value
+               };
+               pduVarbinds.push (varbind);
+       }
+       
+       options.port = this.trapPort;
+
+       this.simpleGet (InformRequestPdu, feedCb, pduVarbinds, responseCb, options);
+
+       return this;
+};
+
+Session.prototype.onClose = function () {
+       this.cancelRequests (new Error ("Socket forcibly closed"));
+       this.emit ("close");
+};
+
+Session.prototype.onError = function (error) {
+       this.emit (error);
+};
+
+Session.prototype.onMsg = function (buffer, remote) {
+       try {
+               var message = new ResponseMessage (buffer);
+       } catch (error) {
+               this.emit("error", error);
+       }
+
+       var req = this.unregisterRequest (message.pdu.id);
+       if (! req)
+               return;
+
+       try {
+               if (message.version != req.message.version) {
+                       req.responseCb (req, new ResponseInvalidError ("Version in request '"
+                                       + req.message.version + "' does not match version in "
+                                       + "response '" + message.version));
+               } else if (message.community != req.message.community) {
+                       req.responseCb (req, new ResponseInvalidError ("Community '"
+                                       + req.message.community + "' in request does not match "
+                                       + "community '" + message.community + "' in response"));
+               } else if (message.pdu.type == PduType.GetResponse) {
+                       req.onResponse (req, message);
+               } else {
+                       req.responseCb (req, new ResponseInvalidError ("Unknown PDU type '"
+                                       + message.pdu.type + "' in response"));
+               }
+       } catch (error) {
+               req.responseCb (req, error);
+       }
+};
+
+Session.prototype.onSimpleGetResponse = function (req, message) {
+       var pdu = message.pdu;
+
+       if (pdu.errorStatus > 0) {
+               var statusString = ErrorStatus[pdu.errorStatus]
+                               || ErrorStatus.GeneralError;
+               var statusCode = ErrorStatus[statusString]
+                               || ErrorStatus[ErrorStatus.GeneralError];
+
+               if (pdu.errorIndex <= 0 || pdu.errorIndex > pdu.varbinds.length) {
+                       req.responseCb (new RequestFailedError (statusString, statusCode));
+               } else {
+                       var oid = pdu.varbinds[pdu.errorIndex - 1].oid;
+                       var error = new RequestFailedError (statusString + ": " + oid,
+                                       statusCode);
+                       req.responseCb (error);
+               }
+       } else {
+               req.feedCb (req, message);
+       }
+};
+
+Session.prototype.registerRequest = function (req) {
+       if (! this.reqs[req.id]) {
+               this.reqs[req.id] = req;
+               if (this.reqCount <= 0)
+                       this.dgram.ref();
+               this.reqCount++;
+       }
+       var me = this;
+       req.timer = setTimeout (function () {
+               if (req.retries-- > 0) {
+                       me.send (req);
+               } else {
+                       me.unregisterRequest (req.id);
+                       req.responseCb (new RequestTimedOutError (
+                                       "Request timed out"));
+               }
+       }, req.timeout);
+};
+
+Session.prototype.send = function (req, noWait) {
+       try {
+               var me = this;
+               
+               var buffer = req.message.toBuffer ();
+
+               this.dgram.send (buffer, 0, buffer.length, req.port, this.target,
+                               function (error, bytes) {
+                       if (error) {
+                               req.responseCb (error);
+                       } else {
+                               if (noWait) {
+                                       req.responseCb (null);
+                               } else {
+                                       me.registerRequest (req);
+                               }
+                       }
+               });
+       } catch (error) {
+               req.responseCb (error);
+       }
+       
+       return this;
+};
+
+Session.prototype.set = function (varbinds, responseCb) {
+       function feedCb (req, message) {
+               var pdu = message.pdu;
+               var varbinds = [];
+
+               if (req.message.pdu.varbinds.length != pdu.varbinds.length) {
+                       req.responseCb (new ResponseInvalidError ("Requested OIDs do not "
+                                       + "match response OIDs"));
+               } else {
+                       for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
+                               if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) {
+                                       req.responseCb (new ResponseInvalidError ("OID '"
+                                                       + req.message.pdu.varbinds[i].oid
+                                                       + "' in request at positiion '" + i + "' does not "
+                                                       + "match OID '" + pdu.varbinds[i].oid + "' in response "
+                                                       + "at position '" + i + "'"));
+                                       return;
+                               } else {
+                                       varbinds.push (pdu.varbinds[i]);
+                               }
+                       }
+
+                       req.responseCb (null, varbinds);
+               }
+       };
+
+       var pduVarbinds = [];
+
+       for (var i = 0; i < varbinds.length; i++) {
+               var varbind = {
+                       oid: varbinds[i].oid,
+                       type: varbinds[i].type,
+                       value: varbinds[i].value
+               };
+               pduVarbinds.push (varbind);
+       }
+
+       this.simpleGet (SetRequestPdu, feedCb, pduVarbinds, responseCb);
+
+       return this;
+};
+
+Session.prototype.simpleGet = function (pduClass, feedCb, varbinds,
+               responseCb, options) {
+       var req = {}
+
+       try {
+               var id = _generateId ();
+               var pdu = new pduClass (id, varbinds, options);
+               var message = new RequestMessage (this.version, this.community, pdu);
+
+               req = {
+                       id: id,
+                       message: message,
+                       responseCb: responseCb,
+                       retries: this.retries,
+                       timeout: this.timeout,
+                       onResponse: this.onSimpleGetResponse,
+                       feedCb: feedCb,
+                       port: (options && options.port) ? options.port : this.port
+               };
+
+               this.send (req);
+       } catch (error) {
+               if (req.responseCb)
+                       req.responseCb (error);
+       }
+};
+
+function subtreeCb (req, varbinds) {
+       var done = 0;
+
+       for (var i = varbinds.length; i > 0; i--) {
+               if (! oidInSubtree (req.baseOid, varbinds[i - 1].oid)) {
+                       done = 1;
+                       varbinds.pop ();
+               }
+       }
+
+       if (varbinds.length > 0)
+               req.feedCb (varbinds);
+
+       if (done)
+               return true;
+}
+
+Session.prototype.subtree  = function () {
+       var me = this;
+       var oid = arguments[0];
+       var maxRepetitions, feedCb, doneCb;
+
+       if (arguments.length < 4) {
+               maxRepetitions = 20;
+               feedCb = arguments[1];
+               doneCb = arguments[2];
+       } else {
+               maxRepetitions = arguments[1];
+               feedCb = arguments[2];
+               doneCb = arguments[3];
+       }
+
+       var req = {
+               feedCb: feedCb,
+               doneCb: doneCb,
+               maxRepetitions: maxRepetitions,
+               baseOid: oid
+       };
+
+       this.walk (oid, maxRepetitions, subtreeCb.bind (me, req), doneCb);
+
+       return this;
+}
+
+function tableColumnsResponseCb (req, error) {
+       if (error) {
+               req.responseCb (error);
+       } else if (req.error) {
+               req.responseCb (req.error);
+       } else {
+               if (req.columns.length > 0) {
+                       var column = req.columns.pop ();
+                       var me = this;
+                       this.subtree (req.rowOid + column, req.maxRepetitions,
+                                       tableColumnsFeedCb.bind (me, req),
+                                       tableColumnsResponseCb.bind (me, req));
+               } else {
+                       req.responseCb (null, req.table);
+               }
+       }
+}
+
+function tableColumnsFeedCb (req, varbinds) {
+       for (var i = 0; i < varbinds.length; i++) {
+               if (isVarbindError (varbinds[i])) {
+                       req.error = new RequestFailedError (varbindError (varbind[i]));
+                       return true;
+               }
+
+               var oid = varbinds[i].oid.replace (req.rowOid, "")
+               if (oid && oid != varbinds[i].oid) {
+                       var match = oid.match (/^(\d+)\.(.+)$/);
+                       if (match && match[1] > 0) {
+                               if (! req.table[match[2]])
+                                       req.table[match[2]] = {};
+                               req.table[match[2]][match[1]] = varbinds[i].value;
+                       }
+               }
+       }
+}
+
+Session.prototype.tableColumns = function () {
+       var me = this;
+
+       var oid = arguments[0];
+       var columns = arguments[1];
+       var maxRepetitions, responseCb;
+
+       if (arguments.length < 4) {
+               responseCb = arguments[2];
+               maxRepetitions = 20;
+       } else {
+               maxRepetitions = arguments[2];
+               responseCb = arguments[3];
+       }
+
+       var req = {
+               responseCb: responseCb,
+               maxRepetitions: maxRepetitions,
+               baseOid: oid,
+               rowOid: oid + ".1.",
+               columns: columns.slice(0),
+               table: {}
+       };
+
+       if (req.columns.length > 0) {
+               var column = req.columns.pop ();
+               this.subtree (req.rowOid + column, maxRepetitions,
+                               tableColumnsFeedCb.bind (me, req),
+                               tableColumnsResponseCb.bind (me, req));
+       }
+
+       return this;
+}
+
+function tableResponseCb (req, error) {
+       if (error)
+               req.responseCb (error);
+       else if (req.error)
+               req.responseCb (req.error);
+       else
+               req.responseCb (null, req.table);
+}
+
+function tableFeedCb (req, varbinds) {
+       for (var i = 0; i < varbinds.length; i++) {
+               if (isVarbindError (varbinds[i])) {
+                       req.error = new RequestFailedError (varbindError (varbind[i]));
+                       return true;
+               }
+
+               var oid = varbinds[i].oid.replace (req.rowOid, "")
+               if (oid && oid != varbinds[i].oid) {
+                       var match = oid.match (/^(\d+)\.(.+)$/);
+                       if (match && match[1] > 0) {
+                               if (! req.table[match[2]])
+                                       req.table[match[2]] = {};
+                               req.table[match[2]][match[1]] = varbinds[i].value;
+                       }
+               }
+       }
+}
+
+Session.prototype.table = function () {
+       var me = this;
+
+       var oid = arguments[0];
+       var maxRepetitions, responseCb;
+
+       if (arguments.length < 3) {
+               responseCb = arguments[1];
+               maxRepetitions = 20;
+       } else {
+               maxRepetitions = arguments[1];
+               responseCb = arguments[2];
+       }
+
+       var req = {
+               responseCb: responseCb,
+               maxRepetitions: maxRepetitions,
+               baseOid: oid,
+               rowOid: oid + ".1.",
+               table: {}
+       };
+
+       this.subtree (oid, maxRepetitions, tableFeedCb.bind (me, req),
+                       tableResponseCb.bind (me, req));
+
+       return this;
+}
+
+Session.prototype.trap = function () {
+       var req = {};
+
+       try {
+               var typeOrOid = arguments[0];
+               var varbinds, options = {}, responseCb;
+
+               /**
+                ** Support the following signatures:
+                ** 
+                **    typeOrOid, varbinds, options, callback
+                **    typeOrOid, varbinds, agentAddr, callback
+                **    typeOrOid, varbinds, callback
+                **    typeOrOid, agentAddr, callback
+                **    typeOrOid, options, callback
+                **    typeOrOid, callback
+                **/
+               if (arguments.length >= 4) {
+                       varbinds = arguments[1];
+                       if (typeof arguments[2] == "string") {
+                               options.agentAddr = arguments[2];
+                       } else if (arguments[2].constructor != Array) {
+                               options = arguments[2];
+                       }
+                       responseCb = arguments[3];
+               } else if (arguments.length >= 3) {
+                       if (typeof arguments[1] == "string") {
+                               varbinds = [];
+                               options.agentAddr = arguments[1];
+                       } else if (arguments[1].constructor != Array) {
+                               varbinds = [];
+                               options = arguments[1];
+                       } else {
+                               varbinds = arguments[1];
+                               agentAddr = null;
+                       }
+                       responseCb = arguments[2];
+               } else {
+                       varbinds = [];
+                       responseCb = arguments[1];
+               }
+
+               var pdu, pduVarbinds = [];
+
+               for (var i = 0; i < varbinds.length; i++) {
+                       var varbind = {
+                               oid: varbinds[i].oid,
+                               type: varbinds[i].type,
+                               value: varbinds[i].value
+                       };
+                       pduVarbinds.push (varbind);
+               }
+               
+               var id = _generateId ();
+
+               if (this.version == Version2c) {
+                       if (typeof typeOrOid != "string")
+                               typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1);
+
+                       pduVarbinds.unshift (
+                               {
+                                       oid: "1.3.6.1.2.1.1.3.0",
+                                       type: ObjectType.TimeTicks,
+                                       value: options.upTime || Math.floor (process.uptime () * 100)
+                               },
+                               {
+                                       oid: "1.3.6.1.6.3.1.1.4.1.0",
+                                       type: ObjectType.OID,
+                                       value: typeOrOid
+                               }
+                       );
+
+                       pdu = new TrapV2Pdu (id, pduVarbinds, options);
+               } else {
+                       pdu = new TrapPdu (typeOrOid, pduVarbinds, options);
+               }
+
+               var message = new RequestMessage (this.version, this.community, pdu);
+
+               req = {
+                       id: id,
+                       message: message,
+                       responseCb: responseCb,
+                       port: this.trapPort
+               };
+
+               this.send (req, true);
+       } catch (error) {
+               if (req.responseCb)
+                       req.responseCb (error);
+       }
+
+       return this;
+};
+
+Session.prototype.unregisterRequest = function (id) {
+       var req = this.reqs[id];
+       if (req) {
+               delete this.reqs[id];
+               clearTimeout (req.timer);
+               delete req.timer;
+               this.reqCount--;
+               if (this.reqCount <= 0)
+                       this.dgram.unref();
+               return req;
+       } else {
+               return null;
+       }
+};
+
+function walkCb (req, error, varbinds) {
+       var done = 0;
+       var oid;
+
+       if (error) {
+               if (error instanceof RequestFailedError) {
+                       if (error.status != ErrorStatus.NoSuchName) {
+                               req.doneCb (error);
+                               return;
+                       } else {
+                               // signal the version 1 walk code below that it should stop
+                               done = 1;
+                       }
+               } else {
+                       req.doneCb (error);
+                       return;
+               }
+       }
+
+       if (this.version == Version2c) {
+               for (var i = varbinds[0].length; i > 0; i--) {
+                       if (varbinds[0][i - 1].type == ObjectType.EndOfMibView) {
+                               varbinds[0].pop ();
+                               done = 1;
+                       }
+               }
+               if (req.feedCb (varbinds[0]))
+                       done = 1;
+               if (! done)
+                       oid = varbinds[0][varbinds[0].length - 1].oid;
+       } else {
+               if (! done) {
+                       if (req.feedCb (varbinds)) {
+                               done = 1;
+                       } else {
+                               oid = varbinds[0].oid;
+                       }
+               }
+       }
+
+       if (done)
+               req.doneCb (null);
+       else
+               this.walk (oid, req.maxRepetitions, req.feedCb, req.doneCb,
+                               req.baseOid);
+}
+
+Session.prototype.walk  = function () {
+       var me = this;
+       var oid = arguments[0];
+       var maxRepetitions, feedCb, doneCb, baseOid;
+
+       if (arguments.length < 4) {
+               maxRepetitions = 20;
+               feedCb = arguments[1];
+               doneCb = arguments[2];
+       } else {
+               maxRepetitions = arguments[1];
+               feedCb = arguments[2];
+               doneCb = arguments[3];
+       }
+
+       var req = {
+               maxRepetitions: maxRepetitions,
+               feedCb: feedCb,
+               doneCb: doneCb
+       };
+
+       if (this.version == Version2c)
+               this.getBulk ([oid], 0, maxRepetitions,
+                               walkCb.bind (me, req));
+       else
+               this.getNext ([oid], walkCb.bind (me, req));
+
+       return this;
+}
+
+/*****************************************************************************
+ ** Exports
+ **/
+
+exports.Session = Session;
+
+exports.createSession = function (target, community, version, options) {
+       return new Session (target, community, version, options);
+};
+
+exports.isVarbindError = isVarbindError;
+exports.varbindError = varbindError;
+
+exports.Version1 = Version1;
+exports.Version2c = Version2c;
+
+exports.ErrorStatus = ErrorStatus;
+exports.TrapType = TrapType;
+exports.ObjectType = ObjectType;
+
+exports.ResponseInvalidError = ResponseInvalidError;
+exports.RequestInvalidError = RequestInvalidError;
+exports.RequestFailedError = RequestFailedError;
+exports.RequestTimedOutError = RequestTimedOutError;
+
+/**
+ ** We've added this for testing.
+ **/
+exports.ObjectParser = {
+       readInt: readInt,
+       readUint: readUint
+};
index eeccdf7decc021d127bef496d27bf91679ad21f5..10f979b156c3e18bc681d996b7cd09410f1ea2fc 100755 (executable)
@@ -43,7 +43,7 @@ var netdata = {
        options: {\r
                filename: __filename,\r
                DEBUG: false,\r
-               update_every: 1000,\r
+               update_every: 1,\r
        },\r
 \r
        chartAlgorithms: {\r
@@ -66,8 +66,61 @@ var netdata = {
 \r
        processors: {\r
                http: {\r
+                       name: 'http',\r
 \r
-               },\r
+                       process: function(service, callback) {\r
+                               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request));\r
+\r
+                               var req = http.request(service.request, function(response) {\r
+                                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...');\r
+\r
+                                       var end = false;\r
+                                       var data = '';\r
+                                       response.setEncoding('utf8');\r
+\r
+                                       if(response.statusCode !== 200) {\r
+                                               if(end === false) {\r
+                                                       service.error('Got HTTP code ' + response.statusCode + ', failed to get data.');\r
+                                                       end = true;\r
+                                                       callback(null);\r
+                                               }\r
+                                       }\r
+\r
+                                       response.on('data', function(chunk) {\r
+                                               if(end === false) data += chunk;\r
+                                       });\r
+\r
+                                       response.on('error', function() {\r
+                                               if(end === false) {\r
+                                                       service.error(': Read error, failed to get data.');\r
+                                                       end = true;\r
+                                                       callback(null);\r
+                                               }\r
+                                       });\r
+\r
+                                       response.on('end', function() {\r
+                                               if(end === false) {\r
+                                                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');\r
+                                                       end = true;\r
+                                                       callback(data);\r
+                                               }\r
+                                       });\r
+                               });\r
+\r
+                               req.on('error', function(e) {\r
+                                       service.error('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);\r
+                                       callback(null);\r
+                               });\r
+\r
+                               // write data to request body\r
+                               if(typeof service.postData !== 'undefined' && service.request.method === 'POST') {\r
+                                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData);\r
+                                       req.write(service.postData);\r
+                               }\r
+\r
+                               req.end();\r
+                       }\r
+               }\r
        },\r
 \r
        stringify: function(obj) {\r
@@ -108,6 +161,7 @@ var netdata = {
                service.running = false;\r
                service.started = 0;\r
                service.ended = 0;\r
+               service.next_run = new Date().getTime();\r
 \r
                if(typeof service.update_every === 'undefined')\r
                        service.update_every = service.module.update_every;\r
@@ -118,106 +172,70 @@ var netdata = {
                if(typeof service.processor === 'undefined')\r
                        service.processor = netdata.processors.http;\r
 \r
+               if(service.update_every < netdata.options.update_every)\r
+                       service.update_every = netdata.options.update_every;\r
+\r
                service.commit = function() {\r
                        if(this.added !== true) {\r
                                this.added = true;\r
+                               \r
+                               var now = new Date().getTime();\r
+                               while( this.next_run < now )\r
+                                       this.next_run += (this.update_every * 1000);\r
+\r
                                netdata.services.push(this);\r
                                if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.');\r
                        }\r
                };\r
 \r
-               service._processResponse = function(response, callback) {\r
-                       this.running = false;\r
-                       this.ended = new Date().getTime();\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
-                       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
                service.execute = function(callback) {\r
                        this.module.active++;\r
                        this.running = true;\r
                        this.started = new Date().getTime();\r
                        this.updates++;\r
 \r
-                       if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': making request: ' + netdata.stringify(this.request));\r
+                       if(netdata.options.DEBUG === true)\r
+                               netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this));\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
+                       this.processor.process(this, function(response) {\r
+                               service.ended = new Date().getTime();\r
+                               service.duration = service.ended - service.started;\r
 \r
-                               var end = false;\r
-                               var data = '';\r
-                               response.setEncoding('utf8');\r
+                               if(typeof response === 'undefined')\r
+                                       response = null;\r
 \r
-                               if(response.statusCode !== 200) {\r
-                                       if(end === false) {\r
-                                               service.error('Got HTTP code ' + response.statusCode + ', failed to get data.');\r
-                                               end = true;\r
-                                               service._processResponse(null, callback);\r
-                                       }\r
-                               }\r
+                               if(response !== null)\r
+                                       service.errorClear();\r
 \r
-                               response.on('data', function(chunk) {\r
-                                       if(end === false) data += chunk;\r
-                               });\r
+                               if(netdata.options.DEBUG === true)\r
+                                       netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)');\r
 \r
-                               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
+                               callback(service, response);\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
+                               service.running = false;\r
+                               service.module.active--;\r
+                               if(service.module.active < 0) {\r
+                                       service.module.active = 0;\r
+                                       if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': active module counter below zero.');\r
+                               }\r
 \r
-                       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
+                               if(service.module.active === 0) {\r
+                                       // check if we run under configure\r
+                                       if(service.module.configure_callback !== null) {\r
+                                               if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': configuration finish callback called from processResponse().');\r
+                                               var ccallback = service.module.configure_callback;\r
+                                               service.module.configure_callback = null;\r
+                                               ccallback();\r
+                                       }\r
+                               }\r
                        });\r
-\r
-                       // 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
-                       req.end();\r
                };\r
 \r
                service.update = function() {\r
                        if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...');\r
 \r
                        this.module.update(this, function() {\r
-                               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
@@ -345,7 +363,7 @@ var netdata = {
                        if(typeof(netdata.charts[id]) === 'undefined') {\r
                                netdata.charts[id] = {\r
                                        _created: false,\r
-                                       _updated: false,\r
+                                       _updated: true,\r
                                        _began: false,\r
                                        _counter: 0,\r
                                        _last_updated: 0,\r
@@ -354,8 +372,8 @@ var netdata = {
                                        name: id,\r
                                        title: 'untitled chart',\r
                                        units: 'a unit',\r
-                                       family: id,\r
-                                       category: id,\r
+                                       family: '',\r
+                                       category: '',\r
                                        type: netdata.chartTypes.line,\r
                                        priority: 0,\r
                                        update_every: netdata.options.update_every,\r
@@ -408,7 +426,7 @@ var netdata = {
                        }\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
+                               if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every);\r
                                c.update_every = chart.update_every;\r
                                c._updated = true;\r
                        }\r
@@ -486,16 +504,21 @@ var netdata = {
                        var service = netdata.services[len];\r
 \r
                        if(service.enabled === false || service.running === true) continue;\r
-                       if(now - service.ended < service.update_every) continue;\r
+                       if(now <= service.next_run) continue;\r
 \r
                        service.update();\r
+\r
+                       now = new Date().getTime();\r
+                       while(service.next_run < now)\r
+                               service.next_run += (service.update_every * 1000);\r
                }\r
 \r
-               setTimeout(netdata.runAllServices, 100);\r
+               // 1/10th of update_every in pause\r
+               setTimeout(netdata.runAllServices, netdata.options.update_every * 100);\r
        },\r
 \r
        start: function() {\r
-               if(netdata.options.DEBUG === true) this.debug('started, services:');\r
+               if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services));\r
 \r
                if(this.services.length === 0) {\r
                        this.disableNodePlugin();\r
@@ -524,7 +547,7 @@ var netdata = {
                        },\r
                        agent: new http.Agent({\r
                                keepAlive: true,\r
-                               keepAliveMsecs: netdata.options.update_every,\r
+                               keepAliveMsecs: netdata.options.update_every * 1000,\r
                                maxSockets: 2, // it must be 2 to work\r
                                maxFreeSockets: 1\r
                        })\r
@@ -543,7 +566,7 @@ var netdata = {
                module.update_every = this.options.update_every;\r
 \r
                if(typeof config.update_every !== 'undefined')\r
-                       module.update_every = config.update_every * 1000;\r
+                       module.update_every = config.update_every;\r
 \r
                module.enable_autodetect = (config.enable_autodetect)?true:false;\r
 \r
index 72f998b4b016dfa85fc4488ac63042710c3fa89a..66d45472bcf35381286475f9d66ef55f33d380dc 100755 (executable)
@@ -32,7 +32,7 @@ if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plug
 var webbox = {
        name: __filename,
        enable_autodetect: true,
-       update_every: 1000,
+       update_every: 1,
 
        charts: {},
 
@@ -87,7 +87,7 @@ var webbox = {
                                                category: 'sma_webbox_' + service.name,                 // the category of the chart
                                                type: netdata.chartTypes.area,                                  // the type of the chart
                                                priority: 1000,                                                                 // 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
+                                               update_every: service.update_every,                             // the expected update frequency of the chart
                                                dimensions: {
                                                        'GriPwr': {
                                                                id: 'GriPwr',                                                           // the unique id of the dimension
@@ -123,7 +123,7 @@ var webbox = {
                                                category: 'sma_webbox_' + service.name,                 // the category of the chart
                                                type: netdata.chartTypes.area,                                  // the type of the chart
                                                priority: 1000,                                                                 // 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
+                                               update_every: service.update_every,                             // the expected update frequency of the chart
                                                dimensions: {
                                                        'GriEgyTdy': {
                                                                id: 'GriEgyTdy',                                                                // the unique id of the dimension
@@ -159,7 +159,7 @@ var webbox = {
                                                category: 'sma_webbox_' + service.name,                 // the category of the chart
                                                type: netdata.chartTypes.area,                                  // the type of the chart
                                                priority: 1000,                                                                 // 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
+                                               update_every: service.update_every,                             // the expected update frequency of the chart
                                                dimensions: {
                                                        'GriEgyTot': {
                                                                id: 'GriEgyTot',                                                                // the unique id of the dimension
@@ -211,11 +211,9 @@ var webbox = {
                        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;
 
-                               if(config.servers[len].update_every < 5000)
-                                       config.servers[len].update_every = 5000;
+                               if(config.servers[len].update_every < 5)
+                                       config.servers[len].update_every = 5;
 
                                this.serviceExecute(config.servers[len].name, config.servers[len].hostname, config.servers[len].update_every);
                                added++;
diff --git a/node.d/snmp.node.js b/node.d/snmp.node.js
new file mode 100755 (executable)
index 0000000..fd3cdf5
--- /dev/null
@@ -0,0 +1,327 @@
+'use strict';
+
+// This program will connect to one or more SNMP Agents
+
+// example configuration in /etc/netdata/snmp.conf
+/*
+{
+       "enable_autodetect": false,
+       "update_every": 5,
+       "servers": [
+               {
+                       "hostname": "10.11.12.8",
+                       "community": "public",
+                       "update_every": 10,
+                       "options": { "timeout": 10000 },
+                       "charts": {
+                               "snmp_switch.bandwidth_port1": {
+                                       "title": "Switch Bandwidth for port 1",
+                                       "units": "kilobits/s",
+                                       "type": "area",
+                                       "priority": 1,
+                                       "dimensions": {
+                                               "in": {
+                                                       "oid": ".1.3.6.1.2.1.2.2.1.10.1",
+                                                       "algorithm": "incremental",
+                                                       "multiplier": 8,
+                                                       "divisor": 1024
+                                               },
+                                               "out": {
+                                                       "oid": ".1.3.6.1.2.1.2.2.1.16.1",
+                                                       "algorithm": "incremental",
+                                                       "multiplier": -8,
+                                                       "divisor": 1024
+                                               }
+                                       }
+                               },
+                               "snmp_switch.bandwidth_port2": {
+                                       "title": "Switch Bandwidth for port 2",
+                                       "units": "kilobits/s",
+                                       "type": "area",
+                                       "priority": 1,
+                                       "dimensions": {
+                                               "in": {
+                                                       "oid": ".1.3.6.1.2.1.2.2.1.10.2",
+                                                       "algorithm": "incremental",
+                                                       "multiplier": 8,
+                                                       "divisor": 1024
+                                               },
+                                               "out": {
+                                                       "oid": ".1.3.6.1.2.1.2.2.1.16.2",
+                                                       "algorithm": "incremental",
+                                                       "multiplier": -8,
+                                                       "divisor": 1024
+                                               }
+                                       }
+                               }
+                       }
+               }
+       ]
+}
+*/
+
+// You can also give ranges of charts like the following.
+// This will append 1-24 to id, title, oid (on each dimension)
+// so that 24 charts will be created.
+/*
+{
+       "enable_autodetect": false,
+       "update_every": 10,
+       "servers": [
+               {
+                       "hostname": "10.11.12.8",
+                       "community": "public",
+                       "update_every": 10,
+                       "options": { "timeout": 20000 },
+                       "charts": {
+                               "snmp_switch.bandwidth_port": {
+                                       "title": "Switch Bandwidth for port ",
+                                       "units": "kilobits/s",
+                                       "type": "area",
+                                       "priority": 1,
+                                       "multiply_range": [ 1, 24 ],
+                                       "dimensions": {
+                                               "in": {
+                                                       "oid": "1.3.6.1.2.1.2.2.1.10",
+                                                       "algorithm": "incremental",
+                                                       "multiplier": 8,
+                                                       "divisor": 1024
+                                               },
+                                               "out": {
+                                                       "oid": "1.3.6.1.2.1.2.2.1.16",
+                                                       "algorithm": "incremental",
+                                                       "multiplier": -8,
+                                                       "divisor": 1024
+                                               }
+                                       }
+                               }
+                       }
+               }
+       ]
+}
+*/
+
+var net_snmp = require('net-snmp');
+var extend = require('extend');
+var netdata = require('netdata');
+
+if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');
+
+netdata.processors.snmp = {
+       name: 'snmp',
+
+       process: function(service, callback) {
+               if(typeof service.snmp_oids === 'undefined' || service.snmp_oids === null || service.snmp_oids.length === 0) {
+                       // this is the first time we see this service
+
+                       if(netdata.options.DEBUG === true)
+                               netdata.debug(service.module.name + ': ' + service.name + ': preparing ' + this.name + ' OIDs');
+
+                       // build an index of all OIDs
+                       service.snmp_oids_index = {};
+                       for(var c in service.request.charts) {
+                               // for each chart
+
+                               if(netdata.options.DEBUG === true)
+                                       netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c);
+
+                               for(var d in service.request.charts[c].dimensions) {
+                                       // for each dimension in the chart
+
+                                       if(netdata.options.DEBUG === true)
+                                               netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c + ', dimension: ' + d + ', OID: ' + service.request.charts[c].dimensions[d].oid);
+
+                                       // link it to the point we need to set the value to
+                                       service.snmp_oids_index[service.request.charts[c].dimensions[d].oid] = service.request.charts[c].dimensions[d];
+
+                                       // and set the value to null
+                                       service.request.charts[c].dimensions[d].value = null;
+                               }
+                       }
+                       if(netdata.options.DEBUG === true)
+                               netdata.debug(service.module.name + ': ' + service.name + ': indexed ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids_index));
+
+                       // now create the array of OIDs needed by net-snmp
+                       service.snmp_oids = new Array();
+                       for(var o in service.snmp_oids_index)
+                               service.snmp_oids.push(o);
+
+                       if(netdata.options.DEBUG === true)
+                               netdata.debug(service.module.name + ': ' + service.name + ': final list of ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids));
+               }
+
+               if(service.snmp_oids.length === 0) {
+                       // no OIDs found for this service
+
+                       if(netdata.options.DEBUG === true)
+                               service.error('no OIDs to process.');
+
+                       callback(null);
+                       return;
+               }
+
+               if(typeof service.snmp_session === 'undefined' || service.snmp_session === null) {
+                       // no SNMP session has been created for this service
+                       // the SNMP session is just the initialization of NET-SNMP
+
+                       if(netdata.options.DEBUG === true)
+                               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));
+
+                       // create the SNMP session
+                       service.snmp_session = net_snmp.createSession (service.request.hostname, service.request.community, service.request.options);
+
+                       if(netdata.options.DEBUG === true)
+                               netdata.debug(service.module.name + ': ' + service.name + ': got ' + this.name + ' session: ' + netdata.stringify(service.snmp_session));
+
+                       // if we later need traps, this is how to do it:
+                       //service.snmp_session.trap(net_snmp.TrapType.LinkDown, function(error) {
+                       //      if(error) console.error('trap error: ' + netdata.stringify(error));
+                       //});
+               }
+
+               // do it, get the SNMP values for the sessions we need
+               service.snmp_session.get(service.snmp_oids, function(error, varbinds) {
+                       var ok = 0, failed = 0;
+
+                       if(error) {
+                               service.error('Received error = ' + netdata.stringify(error) + ' varbinds = ' + netdata.stringify(varbinds));
+                       }
+                       else {
+                               if(netdata.options.DEBUG === true)
+                                       netdata.debug(service.module.name + ': ' + service.name + ': got valid ' + service.module.name + ' response: ' + netdata.stringify(varbinds));
+
+                               for(var i = 0; i < varbinds.length; i++) {
+                                       if(net_snmp.isVarbindError(varbinds[i])) {
+                                               if(netdata.options.DEBUG === true)
+                                                       netdata.debug(service.module.name + ': ' + service.name + ': failed ' + service.module.name + ' get for OIDs ' + varbinds[i].oid);
+
+                                               service.error('OID ' + varbinds[i].oid + ' gave error: ' + snmp.varbindError(varbinds[i]));
+                                               service.snmp_oids_index[varbinds[i].oid].value = null;
+                                               failed++;
+                                       }
+                                       else {
+                                               if(netdata.options.DEBUG === true)
+                                                       netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value);
+
+                                               service.snmp_oids_index[varbinds[i].oid].value = varbinds[i].value;
+                                               ok++;
+                                       }
+                               }
+
+                               if(netdata.options.DEBUG === true)
+                                       netdata.debug(service.module.name + ': ' + service.name + ': finished ' + service.module.name + ' with ' + ok + ' successful and ' + failed + ' failed values');
+                       }
+                       
+                       callback((ok > 0)?{ ok: ok, failed: failed }:null);
+               });
+       }
+};
+
+var snmp = {
+       name: __filename,
+       enable_autodetect: true,
+       update_every: 1,
+
+       charts: {},
+
+       processResponse: function(service, data) {
+               if(data !== null) {
+                       if(service.added !== true)
+                               service.commit();
+
+                       for(var c in service.request.charts) {
+                               var chart = snmp.charts[c];
+
+                               if(typeof chart === 'undefined') {
+                                       chart = service.chart(c, service.request.charts[c]);
+                                       snmp.charts[c] = chart;
+                               }
+
+                               service.begin(chart);
+                               
+                               for( var d in service.request.charts[c].dimensions )
+                                       if(service.request.charts[c].dimensions[d].value !== null)
+                                               service.set(d, service.request.charts[c].dimensions[d].value);
+
+                               service.end();
+                       }
+               }
+       },
+
+       // module.serviceExecute()
+       // this function is called only from this module
+       // its purpose is to prepare the request and call
+       // netdata.serviceExecute()
+       serviceExecute: function(conf) {
+               if(netdata.options.DEBUG === true)
+                       netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', update_every: ' + conf.update_every);
+
+               var service = netdata.service({
+                       name: conf.hostname,
+                       request: conf,
+                       update_every: conf.update_every,
+                       module: this,
+                       processor: netdata.processors.snmp
+               });
+
+               // multiply the charts, if required
+               for(var c in service.request.charts) {
+                       if(netdata.options.DEBUG === true)
+                               netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', examining chart: ' + c);
+
+                       if(typeof service.request.charts[c].update_every === 'undefined')
+                               service.request.charts[c].update_every = service.update_every;
+                       
+                       if(typeof service.request.charts[c].multiply_range !== 'undefined') {
+                               var from = service.request.charts[c].multiply_range[0];
+                               var to = service.request.charts[c].multiply_range[1];
+                               var prio = service.request.charts[c].priority || 1;
+
+                               while(from <= to) {
+                                       var id = c + from.toString();
+                                       var chart = extend(true, {}, service.request.charts[c]);
+                                       chart.title += from.toString();
+                                       chart.priority = prio++;
+                                       for(var d in chart.dimensions) {
+                                               chart.dimensions[d].oid += '.' + from.toString();
+                                       }
+                                       service.request.charts[id] = chart;
+                                       from++;
+                               }
+
+                               delete service.request.charts[c];
+                       }
+               }
+
+               service.execute(this.processResponse);
+       },
+
+       configure: function(config) {
+               var added = 0;
+
+               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;
+
+                               this.serviceExecute(config.servers[len]);
+                               added++;
+                       }
+               }
+
+               return added;
+       },
+
+       // module.update()
+       // this is called repeatidly to collect data, by calling
+       // service.execute()
+       update: function(service, callback) {
+               service.execute(function(serv, data) {
+                       service.module.processResponse(serv, data);
+                       callback();
+               });
+       },
+};
+
+module.exports = snmp;
index 13ac7aa8824394ea593c5b2773904a39d14fcc51..96e00694e463b602995436218cf1193ab97e3023 100755 (executable)
@@ -9,7 +9,6 @@ var NETDATA_PLUGINS_DIR = process.env.NETDATA_PLUGINS_DIR || __dirname;
 var NETDATA_CONFIG_DIR = process.env.NETDATA_CONFIG_DIR || '/etc/netdata';
 var NETDATA_UPDATE_EVERY = process.env.NETDATA_UPDATE_EVERY || 1;
 var NODE_D_DIR = NETDATA_PLUGINS_DIR + '/../node.d';
-NETDATA_UPDATE_EVERY = NETDATA_UPDATE_EVERY * 1000;
 
 // make sure the modules are found
 process.mainModule.paths.unshift(NODE_D_DIR + '/node_modules');
@@ -133,14 +132,14 @@ function dumpError(err) {
                                                try {
                                                        var x = parseInt(val);
                                                        if(x > 0) {
-                                                               netdata.options.update_every = x * 1000;
+                                                               netdata.options.update_every = x;
                                                                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');
+                                                               netdata.debug('Update frequency set to ' + netdata.options.update_every + ' seconds');
                                                        }
                                                        else netdata.error('Ignoring parameter: ' + val);
                                                }
@@ -155,9 +154,9 @@ function dumpError(err) {
        });
 }
 
-if(netdata.options.update_every < 1000) {
+if(netdata.options.update_every < 1) {
        netdata.debug('Adjusting update frequency to 1 second');
-       netdata.options.update_every = 1000;
+       netdata.options.update_every = 1;
 }
 
 // --------------------------------------------------------------------------------------------------------------------
@@ -174,7 +173,7 @@ function findModules() {
                        var n = files[len].substring(0, m.index);
 
                        if(typeof(netdata.options.modules[n]) === 'undefined')
-                               netdata.options.modules[n] = { enabled: netdata.options.modules_enable_all };
+                               netdata.options.modules[n] = { name: n, enabled: netdata.options.modules_enable_all };
 
                        if(netdata.options.modules[n].enabled === true) {
                                netdata.options.modules[n].name = n;
@@ -201,7 +200,7 @@ function findModules() {
                                // load its configuration
                                var c = {
                                        enable_autodetect: netdata.options.modules_enable_autodetect,
-                                       update_every: Math.round(netdata.options.update_every / 1000)
+                                       update_every: netdata.options.update_every
                                };
                                try {
                                        netdata.debug('loading module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
@@ -239,6 +238,7 @@ function findModules() {
                }
        }
 
+       // netdata.debug(netdata.options.modules);
        return found;
 }