• python 使用 paramiko 和 threading 模块 在本地windows上实现同时与多个远程Linux机子连接操作cmd命令和上传(下载)单个文件或者整个目录


    关键知识:  

      paramiko 模块, stat模块,os模块

      threading 模块 --> 实现同时与多个远程上传和下载单个文件或者整个目录

      paramiko 模块 --> 实现链接远程,传输数据

      stat中的  stat.S_ISDIR(remote_path_mode)  -->  判断远程是目录还是文件

      os中的 os.walk(path)   --> 提取本地目录中的所有文件和子目录

       

    定义一个基础类Connection,继承 threading.Thread

     1 import paramiko, datetime, os, threading, re, stat
     2 
     3 
     4 class Connection(threading.Thread):
     5 
     6     def __init__(self, hostname=None, port=None, password=None, username=None):
     7         super(Connection, self).__init__()
     8         self.hostname = hostname
     9         self.port = port
    10         self.password = password
    11         self.username = username
    12         self.str_host = '%s:%s' % (self.hostname, self.port)
    13 
    14     def run(self):
    15         pass

    定义一个cmd执行类CMDThread,继承Connection. CMDThread类实例化后会处理cmd相关命令

     1 class CMDThread(Connection):
     2 
     3     def __init__(self, hostname=None, port=None, password=None, username=None, echo_cmd=None):
     4         super(CMDThread, self).__init__(hostname, port, password, username,)
     5         self.echo_cmd = echo_cmd
     6 
     7     def run(self):
     8         paramiko.util.log_to_file('MyFabric.log')
     9         ssh = paramiko.SSHClient()
    10         ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    11         try:
    12             ssh.connect(hostname=self.hostname, port=self.port, username=self.username, password=self.password)
    13             stdin, stdout, stderr = ssh.exec_command(self.echo_cmd)
    14             res, err = stdout.read(), stderr.read()
    15             result = res if res else err
    16             print(self.str_host.center(60, '-'))
    17             print(result.decode())
    18             ssh.close()
    19         except Exception as e:
    20             print(e)

    定义一个上传文件的类PutThread,继承Connection,PutThread实例化, 先判断本地要上传的是单个文件还是整个目录,然后链接远程上传至远程指定位置.

     1 class PutThread(Connection):
     2 
     3     def __init__(self, hostname=None, password=None, username=None, port=None, local_path=None, remote_path=None):
     4         super(PutThread, self).__init__(hostname, password, username, port)
     5         self.local_path = local_path
     6         self.remote_path = remote_path
     7 
     8     def run(self):
     9         paramiko.util.log_to_file('MyFabric.log')
    10         t = paramiko.Transport((self.hostname, self.port))
    11         t.connect(username=self.username, password=self.password)
    12         sftp = paramiko.SFTPClient.from_transport(t)
    13         if os.path.isfile(self.local_path):
    14             filename = os.path.split(self.local_path)[-1]
    15             remote_file = os.path.join(self.remote_path, filename).replace('\', '/')
    16             try:
    17                 sftp.put(self.local_path, remote_file)
    18                 print('[*] Put dir %s success %s --%s' % (filename, datetime.datetime.now(), self.str_host))
    19                 t.close()
    20             except Exception as e:
    21                 print('[*] Put file %s failed %s Err:%s --%s' % (filename, datetime.datetime.now(), e, self.str_host))
    22         elif os.path.isdir(self.local_path):
    23             local_dir_root = os.path.split(self.local_path)[0].replace('\', '/')
    24             dir_name = os.path.split(self.local_path)[-1]
    25             remote_dir_root = self.remote_path
    26             remote_files_path = os.path.join(remote_dir_root, dir_name).replace('\', '/')
    27             try:
    28                 try:
    29                     sftp.stat(remote_files_path)
    30                 except IOError:
    31                     print('mkdir remote dir [%s]' % remote_files_path)
    32                     sftp.mkdir(remote_files_path)
    33                 for root, dirs, files in os.walk(self.local_path):
    34                     if local_dir_root:          # 这里判断输入的self.local_path是不是abspath, 然后做相应的路径字符串拼接
    35                         remote_root = re.sub(local_dir_root, remote_dir_root, root.replace('\', '/'))
    36                     else:
    37                         remote_root = os.path.join(remote_dir_root, root).replace('\', '/')
    38                     if dirs:
    39                         for name in dirs:
    40                             mk_remote_path = os.path.join(remote_root, name).replace('\', '/') 
    41                             sftp.mkdir(mk_remote_path)
    42                     if files:
    43                         for filename in files:
    44                             local_file = os.path.join(root, filename)
    45                             remote_file = os.path.join(remote_root, filename).replace('\', '/')
    46                             sftp.put(local_file, remote_file)
    47                 print('[*] Put dir %s success %s --%s' % (dir_name, datetime.datetime.now(), self.str_host))
    48                 t.close()
    49             except Exception as e:
    50                 print('[*] Put dir %s failed  %s Err:%s --%s' % (dir_name, datetime.datetime.now(), e, self.str_host))

    定义一个下载文件的类GetThread, 继承Connection.GetThread实例化后,会先链接远程,判断远程下载的是单个文件还是整个目录, 然后在本地指定位置建立一个以远程hostname命名的文件夹,来存放从该远程下载的文件或整个目录

     1 class GetThread(Connection):
     2     """
     3     'local_path' must be abspath
     4     """
     5     def __init__(self, hostname=None, password=None, username=None, port=None, local_path=None, remote_path=None):
     6         super(GetThread, self).__init__(hostname, password, username, port)
     7         self.local_path = local_path
     8         self.remote_path = remote_path
     9 
    10     def run(self):
    11         paramiko.util.log_to_file('MyFabric.log')
    12         t = paramiko.Transport((self.hostname, self.port))
    13         t.connect(username=self.username, password=self.password)
    14         sftp = paramiko.SFTPClient.from_transport(t)
    15         remote_path_mode = sftp.stat(self.remote_path).st_mode     # 获取远程文件的类型st_mode
    16         if stat.S_ISDIR(remote_path_mode):                         # 使用stat模块的S_ISDIR方法判断远程文件的st_mode是否是目录类型
    17             all_files = get_all_files(sftp, self.remote_path)      # get_all_files函数用来从远程目录提取该目录下所有文件的路径
    18             try:
    19                 for remote_file in all_files:
    20                     local_root_path = self.local_path + '/' + self.hostname  # 这里做一个本地路径的字符串拼接,把远程hostname拼进去,为后面建立相应的文件夹做准备
    21                     remote_root_path = os.path.split(self.remote_path)[0]
    22                     local_file = re.sub(remote_root_path, local_root_path, remote_file)  # 拼接好文件的完整路径
    23                     root = os.path.split(local_file)[0]                                  # 这个是该文件的根目录
    24                     check_dirs(root)                    # check_dirs函数用来检查该文件的根目录root是否在本地存在,如果不存在就建立该目录
    25                     sftp.get(remote_file, local_file)
    26                 print('[*] Get dir %s success %s --%s' % (self.remote_path,
    27                                                           datetime.datetime.now(),
    28                                                           self.str_host))
    29             except Exception as e:
    30                 print('[*] Get dir %s failed %s Err:%s --%s' % (self.remote_path,
    31                                                                 datetime.datetime.now(),
    32                                                                 e,
    33                                                                 self.str_host))
    34         else:
    35             filename = os.path.split(self.remote_path)[-1]
    36             local_root_path = self.local_path + '/' + self.hostname
    37             local_file = local_root_path + '/' + filename
    38             check_dirs(local_root_path)    # 同上检查目录是否存在,不存在就建立目录
    39             try:
    40                 sftp.get(self.remote_path, local_file)
    41                 print('[*] Get file %s success %s --%s' % (filename, datetime.datetime.now(), self.str_host))
    42             except Exception as e:
    43                 print('[*] Get file %s failed %s Err:%s --%s' % (filename, datetime.datetime.now(), e, self.str_host))
    44         t.close()

    其中get_all_files用来从远程目录提取该目录下所有文件的路径.

     1 def get_all_files(sftp, remote_dir):
     2     """
     3     get all abspath of files in remote dir
     4     :param sftp:
     5     :param remote_dir:
     6     :return:
     7     """
     8     all_files = []
     9     if remote_dir[-1] == '/':
    10         remote_dir = remote_dir[0:-1]
    11     files = sftp.listdir_attr(remote_dir)          # 获取目录下的所有文件和子目录stat信息
    12     for i in files:
    13         file_path = remote_dir + '/' + i.filename  # 拼接完整的文件路径
    14         if stat.S_ISDIR(i.st_mode):                # 用stat模块的S_IDDIR方法判断文件是否是目录 
    15             all_files.extend(get_all_files(sftp, file_path)) # 如果是子目录就递归处理此目录
    16         else:
    17             all_files.append(file_path)            # 如果不是子目录,就把文件路径加到all_files这个列表中
    18     return all_files

    注意: 其中列表操作extend和append的区别,extend()的参数只能是列表,append()的参数什么都可以,具体可自行百度.

    其中check_dirs 函数是在本地windows上检查目录是否存在,不存在就创建目录

     1 def check_dirs(path):
     2     """
     3     Check the root of 'path'  and create the root without existing
     4     :param path:
     5     :return:
     6     """
     7     dirs = get_dirs_path(path)[::-1]   # get_dirs_path函数用来提取一个目录的所有层级根目录路径包括自己本身的路径
     8     for i in dirs:
     9         if not os.path.exists(i):
    10             os.mkdir(i)

    get_dirs_path(path)获取到的是类似这样的列表['c:/Desktop/test' , 'c:/Desktop', 'c:'],  [::-1]这个是把列表顺序反转, --->['c:', 'c:/Desktop', 'c:/Desktop/test']

    其中 get_dirs_path函数用来提取一个目录的所有层级根目录路径包括自己本身的路径, 类似上面的列表

     1 def get_dirs_path(path):    # 这里的path必须是abspath
     2     """
     3     gets the root directory at all levels of path(including 'path')
     4     example: 'c:/Desktop/test/' --> ['c:/Desktop/test' , 'c:/Desktop', 'c:']
     5     'path' must be abspath on windows
     6     """
     7     dirs = []
     8     if path[-1] == '/':
     9         path = path[0:-1]
    10     dirs.append(path)
    11     lis = os.path.split(path)   # 把根目录路径和目录名字分开,然后一层一层扒开处理直到最后出现类似这样的列表['c:', '']这个就是最终扒开的不能再扒了,再扒就进入死循环了,所以下面要在出现这个情况的时候终止递归
    12     path = lis[0]
    13     if lis[-1]:
    14         dirs.extend(get_dirs_path(path))    # 这里也是用递归方法处理一个路径
    15     return dirs

    下面就是主逻辑了

     1 def cmd_handler():
     2     hosts = get_hosts()
     3     echo_cmd = input('Enter echo cmd:').strip()
     4     obj = []
     5     for host in hosts:
     6         t = CMDThread(host['hostname'], host['port'], host['password'], host['username'], echo_cmd)
     7         t.start()
     8         obj.append(t)
     9     for i in obj:
    10         i.join()
    11     print('end'.center(60, '-'))
    12 
    13 
    14 def put_handler():
    15     transport(tag='put')
    16 
    17 
    18 def get_handler():
    19     transport(tag='get')
    20 
    21 
    22 def transport(tag=None):
    23     hosts = get_hosts()
    24     while True:
    25         remote_path = input('remote path >>').strip().replace('\', '/')
    26         local_path = input('local path >>').strip().replace('\', '/')
    27         if local_path and remote_path:
    28             break
    29         if not local_path or not remote_path:
    30             continue
    31     obj = []
    32     for host in hosts:
    33         if tag == 'get':
    34             t = GetThread(host['hostname'], host['port'], host['password'], host['username'], local_path, remote_path)
    35         elif tag == 'put':
    36             t = PutThread(host['hostname'], host['port'], host['password'], host['username'], local_path, remote_path)
    37         t.start()
    38         obj.append(t)
    39     for i in obj:
    40         i.join()
    41     print('end'.center(60, '-'))
    42 
    43 
    44 def get_hosts():
    45     hosts = []
    46     with open('hosts_setting', 'r') as f:
    47         dic = {}
    48         for line in f:
    49             date = line.strip().split()
    50             dic['hostname'] = date[0].split(':')[0]
    51             dic['port'] = int(date[0].split(':')[-1])
    52             dic['username'] = date[1]
    53             dic['password'] = date[-1]
    54             hosts.append(dic)
    55             dic = {}
    56     return hosts
    57 
    58 if __name__ == '__main__':
    59     while True:
    60         print('''
    61         menu:
    62             1.cmd
    63             2.put
    64             3.get
    65             4.quit
    66         ''')
    67         menu = {
    68             '1': cmd_handler,
    69             '2': put_handler,
    70             '3': get_handler,
    71             '4': exit
    72         }
    73         choice = input('Choice handler:').strip()
    74         if choice in menu:
    75             menu[choice]()
    程序主逻辑

    有个小问题就是, 如果遇到目录里面有空的子目录,  上传可以把空子目录结构一起复制到远程,  但下载不会把空的子目录结构也复制到本地, 有需要的亲,自己可以动手写,这里就不多说了.

  • 相关阅读:
    懒加载数据,在取出数据时容易出的bug....
    小程序申请及小程序发布
    SSL证书申请及安装
    iview modal 覆盖问题
    取消div独占一行的特性
    GPS坐标互转:WGS-84(GPS)、GCJ-02(Google地图)、BD-09(百度地图)、(百度坐标转谷歌坐标)
    工厂方法模式
    STS创建SpringBoot项目
    snmp协议学习记录
    操作系统命令学习记录
  • 原文地址:https://www.cnblogs.com/JayeHe/p/7372781.html
Copyright © 2020-2023  润新知