Skip to content

Commit

Permalink
Parse output from ping directly
Browse files Browse the repository at this point in the history
  • Loading branch information
mondwan committed Oct 21, 2016
1 parent 6424159 commit 6eb3348
Show file tree
Hide file tree
Showing 9 changed files with 532 additions and 123 deletions.
25 changes: 8 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#NODE-PING
a ping wrapper for nodejs

@last-modified: 2016-04-24 00:00
@last-modified: 2016-10-21 12:43

#LICENSE MIT

Expand Down Expand Up @@ -113,27 +113,18 @@ Below is the possible configuration

```js
/**
* Resolved response
* Parsed response
* @typedef {object} PingResponse
* @param {string} host - The input IP address or HOST
* @param {string} numeric_host - Target IP address
* @param {boolean} alive - True for existed host
* @param {string} output - Raw stdout from system ping
* @param {number} time - Time (float) in ms for first successful ping response
* @param {number} min - Minimum time for collection records
* @param {number} max - Maximum time for collection records
* @param {number} avg - Average time for collection records
* @param {number} stddev - Standard deviation time for collected records
* @param {string} min - Minimum time for collection records
* @param {string} max - Maximum time for collection records
* @param {string} avg - Average time for collection records
* @param {string} stddev - Standard deviation time for collected records
*/

{
host: addr,
alive: isAlive,
output: outstring,
time: time,
min: min,
max: max,
avg: avg,
stddev: stddev,
}
```

#### Note
Expand Down
111 changes: 111 additions & 0 deletions lib/builder/factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use strict';

var __ = require('underscore');
var util = require('util');

// Our library
var linuxBuilder = require('./linux');
var macBuilder = require('./mac');
var winBuilder = require('./win');

/**
* A factory creates argument builders for different platform
* @constructor
*/
function factory() {}

/**
* Check out linux platform
*/
factory.isLinux = function (p) {
var platforms = [
'aix',
'linux',
];

return platforms.indexOf(p) >= 0;
};

/**
* Check out macos platform
*/
factory.isMacOS = function (p) {
var platforms = [
'darwin',
'freebsd',
];

return platforms.indexOf(p) >= 0;
};

/**
* Check out window platform
*/
factory.isWindow = function (p) {
return p && p.match(/^win/) !== null;
};

/**
* Check whether given platform is supported
* @param {string} p - Name of the platform
* @return {bool} - True or False
*/
factory.isPlatformSupport = function (p) {
return __.some([
this.isWindow(p),
this.isLinux(p),
this.isMacOS(p),
]);
};

/**
* Return a path to the ping executable in the system
* @param {string} platform - Name of the platform
* @return {object} - Argument builder
* @throw if given platform is not supported
*/
factory.getExecutablePath = function (platform) {
if (!this.isPlatformSupport(platform)) {
throw new Error(util.format('Platform |%s| is not support', platform));
}

var ret = null;

if (platform === 'aix') {
ret = '/usr/sbin/ping';
} else if (factory.isLinux(platform)) {
ret = '/bin/ping';
} else if (factory.isWindow(platform)) {
ret = process.env.SystemRoot + '/system32/ping.exe';
} else if (factory.isMacOS(platform)) {
ret = '/sbin/ping';
}

return ret;
};

/**
* Create a builder
* @param {string} platform - Name of the platform
* @return {object} - Argument builder
* @throw if given platform is not supported
*/
factory.createBuilder = function (platform) {
if (!this.isPlatformSupport(platform)) {
throw new Error(util.format('Platform |%s| is not support', platform));
}

var ret = null;

if (factory.isLinux(platform)) {
ret = linuxBuilder;
} else if (factory.isWindow(platform)) {
ret = winBuilder;
} else if (factory.isMacOS(platform)) {
ret = macBuilder;
}

return ret;
};

module.exports = factory;
172 changes: 172 additions & 0 deletions lib/parser/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
'use strict';
/* eslint no-unused-vars: 0 */

var __ = require('underscore');

/**
* Parsed response
* @typedef {object} PingResponse
* @param {string} host - The input IP address or HOST
* @param {string} numeric_host - Target IP address
* @param {boolean} alive - True for existed host
* @param {string} output - Raw stdout from system ping
* @param {number} time - Time (float) in ms for first successful ping response
* @param {string} min - Minimum time for collection records
* @param {string} max - Maximum time for collection records
* @param {string} avg - Average time for collection records
* @param {string} stddev - Standard deviation time for collected records
*/

/**
* @constructor
*/
function parser() {
// Initial state is 0
this._state = 0;

// Initial cache value
this._response = {
host: 'unknown',
alive: false,
output: 'unknown',
time: 'unknown',
min: 'unknown',
max: 'unknown',
avg: 'unknown',
stddev: 'unknown',
};

// Initial times storage for ping time
this._times = [];

// Initial lines storage for ping output
this._lines = [];
}

/**
* Enum for parser states
* @readonly
* @enum {number}
*/
parser.prototype.STATES = {
INIT: 0,
HEADER: 1,
BODY: 2,
FOOTER: 3,
END: 4,
};

/**
* Change state of this parser
* @param {number} state - parser.STATES
* @return {this} - This instance
*/
parser.prototype._changeState = function (state) {
var states = __.values(this.STATES);
if (states.indexOf(state) < 0) {
throw new Error('Unknown state');
}

this._state = state;

return this;
};

/**
* Process output's header
* @param {string} line - A line from system ping
*/
parser.prototype._processHeader = function (line) {
throw new Error('Subclass should implement this method');
};

/**
* Process output's body
* @param {string} line - A line from system ping
*/
parser.prototype._processBody = function (line) {
throw new Error('Subclass should implement this method');
};

/**
* Process output's footer
* @param {string} line - A line from system ping
*/
parser.prototype._processFooter = function (line) {
throw new Error('Subclass should implement this method');
};

/**
* Process a line from system ping
* @param {string} line - A line from system ping
* @return {this} - This instance
*/
parser.prototype.eat = function (line) {
var headerStates = [
this.STATES.INIT,
this.STATES.HEADER,
];

// Store lines
this._lines.push(line);

if (headerStates.indexOf(this._state) >= 0) {
this._processHeader(line);
} else if (this._state === this.STATES.BODY) {
this._processBody(line);
} else if (this._state === this.STATES.FOOTER) {
this._processFooter(line);
} else if (this._state === this.STATES.END) {
// Do nothing
} else {
throw new Error('Unknown state');
}

return this;
};

/**
* Get results after parsing certain lines from system ping
* @return {PingResponse} - Response from parsing ping output
*/
parser.prototype.getResult = function () {
var ret = __.extend({}, this._response);

// Concat output
ret.output = this._lines.join('\n');

// Determine alive
ret.alive = this._times.length > 0;

// Update time at first successful line
if (ret.alive) {
ret.time = this._response.time = this._times[0];
}

// Get stddev
if (
ret.stddev === 'unknown' && ret.alive
) {
var N = this._times.length;

var variances = __.reduce(this._times, function (m, time) {
return m + Math.pow((time - ret.avg), 2);
}, 0) / N;

ret.stddev = Math.round(
Math.sqrt(variances) * 1000
) / 1000;
}

// Fix min, avg, max, stddev up to 3 decimal points
__.each(['min', 'avg', 'max', 'stddev'], function (key) {
var v = ret[key];
if (__.isNumber(v)) {
ret[key] = v.toFixed(3);
}
});

return ret;
};

module.exports = parser;
36 changes: 36 additions & 0 deletions lib/parser/factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

var util = require('util');

var builderFactory = require('../builder/factory');
var WinParser = require('./win');
var MacParser = require('./mac');

/**
* A factory creates a parser for parsing output from system ping
* @constructor
*/
function factory() {}

/**
* Create a parser for a given platform
* @param {string} platform - Name of the platform
* @return {object} - Parser
* @throw if given platform is not supported
*/
factory.createParser = function (platform) {
if (!builderFactory.isPlatformSupport(platform)) {
throw new Error(util.format('Platform |%s| is not support', platform));
}

var ret = null;
if (builderFactory.isWindow(platform)) {
ret = new WinParser();
} else if (builderFactory.isMacOS(platform)) {
ret = new MacParser();
}

return ret;
};

module.exports = factory;
Empty file added lib/parser/linux.js
Empty file.
Loading

0 comments on commit 6eb3348

Please sign in to comment.