const { Injectable } = require('flitter-di') const ImplementationError = require('libflitter/errors/ImplementationError') 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'] } _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` constructor(config) { super() this.config = config this.name = config.name if ( config.packages && config.packages.type ) { if ( config.packages.type === 'dnf' ) { this.packages = new DNFManager(this) } else if ( config.packages.type === 'apt' ) { this.packages = new 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 = new SystemDManager(this) } else { throw new Error(`Invalid or unknown service manager type: ${config.services.type}`) } } } 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 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 new UniversalPath(this, file_string, UniversalPath.PATH_TYPE_FILE) } async get_path(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)) } 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_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 ) { throw new Error('Unable to get line output from command: '+command) } return this.utility.infer(result.clean_out[0].trim()) } async run(command) { const result = await this.execute(command) if ( result.exit_code !== 0 ) { throw new Error('Unable to run command: '+command) } return result } async list_files_in_directory(local_path) { throw new ImplementationError() } async _cleanup() {} async reboot() { await this.run_line_result(this._reboot_command) } } module.exports = exports = Host