diff --git a/src/graphs.js b/src/graphs.js index 9191da200..cbe4642de 100644 --- a/src/graphs.js +++ b/src/graphs.js @@ -2,7 +2,7 @@ exports = module.exports = { getSystem, - getApp + getContainerStats }; const apps = require('./apps.js'), @@ -10,6 +10,7 @@ const apps = require('./apps.js'), BoxError = require('./boxerror.js'), docker = require('./docker.js'), 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)' @@ -25,8 +26,8 @@ async function getGraphiteUrl() { return `http://${ip}:8000/graphite-web/render`; } -async function getApp(app, fromMinutes, noNullPoints) { - assert.strictEqual(typeof app, 'object'); +async function getContainerStats(name, fromMinutes, noNullPoints) { + assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof fromMinutes, 'number'); assert.strictEqual(typeof noNullPoints, 'boolean'); @@ -34,13 +35,13 @@ async function getApp(app, fromMinutes, noNullPoints) { const graphiteUrl = await getGraphiteUrl(); 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")`, - `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")`, + `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 = []; @@ -94,8 +95,13 @@ async function getSystem(fromMinutes, noNullPoints) { const appResponses = {}; for (const app of await apps.list()) { - appResponses[app.id] = await getApp(app, fromMinutes, noNullPoints); + appResponses[app.id] = await getContainerStats(app.id, fromMinutes, noNullPoints); } - return { cpu: memCpuResponse.body[0], memory: memCpuResponse.body[1], apps: appResponses }; + 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 }; } diff --git a/src/routes/apps.js b/src/routes/apps.js index 9197126db..3c9dd6626 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -58,6 +58,7 @@ exports = module.exports = { updateBackup, getLimits, + getGraphs, load }; @@ -68,6 +69,7 @@ const apps = require('../apps.js'), BoxError = require('../boxerror.js'), constants = require('../constants.js'), debug = require('debug')('box:routes/apps'), + graphs = require('../graphs.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, safe = require('safetydance'), @@ -959,3 +961,16 @@ async function getLimits(req, res, next) { next(new HttpSuccess(200, { limits })); } + +async function getGraphs(req, res, next) { + assert.strictEqual(typeof req.app, 'object'); + + if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); + + const fromMinutes = parseInt(req.query.fromMinutes); + const noNullPoints = !!req.query.noNullPoints; + const [error, result] = await safe(graphs.getContainerStats(req.app.id, fromMinutes, noNullPoints)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, result)); +} diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 4935ae6b1..6771e8f0c 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -25,7 +25,8 @@ exports = module.exports = { getServerIpv6, getLanguages, syncExternalLdap, - syncDnsRecords + syncDnsRecords, + getSystemGraphs }; const assert = require('assert'), @@ -36,6 +37,7 @@ const assert = require('assert'), debug = require('debug')('box:routes/cloudron'), eventlog = require('../eventlog.js'), externalLdap = require('../externalldap.js'), + graphs = require('../graphs.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, safe = require('safetydance'), @@ -343,3 +345,14 @@ async function syncDnsRecords(req, res, next) { next(new HttpSuccess(201, { taskId })); } + +async function getSystemGraphs(req, res, next) { + if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); + + const fromMinutes = parseInt(req.query.fromMinutes); + const noNullPoints = !!req.query.noNullPoints; + const [error, result] = await safe(graphs.getSystem(fromMinutes, noNullPoints)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, result)); +} diff --git a/src/routes/graphs.js b/src/routes/graphs.js deleted file mode 100644 index 3706f1dc5..000000000 --- a/src/routes/graphs.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -exports = module.exports = { - getSystemGraphs, - getAppGraphs -}; - -const assert = require('assert'), - graphs = require('../graphs.js'), - HttpError = require('connect-lastmile').HttpError, - HttpSuccess = require('connect-lastmile').HttpSuccess, - safe = require('safetydance'); - -async function getSystemGraphs(req, res, next) { - if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); - - const fromMinutes = parseInt(req.query.fromMinutes); - const noNullPoints = !!req.query.noNullPoints; - const [error, result] = await safe(graphs.getSystem(fromMinutes, noNullPoints)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(200, result)); -} - -async function getAppGraphs(req, res, next) { - assert.strictEqual(typeof req.app, 'object'); - - if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); - - const fromMinutes = parseInt(req.query.fromMinutes); - const noNullPoints = !!req.query.noNullPoints; - const [error, result] = await safe(graphs.getApp(req.app, fromMinutes, noNullPoints)); - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(200, result)); -} diff --git a/src/routes/index.js b/src/routes/index.js index 35a7c7bc5..8629b8cde 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -12,7 +12,6 @@ exports = module.exports = { domains: require('./domains.js'), eventlog: require('./eventlog.js'), filemanager: require('./filemanager.js'), - graphs: require('./graphs.js'), groups: require('./groups.js'), mail: require('./mail.js'), mailserver: require('./mailserver.js'), diff --git a/src/routes/services.js b/src/routes/services.js index 38ac6a589..5ba6759ee 100644 --- a/src/routes/services.js +++ b/src/routes/services.js @@ -7,12 +7,14 @@ exports = module.exports = { getLogs, getLogStream, restart, - rebuild + rebuild, + getGraphs }; const assert = require('assert'), AuditSource = require('../auditsource.js'), BoxError = require('../boxerror.js'), + graphs = require('../graphs.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, safe = require('safetydance'), @@ -131,3 +133,16 @@ async function rebuild(req, res, next) { next(new HttpSuccess(202, {})); } + +async function getGraphs(req, res, next) { + assert.strictEqual(typeof req.params.service, 'string'); + + if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); + + const fromMinutes = parseInt(req.query.fromMinutes); + const noNullPoints = !!req.query.noNullPoints; + const [error, result] = await safe(graphs.getContainerStats(req.params.service, fromMinutes, noNullPoints)); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, result)); +} diff --git a/src/server.js b/src/server.js index 0e3fb7f2e..615996f43 100644 --- a/src/server.js +++ b/src/server.js @@ -116,7 +116,7 @@ function initializeExpressSync() { router.post('/api/v1/cloudron/check_for_updates', json, token, authorizeAdmin, routes.cloudron.checkForUpdates); router.get ('/api/v1/cloudron/reboot', token, authorizeAdmin, routes.cloudron.isRebootRequired); router.post('/api/v1/cloudron/reboot', json, token, authorizeAdmin, routes.cloudron.reboot); - router.get ('/api/v1/cloudron/graphs', token, authorizeAdmin, routes.graphs.getSystemGraphs); + router.get ('/api/v1/cloudron/graphs', token, authorizeAdmin, routes.cloudron.getSystemGraphs); router.get ('/api/v1/cloudron/disks', token, authorizeAdmin, routes.cloudron.getDisks); router.get ('/api/v1/cloudron/disk_usage', token, authorizeAdmin, routes.cloudron.getDiskUsage); router.post('/api/v1/cloudron/disk_usage', token, authorizeAdmin, routes.cloudron.updateDiskUsage); @@ -250,7 +250,7 @@ function initializeExpressSync() { router.get ('/api/v1/apps/:id/eventlog', token, routes.apps.load, authorizeOperator, routes.apps.listEventlog); router.get ('/api/v1/apps/:id/limits', token, routes.apps.load, authorizeOperator, routes.apps.getLimits); router.get ('/api/v1/apps/:id/task', token, routes.apps.load, authorizeOperator, routes.apps.getTask); - router.get ('/api/v1/apps/:id/graphs', token, routes.apps.load, authorizeOperator, routes.graphs.getAppGraphs); + router.get ('/api/v1/apps/:id/graphs', token, routes.apps.load, authorizeOperator, routes.apps.getGraphs); router.post('/api/v1/apps/:id/clone', json, token, routes.apps.load, authorizeAdmin, routes.apps.clone); router.get ('/api/v1/apps/:id/download', token, routes.apps.load, authorizeOperator, routes.apps.downloadFile); router.post('/api/v1/apps/:id/upload', json, token, multipart, routes.apps.load, authorizeOperator, routes.apps.uploadFile); @@ -356,6 +356,7 @@ function initializeExpressSync() { router.get ('/api/v1/services', token, authorizeAdmin, routes.services.list); router.get ('/api/v1/services/:service', token, authorizeAdmin, routes.services.get); router.post('/api/v1/services/:service', json, token, authorizeAdmin, routes.services.configure); + router.get ('/api/v1/services/:service/graphs', token, authorizeAdmin, routes.services.getGraphs); router.get ('/api/v1/services/:service/logs', token, authorizeAdmin, routes.services.getLogs); router.get ('/api/v1/services/:service/logstream', token, authorizeAdmin, routes.services.getLogStream); router.post('/api/v1/services/:service/restart', json, token, authorizeAdmin, routes.services.restart); diff --git a/src/services.js b/src/services.js index a3fd5372d..98e4561b7 100644 --- a/src/services.js +++ b/src/services.js @@ -310,7 +310,7 @@ async function containerStatus(containerName, tokenEnvName) { } async function listServices() { - let serviceIds = Object.keys(SERVICES); + const serviceIds = Object.keys(SERVICES); const result = await apps.list(); for (let app of result) {