123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- 'use strict';
- // @ts-check
- // ==================================================================================
- // docker.js
- // ----------------------------------------------------------------------------------
- // Description: System Information - library
- // for Node.js
- // Copyright: (c) 2014 - 2020
- // Author: Sebastian Hildebrandt
- // ----------------------------------------------------------------------------------
- // License: MIT
- // ==================================================================================
- // 13. Docker
- // ----------------------------------------------------------------------------------
-
- const util = require('./util');
- const DockerSocket = require('./dockerSocket');
-
- let _platform = process.platform;
- const _windows = (_platform === 'win32');
-
- let _docker_container_stats = {};
- let _docker_socket;
- let _docker_last_read = 0;
-
-
- // --------------------------
- // get containers (parameter all: get also inactive/exited containers)
-
- function dockerInfo(callback) {
- return new Promise((resolve) => {
- process.nextTick(() => {
- if (!_docker_socket) {
- _docker_socket = new DockerSocket();
- }
- const result = {};
-
- _docker_socket.getInfo(data => {
- result.id = data.ID;
- result.containers = data.Containers;
- result.containersRunning = data.ContainersRunning;
- result.containersPaused = data.ContainersPaused;
- result.containersStopped = data.ContainersStopped;
- result.images = data.Images;
- result.driver = data.Driver;
- result.memoryLimit = data.MemoryLimit;
- result.swapLimit = data.SwapLimit;
- result.kernelMemory = data.KernelMemory;
- result.cpuCfsPeriod = data.CpuCfsPeriod;
- result.cpuCfsQuota = data.CpuCfsQuota;
- result.cpuShares = data.CPUShares;
- result.cpuSet = data.CPUSet;
- result.ipv4Forwarding = data.IPv4Forwarding;
- result.bridgeNfIptables = data.BridgeNfIptables;
- result.bridgeNfIp6tables = data.BridgeNfIp6tables;
- result.debug = data.Debug;
- result.nfd = data.NFd;
- result.oomKillDisable = data.OomKillDisable;
- result.ngoroutines = data.NGoroutines;
- result.systemTime = data.SystemTime;
- result.loggingDriver = data.LoggingDriver;
- result.cgroupDriver = data.CgroupDriver;
- result.nEventsListener = data.NEventsListener;
- result.kernelVersion = data.KernelVersion;
- result.operatingSystem = data.OperatingSystem;
- result.osType = data.OSType;
- result.architecture = data.Architecture;
- result.ncpu = data.NCPU;
- result.memTotal = data.MemTotal;
- result.dockerRootDir = data.DockerRootDir;
- result.httpProxy = data.HttpProxy;
- result.httpsProxy = data.HttpsProxy;
- result.noProxy = data.NoProxy;
- result.name = data.Name;
- result.labels = data.Labels;
- result.experimentalBuild = data.ExperimentalBuild;
- result.serverVersion = data.ServerVersion;
- result.clusterStore = data.ClusterStore;
- result.clusterAdvertise = data.ClusterAdvertise;
- result.defaultRuntime = data.DefaultRuntime;
- result.liveRestoreEnabled = data.LiveRestoreEnabled;
- result.isolation = data.Isolation;
- result.initBinary = data.InitBinary;
- result.productLicense = data.ProductLicense;
- if (callback) { callback(result); }
- resolve(result);
- });
- });
- });
- }
-
- exports.dockerInfo = dockerInfo;
-
- 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 (util.isFunction(all) && !callback) {
- callback = all;
- all = false;
- }
-
- all = all || false;
- let result = [];
- return new Promise((resolve) => {
- process.nextTick(() => {
- if (!_docker_socket) {
- _docker_socket = new DockerSocket();
- }
- const workload = [];
-
- _docker_socket.listContainers(all, data => {
- let docker_containers = {};
- try {
- docker_containers = data;
- if (docker_containers && Object.prototype.toString.call(docker_containers) === '[object Array]' && docker_containers.length > 0) {
- // GC in _docker_container_stats
- for (let key in _docker_container_stats) {
- if ({}.hasOwnProperty.call(_docker_container_stats, key)) {
- if (!inContainers(docker_containers, key)) delete _docker_container_stats[key];
- }
- }
-
- docker_containers.forEach(function (element) {
-
- if (element.Names && Object.prototype.toString.call(element.Names) === '[object Array]' && element.Names.length > 0) {
- element.Name = element.Names[0].replace(/^\/|\/$/g, '');
- }
- workload.push(dockerContainerInspect(element.Id.trim(), element));
- // 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
- // });
- });
- if (workload.length) {
- Promise.all(
- workload
- ).then(data => {
- if (callback) { callback(data); }
- resolve(data);
- });
- } else {
- if (callback) { callback(result); }
- resolve(result);
- }
- }
- } catch (err) {
- // GC in _docker_container_stats
- for (let key in _docker_container_stats) {
- if ({}.hasOwnProperty.call(_docker_container_stats, key)) {
- if (!inContainers(docker_containers, key)) delete _docker_container_stats[key];
- }
- }
- if (callback) { callback(result); }
- resolve(result);
- }
- });
- });
- });
- }
-
- // --------------------------
- // container inspect (for one container)
-
- function dockerContainerInspect(containerID, payload) {
- containerID = containerID || '';
- return new Promise((resolve) => {
- process.nextTick(() => {
- if (containerID) {
-
- if (!_docker_socket) {
- _docker_socket = new DockerSocket();
- }
-
- _docker_socket.getInspect(containerID.trim(), data => {
- try {
- resolve({
- id: payload.Id,
- name: payload.Name,
- image: payload.Image,
- imageID: payload.ImageID,
- command: payload.Command,
- created: payload.Created,
- started: data.State && data.State.StartedAt ? Math.round(new Date(data.State.StartedAt).getTime() / 1000) : 0,
- finished: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? Math.round(new Date(data.State.FinishedAt).getTime() / 1000) : 0,
- createdAt: data.Created ? data.Created : '',
- startedAt: data.State && data.State.StartedAt ? data.State.StartedAt : '',
- finishedAt: data.State && data.State.FinishedAt && !data.State.FinishedAt.startsWith('0001-01-01') ? data.State.FinishedAt : '',
- state: payload.State,
- restartCount: data.RestartCount || 0,
- platform: data.Platform || '',
- driver: data.Driver || '',
- ports: payload.Ports,
- mounts: payload.Mounts,
- // hostconfig: payload.HostConfig,
- // network: payload.NetworkSettings
- });
- } catch (err) {
- resolve();
- }
- });
- } else {
- resolve();
- }
- });
- });
- }
-
- exports.dockerContainers = dockerContainers;
-
- // --------------------------
- // helper functions for calculation of docker stats
-
- function docker_calcCPUPercent(cpu_stats, precpu_stats) {
- /**
- * @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
- */
-
- if (!_windows) {
- let cpuPercent = 0.0;
- // calculate the change for the cpu usage of the container in between readings
- let cpuDelta = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
- // calculate the change for the entire system between readings
- let systemDelta = cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage;
-
- if (systemDelta > 0.0 && cpuDelta > 0.0) {
- // calculate the change for the cpu usage of the container in between readings
- cpuPercent = (cpuDelta / systemDelta) * cpu_stats.cpu_usage.percpu_usage.length * 100.0;
- }
-
- return cpuPercent;
- } else {
- let nanoSecNow = util.nanoSeconds();
- let cpuPercent = 0.0;
- if (_docker_last_read > 0) {
- let possIntervals = (nanoSecNow - _docker_last_read); // / 100 * os.cpus().length;
- let intervalsUsed = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
- if (possIntervals > 0) {
- cpuPercent = 100.0 * intervalsUsed / possIntervals;
- }
- }
- _docker_last_read = nanoSecNow;
- return cpuPercent;
- }
- }
-
- function docker_calcNetworkIO(networks) {
- let rx;
- let tx;
- for (let key in networks) {
- // skip loop if the property is from prototype
- if (!{}.hasOwnProperty.call(networks, key)) continue;
-
- /**
- * @namespace
- * @property {number} rx_bytes
- * @property {number} tx_bytes
- */
- let 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;
- }
-
- function dockerContainerStats(containerIDs, callback) {
-
- let containerArray = [];
- // fallback - if only callback is given
- if (util.isFunction(containerIDs) && !callback) {
- callback = containerIDs;
- containerArray = ['*'];
- } else {
- containerIDs = containerIDs || '*';
- containerIDs = containerIDs.trim().toLowerCase().replace(/,+/g, '|');
- containerArray = containerIDs.split('|');
- }
-
- return new Promise((resolve) => {
- process.nextTick(() => {
-
- const result = [];
-
- const workload = [];
- if (containerArray.length && containerArray[0].trim() === '*') {
- containerArray = [];
- dockerContainers().then(allContainers => {
- for (let container of allContainers) {
- containerArray.push(container.id);
- }
- dockerContainerStats(containerArray.join(',')).then(result => {
- if (callback) { callback(result); }
- resolve(result);
- });
- });
- } else {
- for (let containerID of containerArray) {
- workload.push(dockerContainerStatsSingle(containerID.trim()));
- }
- if (workload.length) {
- Promise.all(
- workload
- ).then(data => {
- if (callback) { callback(data); }
- resolve(data);
- });
- } else {
- if (callback) { callback(result); }
- resolve(result);
- }
- }
- });
- });
- }
-
- // --------------------------
- // container stats (for one container)
-
- function dockerContainerStatsSingle(containerID) {
- containerID = containerID || '';
- let 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) => {
- process.nextTick(() => {
- if (containerID) {
-
- if (!_docker_socket) {
- _docker_socket = new DockerSocket();
- }
-
- _docker_socket.getInspect(containerID, dataInspect => {
- try {
- _docker_socket.getStats(containerID, data => {
- try {
- let stats = data;
- /**
- * @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
- */
-
- 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 && stats.precpu_stats ? docker_calcCPUPercent(stats.cpu_stats, stats.precpu_stats) : 0);
- result.pids = (stats.pids_stats && stats.pids_stats.current ? stats.pids_stats.current : 0);
- result.restartCount = (dataInspect.RestartCount ? dataInspect.RestartCount : 0);
- if (stats.networks) result.netIO = docker_calcNetworkIO(stats.networks);
- if (stats.blkio_stats) result.blockIO = docker_calcBlockIO(stats.blkio_stats);
- result.cpu_stats = (stats.cpu_stats ? stats.cpu_stats : {});
- result.precpu_stats = (stats.precpu_stats ? stats.precpu_stats : {});
- result.memory_stats = (stats.memory_stats ? stats.memory_stats : {});
- result.networks = (stats.networks ? stats.networks : {});
- }
- } catch (err) {
- util.noop();
- }
- // }
- resolve(result);
- });
- } catch (err) {
- util.noop();
- }
- });
- } else {
- resolve(result);
- }
- });
- });
- }
-
- exports.dockerContainerStats = dockerContainerStats;
-
- // --------------------------
- // container processes (for one container)
-
- function dockerContainerProcesses(containerID, callback) {
- containerID = containerID || '';
- let result = [];
- return new Promise((resolve) => {
- process.nextTick(() => {
- if (containerID) {
-
- if (!_docker_socket) {
- _docker_socket = new DockerSocket();
- }
-
- _docker_socket.getProcesses(containerID, data => {
- /**
- * @namespace
- * @property {Array} Titles
- * @property {Array} Processes
- **/
- try {
- if (data && data.Titles && data.Processes) {
- let titles = data.Titles.map(function (value) {
- return value.toUpperCase();
- });
- let pos_pid = titles.indexOf('PID');
- let pos_ppid = titles.indexOf('PPID');
- let pos_pgid = titles.indexOf('PGID');
- let pos_vsz = titles.indexOf('VSZ');
- let pos_time = titles.indexOf('TIME');
- let pos_elapsed = titles.indexOf('ELAPSED');
- let pos_ni = titles.indexOf('NI');
- let pos_ruser = titles.indexOf('RUSER');
- let pos_user = titles.indexOf('USER');
- let pos_rgroup = titles.indexOf('RGROUP');
- let pos_group = titles.indexOf('GROUP');
- let pos_stat = titles.indexOf('STAT');
- let pos_rss = titles.indexOf('RSS');
- let pos_command = titles.indexOf('COMMAND');
-
- data.Processes.forEach(process => {
- result.push({
- pid_host: (pos_pid >= 0 ? process[pos_pid] : ''),
- ppid: (pos_ppid >= 0 ? process[pos_ppid] : ''),
- pgid: (pos_pgid >= 0 ? process[pos_pgid] : ''),
- user: (pos_user >= 0 ? process[pos_user] : ''),
- ruser: (pos_ruser >= 0 ? process[pos_ruser] : ''),
- group: (pos_group >= 0 ? process[pos_group] : ''),
- rgroup: (pos_rgroup >= 0 ? process[pos_rgroup] : ''),
- stat: (pos_stat >= 0 ? process[pos_stat] : ''),
- time: (pos_time >= 0 ? process[pos_time] : ''),
- elapsed: (pos_elapsed >= 0 ? process[pos_elapsed] : ''),
- nice: (pos_ni >= 0 ? process[pos_ni] : ''),
- rss: (pos_rss >= 0 ? process[pos_rss] : ''),
- vsz: (pos_vsz >= 0 ? process[pos_vsz] : ''),
- command: (pos_command >= 0 ? process[pos_command] : '')
- });
- });
- }
- } catch (err) {
- util.noop();
- }
- if (callback) { callback(result); }
- resolve(result);
- });
- } else {
- if (callback) { callback(result); }
- resolve(result);
- }
- });
- });
- }
-
- exports.dockerContainerProcesses = dockerContainerProcesses;
-
- function dockerAll(callback) {
- return new Promise((resolve) => {
- process.nextTick(() => {
- dockerContainers(true).then(result => {
- if (result && Object.prototype.toString.call(result) === '[object Array]' && result.length > 0) {
- let l = result.length;
- result.forEach(function (element) {
- dockerContainerStats(element.id).then(res => {
- // include stats in array
- element.mem_usage = res[0].mem_usage;
- element.mem_limit = res[0].mem_limit;
- element.mem_percent = res[0].mem_percent;
- element.cpu_percent = res[0].cpu_percent;
- element.pids = res[0].pids;
- element.netIO = res[0].netIO;
- element.blockIO = res[0].blockIO;
- element.cpu_stats = res[0].cpu_stats;
- element.precpu_stats = res[0].precpu_stats;
- element.memory_stats = res[0].memory_stats;
- element.networks = res[0].networks;
-
- dockerContainerProcesses(element.id).then(processes => {
- element.processes = processes;
-
- l -= 1;
- if (l === 0) {
- if (callback) { callback(result); }
- resolve(result);
- }
- });
- // all done??
- });
- });
- } else {
- if (callback) { callback(result); }
- resolve(result);
- }
- });
- });
- });
- }
-
- exports.dockerAll = dockerAll;
|