Skip to content

Instantly share code, notes, and snippets.

@cidrblock
Created November 2, 2016 11:34
Show Gist options
  • Save cidrblock/1e459d2a0afc213c438f8fd3d698a1cd to your computer and use it in GitHub Desktop.
Save cidrblock/1e459d2a0afc213c438f8fd3d698a1cd to your computer and use it in GitHub Desktop.
snmpms.js
// Int64.js
//
// Copyright (c) 2012 Robert Kieffer
// MIT License - http://opensource.org/licenses/mit-license.php
/**
* Support for handling 64-bit int numbers in Javascript (node.js)
*
* JS Numbers are IEEE-754 binary double-precision floats, which limits the
* range of values that can be represented with integer precision to:
*
* 2^^53 <= N <= 2^53
*
* Int64 objects wrap a node Buffer that holds the 8-bytes of int64 data. These
* objects operate directly on the buffer which means that if they are created
* using an existing buffer then setting the value will modify the Buffer, and
* vice-versa.
*
* Internal Representation
*
* The internal buffer format is Big Endian. I.e. the most-significant byte is
* at buffer[0], the least-significant at buffer[7]. For the purposes of
* converting to/from JS native numbers, the value is assumed to be a signed
* integer stored in 2's complement form.
*
* For details about IEEE-754 see:
* http://en.wikipedia.org/wiki/Double_precision_floating-point_format
*/
// Useful masks and values for bit twiddling
var MASK31 = 0x7fffffff, VAL31 = 0x80000000;
var MASK32 = 0xffffffff, VAL32 = 0x100000000;
// Map for converting hex octets to strings
var _HEX = [];
for (var i = 0; i < 256; i++) {
_HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);
}
//
// Int64
//
/**
* Constructor accepts any of the following argument types:
*
* new Int64(buffer[, offset=0]) - Existing Buffer with byte offset
* new Int64(string) - Hex string (throws if n is outside int64 range)
* new Int64(number) - Number (throws if n is outside int64 range)
* new Int64(hi, lo) - Raw bits as two 32-bit values
*/
var Int64 = module.exports = function(a1, a2) {
if (a1 instanceof Buffer) {
this.buffer = a1;
this.offset = a2 || 0;
} else {
this.buffer = this.buffer || new Buffer(8);
this.offset = 0;
this.setValue.apply(this, arguments);
}
};
// Max integer value that JS can accurately represent
Int64.MAX_INT = Math.pow(2, 53);
// Min integer value that JS can accurately represent
Int64.MIN_INT = -Math.pow(2, 53);
Int64.prototype = {
/**
* Do in-place 2's compliment. See
* http://en.wikipedia.org/wiki/Two's_complement
*/
_2scomp: function() {
var b = this.buffer, o = this.offset, carry = 1;
for (var i = o + 7; i >= o; i--) {
var v = (b[i] ^ 0xff) + carry;
b[i] = v & 0xff;
carry = v >> 8;
}
},
/**
* Set the value. Takes any of the following arguments:
*
* setValue(string) - A hexidecimal string
* setValue(number) - Number (throws if n is outside int64 range)
* setValue(hi, lo) - Raw bits as two 32-bit values
*/
setValue: function(hi, lo) {
var negate = false;
if (arguments.length == 1) {
if (typeof(hi) == 'number') {
// Simplify bitfield retrieval by using abs() value. We restore sign
// later
negate = hi < 0;
hi = Math.abs(hi);
lo = hi % VAL32;
hi = hi / VAL32;
if (hi > VAL32) throw new RangeError(hi + ' is outside Int64 range');
hi = hi | 0;
} else if (typeof(hi) == 'string') {
hi = (hi + '').replace(/^0x/, '');
lo = hi.substr(-8);
hi = hi.length > 8 ? hi.substr(0, hi.length - 8) : '';
hi = parseInt(hi, 16);
lo = parseInt(lo, 16);
} else {
throw new Error(hi + ' must be a Number or String');
}
}
// Technically we should throw if hi or lo is outside int32 range here, but
// it's not worth the effort. Anything past the 32'nd bit is ignored.
// Copy bytes to buffer
var b = this.buffer, o = this.offset;
for (var i = 7; i >= 0; i--) {
b[o+i] = lo & 0xff;
lo = i == 4 ? hi : lo >>> 8;
}
// Restore sign of passed argument
if (negate) this._2scomp();
},
/**
* Convert to a native JS number.
*
* WARNING: Do not expect this value to be accurate to integer precision for
* large (positive or negative) numbers!
*
* @param allowImprecise If true, no check is performed to verify the
* returned value is accurate to integer precision. If false, imprecise
* numbers (very large positive or negative numbers) will be forced to +/-
* Infinity.
*/
toNumber: function(allowImprecise) {
var b = this.buffer, o = this.offset;
// Running sum of octets, doing a 2's complement
var negate = b[o] & 0x80, x = 0, carry = 1;
for (var i = 7, m = 1; i >= 0; i--, m *= 256) {
var v = b[o+i];
// 2's complement for negative numbers
if (negate) {
v = (v ^ 0xff) + carry;
carry = v >> 8;
v = v & 0xff;
}
x += v * m;
}
// Return Infinity if we've lost integer precision
if (!allowImprecise && x >= Int64.MAX_INT) {
return negate ? -Infinity : Infinity;
}
return negate ? -x : x;
},
/**
* Convert to a JS Number. Returns +/-Infinity for values that can't be
* represented to integer precision.
*/
valueOf: function() {
return this.toNumber(false);
},
/**
* Return string value
*
* @param radix Just like Number#toString()'s radix
*/
toString: function(radix) {
//return this.valueOf().toString(radix || 10);
return require('biginteger').BigInteger.parse(this.toOctetString(), 16).toString(radix);
},
/**
* Return a string showing the buffer octets, with MSB on the left.
*
* @param sep separator string. default is '' (empty string)
*/
toOctetString: function(sep) {
var out = new Array(8);
var b = this.buffer, o = this.offset;
for (var i = 0; i < 8; i++) {
out[i] = _HEX[b[o+i]];
}
return out.join(sep || '');
},
/**
* Pretty output in console.log
*/
inspect: function() {
return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']';
}
};
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var snmp = require ("net-snmp");
var port = process.env.PORT || 8080;
var router = express.Router();
var Int64 = require('./Int64')
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
router.post('/poll', function(req, res) {
if (req.body.type == "singles") {
singlePoll(req.body)
} else if (req.body.type == "table") {
tablePoll(req.body)
}
res.json({ message: 'ok' });
});
app.use('/', router);
app.listen(port);
function singlePoll(request) {
console.log(request)
var session = snmp.createSession (request.device, request.community);
var oids = request.oids;
session.get (oids, function (error, varbinds) {
if (error) {
console.error (error);
} else {
for (var i = 0; i < varbinds.length; i++)
if (snmp.isVarbindError (varbinds[i]))
console.error (snmp.varbindError (varbinds[i]))
else
console.log (varbinds[i].oid + " = " + varbinds[i].value);
}
});
}
function tablePoll(request) {
var options = {
port: 161,
retries: 1,
timeout: 5000,
transport: "udp4",
version: snmp.Version2c
};
var session = snmp.createSession (request.device, request.community, options);
var oid = request.oid;
var columns = Object.keys(request.columns);
function render(value, type) {
switch(type) {
case "OctetString":
return String(value)
break;
case "Counter64":
x = new Int64(new Buffer(value))
return x.toString();
break;
case "Integer":
return value;
break;
}
}
function responseCb (error, table) {
results = {}
if (error) {
console.error (error.toString ());
} else {
var metrics = []
for (i in table) {
for (j in request.metrics) {
var column = request.columns[request.metrics[j]]
var metric = []
for (k in request.metricFormat) {
switch(request.metricFormat[k].type) {
case "key":
metric.push(request[request.metricFormat[k].value]);
break;
case "string":
metric.push(request.metricFormat[k].value);
break;
case "reference":
var value = table[i][request.metricFormat[k].column]
var type = (request.columns[request.metricFormat[k].column].type)
metric.push(render(value,type));
break;
case "metric":
metric.push(column.name);
break;
} //switch
} // for (k in request.metricFormat)
var value = render(table[i][request.metrics[j]], column.type)
metrics.push(metric.join('.') + " " + value)
} // for (j in request.metrics)
}
console.log(metrics)
} // else
}
var maxRepetitions = 5000;
session.tableColumns (oid, columns, maxRepetitions, responseCb);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment