import json import os.path import traceback from typing import Optional, Tuple, Callable, Union, Dict from mod.base.ssh_executor import SSHExecutor, CommandResult from mod.project.node.dbutil import ServerNodeDB, Node import public def is_much_difference(a:float, b:float)->bool: if a == 0 or b == 0: return True ratio = a / b return ratio >= 10 or ratio <= 0.1 class SSHApi: is_local = False _local_scripts_dir = os.path.join(os.path.dirname(__file__), "ssh_warp_scripts") def __init__(self, host, port: int=22, username: str="root", password=None, pkey=None, pkey_passwd=None, threading_mod=False, timeout=20): self._real_ssh_conf = { "host": host, "username": username, "port": port, "password": password, "key_file": "", "passphrase": pkey_passwd, "key_data": pkey, "strict_host_key_checking": False, "allow_agent": False, "look_for_keys": False, "threading_mod": threading_mod, "timeout": timeout, } self._ssh_executor: Optional[SSHExecutor] = None @classmethod def new_by_id(cls, node_id: int, threading_mod=False) -> Optional["SSHApi"]: data = ServerNodeDB().get_node_by_id(node_id) if not data or not isinstance(data, dict): return None data["ssh_conf"] = json.loads(data["ssh_conf"]) if not data["ssh_conf"]: return None data["ssh_conf"]["threading_mod"] = threading_mod return cls(**data["ssh_conf"]) def _get_ssh_executor(self) -> SSHExecutor: if self._ssh_executor: return self._ssh_executor self._ssh_executor = SSHExecutor(**self._real_ssh_conf) return self._ssh_executor def get_net_work(self) -> Tuple[Optional[dict], str]: data, err = self._run_script("system_info.sh") if err: return None, err if not data.exit_code == 0: return None, data.stderr try: data = json.loads(data.stdout) if isinstance(data, dict) and "cpu" in data and "mem" in data: return self._tans_net_work_form_data(data), "" return None, "data in wrong format: %s" % str(data) except Exception as e: return None, str(e) @staticmethod def _tans_net_work_form_data(data: dict): data["mem"]["memAvailable"] = round(data["mem"]["memAvailable"] / 1024 / 1024, 2) data["mem"]["memBuffers"] = round(data["mem"]["memBuffers"] / 1024 / 1024, 2) data["mem"]["memCached"] = round(data["mem"]["memCached"] / 1024 / 1024, 2) data["mem"]["memFree"] = round(data["mem"]["memFree"] / 1024 / 1024, 2) data["mem"]["memRealUsed"] = round(data["mem"]["memRealUsed"] / 1024 / 1024, 2) data["mem"]["memShared"] = round(data["mem"]["memShared"] / 1024 / 1024, 2) data["mem"]["memTotal"] = round(data["mem"]["memTotal"] / 1024 / 1024, 2) data["physical_memory"]= round(data["physical_memory"] / 1024 / 1024, 2) if is_much_difference(data["mem"]["memTotal"], data["physical_memory"]): if data["mem"]["memTotal"] >= 1024: data["mem"]["memNewTotal"] = "%.2fGB" % (data["mem"]["memTotal"] / 1024) else: data["mem"]["memNewTotal"] = "%.2fMB" % data["mem"]["memTotal"] else: if data["physical_memory"] >= 1024: data["mem"]["memNewTotal"] = "%.2fGB" % (data["physical_memory"] / 1024) else: data["mem"]["memNewTotal"] = "%.2fMB" % data["physical_memory"] return data def _run_script(self, script_name: str) -> Tuple[Optional[CommandResult], str]: local_file = os.path.join(self._local_scripts_dir, script_name) if not os.path.exists(local_file): return None, "Script does not exist" executor = None try: executor = self._get_ssh_executor() executor.open() result = executor.execute_local_script_collect(local_file) return result, "" except RuntimeError: return None, "SSH connection failed" except Exception as e: return None, str(e) finally: if executor: executor.close() def target_file_exits(self, target_file: str) -> Tuple[bool, str]: try: executor = self._get_ssh_executor() executor.open() result, err = executor.path_exists(target_file) return result, err except RuntimeError: print(traceback.format_exc(), flush=True) return False, "SSH connection failed" except Exception as e: print(traceback.format_exc(), flush=True) return False, str(e) def create_dir(self, path: str) -> Tuple[bool, str]: try: executor = self._get_ssh_executor() executor.open() result, err = executor.create_dir(path) return result, err except RuntimeError: print(traceback.format_exc()) return False, "SSH connection failed" except Exception as e: return False, str(e) def upload_file(self, filename: str, target_path: str, mode: str = "cover", call_log: Callable[[int, str], None] = None) -> str: if not os.path.isfile(filename): return "File: {} does not exist".format(filename) target_file = os.path.join(target_path, os.path.basename(filename)) path_info = self.path_info(target_file) if isinstance(path_info, str): return path_info if path_info['exists'] and mode == "ignore": call_log(0, "File upload:{} -> {},The target file already exists, skip uploading".format(filename, target_file)) return "" if path_info['exists'] and mode == "rename": upload_name = "{}_{}".format(os.path.basename(filename), public.md5(filename)) call_log(0, "File upload:{} -> {},The target file already exists, it will be renamed to {}".format(filename, target_file, upload_name)) else: upload_name = os.path.basename(filename) try: executor = self._get_ssh_executor() executor.open() def progress_callback(current_size: int, total_size: int): if total_size == 0: return call_log(current_size * 100 // total_size, "" ) executor.upload(filename, os.path.join(target_path, upload_name), progress_callback=progress_callback) except RuntimeError: print(traceback.format_exc(), flush=True) return "SSH connection failed" except Exception as e: print(traceback.format_exc(), flush=True) return str(e) return "" def upload_dir_check(self, target_file: str) -> str: try: executor = self._get_ssh_executor() executor.open() path_info = executor.path_info(target_file) if not path_info['exists']: return "" if path_info['is_dir']: return "The name path is not a directory" return "" except RuntimeError: print(traceback.format_exc(), flush=True) return "SSH connection failed" except Exception as e: print(traceback.format_exc(), flush=True) return str(e) def path_info(self, path: str) -> Union[str, Dict]: try: executor = self._get_ssh_executor() executor.open() path_info = executor.path_info(path) return path_info except RuntimeError as e: print(traceback.format_exc(), flush=True) return "SSH connection failed: {}".format(str(e)) except Exception as e: print(traceback.format_exc(), flush=True) return "Failed to obtain path information:{}".format(str(e))