• Python paramiko模块学习记录、封装类的使用及交换机配置批量备份程序的实现


    利用Python3 paramiko实现了基于交换机配置自动批量备份的实现,代码已托管GitHub,仓库地址:switchboardbackup,有疑问可在Github提问。


    前言

    paramiko是基于Python实现SSH连接的第三方库,底层采用了cryptography
    安装该第三方库,直接使用命令 pip install paramiko

    官方文档:http://docs.paramiko.org/en/stable/index.html

    借鉴博文:https://www.cnblogs.com/xiao-apple36/p/9144092.html

    Paramiko介绍

    paramiko包含两个核心组件:SSHClient和SFTPClient。

    • SSHClient的作用类似于Linux的ssh命令,是对SSH会话的封装,该类封装了传输(Transport),通道(Channel)及SFTPClient建立的方法(open_sftp),通常用于执行远程命令。
    • SFTPClient的作用类似与Linux的sftp命令,是对SFTP客户端的封装,用以实现远程文件操作,如文件上传、下载、修改文件权限等操作。
    1、Channel:是一种类Socket,一种安全的SSH传输通道;
    2、Transport:是一种加密的会话,使用时会同步创建了一个加密的Tunnels(通道),这个Tunnels叫做Channel;
    3、Session:是client与Server保持连接的对象,用connect()/start_client()/start_server()开始会话。
    

    SSHClient常用的方法介绍

    SSHClient类是SSH服务会话的高级表示,该类封装了传输(transport)、通道(channel)及SFTPClient的校验、建立的方法,通常用于执行远程命令

    • connect()
      实现远程服务器的连接与认证,对于该方法只有hostname是必传参数。
    常用参数
    hostname 连接的目标主机
    port=SSH_PORT 指定端口
    username=None 验证的用户名
    password=None 验证的用户密码
    pkey=None 私钥方式用于身份验证
    key_filename=None 一个文件名或文件列表,指定私钥文件
    timeout=None 可选的tcp连接超时时间
    allow_agent=True, 是否允许连接到ssh代理,默认为True 允许
    look_for_keys=True 是否在~/.ssh中搜索私钥文件,默认为True 允许
    compress=False, 是否打开压缩
    
    • set_missing_host_key_policy()
      设置远程服务器没有在know_hosts文件中记录时的应对策略。目前支持三种策略:

      • AutoAddPolicy 自动添加主机名及主机密钥到本地HostKeys对象,不依赖load_system_host_key的配置。即新建立ssh连接时不需要再输入yes或no进行确认
      • WarningPolicy 用于记录一个未知的主机密钥的python警告。并接受,功能上和AutoAddPolicy类似,但是会提示是新连接
      • RejectPolicy 自动拒绝未知的主机名和密钥,依赖load_system_host_key的配置。此为默认选项
    • set_missing_host_key_policy()
      在远程服务器执行Linux命令的方法。

    • open_sftp()
      在当前ssh会话的基础上创建一个sftp会话。该方法会返回一个SFTPClient对象。

    • 运用实例

    # --------基本的连接--------
    import paramiko
     
       # 实例化SSHClient
       client = paramiko.SSHClient()
     
       # 自动添加策略,保存服务器的主机名和密钥信息,如果不添加,那么不再本地know_hosts文件中记录的主机将无法连接
       client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     
       # 连接SSH服务端,以用户名和密码进行认证
       client.connect(hostname='192.168.1.105', port=22, username='root', password='123456')
     
       # 打开一个Channel并执行命令
       stdin, stdout, stderr = client.exec_command('df -h ')  # stdout 为正确输出,stderr为错误输出,同时是有1个变量有值
     
       # 打印执行结果
       print(stdout.read().decode('utf-8'))
     
       # 关闭SSHClient
       client.close()
    
    # --------密钥连接--------
    # 配置私人密钥文件位置
    private = paramiko.RSAKey.from_private_key_file('/Users/ch/.ssh/id_rsa')
     
    #实例化SSHClient
    client = paramiko.SSHClient()
     
    #自动添加策略,保存服务器的主机名和密钥信息,如果不添加,那么不再本地know_hosts文件中记录的主机将无法连接
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     
    #连接SSH服务端,以用户名和密码进行认证
    client.connect(hostname='10.0.0.1',port=22,username='root',pkey=private)
    
    # --------SSHClient 封装 Transport--------
    import paramiko
     
       # 创建一个通道
       transport = paramiko.Transport(('hostname', 22))
       transport.connect(username='root', password='123')
     
       ssh = paramiko.SSHClient()
       ssh._transport = transport
     
       stdin, stdout, stderr = ssh.exec_command('df -h')
       print(stdout.read().decode('utf-8'))
     
       transport.close()
    

    SFTPClient常用的方法介绍

    SFTPClient作为一个SFTP客户端对象,根据SSH传输协议的sftp会话,实现远程文件操作,比如文件上传、下载、权限、状态等操作。

    • from_transport(cls,t)
      创建一个已连通的SFTP客户端通道。t(transport),一个已通过验证的传输对象。
    import paramiko
    
    t = paramiko.Transport(('192.168.56.132',22))
    t.connect(username='root',password='1234567')
    sftp = paramiko.SFTPClient.from_transport(t)
    
    • put(localpath, remotepath, callback=None, confirm=True)
      上传本地文件到远程SFTP服务端。
      • localpath(str类型),需上传的本地文件(源);
      • remotepath(str类型),远程路径(目标);
      • callback(funcation(int,int)),获取已接收的字节数及总传输字节数,以便回调函数调用,默认为None;
      • confirm(bool类型),文件上传完毕后是否调用stat()方法,以便确认文件的大小。
    localpath='/home/access.log'
    remotepath='/data/logs/access.log'
    sftp.put(localpath,remotepath)
    
    • get(remotepath, localpath, callback=None)
      从远程SFTP服务端下载文件到本地。
      • remotepath(str类型),需要下载的远程文件(源);
      • callback(funcation(int,int)),获取已接收的字节数及总传输字节数,以便回调函数调用,默认为None;
    remotepath = '/data/logs/access.log'
    localpath = '/home/access.log'
    sftp.get(remotepath,localpath)
    
    • 其它方法
      • mkdir,在SFTP服务端创建目录,如sftp.mkdir("/home/userdir",mode=0777),默认模式是0777(八进制),在某些系统上,mode被忽略。在使用它的地方,当前的umask值首先被屏蔽掉。
      • remove,删除SFTP服务端指定目录,如sftp.remove("/home/userdir")。
      • rename,重命名SFTP服务端文件或目录,如sftp.rename("/home/test.sh","/home/testfile.sh")
      • stat,获取远程SFTP服务端指定文件信息,如sftp.stat("/home/testfile.sh")。
      • listdir,获取远程SFTP服务端指定目录列表,以Python的列表(List)形式返回,如sftp.listdir("/home")。

    封装类sshConn.py

    以下封装类可直接使用,

    #coding:utf8
    
    import paramiko
    import os
    import traceback
    
    class SshConnectError(Exception):#
        pass
    
    class controlHost:
        def __init__(self, host, username, password, port=22, key_file='/root/.ssh/id_rsa'):##本地密钥文件路径
            self.host = host
            self.pkey = paramiko.RSAKey.from_private_key_file(key_file)
            self.ssh = controlHost.__sshConn(self.host, username, password, self.pkey, int(port)) #调用类中的静态方法__sshConn 返回ssh连接对象
            self.sftp = self.__sftpConn()
    
    
        def close(self):
            if hasattr(self.ssh, "close"):
                self.ssh.close()
    
        @staticmethod
        def __sshConn(host, username, password, pkey, port):
            ssh = paramiko.SSHClient()##创建一个SSH客户端client对象
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            try:
                ssh.connect(hostname=host, port=int(port), username=username, pkey=pkey) #免密登陆方式
                # print('免密登陆方式')
            except:
                try:
                    ssh.connect(hostname=host, port=int(port), username=username, password=password)#密码认证
                    # print('密码认证')
                except:
                    raise SshConnectError("SSH Connect %s Error!" %host)
                else:
                    return ssh
            else:
                return ssh
    
        #返回sftp通道实例对象 方法
        def __sftpConn(self):
            transport = self.ssh.get_transport() #1.先ssh连上,2.再建立通道
            sftp = paramiko.SFTPClient.from_transport(transport) #创建一个已连通的SFTP客户端通道。
            return sftp
    
    
        #执行命令方法
        def exeCommand(self, cmd, timeout=300):
            _, stdout, stderr = self.ssh.exec_command(cmd, timeout=timeout)
            try:
                channel = stdout.channel
                #print('channel',channel)
                exit_code = channel.recv_exit_status()
                #print('exit_code',exit_code)报错返回码是127,没有报错是0
                stdout = stdout.read().strip()
                stderr = stderr.read().strip()
                return {"status": 1, "stdout": stdout, "stderr": stderr, 'exit_code': exit_code}
            except:
                return {"status": 0, "stdout": stdout, "stderr": stderr, 'exit_code': 127}
    
        #文件上传下载方法
        def sftpFile(self, localpath, remotepath, action):
            try:
                if action == 'push':
                    dirname = os.path.dirname(remotepath)
                    self.exeCommand("mkdir -p %s" % dirname)
                    self.sftp.put(localpath, remotepath)
                    return {"status": 1, "message": 'sftp %s %s success!' % (self.host, action)}
                elif action == "pull":
                    dirname = os.path.dirname(localpath)
                    if not os.path.exists(dirname):
                        os.makedirs(dirname)
                    # if os.path.exists(localpath):
                    #     os.remove(localpath)
                    self.sftp.get(remotepath, localpath)
                    return {"status": 1, "stdout": 'sftp %s %s success!' % (self.host, action), "stderr": ""}
            except Exception as e:
                return {"status": 0, "stderr": 'sftp %s %s failed %s' % (self.host, action, str(e)), "stdout": ""}
    
        @staticmethod
        def iter_local_path(abs_path):
            '''遍历本机该目录中所以的文件,并返回'''
            result = set([])
            for j in os.walk(abs_path):
                print(j)
                base_path = j[0]
                file_list = j[2]
                for k in file_list:
                    p = os.path.join(base_path, k)
                    result.add(p)
            return result
    
        def iter_remote_path(self, abs_path):
            '''获取远程主机abs_path下的所以文件'''
            result = set([])
            try:
                stat = str(self.sftp.lstat(abs_path))
                print('stat',stat)
            except FileNotFoundError:
                return result
            else:
                if stat.startswith("d"):
                    file_list = self.exeCommand("ls %s" %abs_path)["stdout"].decode(encoding='utf-8').strip().splitlines()
                    #Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
                     #Python splitlines() 按照行('
    ', '
    ', 
    ')分隔,返回一个包含各行作为元素的列表,如果参数 keepends (默认值) False,不包含换行符,如果为 True,则保留换行符。
    
                    for j in file_list:
                        p = os.path.join(abs_path, j)
                        result.update(self.iter_remote_path(p))  #合并 并集U
                else:
                    result.add(abs_path)
            return result
    
    if __name__ == '__main__':
        x = controlHost("192.168.1.40", 'root', 'Gota34cc')
    
        #测试 获取本地某个目录的所有文件
        # w = x.iter_local_path("/root/test")
        # print(w)
    
        # 测试 获取远程主机某个目录的所有文件
        # y = x.iter_remote_path("/root/test")
        # print(y)
    
        # 测试 命令执行方法
        # y = x.exeCommand("uname -r")
        # print(y)
    
        # 测试 上传下载
        w = x.sftpFile("/tmp/ansible.txt", '/tmp/xx.txt', "push")  #将本地机器的/tmp/ansible.txt,上传至远程主机/tmp目录下并命名为xx.sh
        # w = x.sftpFile('/tmp/aaaa.py', '/tmp/xyz.py', 'pull')
        print(w)
    
        x.close()
    
    

    个人博客:Loak 正 - 关注人工智能及互联网的个人博客
    文章地址:Python paramiko模块学习记录、封装类的使用及交换机配置批量备份程序的实现

  • 相关阅读:
    c++ STL中的vector与list为什么没有提供find操作?
    转发:CAOZ星球提问。 遇到很大瓶颈,想离职又不敢离职怎么办
    转发 :caoz:数据分析这点事
    那些绕不开的Linux
    记录 《 Bootstrap 基础教程》 学习笔记 第一天
    迈出你的第一步——天助自助者
    this指向问题
    小结
    前端小白的福利
    真实案例分享
  • 原文地址:https://www.cnblogs.com/l0zh/p/13739729.html
Copyright © 2020-2023  润新知