diff --git a/README.md b/README.md
index 0e17fc2..4a0b4b5 100644
--- a/README.md
+++ b/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
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
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
diff --git a/lib/index.js b/lib/index.js
index 9446938..560803d 100644
--- a/lib/index.js
+++ b/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;
diff --git a/package.json b/package.json
index bac503f..922793f 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,8 @@
"network connections",
"processes",
"users",
- "internet"
+ "internet",
+ "docker"
],
"repository": {
"type": "git",