diff options
Diffstat (limited to 'node_modules/systeminformation/lib/docker.js')
-rw-r--r-- | node_modules/systeminformation/lib/docker.js | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/node_modules/systeminformation/lib/docker.js b/node_modules/systeminformation/lib/docker.js new file mode 100644 index 0000000..fe93d1a --- /dev/null +++ b/node_modules/systeminformation/lib/docker.js @@ -0,0 +1,764 @@ +'use strict'; +// @ts-check +// ================================================================================== +// docker.js +// ---------------------------------------------------------------------------------- +// Description: System Information - library +// for Node.js +// Copyright: (c) 2014 - 2022 +// 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 dockerImages(all, callback) { + + // fallback - if only callback is given + if (util.isFunction(all) && !callback) { + callback = all; + all = false; + } + if (typeof all === 'string' && all === 'true') { + all = true; + } + if (typeof all !== 'boolean' && all !== undefined) { + all = false; + } + + all = all || false; + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + const workload = []; + + _docker_socket.listImages(all, data => { + let dockerImages = {}; + try { + dockerImages = data; + if (dockerImages && Object.prototype.toString.call(dockerImages) === '[object Array]' && dockerImages.length > 0) { + + dockerImages.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(dockerImagesInspect(element.Id.trim(), element)); + }); + if (workload.length) { + Promise.all( + workload + ).then(data => { + if (callback) { callback(data); } + resolve(data); + }); + } else { + if (callback) { callback(result); } + resolve(result); + } + } else { + if (callback) { callback(result); } + resolve(result); + } + } catch (err) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); + }); +} + +// -------------------------- +// container inspect (for one container) + +function dockerImagesInspect(imageID, payload) { + return new Promise((resolve) => { + process.nextTick(() => { + imageID = imageID || ''; + if (typeof imageID !== 'string') { + return resolve(); + } + const imageIDSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(imageID, true)).trim(); + if (imageIDSanitized) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.inspectImage(imageIDSanitized.trim(), data => { + try { + resolve({ + id: payload.Id, + container: data.Container, + comment: data.Comment, + os: data.Os, + architecture: data.Architecture, + parent: data.Parent, + dockerVersion: data.DockerVersion, + size: data.Size, + sharedSize: payload.SharedSize, + virtualSize: data.VirtualSize, + author: data.Author, + created: data.Created ? Math.round(new Date(data.Created).getTime() / 1000) : 0, + containerConfig: data.ContainerConfig ? data.ContainerConfig : {}, + graphDriver: data.GraphDriver ? data.GraphDriver : {}, + repoDigests: data.RepoDigests ? data.RepoDigests : {}, + repoTags: data.RepoTags ? data.RepoTags : {}, + config: data.Config ? data.Config : {}, + rootFS: data.RootFS ? data.RootFS : {}, + }); + } catch (err) { + resolve(); + } + }); + } else { + resolve(); + } + }); + }); +} + +exports.dockerImages = dockerImages; + +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; + } + if (typeof all === 'string' && all === 'true') { + all = true; + } + if (typeof all !== 'boolean' && all !== undefined) { + 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); + } + } 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) { + return new Promise((resolve) => { + process.nextTick(() => { + containerID = containerID || ''; + if (typeof containerID !== 'string') { + return resolve(); + } + const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim(); + if (containerIdSanitized) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.getInspect(containerIdSanitized.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 wx; + 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; + wx = +obj.tx_bytes; + } + return { + rx, + wx + }; +} + +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 = []; + return new Promise((resolve) => { + process.nextTick(() => { + + // fallback - if only callback is given + if (util.isFunction(containerIDs) && !callback) { + callback = containerIDs; + containerArray = ['*']; + } else { + containerIDs = containerIDs || '*'; + if (typeof containerIDs !== 'string') { + if (callback) { callback([]); } + return resolve([]); + } + let containerIDsSanitized = ''; + containerIDsSanitized.__proto__.toLowerCase = util.stringToLower; + containerIDsSanitized.__proto__.replace = util.stringReplace; + containerIDsSanitized.__proto__.trim = util.stringTrim; + + containerIDsSanitized = containerIDs; + containerIDsSanitized = containerIDsSanitized.trim(); + if (containerIDsSanitized !== '*') { + containerIDsSanitized = ''; + const s = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerIDs, true)).trim(); + for (let i = 0; i <= util.mathMin(s.length, 2000); i++) { + if (!(s[i] === undefined)) { + s[i].__proto__.toLowerCase = util.stringToLower; + const sl = s[i].toLowerCase(); + if (sl && sl[0] && !sl[1]) { + containerIDsSanitized = containerIDsSanitized + sl[0]; + } + } + } + } + + containerIDsSanitized = containerIDsSanitized.trim().toLowerCase().replace(/,+/g, '|'); + containerArray = containerIDsSanitized.split('|'); + } + + const result = []; + + const workload = []; + if (containerArray.length && containerArray[0].trim() === '*') { + containerArray = []; + dockerContainers().then(allContainers => { + for (let container of allContainers) { + containerArray.push(container.id); + } + if (containerArray.length) { + dockerContainerStats(containerArray.join(',')).then(result => { + if (callback) { callback(result); } + resolve(result); + }); + } else { + 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, + memUsage: 0, + memLimit: 0, + memPercent: 0, + cpuPercent: 0, + pids: 0, + netIO: { + rx: 0, + wx: 0 + }, + blockIO: { + r: 0, + w: 0 + }, + restartCount: 0, + cpuStats: {}, + precpuStats: {}, + memoryStats: {}, + networks: {}, + }; + 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; + + if (!stats.message) { + result.memUsage = (stats.memory_stats && stats.memory_stats.usage ? stats.memory_stats.usage : 0); + result.memLimit = (stats.memory_stats && stats.memory_stats.limit ? stats.memory_stats.limit : 0); + result.memPercent = (stats.memory_stats && stats.memory_stats.usage && stats.memory_stats.limit ? stats.memory_stats.usage / stats.memory_stats.limit * 100.0 : 0); + result.cpuPercent = (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.cpuStats = (stats.cpu_stats ? stats.cpu_stats : {}); + result.precpuStats = (stats.precpu_stats ? stats.precpu_stats : {}); + result.memoryStats = (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) { + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + containerID = containerID || ''; + if (typeof containerID !== 'string') { + return resolve(result); + } + const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim(); + + if (containerIdSanitized) { + + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + + _docker_socket.getProcesses(containerIdSanitized, 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({ + pidHost: (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 dockerVolumes(callback) { + + let result = []; + return new Promise((resolve) => { + process.nextTick(() => { + if (!_docker_socket) { + _docker_socket = new DockerSocket(); + } + _docker_socket.listVolumes(data => { + let dockerVolumes = {}; + try { + dockerVolumes = data; + if (dockerVolumes && dockerVolumes.Volumes && Object.prototype.toString.call(dockerVolumes.Volumes) === '[object Array]' && dockerVolumes.Volumes.length > 0) { + + dockerVolumes.Volumes.forEach(function (element) { + + result.push({ + name: element.Name, + driver: element.Driver, + labels: element.Labels, + mountpoint: element.Mountpoint, + options: element.Options, + scope: element.Scope, + created: element.CreatedAt ? Math.round(new Date(element.CreatedAt).getTime() / 1000) : 0, + }); + }); + if (callback) { callback(result); } + resolve(result); + } else { + if (callback) { callback(result); } + resolve(result); + } + } catch (err) { + if (callback) { callback(result); } + resolve(result); + } + }); + }); + }); +} + +exports.dockerVolumes = dockerVolumes; +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.memUsage = res[0].memUsage; + element.memLimit = res[0].memLimit; + element.memPercent = res[0].memPercent; + element.cpuPercent = res[0].cpuPercent; + element.pids = res[0].pids; + element.netIO = res[0].netIO; + element.blockIO = res[0].blockIO; + element.cpuStats = res[0].cpuStats; + element.precpuStats = res[0].precpuStats; + element.memoryStats = res[0].memoryStats; + 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; |