From 80272352b3067ebb2cb3011cfbeeef5e9d464fa6 Mon Sep 17 00:00:00 2001
From: smjiao <smjiao@isoftstone.com>
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

