system: add disk usage task
This commit is contained in:
+41
-52
@@ -8,19 +8,18 @@ exports = module.exports = {
|
||||
getSwaps,
|
||||
checkDiskSpace,
|
||||
getMemory,
|
||||
getDiskUsage,
|
||||
updateDiskUsage,
|
||||
startUpdateDiskUsage,
|
||||
getLogs,
|
||||
getBlockDevices,
|
||||
runSystemChecks,
|
||||
getProvider,
|
||||
getCpus,
|
||||
getFilesystems
|
||||
getFilesystems,
|
||||
getFilesystemUsage
|
||||
};
|
||||
|
||||
const apps = require('./apps.js'),
|
||||
assert = require('assert'),
|
||||
{ AsyncTask } = require('./asynctask.js'),
|
||||
backups = require('./backups.js'),
|
||||
BoxError = require('./boxerror.js'),
|
||||
debug = require('debug')('box:system'),
|
||||
@@ -35,26 +34,27 @@ const apps = require('./apps.js'),
|
||||
paths = require('./paths.js'),
|
||||
safe = require('safetydance'),
|
||||
shell = require('./shell.js')('system'),
|
||||
tasks = require('./tasks.js'),
|
||||
volumes = require('./volumes.js');
|
||||
|
||||
const DU_CMD = path.join(__dirname, 'scripts/du.sh');
|
||||
const HDPARM_CMD = path.join(__dirname, 'scripts/hdparm.sh');
|
||||
const REBOOT_CMD = path.join(__dirname, 'scripts/reboot.sh');
|
||||
|
||||
async function du(file) {
|
||||
async function du(file, options) {
|
||||
assert.strictEqual(typeof file, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const [error, stdoutResult] = await safe(shell.sudo([ DU_CMD, file ], { encoding: 'utf8' }));
|
||||
const [error, stdoutResult] = await safe(shell.sudo([ DU_CMD, file ], { encoding: 'utf8', signal: options.signal }));
|
||||
if (error) throw new BoxError(BoxError.FS_ERROR, error);
|
||||
|
||||
return parseInt(stdoutResult.trim(), 10);
|
||||
}
|
||||
|
||||
async function hdparm(file) {
|
||||
async function hdparm(file, options) {
|
||||
assert.strictEqual(typeof file, 'string');
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
|
||||
const [error, stdoutResult] = await safe(shell.sudo([ HDPARM_CMD, file ], { encoding: 'utf8' }));
|
||||
const [error, stdoutResult] = await safe(shell.sudo([ HDPARM_CMD, file ], { encoding: 'utf8', signal: options.signal }));
|
||||
if (error) throw new BoxError(BoxError.FS_ERROR, error);
|
||||
|
||||
const lines = stdoutResult.split('\n');
|
||||
@@ -218,62 +218,57 @@ async function getMemory() {
|
||||
};
|
||||
}
|
||||
|
||||
async function getDiskUsage() {
|
||||
const cache = safe.JSON.parse(safe.fs.readFileSync(paths.DISK_USAGE_CACHE_FILE, 'utf8'));
|
||||
if (cache?.disks) {
|
||||
cache.filesystems = cache.disks; // legacy cache file had "disks"
|
||||
delete cache.disks;
|
||||
class FilesystemUsageTask extends AsyncTask {
|
||||
#filesystem;
|
||||
|
||||
constructor(filesystem) {
|
||||
super(`FileSystemUsageTask(${filesystem.filesystem})`);
|
||||
this.#filesystem = filesystem;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
async function updateDiskUsage(progressCallback) {
|
||||
assert.strictEqual(typeof progressCallback, 'function');
|
||||
async _run(signal) {
|
||||
const { filesystem, type, contents } = this.#filesystem;
|
||||
|
||||
const filesystems = await getFilesystems();
|
||||
const now = Date.now();
|
||||
let percent = 5;
|
||||
|
||||
let percent = 1;
|
||||
|
||||
const dockerDf = await docker.df();
|
||||
const excludePaths = (safe.fs.readFileSync(paths.DISK_USAGE_EXCLUDE_FILE, 'utf8') || '').split('\n');
|
||||
|
||||
const fsCount = Object.keys(filesystems).length;
|
||||
for (const fsPath in filesystems) {
|
||||
const filesystem = filesystems[fsPath];
|
||||
|
||||
if (filesystem.type === 'ext4' || filesystem.type === 'xfs') { // hdparm only works with block devices
|
||||
const [speedError, speed] = await safe(hdparm(fsPath));
|
||||
if (speedError) progressCallback({ message: `hdparm error: ${speedError.message}`});
|
||||
filesystem.speed = speedError ? -1 : speed;
|
||||
if (type === 'ext4' || type === 'xfs') { // hdparm only works with block devices
|
||||
this.emitProgress(percent, 'Calculating Disk Speed');
|
||||
const [speedError, speed] = await safe(hdparm(filesystem, { signal }));
|
||||
if (speedError) debug(`hdparm error ${filesystem}: ${speedError.message}`);
|
||||
this.emitData({ speed: speedError ? -1 : speed });
|
||||
} else {
|
||||
filesystem.speed = -1;
|
||||
this.emitData({ speed: -1 });
|
||||
}
|
||||
|
||||
percent += (100/fsCount);
|
||||
progressCallback({ percent, message: `Checking contents of ${fsPath}`});
|
||||
const dockerDf = await docker.df();
|
||||
|
||||
for (const content of filesystem.contents) {
|
||||
progressCallback({ message: `Checking du of ${content.id} ${content.path}`});
|
||||
for (const content of contents) {
|
||||
percent += (100/contents.length);
|
||||
if (signal.aborted) return;
|
||||
|
||||
this.emitProgress(percent,`Checking du of ${content.id} ${content.path}`);
|
||||
if (content.id === 'docker') {
|
||||
content.usage = dockerDf.LayersSize;
|
||||
} else if (content.id === 'docker-volumes') {
|
||||
content.usage = dockerDf.Volumes.map((v) => v.UsageData.Size).reduce((a,b) => a + b, 0);
|
||||
} else if (excludePaths.includes(fsPath)) {
|
||||
debug(`updateDiskUsage: skipping since path ${fsPath} is excluded`);
|
||||
content.usage = 0;
|
||||
} else {
|
||||
const [error, usage] = await safe(du(content.path));
|
||||
if (error) progressCallback({ message: `du error: ${error.message}`}); // can happen if app is installing etc
|
||||
const [error, usage] = await safe(du(content.path, { signal }));
|
||||
if (error) debug(`du error ${content.path}: ${error.message}`); // can happen if app is installing etc
|
||||
content.usage = usage || 0;
|
||||
}
|
||||
progressCallback({ message: `du of ${JSON.stringify(content)}: ${content.usage}`});
|
||||
this.emitData({ content });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!safe.fs.writeFileSync(paths.DISK_USAGE_CACHE_FILE, JSON.stringify({ ts: now, filesystems }, null, 4), 'utf8')) throw new BoxError(BoxError.FS_ERROR, `Could not write du cache file: ${safe.error.message}`);
|
||||
async function getFilesystemUsage(fsPath) {
|
||||
assert.strictEqual(typeof fsPath, 'string');
|
||||
|
||||
return filesystems;
|
||||
const filesystems = await getFilesystems();
|
||||
if (!(fsPath in filesystems)) throw new BoxError(BoxError.BAD_FIELD, 'No such filesystem');
|
||||
|
||||
const filesystem = filesystems[fsPath];
|
||||
return new FilesystemUsageTask(filesystem);
|
||||
}
|
||||
|
||||
async function reboot() {
|
||||
@@ -303,12 +298,6 @@ async function getInfo() {
|
||||
};
|
||||
}
|
||||
|
||||
async function startUpdateDiskUsage() {
|
||||
const taskId = await tasks.add(tasks.TASK_UPDATE_DISK_USAGE, []);
|
||||
safe(tasks.startTask(taskId, {}), { debug }); // background
|
||||
return taskId;
|
||||
}
|
||||
|
||||
async function getLogs(unit, options) {
|
||||
assert.strictEqual(typeof unit, 'string');
|
||||
assert(options && typeof options === 'object');
|
||||
|
||||
Reference in New Issue
Block a user