diff --git a/src/views/system.html b/src/views/system.html index 1fc5e8691..2f1261cd7 100644 --- a/src/views/system.html +++ b/src/views/system.html @@ -64,32 +64,34 @@
-
+

- {{ errorMessage }} + {{ disks.errorMessage }}
-
+

-
-
-

{{ disk.filesystem }} mounted at {{ disk.mountpoint }} {{ disk.available | prettyDiskSize }} of {{ disk.size | prettyDiskSize }} available

-
-
+
+
+
+

{{ disk.filesystem }} mounted at {{ disk.mountpoint }} {{ disk.available | prettyDiskSize }} of {{ disk.size | prettyDiskSize }} available

+
+
+
+
+

This {{ disk.type }} disk contains:

+
-
-

This {{ disk.type }} disk contains:

-
@@ -108,12 +110,12 @@
-
+

-
+
diff --git a/src/views/system.js b/src/views/system.js index 8a773dfe0..11823cc9a 100644 --- a/src/views/system.js +++ b/src/views/system.js @@ -2,20 +2,15 @@ /* global angular:false */ /* global $:false */ +/* global asyncForEach */ angular.module('Application').controller('SystemController', ['$scope', '$location', '$timeout', 'Client', function ($scope, $location, $timeout, Client) { Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); }); $scope.config = Client.getConfig(); - $scope.ready = false; + $scope.servicesReady = false; $scope.services = []; - $scope.disks = []; $scope.memory = null; - $scope.errorMessage = ''; - - $scope.setError = function (context, error) { - $scope.errorMessage = 'Error loading ' + context + ' : ' + error.message; - }; // http://stackoverflow.com/questions/1484506/random-color-generator-in-javascript function getRandomColor() { @@ -151,93 +146,110 @@ angular.module('Application').controller('SystemController', ['$scope', '$locati } }; - function updateDiskGraphs() { - // https://graphite.readthedocs.io/en/latest/render_api.html#paths-and-wildcards - // on scaleway, for some reason docker devices are collected as part of collectd - // until we figure why just hardcode popular disk devices - https://www.mjmwired.net/kernel/Documentation/devices.txt - Client.disks(function (error, result) { - if (error) return $scope.setError('disk', error); + $scope.disks = { + busy: true, + errorMessage: '', + disks: [], - // segregate locations into the correct disks based on 'filesystem' - result.disks.forEach(function (disk, index) { - disk.id = index; - disk.contains = []; + setError: function (error) { + $scope.disks.errorMessage = 'Error loading disk : ' + error.message; + }, - if (disk.filesystem === result.platformDataDisk) disk.contains.push({ label: 'Platform data', id: 'platformdata', usage: 0 }); - if (disk.filesystem === result.boxDataDisk) disk.contains.push({ label: 'Box data', id: 'boxdata', usage: 0 }); - if (disk.filesystem === result.dockerDataDisk) disk.contains.push({ label: 'Docker images', id: 'docker', usage: 0 }); - if (disk.filesystem === result.mailDataDisk) disk.contains.push({ label: 'Email data', id: 'maildata', usage: 0 }); - if (disk.filesystem === result.backupsDisk) disk.contains.push({ label: 'Backup data', id: 'cloudron-backup', usage: 0 }); + update: function () { + $scope.disks.busy = true; - const apps = Object.keys(result.apps).filter(function (appId) { return result.apps[appId] === disk.filesystem; }); - apps.forEach(function (appId) { - var app = Client.getCachedAppSync(appId); - disk.contains.push({ app: app, label: app.label || app.fqdn, id: appId, usage: 0 }); + // https://graphite.readthedocs.io/en/latest/render_api.html#paths-and-wildcards + // on scaleway, for some reason docker devices are collected as part of collectd + // until we figure why just hardcode popular disk devices - https://www.mjmwired.net/kernel/Documentation/devices.txt + Client.disks(function (error, result) { + if (error) return $scope.disks.setError(error); + + // segregate locations into the correct disks based on 'filesystem' + result.disks.forEach(function (disk, index) { + disk.id = index; + disk.contains = []; + + if (disk.filesystem === result.platformDataDisk) disk.contains.push({ label: 'Platform data', id: 'platformdata', usage: 0 }); + if (disk.filesystem === result.boxDataDisk) disk.contains.push({ label: 'Box data', id: 'boxdata', usage: 0 }); + if (disk.filesystem === result.dockerDataDisk) disk.contains.push({ label: 'Docker images', id: 'docker', usage: 0 }); + if (disk.filesystem === result.mailDataDisk) disk.contains.push({ label: 'Email data', id: 'maildata', usage: 0 }); + if (disk.filesystem === result.backupsDisk) disk.contains.push({ label: 'Backup data', id: 'cloudron-backup', usage: 0 }); + + const apps = Object.keys(result.apps).filter(function (appId) { return result.apps[appId] === disk.filesystem; }); + apps.forEach(function (appId) { + var app = Client.getCachedAppSync(appId); + disk.contains.push({ app: app, label: app.label || app.fqdn, id: appId, usage: 0 }); + }); }); - }); - $scope.disks = result.disks; // [ { filesystem, type, size, used, available, capacity, mountpoint }] + $scope.disks.disks = result.disks; // [ { filesystem, type, size, used, available, capacity, mountpoint }] - // render data of each disk - $scope.disks.forEach(function (disk, index) { - // /dev/sda1 -> sda1 - // /dev/mapper/foo -> mapper_foo (see #348) - var diskName = disk.filesystem.slice(disk.filesystem.indexOf('/', 1) + 1); - diskName = diskName.replace(/\//g, '_'); + // render data of each disk + asyncForEach(result.disks, function (disk, iteratorCallback) { + // /dev/sda1 -> sda1 + // /dev/mapper/foo -> mapper_foo (see #348) + var diskName = disk.filesystem.slice(disk.filesystem.indexOf('/', 1) + 1); + diskName = diskName.replace(/\//g, '_'); - // use collectd instead of df data so the timeframe matches with the du data - Client.graphs([ - 'absolute(collectd.localhost.df-' + diskName + '.df_complex-free)', - 'absolute(collectd.localhost.df-' + diskName + '.df_complex-reserved)', // reserved for root (default: 5%) tune2fs -l/m - 'absolute(collectd.localhost.df-' + diskName + '.df_complex-used)' - ], '-1min', {}, function (error, data) { - if (error) return $scope.setError('disk', error); + // use collectd instead of df data so the timeframe matches with the du data + Client.graphs([ + 'absolute(collectd.localhost.df-' + diskName + '.df_complex-free)', + 'absolute(collectd.localhost.df-' + diskName + '.df_complex-reserved)', // reserved for root (default: 5%) tune2fs -l/m + 'absolute(collectd.localhost.df-' + diskName + '.df_complex-used)' + ], '-1min', {}, function (error, data) { + if (error) return iteratorCallback(error); - disk.size = data[2].datapoints[0][0] + data[1].datapoints[0][0] + data[0].datapoints[0][0]; - disk.free = data[0].datapoints[0][0]; - disk.occupied = data[2].datapoints[0][0]; + disk.size = data[2].datapoints[0][0] + data[1].datapoints[0][0] + data[0].datapoints[0][0]; + disk.free = data[0].datapoints[0][0]; + disk.occupied = data[2].datapoints[0][0]; - colorIndex = 0; - disk.contains.forEach(function (content) { - content.color = getNextColor(); - }); - - // get disk usage data - var graphiteQueries = disk.contains.map(function (content) { - return 'absolute(collectd.localhost.du-' + content.id + '.capacity-usage)'; - }); - - Client.graphs(graphiteQueries, '-1day', { noNullPoints: true }, function (error, data) { - if (error) return $scope.setError('disk', error); - - var usageOther = disk.occupied; - - data.forEach(function (d) { - var content = disk.contains.find(function (content) { return d.target.indexOf(content.id) !== -1; }); - if (!content) return; // didn't match any content - - var tmp = d.datapoints[d.datapoints.length-1][0]; - content.usage = tmp; - - // deduct from overal disk usage to track other - usageOther -= tmp; + colorIndex = 0; + disk.contains.forEach(function (content) { + content.color = getNextColor(); }); - if (index === 0) { // the root mount point is the first disk - disk.contains.push({ - label: 'Everything else (Ubuntu, Swap, etc)', - id: 'other', - color: '#27CE65', - usage: usageOther - }); - } + // get disk usage data + var graphiteQueries = disk.contains.map(function (content) { + return 'absolute(collectd.localhost.du-' + content.id + '.capacity-usage)'; + }); - disk.contains.sort(function (x, y) { return x.usage > y.usage; }); // sort by usage + Client.graphs(graphiteQueries, '-1day', { noNullPoints: true }, function (error, data) { + if (error) return iteratorCallback(error); + + var usageOther = disk.occupied; + + data.forEach(function (d) { + var content = disk.contains.find(function (content) { return d.target.indexOf(content.id) !== -1; }); + if (!content) return; // didn't match any content + + var tmp = d.datapoints[d.datapoints.length-1][0]; + content.usage = tmp; + + // deduct from overal disk usage to track other + usageOther -= tmp; + }); + + if ($scope.disks.disks[0] === disk) { // the root mount point is the first disk + disk.contains.push({ + label: 'Everything else (Ubuntu, Swap, etc)', + id: 'other', + color: '#27CE65', + usage: usageOther + }); + } + + disk.contains.sort(function (x, y) { return x.usage > y.usage; }); // sort by usage + + iteratorCallback(); + }); }); + }, function iteratorDone(error) { + if (error) $scope.disks.setError(error); + $scope.disks.busy = false; }); }); - }); - } + } + }; Client.onReady(function () { Client.memory(function (error, memory) { @@ -245,7 +257,7 @@ angular.module('Application').controller('SystemController', ['$scope', '$locati $scope.memory = memory; - updateDiskGraphs(); + $scope.disks.update(); Client.getServices(function (error, result) { if (error) return Client.error(error); @@ -257,7 +269,7 @@ angular.module('Application').controller('SystemController', ['$scope', '$locati refresh(s.name); }); - $scope.ready = true; + $scope.servicesReady = true; }); }); });