domotz-remote-pawn
Version:
Domotz Agent
214 lines (187 loc) • 8.31 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2017 Domotz Ltd
*
* 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/>.
*
*
* Created by Alessio Sanfratello <asanfratello@domotz.com> on 09/02/17.
*/
const DISCOVERY_SEMAPHORE = 'SNMP_CUSTOM_SEMAPHORE';
const snmpCommonDiscovery = require('./snmp_common');
var snmpDiscoveryFactory = function (resourceLocator, commandBuilder, responseRendering, snmpGetterFactory) {
'use strict';
var mutex = resourceLocator.utils.mutex;
var q = resourceLocator.q;
var engineRequest = resourceLocator.engineRequest;
var snmpCommonTransformer = resourceLocator.snmpCommon;
var snmpDiscoverySnapshotData = {};
var macToOidDict = null;
var myConsole = resourceLocator.log.decorateLogs();
var interfacesBinding = resourceLocator.interfacesBindingStorage;
var snmpCommon = snmpCommonDiscovery.snmpCommonOptions();
var lodash = resourceLocator.lodash;
var async = resourceLocator.async;
var snmpCheckStatus = resourceLocator.discovery.snmp_check_status_discovery;
var settingsManager = resourceLocator.settingsManager;
var ipConflict = resourceLocator.networkTools.ipConflictDetection;
function invalidate() {
macToOidDict = null;
}
var lockDiscovery = function () {
return mutex.lock(DISCOVERY_SEMAPHORE, 1);
};
var unlockDiscovery = function () {
mutex.unlock(DISCOVERY_SEMAPHORE, 1);
};
var deferredSnmpBulkGet = function (snmpCommand, host, options, mac, cb) {
var commandOptions = snmpCommon.getSnmpOptions(options);
myConsole.debug('Command Options are %s -> %s', mac, JSON.stringify(commandOptions));
var callbackCalled = false;
snmpGetterFactory(snmpCommand, resourceLocator).execute(function (result) {
if (result.error) {
if (!callbackCalled) {
callbackCalled = true;
cb(null, null);
}
return;
}
myConsole.debug('Received result for %s: %s Bytes', host, JSON.stringify(result).length);
result.host = host;
result.mac = mac;
if (!callbackCalled) {
callbackCalled = true;
cb(null, result);
}
}, commandOptions);
};
var renderDiscoveryData = function (rawResultList) {
myConsole.debug('Rendering discover raw list : |%s|', JSON.stringify(rawResultList));
rawResultList.forEach(function (rawResult) {
var ipAddress = rawResult.host;
var mac = rawResult.mac;
var data = responseRendering(resourceLocator, rawResult);
myConsole.debug('Parsed Result: %s - |%s|', ipAddress, JSON.stringify(data));
if (data.error === false) {
snmpDiscoverySnapshotData[mac] = data.oids;
}
});
};
var doDiscovery = function (callback) {
snmpDiscoverySnapshotData = {};
var communitiesProvider = resourceLocator.snmpCommunitiesProvider;
return communitiesProvider.get().then(function (communities) {
myConsole.debug('Found communities %s ', JSON.stringify(communities));
var functions = [];
interfacesBinding.listFresh().forEach(function (device) {
if (ipConflict.checkIfIpInConflict(device.ip)) {
myConsole.warn('Skip SNMP custom discovery for conflicting IP: %s', device.ip);
return;
}
if (macToOidDict[device.mac] === undefined) {
return;
}
if (!snmpCheckStatus.isSNMPEnabledForMacAddress(device.mac)) {
return;
}
myConsole.info('Gathering information for %s', device.ip);
var snmpCommand = commandBuilder.buildBulkGetCommand(macToOidDict[device.mac], device.ip);
snmpCommand.customTransformer = snmpCommonTransformer.transformerTypeInvariant;
functions.push(deferredSnmpBulkGet.bind(null, snmpCommand, device.ip, communities[device.mac], device.mac));
});
var maxParallel = settingsManager.getSetting('configuration.settings.snmp_custom_discovery.parallel_limit', 10);
var startTime = new Date();
async.parallelLimit(functions, maxParallel, function (error, results) {
var elapsed = (new Date() - startTime) / 1000;
myConsole.info('discovery completed in %ss. Parallelism is %s', elapsed, maxParallel);
if (error) {
unlockDiscovery();
if (callback) {
callback(error);
}
return;
}
var filteredResults = lodash.filter(results, function (v) {
return v !== null;
});
renderDiscoveryData(filteredResults);
unlockDiscovery();
if (callback) {
callback(null, snmpDiscoverySnapshotData);
}
});
});
};
var discoverCustom = function (callback) {
if (!lockDiscovery()) {
myConsole.warn('WARNING: SNMP Custom Discovery already in progress, ignoring the call');
return q('BUSY');
}
return getOidListToDiscover().then(function () {
return doDiscovery(callback);
});
};
function countOIDs(data) {
return lodash.reduce(
data,
function (accumulator, oids) {
return accumulator + Object.keys(oids).length;
},
0
);
}
var getOidListToDiscover = function () {
if (macToOidDict === null) {
myConsole.debug('Requesting OID sensors to engine');
return engineRequest.sendWithPromise('/oid-sensor', 'GET').then(function (response) {
macToOidDict = response;
myConsole.info('OID sensors count is %s', countOIDs(macToOidDict));
});
} else {
myConsole.info('Local OID sensors count is %s', countOIDs(macToOidDict));
return q.resolve();
}
};
/**
* Send to engine all data gathered from devices with one HTTP post
* @param discoveryData data gathered from devices
*/
var sendDiscoveryDataToEngine = function (discoveryData) {
var retrievedOIDsDeviceNumber = Object.keys(discoveryData).length;
if (retrievedOIDsDeviceNumber === 0) {
myConsole.info('No OID data gathered, sending nothing to engine');
} else {
try {
var resource = '/oid-sensor/sample';
myConsole.info(' Sending %s OID values to engine', countOIDs(discoveryData));
engineRequest.sendWithPromise(resource, 'POST', discoveryData, null, null, { doNotFollow204: true }).catch(function (e) {
myConsole.error('Error sending SNMP data to engine: %s', JSON.stringify(e));
});
} catch (e) {
myConsole.error('Error preparing data to send %s', JSON.stringify(e));
}
}
};
return {
discoverCustom: discoverCustom,
invalidate: invalidate,
sendDiscoveryDataToEngine: sendDiscoveryDataToEngine,
isDiscoveryInProgress: function () {
return mutex.isLocked(DISCOVERY_SEMAPHORE);
},
getCurrentSnapshotData: function () {
return snmpDiscoverySnapshotData;
},
};
};
module.exports.factory = snmpDiscoveryFactory;