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
|
||||
|
||||
- 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
|
||||
|
||||
- `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
|
||||
|
||||
@ -137,7 +149,8 @@ This library is splitted in several sections:
|
||||
8. Processes
|
||||
9. Users
|
||||
10. Internet
|
||||
11. GetAll
|
||||
11. Docker
|
||||
12. GetAll
|
||||
|
||||
### Function Reference and OS Support
|
||||
|
||||
@ -258,6 +271,28 @@ This library is splitted in several sections:
|
||||
| - status | X | X | status code |
|
||||
| - 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.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.getDynamicData(srv,iface,cb) | X | X | all dynamic 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 |
|
||||
| -------------- | -------------- | -------- |
|
||||
| 3.1.0 | 2016-08-18 | added Docker stats |
|
||||
| 3.0.1 | 2016-08-17 | Bug-Fix disksIO, users, updated docs |
|
||||
| 3.0.0 | 2016-08-03 | new major version 3.0 |
|
||||
| 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
|
||||
|
||||
[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-url]: https://david-dm.org/sebhildebrandt/systeminformation
|
||||
|
||||
310
lib/index.js
310
lib/index.js
@ -27,7 +27,8 @@
|
||||
// 8. Processes
|
||||
// 9. Users/Sessions
|
||||
// 10. Internet
|
||||
// 11. GetAll - get all data
|
||||
// 11. Docker
|
||||
// 12. GetAll - get all data
|
||||
//
|
||||
// ==================================================================================
|
||||
//
|
||||
@ -78,6 +79,7 @@
|
||||
// --------------------------------
|
||||
//
|
||||
// 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.0 2016-08-03 new major version 3.0
|
||||
// 2.0.5 2016-02-22 some more tiny correction ...
|
||||
@ -119,6 +121,7 @@ var _cpu_speed = '0.00';
|
||||
var _fs_speed = {};
|
||||
var _disk_io = {};
|
||||
var _default_iface;
|
||||
var _docker_container_stats = {};
|
||||
|
||||
const NOT_SUPPORTED = 'not supported';
|
||||
|
||||
@ -338,6 +341,14 @@ function osInfo(callback) {
|
||||
|
||||
exec("cat /etc/*-release", function (error, stdout) {
|
||||
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 lines = stdout.toString().split('\n');
|
||||
lines.forEach(function (line) {
|
||||
@ -1929,7 +1940,295 @@ function inetLatency(host, callback) {
|
||||
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
|
||||
data.time = time();
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @property {Object} versions
|
||||
* @property {string} versions.node
|
||||
* @property {string} versions.v8
|
||||
*/
|
||||
data.node = process.versions.node;
|
||||
data.v8 = process.versions.v8;
|
||||
|
||||
|
||||
@ -28,7 +28,8 @@
|
||||
"network connections",
|
||||
"processes",
|
||||
"users",
|
||||
"internet"
|
||||
"internet",
|
||||
"docker"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user