2022-09-14 13:03:14 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
exports = module.exports = {
|
2022-09-15 11:28:41 +02:00
|
|
|
getSystem,
|
2022-09-16 09:40:47 +02:00
|
|
|
getByApp
|
2022-09-14 13:03:14 +02:00
|
|
|
};
|
|
|
|
|
|
2022-10-12 10:30:02 +02:00
|
|
|
const assert = require('assert'),
|
2022-09-14 13:03:14 +02:00
|
|
|
BoxError = require('./boxerror.js'),
|
2022-10-11 12:44:37 +02:00
|
|
|
docker = require('./docker.js'),
|
2022-09-14 13:03:14 +02:00
|
|
|
safe = require('safetydance'),
|
2022-10-12 10:30:02 +02:00
|
|
|
superagent = require('superagent');
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-11 12:44:37 +02:00
|
|
|
// for testing locally: curl 'http://${graphite-ip}:8000/graphite-web/render?format=json&from=-1min&target=absolute(collectd.localhost.du-docker.capacity-usage)'
|
2022-10-11 19:06:26 +02:00
|
|
|
// the datapoint is (value, timestamp) https://graphite.readthedocs.io/en/latest/
|
2022-10-11 12:44:37 +02:00
|
|
|
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`;
|
|
|
|
|
}
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-09-16 09:40:47 +02:00
|
|
|
async function getByApp(app, fromMinutes, noNullPoints) {
|
|
|
|
|
assert.strictEqual(typeof app, 'object');
|
2022-09-14 13:03:14 +02:00
|
|
|
assert.strictEqual(typeof fromMinutes, 'number');
|
|
|
|
|
assert.strictEqual(typeof noNullPoints, 'boolean');
|
|
|
|
|
|
|
|
|
|
const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5;
|
2022-10-11 12:44:37 +02:00
|
|
|
const graphiteUrl = await getGraphiteUrl();
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-10 19:52:29 +02:00
|
|
|
const targets = [
|
|
|
|
|
`summarize(collectd.localhost.docker-stats-${app.id}.gauge-cpu-perc, "${timeBucketSize}min", "avg")`,
|
|
|
|
|
`summarize(collectd.localhost.docker-stats-${app.id}.gauge-mem-used, "${timeBucketSize}min", "avg")`,
|
|
|
|
|
// `summarize(collectd.localhost.docker-stats-${app.id}.gauge-mem-max, "${timeBucketSize}min", "avg")`,
|
2022-10-11 19:06:26 +02:00
|
|
|
`summarize(collectd.localhost.docker-stats-${app.id}.counter-blockio-read, "${timeBucketSize}min", "sum")`,
|
|
|
|
|
`summarize(collectd.localhost.docker-stats-${app.id}.counter-blockio-write, "${timeBucketSize}min", "sum")`,
|
|
|
|
|
`summarize(collectd.localhost.docker-stats-${app.id}.counter-network-read, "${timeBucketSize}min", "sum")`,
|
|
|
|
|
`summarize(collectd.localhost.docker-stats-${app.id}.counter-network-write, "${timeBucketSize}min", "sum")`,
|
2022-10-10 19:52:29 +02:00
|
|
|
];
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-10 19:52:29 +02:00
|
|
|
const results = [];
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-10 19:52:29 +02:00
|
|
|
for (const target of targets) {
|
|
|
|
|
const query = {
|
|
|
|
|
target: target,
|
2022-09-16 09:40:47 +02:00
|
|
|
format: 'json',
|
|
|
|
|
from: `-${fromMinutes}min`,
|
2022-10-10 19:52:29 +02:00
|
|
|
until: 'now',
|
|
|
|
|
noNullPoints: !!noNullPoints
|
2022-09-16 09:40:47 +02:00
|
|
|
};
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-11 12:44:37 +02:00
|
|
|
const [error, response] = await safe(superagent.get(graphiteUrl).query(query).timeout(30 * 1000).ok(() => true));
|
2022-10-10 19:52:29 +02:00
|
|
|
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}`);
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-10 19:52:29 +02:00
|
|
|
results.push(response.body[0]);
|
2022-09-16 09:40:47 +02:00
|
|
|
}
|
2022-09-14 13:03:14 +02:00
|
|
|
|
2022-10-10 19:52:29 +02:00
|
|
|
return {
|
|
|
|
|
cpu: results[0],
|
|
|
|
|
memory: results[1],
|
|
|
|
|
blockRead: results[2],
|
|
|
|
|
blockWrite: results[3],
|
|
|
|
|
networkRead: results[4],
|
|
|
|
|
networkWrite: results[5]
|
|
|
|
|
};
|
2022-09-14 13:03:14 +02:00
|
|
|
}
|
2022-09-15 11:28:41 +02:00
|
|
|
|
|
|
|
|
async function getSystem(fromMinutes, noNullPoints) {
|
|
|
|
|
assert.strictEqual(typeof fromMinutes, 'number');
|
|
|
|
|
assert.strictEqual(typeof noNullPoints, 'boolean');
|
|
|
|
|
|
|
|
|
|
const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5;
|
2022-10-11 12:44:37 +02:00
|
|
|
const graphiteUrl = await getGraphiteUrl();
|
2022-09-15 11:28:41 +02:00
|
|
|
|
|
|
|
|
const cpuQuery = `summarize(sum(collectd.localhost.aggregation-cpu-average.cpu-system, collectd.localhost.aggregation-cpu-average.cpu-user), "${timeBucketSize}min", "avg")`;
|
2022-10-11 11:54:02 +02:00
|
|
|
const memoryQuery = `summarize(collectd.localhost.memory.memory-used, "${timeBucketSize}min", "avg")`;
|
2022-09-15 11:28:41 +02:00
|
|
|
|
|
|
|
|
const query = {
|
|
|
|
|
target: [ cpuQuery, memoryQuery ],
|
|
|
|
|
format: 'json',
|
|
|
|
|
from: `-${fromMinutes}min`,
|
|
|
|
|
until: 'now'
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-11 12:44:37 +02:00
|
|
|
const [memCpuError, memCpuResponse] = await safe(superagent.get(graphiteUrl).query(query).timeout(30 * 1000).ok(() => true));
|
2022-09-15 11:28:41 +02:00
|
|
|
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}`);
|
|
|
|
|
|
2022-10-12 10:30:02 +02:00
|
|
|
return { cpu: memCpuResponse.body[0], memory: memCpuResponse.body[1] };
|
2022-09-15 11:28:41 +02:00
|
|
|
}
|