From 80272352b3067ebb2cb3011cfbeeef5e9d464fa6 Mon Sep 17 00:00:00 2001 From: smjiao Date: Mon, 18 Sep 2023 20:37:50 +0800 Subject: [PATCH 1/1] fix bash file sync error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zeus/config_manager/view.py | 48 ++++++++++++++++++++----- zeus/host_manager/ssh.py | 72 +++++++++++++++++++++++++++++++------ 2 files changed, 102 insertions(+), 18 deletions(-) diff --git a/zeus/config_manager/view.py b/zeus/config_manager/view.py index baeef7e..6779153 100644 --- a/zeus/config_manager/view.py +++ b/zeus/config_manager/view.py @@ -16,6 +16,7 @@ Author: Description: Restful APIs for host """ import json +import os from typing import List, Dict from vulcanus.multi_thread_handler import MultiThreadHandler @@ -26,7 +27,7 @@ from zeus.conf.constant import CERES_COLLECT_FILE, CERES_SYNC_CONF from zeus.database.proxy.host import HostProxy from zeus.function.model import ClientConnectArgs from zeus.function.verify.config import CollectConfigSchema, SyncConfigSchema -from zeus.host_manager.ssh import execute_command_and_parse_its_result +from zeus.host_manager.ssh import execute_command_and_parse_its_result, execute_command_sftp_result class CollectConfig(BaseResponse): @@ -222,15 +223,46 @@ class CollectConfig(BaseResponse): class SyncConfig(BaseResponse): - @staticmethod - def sync_config_content(host_info: Dict, sync_config_info: Dict): - command = CERES_SYNC_CONF % json.dumps(sync_config_info) - status, content = execute_command_and_parse_its_result( + def sync_config_by_execute_command_sftp(host_info: Dict, sync_config_info: Dict, local_path: str, + remote_path: str): + content = sync_config_info.get("content") + with open(local_path, "w", encoding="UTF-8") as f: + f.write(content) + status = execute_command_sftp_result( ClientConnectArgs(host_info.get("host_ip"), host_info.get("ssh_port"), - host_info.get("ssh_user"), host_info.get("pkey")), command) + host_info.get("ssh_user"), host_info.get("pkey")), local_path, remote_path) return status + @staticmethod + def sync_config_content(host_info: Dict, sync_config_info: Dict): + join_path = "/tmp" + if sync_config_info.get("file_path") == "/etc/profile": + local_path = os.path.join(join_path, "profile") + remote_path = "/etc/profile" + status = SyncConfig.sync_config_by_execute_command_sftp(host_info, sync_config_info, local_path, + remote_path) + return status + elif sync_config_info.get("file_path") == "/etc/rc.local": + local_path = os.path.join(join_path, "rc.local") + remote_path = "/etc/rc.local" + status = SyncConfig.sync_config_by_execute_command_sftp(host_info, sync_config_info, local_path, + remote_path) + return status + elif sync_config_info.get("file_path") == "/etc/bashrc": + local_path = os.path.join(join_path, "bashrc") + remote_path = "/etc/bashrc" + status = SyncConfig.sync_config_by_execute_command_sftp(host_info, sync_config_info, local_path, + remote_path) + return status + else: + command = CERES_SYNC_CONF % json.dumps(sync_config_info) + + status, content = execute_command_and_parse_its_result( + ClientConnectArgs(host_info.get("host_ip"), host_info.get("ssh_port"), + host_info.get("ssh_user"), host_info.get("pkey")), command) + return status + @BaseResponse.handle(schema=SyncConfigSchema, token=False) def put(self, **params): @@ -244,13 +276,13 @@ class SyncConfig(BaseResponse): } # Query host address from database - proxy = HostProxy(configuration) + proxy = HostProxy() if not proxy.connect(): return self.response(code=state.DATABASE_CONNECT_ERROR, data={"resp": sync_result}) status, host_list = proxy.get_host_info( {"username": "admin", "host_list": [params.get('host_id')]}, True) - if status != state.SUCCEED or len(host_list) == 1: + if status != state.SUCCEED: return self.response(code=status, data={"resp": sync_result}) host_info = host_list[0] diff --git a/zeus/host_manager/ssh.py b/zeus/host_manager/ssh.py index 4c3a259..11f6383 100644 --- a/zeus/host_manager/ssh.py +++ b/zeus/host_manager/ssh.py @@ -15,6 +15,7 @@ from io import StringIO from typing import Tuple import paramiko +from paramiko import sftp from vulcanus.log.log import LOGGER from vulcanus.restful.resp import state @@ -57,7 +58,13 @@ class SSH: """ def __init__(self, ip, username, port, password=None, pkey=None): - self._client_args = {'hostname': ip, 'username': username, 'port': port, "password": password, "pkey": pkey} + self._client_args = { + 'hostname': ip, + 'username': username, + 'port': port, + "password": password, + "pkey": pkey + } self._client = self.client() def client(self): @@ -71,15 +78,15 @@ class SSH: def execute_command(self, command: str, timeout: float = None) -> tuple: """ - create a ssh client, execute command and parse result + create a ssh client, execute command and parse result - Args: - command(str): shell command - timeout(float): the maximum time to wait for the result of command execution + Args: + command(str): shell command + timeout(float): the maximum time to wait for the result of command execution - Returns: - tuple: - status, result, error message + Returns: + tuple: + status, result, error message """ open_channel = self._client.get_transport().open_session(timeout=timeout) open_channel.set_combine_stderr(False) @@ -110,13 +117,14 @@ def execute_command_and_parse_its_result(connect_args: ClientConnectArgs, comman status, result """ if not connect_args.pkey: - return state.SSH_AUTHENTICATION_ERROR, f"ssh authentication failed when connect host " f"{connect_args.host_ip}" + return state.SSH_AUTHENTICATION_ERROR, f"ssh authentication failed when connect host " \ + f"{connect_args.host_ip}" try: client = SSH( ip=connect_args.host_ip, username=connect_args.ssh_user, port=connect_args.ssh_port, - pkey=paramiko.RSAKey.from_private_key(StringIO(connect_args.pkey)), + pkey=paramiko.RSAKey.from_private_key(StringIO(connect_args.pkey)) ) exit_status, stdout, stderr = client.execute_command(command, connect_args.timeout) except socket.error as error: @@ -131,3 +139,47 @@ def execute_command_and_parse_its_result(connect_args: ClientConnectArgs, comman return state.SUCCEED, stdout LOGGER.error(stderr) return state.EXECUTE_COMMAND_ERROR, stderr + + +def execute_command_sftp_result(connect_args: ClientConnectArgs, local_path=None, remote_path=None): + """ + create a ssh client, execute command and parse result + + Args: + connect_args(ClientConnectArgs): e.g + ClientArgs(host_ip='127.0.0.1', ssh_port=22, ssh_user='root', pkey=RSAKey string) + command(str): shell command + + Returns: + tuple: + status, result + """ + global sftp_client, client + if not connect_args.pkey: + return state.SSH_AUTHENTICATION_ERROR, f"ssh authentication failed when connect host " \ + f"{connect_args.host_ip}" + try: + client = SSH( + ip=connect_args.host_ip, + username=connect_args.ssh_user, + port=connect_args.ssh_port, + pkey=paramiko.RSAKey.from_private_key(StringIO(connect_args.pkey)) + ) + sftp_client = client.client().open_sftp() + + # Specifies the path to the local file and the remote file + # Upload files to a remote server + sftp_client.put(local_path, remote_path) + return state.SUCCEED + except socket.error as error: + LOGGER.error(error) + return state.SSH_CONNECTION_ERROR, "SSH.Connection.Error" + except paramiko.ssh_exception.SSHException as error: + LOGGER.error(error) + return state.SSH_AUTHENTICATION_ERROR, "SSH.Authentication.Error" + except Exception as error: + LOGGER.error(error) + return state.SSH_AUTHENTICATION_ERROR, "SSH.Authentication.Error" + finally: + sftp_client.close() + client.close() -- 2.33.1.windows.1