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;
});
});
});