253 lines
9.6 KiB
JavaScript
253 lines
9.6 KiB
JavaScript
const { Injectable } = require('flitter-di')
|
|
const ImplementationError = require('libflitter/errors/ImplementationError')
|
|
const PermissionDeniedError = require('../logical/error/PermissionDeniedError')
|
|
const CommandNotFoundError = require('../logical/error/CommandNotFoundError')
|
|
const FilesystemResourceNotFoundError = require('../logical/error/FilesystemResourceNotFoundError')
|
|
const uuid = require('uuid').v4
|
|
const UniversalPath = require('../logical/UniversalPath')
|
|
const SystemMetrics = require('../logical/SystemMetrics')
|
|
|
|
const DNFManager = require('../logical/packages/DNFManager')
|
|
const APTManager = require('../logical/packages/APTManager')
|
|
|
|
const SystemDManager = require('../logical/services/SystemDManager')
|
|
|
|
class Host extends Injectable {
|
|
static get services() {
|
|
return [...super.services, 'utility', 'app']
|
|
}
|
|
|
|
_temp_path_command = 'mktemp -d'
|
|
_temp_file_command = 'mktemp'
|
|
_cpu_percentage_command = `grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage}'`
|
|
_ram_percentage_command = `free | grep Mem | awk '{print $3/$2 * 100.0}'`
|
|
_mount_point_percentage_command = `df -hl | grep -w '%%MOUNTPOINT%%$' | awk '{print $5}'`
|
|
_list_mount_points_command = `df -hl | grep '/' | awk '{print $6}'`
|
|
_file_directory_delete_command = `rm -rf "%%RESOURCE%%"`
|
|
_resolve_path_command = `readlink -f "%%PATH%%"`
|
|
_reboot_command = `reboot`
|
|
_change_directory_command = `cd "%%PATH%%"`
|
|
_file_directory_permission_fetch_command = `stat -c %a "%%PATH%%"`
|
|
_file_directory_permission_set_command_flat = `chmod %%LEVEL%% "%%PATH%%"`
|
|
_file_directory_permission_set_command_recursive = `chmod -R %%LEVEL%% "%%PATH%%"`
|
|
_file_directory_ownership_fetch_command = `stat -c "%U:%G" "%%PATH%%"`
|
|
_file_directory_ownership_set_command_flat = `chown %%OWNERS%% "%%PATH%%"`
|
|
_file_directory_ownership_set_command_recursive = `chown -R %%OWNERS%% "%%PATH%%"`
|
|
|
|
constructor(config) {
|
|
super()
|
|
this.config = config
|
|
this.name = config.name
|
|
|
|
const injector = this.app.di()
|
|
|
|
if ( config.packages && config.packages.type ) {
|
|
if ( config.packages.type === 'dnf' ) {
|
|
this.packages = injector.make(DNFManager, this)
|
|
} else if ( config.packages.type === 'apt' ) {
|
|
this.packages = injector.make(APTManager, this)
|
|
} else {
|
|
throw new Error(`Invalid or unknown package manager type: ${config.packages.type}`)
|
|
}
|
|
}
|
|
|
|
if ( config.services && config.services.type ) {
|
|
if ( config.services.type === 'systemd' ) {
|
|
this.services = injector.make(SystemDManager, this)
|
|
} else {
|
|
throw new Error(`Invalid or unknown service manager type: ${config.services.type}`)
|
|
}
|
|
}
|
|
|
|
this.injector = injector
|
|
}
|
|
|
|
async execute(command) {
|
|
throw new ImplementationError()
|
|
}
|
|
|
|
async open_file_read_stream(file_path) {
|
|
throw new ImplementationError()
|
|
}
|
|
|
|
async open_file_write_stream(file_path) {
|
|
throw new ImplementationError()
|
|
}
|
|
|
|
async is_alive() {
|
|
try {
|
|
const unique_id = uuid()
|
|
const result = await this.execute(`echo "${unique_id}"`)
|
|
return (result.exit_code === 0 && (result.clean_out.length > 0 && result.clean_out[0] === unique_id))
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
async get_temp_path() {
|
|
const path_string = await this.run_line_result(this._temp_path_command)
|
|
return this.injector.make(UniversalPath, this, path_string, UniversalPath.PATH_TYPE_DIRECTORY)
|
|
// return new UniversalPath(this, path_string, UniversalPath.PATH_TYPE_DIRECTORY)
|
|
}
|
|
|
|
async get_temp_file() {
|
|
const file_string = await this.run_line_result(this._temp_file_command)
|
|
return this.injector.make(UniversalPath, this, file_string, UniversalPath.PATH_TYPE_FILE)
|
|
// return new UniversalPath(this, file_string, UniversalPath.PATH_TYPE_FILE)
|
|
}
|
|
|
|
async get_path(local_path) {
|
|
const host_path = this.injector.make(UniversalPath, this, local_path)
|
|
// const host_path = new UniversalPath(this, local_path)
|
|
await host_path.classify()
|
|
return host_path
|
|
}
|
|
|
|
async metrics() {
|
|
const metric = new SystemMetrics()
|
|
const cpu_percent = Number(await this.run_line_result(this._cpu_percentage_command))
|
|
const ram_percent = Number(await this.run_line_result(this._ram_percentage_command))
|
|
metric.cpu(cpu_percent)
|
|
metric.ram(ram_percent)
|
|
|
|
const mount_points = await this.get_mount_points()
|
|
for ( const point of mount_points ) {
|
|
metric.mount(point, await this.get_mountpoint_utilization(point))
|
|
}
|
|
|
|
return metric
|
|
}
|
|
|
|
async delete_path(resource_path) {
|
|
resource_path = typeof resource_path === 'string' ? resource_path : resource_path.path
|
|
await this.execute(this._file_directory_delete_command.replace('%%RESOURCE%%', resource_path))
|
|
}
|
|
|
|
get_directory_change_command(path) {
|
|
if ( typeof path !== 'string' ) path = path.path
|
|
return this._change_directory_command.replace('%%PATH%%', path)
|
|
}
|
|
|
|
async resolve_path(resource_path) {
|
|
resource_path = typeof resource_path === 'string' ? resource_path : resource_path.path
|
|
return this.run_line_result(this._resolve_path_command.replace('%%PATH%%', resource_path))
|
|
}
|
|
|
|
async get_permissions_for_path(resource_path) {
|
|
resource_path = typeof resource_path === 'string' ? resource_path : resource_path.path
|
|
return this.run_line_result(this._file_directory_permission_fetch_command.replace('%%PATH%%', resource_path))
|
|
}
|
|
|
|
async set_permissions_for_path(resource_path, level, recursive = false) {
|
|
resource_path = typeof resource_path === 'string' ? resource_path : resource_path.path
|
|
let cmd
|
|
if ( recursive ) cmd = this._file_directory_permission_set_command_recursive
|
|
else cmd = this._file_directory_permission_set_command_flat
|
|
cmd = cmd.replace('%%PATH%%', resource_path)
|
|
cmd = cmd.replace('%%LEVEL%%', level)
|
|
await this.run(cmd)
|
|
}
|
|
|
|
async get_ownership_for_path(resource_path) {
|
|
resource_path = typeof resource_path === 'string' ? resource_path : resource_path.path
|
|
const cmd = this._file_directory_ownership_fetch_command.replace('%%PATH%%', resource_path)
|
|
const result = await this.run_line_result(cmd)
|
|
const parts = result.split(':')
|
|
return {user: parts[0], group: parts[1]}
|
|
}
|
|
|
|
async set_ownership_for_path(resource_path, { user, group }, recursive = false) {
|
|
resource_path = typeof resource_path === 'string' ? resource_path : resource_path.path
|
|
let cmd
|
|
if ( recursive ) cmd = this._file_directory_ownership_set_command_recursive
|
|
else cmd = this._file_directory_ownership_set_command_flat
|
|
cmd = cmd.replace('%%PATH%%', resource_path)
|
|
cmd = cmd.replace('%%OWNERS%%', `${user}:${group}`)
|
|
await this.run(cmd)
|
|
}
|
|
|
|
async get_mount_points() {
|
|
const result = await this.execute(this._list_mount_points_command)
|
|
if ( result.exit_code !== 0 ) {
|
|
throw new Error('Unable to determine mount points. Command execution error.')
|
|
}
|
|
|
|
return result.clean_out
|
|
}
|
|
|
|
async get_mountpoint_utilization(mountpoint) {
|
|
const cmd = this._mount_point_percentage_command.replace('%%MOUNTPOINT%%', mountpoint)
|
|
const result = await this.execute(cmd)
|
|
if ( result.exit_code !== 0 ) {
|
|
throw new Error('Unable to determine mount utilization. Command execution error.')
|
|
}
|
|
|
|
return Number(result.clean_out[0].replace('%', ''))/100
|
|
}
|
|
|
|
async run_line_result(command) {
|
|
const result = await this.execute(command)
|
|
if ( result.exit_code !== 0 || result.clean_out.length < 1 ) {
|
|
const E = this._get_result_error_class(result)
|
|
throw new E('Unable to get line output from command: '+command+'\n'+result.clean_err)
|
|
}
|
|
return this.utility.infer(result.clean_out[0].trim())
|
|
}
|
|
|
|
async run(command) {
|
|
const result = await this.execute(command)
|
|
if ( result.exit_code !== 0 ) {
|
|
const E = this._get_result_error_class(result)
|
|
throw new E('Unable to run command: '+command+'\n'+result.clean_err)
|
|
}
|
|
return result
|
|
}
|
|
|
|
async list_files_in_directory(local_path) {
|
|
throw new ImplementationError()
|
|
}
|
|
|
|
async _cleanup() {}
|
|
|
|
async reboot() {
|
|
await this.run_line_result(this._reboot_command)
|
|
}
|
|
|
|
_get_result_error_class(result) {
|
|
const both = `${result.clean_out}\n${result.clean_err}`.toLowerCase()
|
|
const access_denied_phrases = [
|
|
'not permitted',
|
|
'access denied',
|
|
'insufficient permission',
|
|
'permission denied'
|
|
]
|
|
|
|
const not_found_phrases = [
|
|
'command not found',
|
|
]
|
|
|
|
const resource_not_found = [
|
|
'no such file',
|
|
'no such directory',
|
|
'no such file or directory',
|
|
'no such directory or file',
|
|
]
|
|
|
|
for ( const phrase of access_denied_phrases ) {
|
|
if ( both.includes(phrase) ) return PermissionDeniedError
|
|
}
|
|
|
|
for ( const phrase of not_found_phrases ) {
|
|
if ( both.includes(phrase) ) return CommandNotFoundError
|
|
}
|
|
|
|
for ( const phrase of resource_not_found ) {
|
|
if ( both.includes(phrase) ) return FilesystemResourceNotFoundError
|
|
}
|
|
|
|
return Error
|
|
}
|
|
}
|
|
|
|
module.exports = exports = Host
|