domotz-remote-pawn
Version:
Domotz Agent
487 lines (479 loc) • 20.4 kB
JavaScript
/**
* This file is part of Domotz Agent.
*
* @license
* 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/>.
*
* @requires module:sandbox/constants
* @requires module:sandbox/library/ssh
* @requires module:sandbox/library/snmp
* @requires module:sandbox/library/tcp
* @requires module:sandbox/library/telnet
* @requires module:request
* @requires module:util
* @copyright Copyright (C) Domotz Inc
*/
/**
* The Domotz Device object.
* Exposes device related libraries for http, ssh, tcp, telnet remote call executions.
* Contains device information such as its Custom Driver Management credentials and IP address
* @example D.device
* @namespace D.device
* @memberof D
*/
var request = require('request');
var ssh = require('./ssh');
var scp = require('./scp');
var tcp = require('./tcp');
var telnet = require('./telnet');
var snmp = require('./snmp');
var winrm = require('./winrm');
const util = require('util');
const variableLibrary = require('./variable');
/**
* The Http Library Options
* @typedef {object} HttpOptions
* @property {string} url - The url suffix of the request target
* @property {object} [headers] - The Request HTTP Headers defined as key value pairs. Eg {'Accept': 'text/html'}
* @property {string} [body] - The Request HTTP Body
* @property {integer} [port=80] - The port to use for the request. If protocol is set to https the default is 443
* @property {string|object} [auth] - The authentication to use. Eg 'basic' / {'bearer': 'the token'}
* @property {string} [username] - The device username. If not set the custom driver management purpose one is used
* @property {string} [password] - The device password. If not set the custom driver management purpose one is used
* @property {boolean} [jar=false] - if true, remember cookies for future use
* @property {boolean} [useHostname=true] - if false, ip address is used as host.
* If true or not set the device host_name is used as host
* @property {http|https} [protocol=http] - The protocol to use in the request
*/
/**
* The Http library callback
* @typedef {callback} HttpCallback
* @property {string} [error] - The HTTP response error
* @property {object} response - The HTTP response object
* @property {string} [body] - The HTTP response body
*/
function createDevice(device, agentDriverSettings, myConsole) {
function buildSnmpOptions(options) {
if (!options) {
options = {};
}
options.maxRepetitions = options.maxRepetitions || 24;
options.timeout = options.timeout || 5000;
myConsole.debug(util.inspect(options));
return options;
}
function buildTCPOptions(options) {
options.host = device.ip;
assertCommandInOptions(options, 'D.sendTCPCommand');
myConsole.debug(util.inspect(options));
options.message = options.command;
delete options.command;
return options;
}
function buildTelnetOptions(options) {
options.host = device.ip;
assertCommandInOptions(options, 'D.sendTelnetCommand');
myConsole.debug(util.inspect(options));
options.cmd = options.command + '\r';
delete options.command;
return options;
}
function buildSSHOptions(options) {
options.host = device.ip;
addDefaultCredentials(options);
return options;
}
function buildWinRMOptions(options) {
options.host = device.ip;
assertCommandInOptions(options, 'D.sendWinRMCommand');
addDefaultCredentials(options);
return { payload: options };
}
function assertCommandInOptions(options, name) {
if (!options.command) {
throw Error(name + " requires a 'command' in its options parameter");
}
}
function addDefaultCredentials(options) {
if (!options.username && device.credentials) {
options.username = device.credentials.username;
options.password = new Buffer(device.credentials.password, 'base64').toString();
}
}
function buildRequest(options) {
var host = device.user_host || device.ip;
if (typeof options !== 'object') {
options = { url: 'http://' + host + options };
} else {
var protocol = 'http';
var port = '';
var password = '';
if (options.useHostname === false) {
host = device.ip;
}
delete options.useHostname;
if (options.port) {
port = ':' + options.port;
delete options.port;
}
if (options.protocol === 'https') {
protocol = options.protocol;
port = port || ':443';
delete options.protocol;
}
if (device.credentials) {
password = new Buffer(device.credentials.password, 'base64').toString();
options.url = options.url.replace('${username}', device.credentials.username);
options.url = options.url.replace('${password}', password);
}
if (options.auth === 'basic' && device.credentials) {
options.auth = {
user: device.credentials.username,
pass: password,
sendImmediately: true,
};
} else {
options.auth = undefined;
}
if (protocol !== 'http' && protocol !== 'https') {
throw Error('Invalid protocol: ' + protocol);
}
options.url = protocol + '://' + host + port + options.url;
}
myConsole.debug(util.inspect(options));
return options;
}
return {
/**
* Returns stored username of a device.
* @example
* // returns 'the device username'
* D.device.username()
* @memberof D.device
* @readonly
* @function
* @return {string}
*/
username: function () {
return device.credentials.username;
},
/**
* Returns the stored password of a device.
* @example
* // returns 'the device password'
* D.device.password()
* @memberof D.device
* @readonly
* @function
* @return {string}
*/
password: function () {
return new Buffer(device.credentials.password, 'base64').toString();
},
/**
* Returns the IP address of the device object.
* @example
* // returns '192.168.1.1' for a device with this local IP address
* D.device.ip()
* @memberof D.device
* @readonly
* @function
* @return {string}
*/
ip: function () {
return device.ip;
},
/**
* Returns the MAC address of the device object.
* @example
* // returns '00:11:22:33:44:AA' for a device
* D.device.macAddress()
* @memberof D.device
* @readonly
* @function
* @return {string}
*/
macAddress: function () {
return device.hw_address;
},
/**
* Returns the serial number of the device object.
* @example
* // returns 'serial_number' for a device
* D.device.serial()
* @memberof D.device
* @readonly
* @function
* @return {string}
*/
serial: function () {
return device.serial;
},
/**
* Starts an SNMP session with the device.
* @example
* // returns an snmpSession object to use for snmp related queries
* D.device.createSNMPSession()
* [See SNMP Driver Examples]{@link https://github.com/domotz/custom-driver/tree/master/examples/snmp}
* @memberof D.device
* @param {snmpSessionOptions} options - The SNMP Session Options
* @readonly
* @function
* @return {snmpSession}
*/
createSNMPSession: function (options) {
myConsole.info('Creating SNMP session for device: %s', device.ip);
options = buildSnmpOptions(options);
return snmp.createSessionForDevice(myConsole, device, options);
},
/**
* Sends a command to the device via TCP.
* @example
* D.device.sendTCPCommand(options, callback)
* [See TCP Driver Examples]{@link https://github.com/domotz/custom-driver/tree/master/examples/tcp}
* @memberof D.device
* @readonly
* @param {TCPOptions} options - The TCP Command execution options
* @param {TCPCallback} callback - The TCP Command execution callback function
* @function
*/
sendTCPCommand: function (options, callback) {
myConsole.info('Performing TCP Command: %s', options.command);
options = buildTCPOptions(options);
tcp.sendTCPCommand(myConsole, options, callback);
},
/**
* Sends a command to the device via Telnet.
* @example
* D.device.sendTelnetCommand(options, callback)
* [See Telnet Driver Examples]{@link https://github.com/domotz/custom-driver/tree/master/examples/telnet}
* @memberof D.device
* @readonly
* @param {TelnetOptions} options - The Telnet Command execution options
* @param {TelnetCallback} callback - The Telnet Command execution callback function
* @function
*/
sendTelnetCommand: function (options, callback) {
myConsole.info('Performing Telnet Command: %s', options.command);
options = buildTelnetOptions(options);
telnet.sendTelnetCommand(myConsole, options, callback);
},
/**
* Sends a command to the device via SSH.
* @example D.device.sendSSHCommand(options, callback)
* @example
* [See SSH Driver Examples]{@link https://github.com/domotz/custom-driver/tree/master/examples/ssh}
* @memberof D.device
* @readonly
* @param {SshOptions} options - The SSH Command execution options
* @param {SshCallback} callback - The SSH Command execution callback function
* @function
*/
sendSSHCommand: function (options, callback) {
options = buildSSHOptions(options);
return ssh.sendSSHCommand(myConsole, options, callback);
},
/**
* Sends a sequence of commands to the device via SSH.
* @private
* @example D.device.sendSSHCommands(options, callback)
* @memberof D.device
* @readonly
* @param {SshShellSequenceOptions} options - The SSH Commands execution options
* @param {SshShellSequenceCallback} callback - The SSH Commands execution callback function
* @function
*/
sendSSHCommands: function (options, callback) {
options = buildSSHOptions(options);
if (agentDriverSettings.useSystemSSHShellSequence) {
options.use_system_ssh = true;
}
ssh.sendSSHShellSequence(myConsole, options, callback);
},
/**
* The Device SCP library.
* Allows drivers to execute SCP commands towards the device.
* @example D.device.scp
* [See SCP Driver Examples]{@link TODO }
* @memberof D.device
* @namespace D.device.scp
*/
scp: {
/**
* Transfers a file content to a remote server using SCP.
* @example D.device.scp.upload(options, callback)
* @memberof D.device.scp
* @readonly
* @param {scpUploadOptions} options - The SCP upload execution options
* @param {scpUploadCallback} callback - The SCP upload execution callback
* @function
*/
upload: function (options, callback) {
options = buildSSHOptions(options);
return scp.upload(myConsole, options, callback);
},
/**
* Retrieves a file content from a remote server using SCP.
* @example D.device.scp.download(options, callback)
* @memberof D.device.scp
* @readonly
* @param {scpDownloadOptions} options - The SCP download execution options
* @param {scpDownloadCallback} callback - The SCP download execution callback
* @function
*/
download: function (options, callback) {
options = buildSSHOptions(options);
return scp.download(myConsole, options, callback);
},
},
/**
* The Device HTTP library.
* Allows drivers to execute HTTP(s) requests towards the device.
* @example D.device.http
* [See HTTP Driver Examples]{@link https://github.com/domotz/custom-driver/tree/master/examples/http}
* @memberof D.device
* @namespace D.device.http
*/
http: {
/**
* Executes an HTTP GET request towards the device.
* @example D.device.http.get(options, callback)
* @memberof D.device.http
* @readonly
* @param {HttpOptions} options - The HTTP request execution options
* @param {HttpCallback} callback - The HTTP request execution callback
* @function
*/
get: function (options, callback) {
myConsole.info('Performing GET towards:' + options.url);
options = buildRequest(options);
return request.get(options, callback);
},
/**
* Executes an HTTP POST request towards the device.
* @example D.device.http.post(options, callback)
* @memberof D.device.http
* @readonly
* @param {HttpOptions} options - The HTTP request execution options
* @param {HttpCallback} callback - The HTTP request execution callback
* @function
*/
post: function (options, callback) {
myConsole.info('Performing POST towards:' + options.url);
options = buildRequest(options);
return request.post(options, callback);
},
/**
* Executes an HTTP PUT request towards the device.
* @example D.device.http.put(options, callback)
* @memberof D.device.http
* @readonly
* @param {HttpOptions} options - The HTTP request execution options
* @param {HttpCallback} callback - The HTTP request execution callback
* @function
*/
put: function (options, callback) {
myConsole.info('Performing PUT towards:' + options.url);
options = buildRequest(options);
return request.put(options, callback);
},
/**
* Executes an HTTP DELETE request towards the device.
* @example D.device.http.delete(options, callback)
* @memberof D.device.http
* @readonly
* @param {HttpOptions} options - The HTTP request execution options
* @param {HttpCallback} callback - The HTTP request execution callback
* @function
*/
delete: function (options, callback) {
myConsole.info('Performing DELETE towards:' + options.url);
options = buildRequest(options);
return request.delete(options, callback);
},
/**
* Retrieves the TLS certificate of the server.
* @example D.device.http.getTLSCertificate(options, callback)
* @memberof D.device.http
* @readonly
* @param {HttpOptions} options - The HTTP request execution options
* @param {HttpCallback} callback - The TLS info callback
* @example
* // callback returns (error, TLSData) where TLSData has the following format
* {"issuer": "Issuer name", "expiry": "Apr 12 23:59:59 2015 GMT", "valid": false, "certError": "CERT_HAS_EXPIRED"}
* @function
*/
getTLSCertificate(options, callback) {
options = !options ? {} : options;
options.protocol = 'https';
options.url = options.url || '/';
options.rejectUnauthorized = false;
options = buildRequest(options);
myConsole.info('Performing TLS check towards:' + options.url);
request
.get(options, function (err) {
if (err) {
callback(err);
}
})
.on('response', function (resp) {
var cert = resp.connection.getPeerCertificate();
callback(null, {
issuer: cert.issuer.O,
expiry: cert.valid_to,
valid: !resp.connection.authorizationError,
certError: resp.connection.authorizationError,
});
});
},
},
/**
* Sends a command to the device via WinRM. Only basic authentication is supported.
* [WinRM setup instructions]{@link https://help.domotz.com/user-guide/os-monitoring-feature/#htoc-installing-using-domotz-powershell-script1}
* @example D.device.sendWinRMCommand(options, callback)
* @example
* [See WinRM Driver Examples]{@link https://github.com/domotz/custom-driver/tree/master/examples/winrm}
* @memberof D.device
* @readonly
* @param {WinRMOptions} options - The WinRM Command execution options
* @param {WinRMCallback} callback - The WinRM Command execution callback function
* @function
*/
sendWinRMCommand: function (options, callback) {
options = buildWinRMOptions(options);
return winrm.sendWinRMCommand(myConsole, options, callback);
},
/**
* @ignore
* @deprecated
* Creates a custom driver variable to be sent in the D.success callback.
* @example
* // returns {"uid": "1a", "unit": "C", "value": 60, "label": "CPU Temperature"}
* D.device.createVariable('1a', 'CPU Temperature', 60, 'C', D.valueType.NUMBER)
* @memberof D.device
* @function
* @readonly
* @param {string} uid - The identifier of the variable. Must be Unique. Max 50 characters
* @param {string} name - The Name/Label of the variable
* @param {string} value - The Value of the variable
* @param {string} unit - The Unit of measurement of the variable (eg %). Max 10 characters
* @param {ValueType} valueType - The value type of the variable (used for display purposes)
* @return {Variable}
*/
createVariable: function (uid, name, value, unit, valueType) {
return variableLibrary.createVariable(uid, name, value, unit, valueType, agentDriverSettings);
},
};
}
module.exports.device = createDevice;