UNPKG

domotz-remote-pawn

Version:

Domotz Agent

214 lines (187 loc) 8.31 kB
/** 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;