diff --git a/dashboard/src/components/CpuUsage.vue b/dashboard/src/components/CpuUsage.vue index 25b375e1d..1c798a8d5 100644 --- a/dashboard/src/components/CpuUsage.vue +++ b/dashboard/src/components/CpuUsage.vue @@ -12,7 +12,7 @@ const graph = useTemplateRef('graph'); const period = ref(6); async function refresh() { - const [error, result] = await systemModel.graphs(period.value * 60); + const [error, result] = await systemModel.graphs({ fromSecs: period.value * 60 * 60, intervalSecs: 300 }); if (error) return console.error(error); const labels = result.cpu.map(v => { diff --git a/dashboard/src/components/MemoryUsage.vue b/dashboard/src/components/MemoryUsage.vue index 80ca16221..16f7f56a1 100644 --- a/dashboard/src/components/MemoryUsage.vue +++ b/dashboard/src/components/MemoryUsage.vue @@ -13,7 +13,7 @@ const graph = useTemplateRef('graph'); const period = ref(6); async function refresh() { - const [error, result] = await systemModel.graphs(period.value * 60); + const [error, result] = await systemModel.graphs({ fromSecs: period.value * 60 * 60, intervalSecs: 300 }); if (error) return console.error(error); const labels = result.memory.map(v => { diff --git a/dashboard/src/components/app/Graphs.vue b/dashboard/src/components/app/Graphs.vue index b31cda060..74f4b64bf 100644 --- a/dashboard/src/components/app/Graphs.vue +++ b/dashboard/src/components/app/Graphs.vue @@ -136,7 +136,7 @@ function fillGraph(element, contents, chartPropertyName, divisor, max, format, f async function refresh() { busy.value = true; - const [error,result] = await appsModel.graphs(app.id, period.value * 60); + const [error,result] = await appsModel.graphs(app.id, { fromSecs: period.value * 60 * 60, intervalSecs: 300 }); if (error) return console.error(error); blockReadTotal.value = (result.blockReadTotal / ioDivisor / 1000).toFixed(2) + ' MB'; diff --git a/dashboard/src/models/AppsModel.js b/dashboard/src/models/AppsModel.js index 739491143..cd9a9ebda 100644 --- a/dashboard/src/models/AppsModel.js +++ b/dashboard/src/models/AppsModel.js @@ -391,10 +391,10 @@ function create() { if (result.status !== 202) return [result]; return [null]; }, - async graphs(id, fromMinutes) { + async graphs(id, options) { let result; try { - result = await fetcher.get(`${API_ORIGIN}/api/v1/apps/${id}/graphs`, { fromMinutes, access_token: accessToken }); + result = await fetcher.get(`${API_ORIGIN}/api/v1/apps/${id}/graphs`, { fromSecs: options.fromSecs, intervalSecs: options.intervalSecs, access_token: accessToken }); } catch (e) { return [e]; } diff --git a/dashboard/src/models/SystemModel.js b/dashboard/src/models/SystemModel.js index 8cb2b8cf9..4f1e891ca 100644 --- a/dashboard/src/models/SystemModel.js +++ b/dashboard/src/models/SystemModel.js @@ -83,10 +83,10 @@ function create() { if (error || result.status !== 201) return [error || result]; return [null, result.body.taskId]; }, - async graphs(fromMinutes) { + async graphs(options) { let error, result; try { - result = await fetcher.get(`${API_ORIGIN}/api/v1/system/graphs`, { fromMinutes, access_token: accessToken }); + result = await fetcher.get(`${API_ORIGIN}/api/v1/system/graphs`, { fromSecs: options.fromSecs, intervalSecs: options.intervalSecs, access_token: accessToken }); } catch (e) { error = e; } diff --git a/src/graphs.js b/src/graphs.js index dc9dacc32..d21e1d1c2 100644 --- a/src/graphs.js +++ b/src/graphs.js @@ -27,25 +27,25 @@ async function getGraphiteUrl() { return `http://${ip}:8000/graphite-web/render`; } -async function getContainerStats(name, fromMinutes, noNullPoints) { +async function getContainerStats(name, options) { assert.strictEqual(typeof name, 'string'); - assert.strictEqual(typeof fromMinutes, 'number'); - assert.strictEqual(typeof noNullPoints, 'boolean'); + assert.strictEqual(typeof options, 'object'); + + const { fromSecs, intervalSecs, noNullPoints } = options; - const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5; const graphiteUrl = await getGraphiteUrl(); const targets = [ - `summarize(cloudron.container-${name}.cpu-perc, "${timeBucketSize}min", "avg")`, - `summarize(cloudron.container-${name}.mem-used, "${timeBucketSize}min", "avg")`, - `summarize(cloudron.container-${name}.blockio-read, "${timeBucketSize}min", "sum")`, - `summarize(cloudron.container-${name}.blockio-write, "${timeBucketSize}min", "sum")`, - `summarize(cloudron.container-${name}.network-read, "${timeBucketSize}min", "sum")`, - `summarize(cloudron.container-${name}.network-write, "${timeBucketSize}min", "sum")`, - `summarize(cloudron.container-${name}.blockio-read, "${fromMinutes}min", "max")`, - `summarize(cloudron.container-${name}.blockio-write, "${fromMinutes}min", "max")`, - `summarize(cloudron.container-${name}.network-read, "${fromMinutes}min", "max")`, - `summarize(cloudron.container-${name}.network-write, "${fromMinutes}min", "max")`, + `summarize(cloudron.container-${name}.cpu-perc, "${intervalSecs}s", "avg")`, + `summarize(cloudron.container-${name}.mem-used, "${intervalSecs}s", "avg")`, + `summarize(cloudron.container-${name}.blockio-read, "${intervalSecs}s", "sum")`, + `summarize(cloudron.container-${name}.blockio-write, "${intervalSecs}s", "sum")`, + `summarize(cloudron.container-${name}.network-read, "${intervalSecs}s", "sum")`, + `summarize(cloudron.container-${name}.network-write, "${intervalSecs}s", "sum")`, + `summarize(cloudron.container-${name}.blockio-read, "${intervalSecs}s", "max")`, + `summarize(cloudron.container-${name}.blockio-write, "${intervalSecs}s", "max")`, + `summarize(cloudron.container-${name}.network-read, "${intervalSecs}s", "max")`, + `summarize(cloudron.container-${name}.network-write, "${intervalSecs}s", "max")`, ]; const results = []; @@ -54,7 +54,7 @@ async function getContainerStats(name, fromMinutes, noNullPoints) { const query = { target: target, format: 'json', - from: `-${fromMinutes}min`, + from: `-${fromSecs}s`, until: 'now', noNullPoints: !!noNullPoints }; @@ -82,16 +82,16 @@ async function getContainerStats(name, fromMinutes, noNullPoints) { }; } -async function getSystemStats(fromMinutes, noNullPoints) { - assert.strictEqual(typeof fromMinutes, 'number'); - assert.strictEqual(typeof noNullPoints, 'boolean'); +async function getSystemStats(options) { + assert.strictEqual(typeof options, 'object'); + + const { fromSecs, intervalSecs, noNullPoints } = options; - const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5; const graphiteUrl = await getGraphiteUrl(); const targets = [ - `summarize(cloudron.system.cpu-used), "${timeBucketSize}min", "avg")`, - `summarize(cloudron.system.memory-used, "${timeBucketSize}min", "avg")` + `summarize(cloudron.system.cpu-used, "${intervalSecs}s", "avg")`, + `summarize(cloudron.system.memory-used, "${intervalSecs}s", "avg")` ]; const results = []; @@ -100,7 +100,7 @@ async function getSystemStats(fromMinutes, noNullPoints) { const query = { target: target, format: 'json', - from: `-${fromMinutes}min`, + from: `-${fromSecs}s`, until: 'now', noNullPoints: !!noNullPoints }; @@ -118,20 +118,19 @@ async function getSystemStats(fromMinutes, noNullPoints) { }; } -async function getSystem(fromMinutes, noNullPoints) { - assert.strictEqual(typeof fromMinutes, 'number'); - assert.strictEqual(typeof noNullPoints, 'boolean'); +async function getSystem(options) { + assert.strictEqual(typeof options, 'object'); - const systemStats = await getSystemStats(fromMinutes, noNullPoints); + const systemStats = await getSystemStats(options); const appStats = {}; for (const app of await apps.list()) { - appStats[app.id] = await getContainerStats(app.id, fromMinutes, noNullPoints); + appStats[app.id] = await getContainerStats(app.id, options); } const serviceStats = {}; for (const serviceId of await services.listServices()) { - serviceStats[serviceId] = await getContainerStats(serviceId, fromMinutes, noNullPoints); + serviceStats[serviceId] = await getContainerStats(serviceId, options); } return { diff --git a/src/routes/apps.js b/src/routes/apps.js index 055a272ba..a594954f7 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -1064,11 +1064,13 @@ async function getTask(req, res, next) { 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')); + if (!req.query.fromSecs || !parseInt(req.query.fromSecs)) return next(new HttpError(400, 'fromSecs must be a number')); + if (!req.query.intervalSecs || !parseInt(req.query.intervalSecs)) return next(new HttpError(400, 'intervalSecs must be a number')); - const fromMinutes = parseInt(req.query.fromMinutes); + const fromSecs = parseInt(req.query.fromSecs); + const intervalSecs = parseInt(req.query.intervalSecs); const noNullPoints = !!req.query.noNullPoints; - const [error, result] = await safe(graphs.getContainerStats(req.app.id, fromMinutes, noNullPoints)); + const [error, result] = await safe(graphs.getContainerStats(req.app.id, { fromSecs, noNullPoints, intervalSecs })); if (error) return next(new HttpError(500, error)); next(new HttpSuccess(200, result)); diff --git a/src/routes/system.js b/src/routes/system.js index 5ff2844ac..ac6a19614 100644 --- a/src/routes/system.js +++ b/src/routes/system.js @@ -118,11 +118,13 @@ async function getLogStream(req, res, next) { } async function getSystemGraphs(req, res, next) { - if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); + if (!req.query.fromSecs || !parseInt(req.query.fromSecs)) return next(new HttpError(400, 'fromSecs must be a number')); + if (!req.query.intervalSecs || !parseInt(req.query.intervalSecs)) return next(new HttpError(400, 'intervalSecs must be a number')); - const fromMinutes = parseInt(req.query.fromMinutes); + const fromSecs = parseInt(req.query.fromSecs); + const intervalSecs = parseInt(req.query.intervalSecs); const noNullPoints = !!req.query.noNullPoints; - const [error, result] = await safe(graphs.getSystem(fromMinutes, noNullPoints)); + const [error, result] = await safe(graphs.getSystem({ fromSecs, intervalSecs, noNullPoints })); if (error) return next(new HttpError(500, error)); next(new HttpSuccess(200, result));