domotz-remote-pawn
Version:
Domotz Agent
214 lines (188 loc) • 9.2 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2020 Domotz UK LLP
*
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
**/
var GET_HARD_TIMEOUT_SETTING = 'configuration.settings.snmp.hard_timeout.get';
var DEFAULT_GET_HARD_TIMEOUT_VALUE = 12000; // more than DEFAULT_GET_SOFT_TIMEOUT_VALUE * 2 due to default retries = 1
var GET_SOFT_TIMEOUT_SETTING = 'configuration.settings.snmp.soft_timeout.get';
var DEFAULT_GET_SOFT_TIMEOUT_VALUE = 5000;
var TimedSnmpSession = require('./timedSnmpSession');
var snmpGetChunked = function (message, resourceLocator) {
var snmp = resourceLocator.netSnmp;
var cmdUtils = resourceLocator.utils.command;
var snmpCommon = resourceLocator.snmpCommon;
var async = resourceLocator.async;
var commandBuilder = resourceLocator.snmpCommandBuilder;
var snmpCommonTransformer = resourceLocator.snmpCommon;
var settingsManager = resourceLocator.settingsManager;
var chunkSize = settingsManager.getSetting('configuration.settings.snmp.get_chunked.chunk_size', 1);
var cid = message.correlationId;
var myConsole = resourceLocator.log.decorateLogs().decorate(String(cid));
myConsole.info('Init (message %s)', JSON.stringify(message));
var options = message.payload;
var oidsMapping = cmdUtils.extractFromOptions(options, 'oids', {});
var host = cmdUtils.extractFromOptions(options, 'host', 'localhost');
var oids = snmpCommon.extractOids(oidsMapping);
var transformer = message.customTransformer ? message.customTransformer : snmpCommon.transformer;
var hardTimeout = parseInt(settingsManager.getSetting(GET_HARD_TIMEOUT_SETTING, DEFAULT_GET_HARD_TIMEOUT_VALUE));
var softTimeout = parseInt(settingsManager.getSetting(GET_SOFT_TIMEOUT_SETTING, DEFAULT_GET_SOFT_TIMEOUT_VALUE));
options.timeout = options.timeout || softTimeout;
myConsole.debug('Hard Timeout %s, Soft Timeout %s ', hardTimeout, options.timeout);
function extractOidResponse(mainOid, response) {
myConsole.debug('Got response: %s', JSON.stringify(response));
var values = {};
for (var i = 0; i < response.length; i++) {
var oid = response[i].oid;
if (!snmp.isVarbindError(response[i])) {
var value = transformer(oidsMapping[oid])(response[i].value, response[i].type);
myConsole.verbose('SNMP value retrieved: %s=%s', oid, value);
values[oid] = value;
} else {
myConsole.warn('SNMP get - error retrieving oid: %s => varbind error: %s', oid, snmp.varbindError(response[i]));
}
}
myConsole.verbose('Partial value (host %s) (values %s)', host, JSON.stringify(values));
return values;
}
var executeGet = function (host, oid, authProvider, cb) {
var bindings = snmpCommon.getInterfaceBindings(options, resourceLocator);
var snmpSession = null;
try {
snmpSession = snmpCommon.getSession(bindings, host, options, authProvider);
} catch (error) {
cb({ error: error.toString() });
return;
}
myConsole.info('Sending SNMP GET (snmp-version %d) (host %s) (oid %s)', snmpSession.version, host, JSON.stringify(oid));
var callbackHasBeenCalled = false;
function _cb(error, result) {
if (!callbackHasBeenCalled) {
callbackHasBeenCalled = true;
cb(error, result);
} else {
myConsole.debug('Ignoring async callback if called again');
}
}
snmpSession.on('error', function (error) {
var errorMessage = error.toString();
myConsole.error('socket error %s', errorMessage);
if (error.name === 'RangeError') {
myConsole.error('RangeError Detected, falling back to getNative (socket error) %s', errorMessage);
var oidToSend = {};
oidToSend[oid] = oidsMapping[oid];
var snmpCommand = commandBuilder.buildBulkGetCommand(oidToSend, host, options.timeout);
snmpCommand.customTransformer = snmpCommonTransformer.transformerTypeInvariant;
myConsole.info('Using the getNative (command %s)', JSON.stringify(snmpCommand));
var getNative = resourceLocator.snmpGetNative(JSON.parse(JSON.stringify(snmpCommand)), resourceLocator);
var parserGetNative = function (rawResult) {
myConsole.info('getNative received result (values %s)', JSON.stringify(rawResult));
if (rawResult.oids !== undefined) {
_cb(null, rawResult.oids);
} else {
_cb(null, {});
}
try {
snmpSession.close();
} catch (error) {
myConsole.error('Cannot close session: ' + error);
}
};
getNative.execute(parserGetNative);
} else {
try {
snmpSession.close();
} catch (error) {
myConsole.error('Cannot close session: ' + error);
} finally {
_cb(null, {});
}
}
});
var timedSnmpSession = new TimedSnmpSession(snmpSession, { host: host, oids: oids }, myConsole);
timedSnmpSession.get(
[oid],
function (error, response) {
if (error) {
myConsole.info('(%s) - (%s)(%s): ERROR in response: %s', host, cid, oid, error.toString());
_cb(null, {});
} else if (response !== null && response !== undefined) {
_cb(null, extractOidResponse(oid, response));
} else {
myConsole.info('(%s): Set oid invalid', oid);
_cb(null, {});
}
try {
snmpSession.close();
} catch (error) {
myConsole.error('cannot close session: ' + error);
}
},
hardTimeout
);
};
return {
execute: function (callback, authProvider) {
var allFunctions = [];
var chunks = [];
for (var i = 0; i < oids.length; i += chunkSize) {
chunks.push(oids.slice(i, i + chunkSize));
}
chunks.forEach(function (tempOIDs) {
var fChunk = function (cb) {
var functions = [];
for (var z = 0; z < tempOIDs.length; z++) {
var oid = tempOIDs[z];
functions.push(executeGet.bind(null, host, oid, authProvider));
}
async.parallel(functions, function (err, results) {
if (err !== null) {
// This should not happen
cb(err, results);
} else {
cb(null, results);
}
});
};
allFunctions.push(fChunk);
});
async.series(allFunctions, function (err, results) {
if (err !== null) {
myConsole.info('Final Result (host %s) error', host);
callback({ error: 'Cant retrieve oids' });
} else {
var totalResult = {};
for (var i = 0; i < results.length; i++) {
for (var j = 0; j < results[i].length; j++) {
myConsole.verbose('Checking values (host %s) (values %s)', host, JSON.stringify(results[i][j]));
Object.assign(totalResult, results[i][j]);
}
}
myConsole.info('Final Result (host %s) (result %s)', host, JSON.stringify(totalResult));
if (Object.keys(totalResult).length === 0) {
callback({ error: 'Nothing retrieved' });
} else {
callback({ oids: totalResult });
}
}
});
},
describe: function () {
return cid + ' - snmpGetChunked - ' + host + ' - ' + JSON.stringify(oids);
},
// Testing only
_executeGet: executeGet,
};
};
module.exports.command = snmpGetChunked;