%PDF- %PDF-
Direktori : /lib/node_modules/pm2/node_modules/systeminformation/lib/ |
Current File : //lib/node_modules/pm2/node_modules/systeminformation/lib/docker.js |
'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;