Files
cloudron-box/src/system.js

151 lines
4.9 KiB
JavaScript
Raw Normal View History

2019-08-19 13:50:44 -07:00
'use strict';
exports = module.exports = {
2021-01-04 11:05:42 -08:00
getDisks,
checkDiskSpace,
getMemory,
getMemoryAllocation
2019-08-19 13:50:44 -07:00
};
const apps = require('./apps.js'),
assert = require('assert'),
2019-10-22 11:11:41 -07:00
BoxError = require('./boxerror.js'),
2019-08-19 13:50:44 -07:00
debug = require('debug')('box:disks'),
df = require('@sindresorhus/df'),
docker = require('./docker.js'),
notifications = require('./notifications.js'),
2019-11-21 12:55:17 -08:00
os = require('os'),
paths = require('./paths.js'),
2020-01-31 13:37:07 -08:00
safe = require('safetydance'),
2021-01-04 11:05:42 -08:00
settings = require('./settings.js'),
volumes = require('./volumes.js');
2019-08-19 13:50:44 -07:00
2021-05-11 17:50:48 -07:00
async function getVolumeDisks(appsDataDisk) {
2021-01-27 21:47:36 -08:00
assert.strictEqual(typeof appsDataDisk, 'string');
2019-08-19 13:50:44 -07:00
2021-01-04 11:05:42 -08:00
let volumeDisks = {};
2021-05-11 17:50:48 -07:00
const allVolumes = await volumes.list();
2021-01-04 11:05:42 -08:00
2021-05-11 17:50:48 -07:00
for (const volume of allVolumes) {
const [error, result] = await safe(df(volume.hostPath));
volumeDisks[volume.id] = error ? appsDataDisk : result.filesystem; // ignore any errors
}
2021-01-04 11:05:42 -08:00
2021-05-11 17:50:48 -07:00
return volumeDisks;
2021-01-04 11:05:42 -08:00
}
2021-05-11 17:50:48 -07:00
async function getAppDisks(appsDataDisk) {
2021-01-27 21:47:36 -08:00
assert.strictEqual(typeof appsDataDisk, 'string');
2021-01-04 11:05:42 -08:00
let appDisks = {};
2021-08-20 09:19:44 -07:00
const allApps = await apps.list();
for (const app of allApps) {
if (!app.dataDir) {
appDisks[app.id] = appsDataDisk;
} else {
const [error, result] = await safe(df.file(app.dataDir));
appDisks[app.id] = error ? appsDataDisk : result.filesystem; // ignore any errors
}
}
2021-01-04 11:05:42 -08:00
2021-08-20 09:19:44 -07:00
return appDisks;
2021-01-04 11:05:42 -08:00
}
2021-08-25 19:41:46 -07:00
async function getBackupsFilesystem() {
2021-08-19 13:24:38 -07:00
const backupConfig = await settings.getBackupConfig();
2021-01-04 11:05:42 -08:00
2021-08-19 13:24:38 -07:00
if (backupConfig.provider !== 'filesystem') return null;
2021-01-04 11:05:42 -08:00
2021-08-19 13:24:38 -07:00
const result = await df.file(backupConfig.backupFolder);
return result.filesystem;
2021-01-04 11:05:42 -08:00
}
2021-08-25 19:41:46 -07:00
async function getDisks() {
const info = await docker.info();
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
let [error, allDisks] = await safe(df());
if (error) throw new BoxError(BoxError.FS_ERROR, error);
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
// filter by ext4 and then sort to make sure root disk is first
const ext4Disks = allDisks.filter((r) => r.type === 'ext4').sort((a, b) => a.mountpoint.localeCompare(b.mountpoint));
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
const diskInfos = [];
for (const p of [ paths.BOX_DATA_DIR, paths.PLATFORM_DATA_DIR, paths.APPS_DATA_DIR, info.DockerRootDir ]) {
const [dfError, diskInfo] = await safe(df.file(p));
if (dfError) throw new BoxError(BoxError.FS_ERROR, dfError);
diskInfos.push(diskInfo);
}
const backupsFilesystem = await getBackupsFilesystem();
const result = {
disks: ext4Disks, // root disk is first. { filesystem, type, size, used, avialable, capacity, mountpoint }
boxDataDisk: diskInfos[0].filesystem,
mailDataDisk: diskInfos[0].filesystem,
platformDataDisk: diskInfos[1].filesystem,
appsDataDisk: diskInfos[2].filesystem,
dockerDataDisk: diskInfos[3].filesystem,
backupsDisk: backupsFilesystem,
apps: {}, // filled below
volumes: {} // filled below
};
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
result.apps = await getAppDisks(result.appsDataDisk);
result.volumes = await getVolumeDisks(result.appsDataDisk);
2021-08-25 19:41:46 -07:00
return result;
}
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
async function checkDiskSpace() {
debug('checkDiskSpace: checking disk space');
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
const disks = await getDisks();
2021-08-25 19:41:46 -07:00
let markdownMessage = '';
2019-08-19 13:50:44 -07:00
2021-08-25 19:41:46 -07:00
disks.disks.forEach(function (entry) {
// ignore other filesystems but where box, app and platform data is
if (entry.filesystem !== disks.boxDataDisk
&& entry.filesystem !== disks.platformDataDisk
&& entry.filesystem !== disks.appsDataDisk
&& entry.filesystem !== disks.backupsDisk
&& entry.filesystem !== disks.dockerDataDisk) return false;
2021-06-03 12:20:44 -07:00
2021-08-25 19:41:46 -07:00
if (entry.available <= (1.25 * 1024 * 1024 * 1024)) { // 1.5G
markdownMessage += `* ${entry.filesystem} is at ${entry.capacity*100}% capacity.\n`;
}
2019-08-19 13:50:44 -07:00
});
2021-08-25 19:41:46 -07:00
debug(`checkDiskSpace: disk space checked. out of space: ${markdownMessage || 'no'}`);
if (markdownMessage) markdownMessage = `One or more file systems are running out of space. Please increase the disk size at the earliest.\n\n${markdownMessage}`;
await notifications.alert(notifications.ALERT_DISK_SPACE, 'Server is running out of disk space', markdownMessage);
2019-08-19 13:50:44 -07:00
}
2019-11-21 12:55:17 -08:00
function getSwapSize() {
2019-11-21 12:55:17 -08:00
const stdout = safe.child_process.execSync('swapon --noheadings --raw --bytes --show=SIZE', { encoding: 'utf8' });
2019-11-21 13:48:17 -08:00
const swap = !stdout ? 0 : stdout.trim().split('\n').map(x => parseInt(x, 10) || 0).reduce((acc, cur) => acc + cur);
2019-11-21 12:55:17 -08:00
return swap;
}
2021-08-25 19:41:46 -07:00
async function getMemory() {
return {
2019-11-21 12:55:17 -08:00
memory: os.totalmem(),
swap: getSwapSize()
2021-08-25 19:41:46 -07:00
};
2019-11-21 12:55:17 -08:00
}
function getMemoryAllocation(limit) {
2021-01-20 20:05:39 -08:00
let ratio = parseFloat(safe.fs.readFileSync(paths.SWAP_RATIO_FILE, 'utf8'), 10);
if (!ratio) {
const pc = os.totalmem() / (os.totalmem() + getSwapSize());
ratio = Math.round(pc * 10) / 10; // a simple ratio
}
return Math.round(Math.round(limit * ratio) / 1048576) * 1048576; // nearest MB
}