Files
cloudron-box/src/graphs.js
Girish Ramakrishnan 1872cea763 graphs: do not average cpu use
Show like htop/top: cpu core count * 100
2022-10-13 22:36:20 +02:00

115 lines
4.6 KiB
JavaScript

'use strict';
exports = module.exports = {
getSystem,
getContainerStats
};
const apps = require('./apps.js'),
assert = require('assert'),
BoxError = require('./boxerror.js'),
docker = require('./docker.js'),
os = require('os'),
safe = require('safetydance'),
services = require('./services.js'),
superagent = require('superagent');
// for testing locally: curl 'http://${graphite-ip}:8000/graphite-web/render?format=json&from=-1min&target=absolute(collectd.localhost.du-docker.capacity-usage)'
// the datapoint is (value, timestamp) https://graphite.readthedocs.io/en/latest/
async function getGraphiteUrl() {
const [error, result] = await safe(docker.inspect('graphite'));
if (error && error.reason === BoxError.NOT_FOUND) return { status: exports.SERVICE_STATUS_STOPPED };
if (error) throw error;
const ip = safe.query(result, 'NetworkSettings.Networks.cloudron.IPAddress', null);
if (!ip) throw new BoxError(BoxError.INACTIVE, 'Error getting IP of graphite service');
return `http://${ip}:8000/graphite-web/render`;
}
async function getContainerStats(name, fromMinutes, noNullPoints) {
assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof fromMinutes, 'number');
assert.strictEqual(typeof noNullPoints, 'boolean');
const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5;
const graphiteUrl = await getGraphiteUrl();
const targets = [
`summarize(collectd.localhost.docker-stats-${name}.gauge-cpu-perc, "${timeBucketSize}min", "avg")`,
`summarize(collectd.localhost.docker-stats-${name}.gauge-mem-used, "${timeBucketSize}min", "avg")`,
// `summarize(collectd.localhost.docker-stats-${name}.gauge-mem-max, "${timeBucketSize}min", "avg")`,
`summarize(collectd.localhost.docker-stats-${name}.counter-blockio-read, "${timeBucketSize}min", "sum")`,
`summarize(collectd.localhost.docker-stats-${name}.counter-blockio-write, "${timeBucketSize}min", "sum")`,
`summarize(collectd.localhost.docker-stats-${name}.counter-network-read, "${timeBucketSize}min", "sum")`,
`summarize(collectd.localhost.docker-stats-${name}.counter-network-write, "${timeBucketSize}min", "sum")`,
];
const results = [];
for (const target of targets) {
const query = {
target: target,
format: 'json',
from: `-${fromMinutes}min`,
until: 'now',
noNullPoints: !!noNullPoints
};
const [error, response] = await safe(superagent.get(graphiteUrl).query(query).timeout(30 * 1000).ok(() => true));
if (error) throw new BoxError(BoxError.NETWORK_ERROR, error.message);
if (response.status !== 200) throw new BoxError(BoxError.EXTERNAL_ERROR, `Unknown error with ${target}: ${response.status} ${response.text}`);
results.push(response.body[0]);
}
return {
cpu: results[0],
memory: results[1],
blockRead: results[2],
blockWrite: results[3],
networkRead: results[4],
networkWrite: results[5]
};
}
async function getSystem(fromMinutes, noNullPoints) {
assert.strictEqual(typeof fromMinutes, 'number');
assert.strictEqual(typeof noNullPoints, 'boolean');
const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5;
const graphiteUrl = await getGraphiteUrl();
const cpuQuery = `summarize(sum(collectd.localhost.aggregation-cpu-average.cpu-system, collectd.localhost.aggregation-cpu-average.cpu-user), "${timeBucketSize}min", "avg")`;
const memoryQuery = `summarize(collectd.localhost.memory.memory-used, "${timeBucketSize}min", "avg")`;
const query = {
target: [ cpuQuery, memoryQuery ],
format: 'json',
from: `-${fromMinutes}min`,
until: 'now'
};
const [memCpuError, memCpuResponse] = await safe(superagent.get(graphiteUrl).query(query).timeout(30 * 1000).ok(() => true));
if (memCpuError) throw new BoxError(BoxError.NETWORK_ERROR, memCpuError.message);
if (memCpuResponse.status !== 200) throw new BoxError(BoxError.EXTERNAL_ERROR, `Unknown error: ${memCpuResponse.status} ${memCpuResponse.text}`);
const appResponses = {};
for (const app of await apps.list()) {
appResponses[app.id] = await getContainerStats(app.id, fromMinutes, noNullPoints);
}
const serviceResponses = {};
for (const serviceId of await services.listServices()) {
serviceResponses[serviceId] = await getContainerStats(serviceId, fromMinutes, noNullPoints);
}
return {
cpu: memCpuResponse.body[0],
memory: memCpuResponse.body[1],
apps: appResponses,
services: serviceResponses,
cpuCount: os.cpus().length
};
}