UNPKG

domotz-remote-pawn

Version:

Domotz Agent

272 lines (232 loc) 9.91 kB
/** This file is part of Domotz Agent. * Copyright (C) 2016 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/>. * *This module executes the scanning of the known TCP Ports for the currently available * devices, * Created by tom on 18/12/15. */ var factory = function (resourceLocator, parser) { var servicesByIp = {}; var retryInterval = 2000; var maxRetry = 3; var macToIp = {}; var ipToMac = {}; var myConsole = resourceLocator.log.decorateLogs(); var engineMonitoredServicesList = null; function postResult() { var scanningResult = {}; for (var ip in servicesByIp) { if (servicesByIp.hasOwnProperty(ip)) { scanningResult[ipToMac[ip]] = {}; for (var port in servicesByIp[ip]) { if (servicesByIp[ip].hasOwnProperty(port)) { scanningResult[ipToMac[ip]][port] = servicesByIp[ip][port].curr_status; } } } } myConsole.debug('result is: ' + JSON.stringify(scanningResult)); if (!isEmpty(scanningResult)) { resourceLocator.engineRequest.sendWithPromise('/tcp-service/status', 'PUT', scanningResult, function () { myConsole.info('result successfully uploaded, Monitoring cycle ended'); }); } else { myConsole.info('Avoid sending update for empty paylod'); } } function showServicesStatus() { myConsole.debug('show services status %s ...', JSON.stringify(servicesByIp)); } function getParseServiceStatus(result, ip) { try { var servicesStatus = parser(result, ip, myConsole); myConsole.debug('(%s): Got result for ip ', ip, ip, JSON.stringify(servicesStatus)); if (!isEmpty(servicesStatus)) { for (var port in servicesStatus) { if (servicesStatus.hasOwnProperty(port)) { servicesByIp[ip][port].curr_status = servicesStatus[port]; } } } } catch (err) { myConsole.warn('(%s): WARNING IN PARSING NMAP RESULT for ip ', ip, ip); myConsole.warn('(%s): ', ip, err.message); } } function isEmpty(value) { return Object.keys(value).length === 0; } function getPortStringForIp(ip) { var portListAsString = Object.keys(servicesByIp[ip]).toString(); myConsole.debug('(%s): Port String : %s ', ip, portListAsString); return portListAsString; } function getPortStringForIpOnlyOffLine(ip) { var portListOffLine = []; for (var service in servicesByIp[ip]) { if (servicesByIp[ip].hasOwnProperty(service)) { if ( (servicesByIp[ip][service].curr_status !== 'ONLINE' && servicesByIp[ip][service].prev_status === 'ONLINE') || servicesByIp[ip][service].prev_status === 'UNKNOWN' ) { portListOffLine.push(service); } } } var portListAsString = portListOffLine.toString(); myConsole.debug('(%s): Port String (filtered): %s', ip, portListAsString); return portListAsString; } function discoverServicesStatus(ip, portString) { var cmd = resourceLocator.networkTools.nmapCommands.getExplicitPortNmapCmd(ip, portString); myConsole.info('(%s): Scanning IP ports of device', ip); myConsole.info("(%s): command is '%s'", ip, cmd); return resourceLocator.utils.proc .exec(cmd) .then(resourceLocator.xml2Js.toJSON) .then(function (result) { myConsole.debug('(%s): nmap scanning result: %s', ip, JSON.stringify(result)); getParseServiceStatus(result, ip); myConsole.debug('(%s): resolving promise', ip); }) .catch(function (error) { myConsole.error('(%s): Error on collecting nmap data', ip); myConsole.error(error.stack); }); } function discoverServicesStatusWithRetry(ip, portString) { var count = maxRetry; var deferred = resourceLocator.q.defer(); function _retry() { if (count < maxRetry) { portString = getPortStringForIpOnlyOffLine(ip); } if (portString === '') { myConsole.debug('(%s): Exiting retry loop, no further analysis is necessary', ip); return deferred.resolve(); } if (count === 0) { myConsole.debug('(%s): Exiting retry loop, count : %s', ip, count); return deferred.resolve(); } return resourceLocator.q.delay(retryInterval).then(function () { myConsole.debug('(%s): Trying to execute discovery : %s/%s', ip, maxRetry - count + 1, maxRetry); count--; return discoverServicesStatus(ip, portString).then(_retry); }); } resourceLocator.q.nextTick(_retry); return deferred.promise; } function translateToIp() { macToIp = {}; ipToMac = {}; var interfaceInfo = resourceLocator.interfacesBindingStorage.getInterfaceInfo(); for (var mac in interfaceInfo) { if (interfaceInfo.hasOwnProperty(mac)) { macToIp[mac] = interfaceInfo[mac]; ipToMac[interfaceInfo[mac]] = mac; } } myConsole.debug('mac translation table ' + JSON.stringify(macToIp)); } function invalidateList() { 'use strict'; myConsole.info('invalidating monitored devices list'); engineMonitoredServicesList = null; } function remotelyGetServiceStatus(callback) { var parseResult = function (response) { myConsole.debug('using engine response %s ', JSON.stringify(response)); engineMonitoredServicesList = response; translateToIp(); servicesByIp = {}; if (!isEmpty(macToIp)) { for (var mac in response) { if (response.hasOwnProperty(mac)) { if (macToIp[mac]) { servicesByIp[macToIp[mac]] = {}; for (var port in response[mac]) { if (response[mac].hasOwnProperty(port)) { servicesByIp[macToIp[mac]][port] = { prev_status: response[mac][port], curr_status: 'OFFLINE', // as a default, ensures that the final status is OFFLINE even if no data is actually collected about it }; } } } else { myConsole.warn('CANNOT TRANSLATE MAC TO IP ' + mac); } } } myConsole.debug('services by id status: ' + JSON.stringify(servicesByIp)); callback(); } else { myConsole.warn('mac-ip translation table is empty, cannot proceed '); } }; if (engineMonitoredServicesList !== null) { myConsole.debug('using cached tcp service status ...'); parseResult(engineMonitoredServicesList); } else { myConsole.debug('requesting tcp service status ...'); resourceLocator.engineRequest .sendWithPromise('/tcp-service', 'GET') .then(parseResult) .catch(function () { myConsole.debug('Error retrieving tcp service status'); }); } } var locked = false; function monitor() { if (!locked) { try { locked = true; servicesByIp = {}; myConsole.info('Monitoring cycle started ...'); remotelyGetServiceStatus(function () { myConsole.debug('activating monitor cycle ...'); var promises = []; for (var ip in servicesByIp) { if (servicesByIp.hasOwnProperty(ip)) { var portString = getPortStringForIp(ip); promises.push(discoverServicesStatusWithRetry(ip, portString)); } } resourceLocator.q .all(promises) .then(function () { showServicesStatus(); postResult(); }) .finally(function () { //myConsole.debug(promises); }); }); } catch (err) { myConsole.error(err.name + ' ' + err.message); } finally { locked = false; } } } return { monitor: monitor, invalidateList: invalidateList, }; }; module.exports.factory = factory;