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() 按照行('\r', '\r\n', \n')分隔,返回一個包含各行作爲元素的列表,如果參數 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模塊學習記錄、封裝類的使用及交換機配置批量備份程序的實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章