Added Docker support
This commit is contained in:
parent
fbeade938b
commit
21677bcca5
41
README.md
41
README.md
@ -36,6 +36,15 @@ si.cpu()
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## News and Changes
|
||||||
|
|
||||||
|
### Latest Activity
|
||||||
|
|
||||||
|
- Version 3.1.0: added [Docker][docker-url] support. Now you can scan your docker containers and get their stats
|
||||||
|
- Version 3.0.0: added DisksIO - overall diskIO and IOPS values for all mounted volumes
|
||||||
|
|
||||||
|
Here all changes more detailed:
|
||||||
|
|
||||||
### Major (breaking) Changes - Version 3
|
### Major (breaking) Changes - Version 3
|
||||||
|
|
||||||
- works only with [node.js][nodejs-url] **v4.0.0** and above (using now internal ES6 promise function, arrow functions, ...)
|
- works only with [node.js][nodejs-url] **v4.0.0** and above (using now internal ES6 promise function, arrow functions, ...)
|
||||||
@ -52,7 +61,10 @@ si.cpu()
|
|||||||
|
|
||||||
New Functions
|
New Functions
|
||||||
|
|
||||||
- `disksIO`: returns overall diskIO and IOPS values for all mounted volumes
|
- `dockerContainers`: returns a list of all docker containers (new in version 3.1)
|
||||||
|
- `dockerContainerStats`: returns statistics for a specific docker container (new in version 3.1)
|
||||||
|
- `dockerAll`: returns a list of all docker containers including their stats (new in version 3.1)
|
||||||
|
- `disksIO`: returns overall diskIO and IOPS values for all mounted volumes (new in version 3.0)
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
|
|
||||||
@ -137,7 +149,8 @@ This library is splitted in several sections:
|
|||||||
8. Processes
|
8. Processes
|
||||||
9. Users
|
9. Users
|
||||||
10. Internet
|
10. Internet
|
||||||
11. GetAll
|
11. Docker
|
||||||
|
12. GetAll
|
||||||
|
|
||||||
### Function Reference and OS Support
|
### Function Reference and OS Support
|
||||||
|
|
||||||
@ -258,6 +271,28 @@ This library is splitted in several sections:
|
|||||||
| - status | X | X | status code |
|
| - status | X | X | status code |
|
||||||
| - ms | X | X | response time in ms |
|
| - ms | X | X | response time in ms |
|
||||||
| si.inetLatency(host, cb) | X | X | response-time (ms) to external resource<br>host parameter is optional (default 8.8.8.8)|
|
| si.inetLatency(host, cb) | X | X | response-time (ms) to external resource<br>host parameter is optional (default 8.8.8.8)|
|
||||||
|
| si.dockerContainers(all, cb) | X | X | returns array of active/all docker containers |
|
||||||
|
| - [0].id | X | X | ID of container |
|
||||||
|
| - [0].name | X | X | name of container |
|
||||||
|
| - [0].image | X | X | name of image |
|
||||||
|
| - [0].imageID | X | X | ID of image |
|
||||||
|
| - [0].command | X | X | command |
|
||||||
|
| - [0].created | X | X | creation time |
|
||||||
|
| - [0].state | X | X | created, running, exited |
|
||||||
|
| - [0].ports | X | X | array of ports |
|
||||||
|
| - [0].mounts | X | X | array of mounts |
|
||||||
|
| si.dockerContainerStats(id, cb) | X | X | statistics for a specific container |
|
||||||
|
| - id | X | X | Container ID |
|
||||||
|
| - mem_usage | X | X | memory usage in bytes |
|
||||||
|
| - mem_limit | X | X | memory limit (max mem) in bytes |
|
||||||
|
| - mem_percent | X | X | memory usage in percent |
|
||||||
|
| - cpu_percent | X | X | cpu usage in percent |
|
||||||
|
| - pids | X | X | number of processes |
|
||||||
|
| - netIO.rx | X | X | received bytes via network |
|
||||||
|
| - netIO.wx | X | X | sent bytes via network |
|
||||||
|
| - blockIO.r | X | X | bytes read from BlockIO |
|
||||||
|
| - blockIO.w | X | X | bytes written to BlockIO |
|
||||||
|
| si.dockerAll(cb) | X | X | list of all containers including their stats<br>in one single array |
|
||||||
| si.getStaticData(cb) | X | X | all static data at once |
|
| si.getStaticData(cb) | X | X | all static data at once |
|
||||||
| si.getDynamicData(srv,iface,cb) | X | X | all dynamic data at once |
|
| si.getDynamicData(srv,iface,cb) | X | X | all dynamic data at once |
|
||||||
| si.getAllData(srv,iface,cb) | X | X | all data at once |
|
| si.getAllData(srv,iface,cb) | X | X | all data at once |
|
||||||
@ -319,6 +354,7 @@ I am happy to discuss any comments and suggestions. Please feel free to contact
|
|||||||
|
|
||||||
| Version | Date | Comment |
|
| Version | Date | Comment |
|
||||||
| -------------- | -------------- | -------- |
|
| -------------- | -------------- | -------- |
|
||||||
|
| 3.1.0 | 2016-08-18 | added Docker stats |
|
||||||
| 3.0.1 | 2016-08-17 | Bug-Fix disksIO, users, updated docs |
|
| 3.0.1 | 2016-08-17 | Bug-Fix disksIO, users, updated docs |
|
||||||
| 3.0.0 | 2016-08-03 | new major version 3.0 |
|
| 3.0.0 | 2016-08-03 | new major version 3.0 |
|
||||||
| 2.0.5 | 2016-03-02 | changed .gitignore |
|
| 2.0.5 | 2016-03-02 | changed .gitignore |
|
||||||
@ -406,6 +442,7 @@ All other trademarks are the property of their respective owners.
|
|||||||
[npmjs-license]: https://img.shields.io/npm/l/systeminformation.svg?style=flat-square
|
[npmjs-license]: https://img.shields.io/npm/l/systeminformation.svg?style=flat-square
|
||||||
|
|
||||||
[nodejs-url]: https://nodejs.org/en/
|
[nodejs-url]: https://nodejs.org/en/
|
||||||
|
[docker-url]: https://www.docker.com/
|
||||||
|
|
||||||
[daviddm-img]: https://img.shields.io/david/sebhildebrandt/systeminformation.svg?style=flat-square
|
[daviddm-img]: https://img.shields.io/david/sebhildebrandt/systeminformation.svg?style=flat-square
|
||||||
[daviddm-url]: https://david-dm.org/sebhildebrandt/systeminformation
|
[daviddm-url]: https://david-dm.org/sebhildebrandt/systeminformation
|
||||||
|
|||||||
310
lib/index.js
310
lib/index.js
@ -27,7 +27,8 @@
|
|||||||
// 8. Processes
|
// 8. Processes
|
||||||
// 9. Users/Sessions
|
// 9. Users/Sessions
|
||||||
// 10. Internet
|
// 10. Internet
|
||||||
// 11. GetAll - get all data
|
// 11. Docker
|
||||||
|
// 12. GetAll - get all data
|
||||||
//
|
//
|
||||||
// ==================================================================================
|
// ==================================================================================
|
||||||
//
|
//
|
||||||
@ -78,6 +79,7 @@
|
|||||||
// --------------------------------
|
// --------------------------------
|
||||||
//
|
//
|
||||||
// version date comment
|
// version date comment
|
||||||
|
// 3.1.0 2016-08-18 added docker stats
|
||||||
// 3.0.1 2016-08-17 Bug-Fix disksIO, users, updated docs
|
// 3.0.1 2016-08-17 Bug-Fix disksIO, users, updated docs
|
||||||
// 3.0.0 2016-08-03 new major version 3.0
|
// 3.0.0 2016-08-03 new major version 3.0
|
||||||
// 2.0.5 2016-02-22 some more tiny correction ...
|
// 2.0.5 2016-02-22 some more tiny correction ...
|
||||||
@ -119,6 +121,7 @@ var _cpu_speed = '0.00';
|
|||||||
var _fs_speed = {};
|
var _fs_speed = {};
|
||||||
var _disk_io = {};
|
var _disk_io = {};
|
||||||
var _default_iface;
|
var _default_iface;
|
||||||
|
var _docker_container_stats = {};
|
||||||
|
|
||||||
const NOT_SUPPORTED = 'not supported';
|
const NOT_SUPPORTED = 'not supported';
|
||||||
|
|
||||||
@ -338,6 +341,14 @@ function osInfo(callback) {
|
|||||||
|
|
||||||
exec("cat /etc/*-release", function (error, stdout) {
|
exec("cat /etc/*-release", function (error, stdout) {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {string} DISTRIB_ID
|
||||||
|
* @property {string} NAME
|
||||||
|
* @property {string} DISTRIB_RELEASE
|
||||||
|
* @property {string} VERSION_ID
|
||||||
|
* @property {string} DISTRIB_CODENAME
|
||||||
|
*/
|
||||||
var release = {};
|
var release = {};
|
||||||
var lines = stdout.toString().split('\n');
|
var lines = stdout.toString().split('\n');
|
||||||
lines.forEach(function (line) {
|
lines.forEach(function (line) {
|
||||||
@ -1929,7 +1940,295 @@ function inetLatency(host, callback) {
|
|||||||
exports.inetLatency = inetLatency;
|
exports.inetLatency = inetLatency;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// 11. get all
|
// 11. Docker
|
||||||
|
// ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// get containers (parameter all: get also inactive/exited containers)
|
||||||
|
|
||||||
|
function dockerContainers(all, callback) {
|
||||||
|
|
||||||
|
function inContainers(containers, id) {
|
||||||
|
let filtered = containers.filter(obj => {
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {string} Id
|
||||||
|
*/
|
||||||
|
return (obj.Id && (obj.Id == id))
|
||||||
|
});
|
||||||
|
return (filtered.length > 0);
|
||||||
|
}
|
||||||
|
// fallback - if only callback is given
|
||||||
|
if (isFunction(all) && !callback) {
|
||||||
|
callback = all;
|
||||||
|
all = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
all = all || false;
|
||||||
|
var result = [];
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
process.nextTick(() => {
|
||||||
|
if (_windows) {
|
||||||
|
let error = new Error(NOT_SUPPORTED);
|
||||||
|
if (callback) { callback(NOT_SUPPORTED) }
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
let cmd = "curl --unix-socket /var/run/docker.sock http:/containers/json" + (all ? "?all=1": "");
|
||||||
|
exec(cmd, function (error, stdout) {
|
||||||
|
if (!error) {
|
||||||
|
try {
|
||||||
|
let jsonString = stdout.toString();
|
||||||
|
var docker_containers = JSON.parse(jsonString);
|
||||||
|
// console.log(docker_containers)
|
||||||
|
if (docker_containers && Object.prototype.toString.call(docker_containers) === '[object Array]' && docker_containers.length > 0) {
|
||||||
|
docker_containers.forEach(function (element) {
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {string} Id
|
||||||
|
* @property {string} Name
|
||||||
|
* @property {string} Image
|
||||||
|
* @property {string} ImageID
|
||||||
|
* @property {string} Command
|
||||||
|
* @property {number} Created
|
||||||
|
* @property {string} State
|
||||||
|
* @property {Array} Names
|
||||||
|
* @property {Array} Ports
|
||||||
|
* @property {Array} Mounts
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (element.Names && Object.prototype.toString.call(element.Names) === '[object Array]' && element.Names.length > 0) {
|
||||||
|
element.Name = element.Names[0].replace(/^\/|\/$/g, '');
|
||||||
|
}
|
||||||
|
result.push({
|
||||||
|
id: element.Id,
|
||||||
|
name: element.Name,
|
||||||
|
image: element.Image,
|
||||||
|
imageID: element.ImageID,
|
||||||
|
command: element.Command,
|
||||||
|
created: element.Created,
|
||||||
|
state: element.State,
|
||||||
|
ports: element.Ports,
|
||||||
|
mounts: element.Mounts,
|
||||||
|
// hostconfig: element.HostConfig,
|
||||||
|
// network: element.NetworkSettings
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC in _docker_container_stats
|
||||||
|
for (var key in _docker_container_stats) {
|
||||||
|
if (_docker_container_stats.hasOwnProperty(key)) {
|
||||||
|
if (!inContainers(docker_containers, key)) delete _docker_container_stats[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (callback) { callback(result) }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.dockerContainers = dockerContainers;
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// helper functions for calculation of docker stats
|
||||||
|
|
||||||
|
function docker_calcCPUPercent(cpu_stats, id) {
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {object} cpu_usage
|
||||||
|
* @property {number} cpu_usage.total_usage
|
||||||
|
* @property {number} system_cpu_usage
|
||||||
|
* @property {object} cpu_usage
|
||||||
|
* @property {Array} cpu_usage.percpu_usage
|
||||||
|
*/
|
||||||
|
|
||||||
|
var cpuPercent = 0.0;
|
||||||
|
// calculate the change for the cpu usage of the container in between readings
|
||||||
|
var cpuDelta = cpu_stats.cpu_usage.total_usage - (_docker_container_stats[id] && _docker_container_stats[id].prev_CPU ? _docker_container_stats[id].prev_CPU : 0);
|
||||||
|
// calculate the change for the entire system between readings
|
||||||
|
var systemDelta = cpu_stats.system_cpu_usage - (_docker_container_stats[id] && _docker_container_stats[id].prev_system ? _docker_container_stats[id].prev_system : 0);
|
||||||
|
|
||||||
|
if (systemDelta > 0.0 && cpuDelta > 0.0) {
|
||||||
|
cpuPercent = (cpuDelta / systemDelta) * cpu_stats.cpu_usage.percpu_usage.length * 100.0;
|
||||||
|
}
|
||||||
|
if (!_docker_container_stats[id]) _docker_container_stats[id] = {};
|
||||||
|
_docker_container_stats[id].prev_CPU = cpu_stats.cpu_usage.total_usage;
|
||||||
|
_docker_container_stats[id].prev_system = cpu_stats.system_cpu_usage;
|
||||||
|
|
||||||
|
return cpuPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
function docker_calcNetworkIO(networks) {
|
||||||
|
var rx;
|
||||||
|
var tx;
|
||||||
|
for (var key in networks) {
|
||||||
|
// skip loop if the property is from prototype
|
||||||
|
if (!networks.hasOwnProperty(key)) continue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {number} rx_bytes
|
||||||
|
* @property {number} tx_bytes
|
||||||
|
*/
|
||||||
|
var obj = networks[key];
|
||||||
|
rx =+ obj.rx_bytes;
|
||||||
|
tx =+ obj.tx_bytes;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
rx: rx,
|
||||||
|
tx: tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function docker_calcBlockIO(blkio_stats) {
|
||||||
|
let result = {
|
||||||
|
r: 0,
|
||||||
|
w: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {Array} io_service_bytes_recursive
|
||||||
|
*/
|
||||||
|
if (blkio_stats && blkio_stats.io_service_bytes_recursive && Object.prototype.toString.call( blkio_stats.io_service_bytes_recursive ) === '[object Array]' && blkio_stats.io_service_bytes_recursive.length > 0) {
|
||||||
|
blkio_stats.io_service_bytes_recursive.forEach( function(element) {
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {string} op
|
||||||
|
* @property {number} value
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (element.op && element.op.toLowerCase() == 'read' && element.value) {
|
||||||
|
result.r += element.value;
|
||||||
|
}
|
||||||
|
if (element.op && element.op.toLowerCase() == 'write' && element.value) {
|
||||||
|
result.w += element.value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// container Stats (for one container)
|
||||||
|
|
||||||
|
function dockerContainerStats(containerID, callback) {
|
||||||
|
containerID = containerID || '';
|
||||||
|
var result = {
|
||||||
|
id: containerID,
|
||||||
|
mem_usage: 0,
|
||||||
|
mem_limit: 0,
|
||||||
|
mem_percent: 0,
|
||||||
|
cpu_percent: 0,
|
||||||
|
pids: 0,
|
||||||
|
netIO: {
|
||||||
|
rx: 0,
|
||||||
|
wx: 0
|
||||||
|
},
|
||||||
|
blockIO: {
|
||||||
|
r: 0,
|
||||||
|
w: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
process.nextTick(() => {
|
||||||
|
if (_windows) {
|
||||||
|
let error = new Error(NOT_SUPPORTED);
|
||||||
|
if (callback) { callback(NOT_SUPPORTED) }
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
if (containerID) {
|
||||||
|
let cmd = "curl --unix-socket /var/run/docker.sock http:/containers/" + containerID + "/stats?stream=0";
|
||||||
|
exec(cmd, function (error, stdout) {
|
||||||
|
if (!error) {
|
||||||
|
let jsonString = stdout.toString();
|
||||||
|
try {
|
||||||
|
let stats = JSON.parse(jsonString);
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {Object} memory_stats
|
||||||
|
* @property {number} memory_stats.usage
|
||||||
|
* @property {number} memory_stats.limit
|
||||||
|
* @property {Object} cpu_stats
|
||||||
|
* @property {Object} pids_stats
|
||||||
|
* @property {number} pids_stats.current
|
||||||
|
* @property {Object} networks
|
||||||
|
* @property {Object} blkio_stats
|
||||||
|
*/
|
||||||
|
|
||||||
|
//console.log(stats);
|
||||||
|
if (!stats.message) {
|
||||||
|
result.mem_usage = (stats.memory_stats && stats.memory_stats.usage ? stats.memory_stats.usage : 0);
|
||||||
|
result.mem_limit = (stats.memory_stats && stats.memory_stats.limit ? stats.memory_stats.limit : 0);
|
||||||
|
result.mem_percent = (stats.memory_stats && stats.memory_stats.usage && stats.memory_stats.limit ? stats.memory_stats.usage / stats.memory_stats.limit * 100.0 : 0);
|
||||||
|
result.cpu_percent = (stats.cpu_stats ? docker_calcCPUPercent(stats.cpu_stats, containerID) : 0);
|
||||||
|
result.pids = (stats.pids_stats && stats.pids_stats.current ? stats.pids_stats.current : 0);
|
||||||
|
if (stats.networks) result.netIO = docker_calcNetworkIO(stats.networks);
|
||||||
|
if (stats.blkio_stats)result.blockIO = docker_calcBlockIO(stats.blkio_stats);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (callback) { callback(result) }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (callback) { callback(result) }
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.dockerContainerStats = dockerContainerStats;
|
||||||
|
|
||||||
|
function dockerAll(callback) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
process.nextTick(() => {
|
||||||
|
if (_windows) {
|
||||||
|
let error = new Error(NOT_SUPPORTED);
|
||||||
|
if (callback) { callback(NOT_SUPPORTED) }
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
dockerContainers(true).then(result => {
|
||||||
|
if (result && Object.prototype.toString.call( result ) === '[object Array]' && result.length > 0) {
|
||||||
|
var l = result.length;
|
||||||
|
result.forEach( function(element) {
|
||||||
|
dockerContainerStats(element.id).then(res => {
|
||||||
|
// include stats in array
|
||||||
|
element.mem_usage = res.mem_usage;
|
||||||
|
element.mem_limit = res.mem_limit;
|
||||||
|
element.mem_percent = res.mem_percent;
|
||||||
|
element.cpu_percent = res.cpu_percent;
|
||||||
|
element.pids = res.pids;
|
||||||
|
element.netIO = res.netIO;
|
||||||
|
element.blockIO = res.blockIO;
|
||||||
|
|
||||||
|
// all done??
|
||||||
|
l -= 1;
|
||||||
|
if (l == 0) {
|
||||||
|
if (callback) { callback(result) }
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (callback) { callback(result) }
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.dockerAll = dockerAll;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------
|
||||||
|
// 12. get all
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// --------------------------
|
// --------------------------
|
||||||
@ -2023,6 +2322,13 @@ function getDynamicData(srv, iface, callback) {
|
|||||||
|
|
||||||
// get time
|
// get time
|
||||||
data.time = time();
|
data.time = time();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace
|
||||||
|
* @property {Object} versions
|
||||||
|
* @property {string} versions.node
|
||||||
|
* @property {string} versions.v8
|
||||||
|
*/
|
||||||
data.node = process.versions.node;
|
data.node = process.versions.node;
|
||||||
data.v8 = process.versions.v8;
|
data.v8 = process.versions.v8;
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,8 @@
|
|||||||
"network connections",
|
"network connections",
|
||||||
"processes",
|
"processes",
|
||||||
"users",
|
"users",
|
||||||
"internet"
|
"internet",
|
||||||
|
"docker"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user