domotz-remote-pawn
Version:
Domotz Agent
272 lines (232 loc) • 9.91 kB
JavaScript
/** 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;