介紹
Paramiko 一個第三方包,需要單獨安裝我們知道遠端批次主機管理,比如ansible、Fabric,不需要安裝客戶端的遠端執行命令等,這些都是基於Python原生的SSH,相當於模擬了一個SSH客戶端。其實用的就是paramiko。其實想Fabric這種東西你自己也可以寫,其實特別簡單。paramiko 就是模擬了SSH的客戶端,然後透過和SSH伺服器互動就可以登入,執行命令等操作。
程式碼例項
簡單的SSH登入實現
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # Author: rex.cheny 4 # E-mail: rex.cheny@outlook.com 5 6 7 import paramiko 8 9 10 def main(): 11 # 例項化SSH客戶端 12 ssh = paramiko.SSHClient() 13 # 允許連線不在 known_hosts 檔案的IP 14 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 15 # 連線伺服器 16 ssh.connect(hostname="172.16.42.136", port=22, username="root", password="12qwaszx!") 17 18 # 執行命令,它返回三個結果,stdin 是你輸入的,stdout是它返回給你的,stderr是錯誤資訊 19 stdin, stdout, stderr = ssh.exec_command("ls /") 20 result = stdout.read() 21 print(result.decode(encoding="utf-8")) 22 ssh.close() 23 24 25 if __name__ == '__main__': 26 main()
簡單的SFTP實現
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # Author: rex.cheny 4 # E-mail: rex.cheny@outlook.com 5 6 import paramiko 7 8 9 def main(): 10 try: 11 # 例項化SSH客戶端 12 ssh = paramiko.SSHClient() 13 # 允許連線不在 known_hosts 檔案的IP 14 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 15 # 連線伺服器 16 ssh.connect(hostname="172.16.42.136", port=22, username="root", password="12qwaszx!") 17 transport = ssh.get_transport() 18 19 """ 20 上面程式碼也可以這樣寫 21 transport = paramiko.Transport((host, port)) 22 transport.connect(username=username, password=password) 23 """ 24 25 sftp = paramiko.SFTPClient.from_transport(transport) 26 sftp.put("./readme", "/tmp") 27 transport.close() 28 except Exception as err: 29 print(err) 30 31 32 if __name__ == '__main__': 33 main()
包含SFTP和SSH功能的程式碼例項
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import sys 5 import os 6 import paramiko 7 import configparser 8 9 10 class paramikoTools(object): 11 12 def __init__(self, hostname, port, username, password, privateKeyFile=None, privateKeyFilePwd=None): 13 """ 14 15 :param hostname: SSH伺服器地址 16 :param port: 埠 17 :param username: 使用者名稱 18 :param password: 密碼 19 :param privateKeyFile: 私鑰路徑,如果沒有則不用填寫 20 :param privateKeyFilePwd: 私鑰解壓密碼如果私鑰加密了請務必輸入否則登陸將會失敗 21 :return: 22 """ 23 self._hostname = hostname 24 self._port = port 25 self._username = username 26 self._password = password 27 self._privateKeyFile = privateKeyFile 28 self._privateKeyFilePwd = privateKeyFilePwd 29 30 # 例項化一個SSH客戶端物件 31 self._sshClient = paramiko.SSHClient() 32 # 第一次SSH登入伺服器需要輸入一個Yes,這個就是幫你自動輸入yes 33 self._sshClient.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 34 # 35 self._transport = None 36 # 37 self._sftpClient = None 38 39 # 用於記錄是否建立了SFTP客戶端例項 40 self._isSftpSessionOpened = False 41 # 用於記錄當前是否已經登入 42 self._isLogined = False 43 44 def login(self): 45 if self._privateKeyFile: 46 if os.path.exists(self._privateKeyFile): 47 self._isLogined = self._loginWithPrivateKey() 48 else: 49 print("私鑰路徑不存在。") 50 else: 51 self._isLogined = self._loginWithPassword() 52 53 # 使用者名稱和密碼登陸方法 54 def _loginWithPassword(self): 55 try: 56 # 連線遠端伺服器 57 self._sshClient.connect(hostname=self._hostname, port=self._port, username=self._username, 58 password=self._password) 59 print("密碼登陸成功。") 60 except Exception as err: 61 # print err 62 print("連線失敗,請檢查主機名、埠、使用者名稱或者密碼是否正確。") 63 return False 64 else: 65 return True 66 67 # 私鑰登陸方法 68 def _loginWithPrivateKey(self): 69 try: 70 if self._privateKeyFilePwd: 71 # 取出私鑰,有時私鑰加密了這裡就需要設定私鑰的密碼,用於對私鑰解密。 72 key = paramiko.RSAKey.from_private_key_file(self._privateKeyFile, password=self._privateKeyFilePwd) 73 else: 74 key = paramiko.RSAKey.from_private_key_file(self._privateKeyFile) 75 76 self._sshClient.connect(hostname=self._hostname, port=self._port, username=self._username, 77 password=self._password, pkey=key) 78 print("秘鑰登陸成功。") 79 except Exception as err: 80 # print err 81 print("連線失敗,請檢查主機名、埠、使用者名稱、密碼、私鑰是否正確。") 82 return False 83 else: 84 return True 85 86 @property 87 def isLogined(self): 88 return self._isLogined 89 90 # 建立SFTP客戶端 91 def _createSftpSession(self): 92 """ 93 我這裡建立SFTP連線的方式和網上略有不同,我這裡是基於現有SSH連線來建立的SFTP會話。網上很多都是單純的建立SFTP會話 94 但是即便單純建立SFTP會話其背後也要建立套接字和身份驗證,可能網上的其他形式如下: 95 t = paramiko.Transport((host, port)) 96 t.connect(username=username, password=password) 97 sftp = paramiko.SFTPClient.from_transport(t) 98 """ 99 if self._isLogined: 100 try: 101 # 使用現有SSH連線獲取一個 transport 102 self._transport = self._sshClient.get_transport() 103 # 把 transport 傳遞進去建立SFTP客戶端連線 104 self._sftpClient = paramiko.SFTPClient.from_transport(self._transport) 105 except Exception as err: 106 print(err) 107 else: 108 self._isSftpSessionOpened = True 109 else: 110 print("ssh會話已經關閉或者沒有建立,無法建立SFTP客戶端連線。") 111 self._isSftpSessionOpened = False 112 113 # 路徑檢查 114 def _pathChect(self, localPath, remotePath): 115 # F 表示檔案; D 表示目錄 116 CHECK_CODE = {"CODE": "0", "LOCALPATHTYPE": "F", "REMOTEPATHTYPE": "F"} 117 if os.path.exists(localPath): 118 return CHECK_CODE 119 else: 120 # 本地路徑檢查失敗,不存在。 121 CHECK_CODE = {"CODE": "101", "LOCALPATHTYPE": "", "REMOTEPATHTYPE": ""} 122 return CHECK_CODE 123 124 # 獲取錯誤碼對資訊 125 def _getCheckCodeDesc(self, temp_code): 126 CHECK_CODE_DICT= {"0": "檢查透過", "101": "本地路徑檢查失敗可能不存在"} 127 DESC = "" 128 if CHECK_CODE_DICT.has_key(temp_code): 129 pass 130 else: 131 DESC = "錯誤碼不存在" 132 return DESC 133 134 # 關閉ssh連線 135 def sshConnectLogOut(self): 136 self._sshClient.close() 137 self._transport.close() 138 self._isLogined = False 139 self._isSftpSessionOpened = False 140 141 # 執行命令 142 def execCommand(self, cmd): 143 result = '' 144 if self._isLogined: 145 try: 146 # 遠端執行命令,會返回元祖,裡面包含輸入、輸出、錯誤 147 stdin, stdout, stderr = self._sshClient.exec_command(cmd) 148 except Exception as err: 149 print(err) 150 else: 151 # 輸出的就是字串內容 152 print(stdout.read()) 153 # return result 154 else: 155 print("當前沒有建立SSH的有效連線。") 156 157 # 上傳檔案 158 def upLoadFile(self, localPath, remotePath): 159 count = 0 160 # 如果SFTP客戶端會話沒有建立則自動建立,嘗試三次 161 for times in range(3): 162 if not self._isSftpSessionOpened: 163 count = times + 1 164 print("第 %d 次嘗試建立SFTP客戶端會話,如果失敗將再嘗試 %d 次" % (count, 3 - count)) 165 self._createSftpSession() 166 if count == 3 and not self._isSftpSessionOpened: 167 return None 168 else: 169 continue 170 else: 171 print("SFTP客戶端會話建立成功。") 172 break 173 174 # 目前這裡是直接上傳,沒有區分檔案還是目錄,所以預設只能支援檔案,本地是檔案路徑、遠端也是檔案路徑 175 try: 176 CODE = self._pathChect(localPath, remotePath)["CODE"] 177 if CODE == "0": 178 # 路徑檢查都成功後如何處理 179 print("開始上傳檔案。") 180 self._sftpClient.put(localPath, remotePath) 181 print("上傳檔案完畢。") 182 else: 183 print(self._getCheckCodeDesc(CODE)) 184 except Exception as err: 185 print(err) 186 else: 187 pass 188 189 # 下載檔案 190 def downLoad(self, remotePath, localPath): 191 pass 192 193 194 def main(): 195 # 建立ini檔案讀取例項 196 config = configparser.ConfigParser() 197 # 設定配置檔案路徑 198 config_file_path = '/Users/rex.chen/PycharmProjects/Study/paramikoTest/config' 199 # 讀取檔案 200 config.read(config_file_path) 201 # 獲取內容,第一個引數是區,第二個引數是 鍵 202 hostname = config.get("ssh", "hostname") 203 port = config.getint("ssh", "port") 204 username = config.get("ssh", "username") 205 password = config.get("ssh", "password") 206 private_key_path = config.get("ssh", "private_key_path") 207 208 a = paramikoTools(hostname, port, username, password, private_key_path, password) 209 a.login() 210 # a.execCommand("df") 211 # a.upLoadFile('/Users/rex.chen/Downloads/mapi-webapp.war', '/home/chenyun/update/mapi-webapp.war') 212 print(a.isLogined) 213 214 215 if __name__ == "__main__": 216 main()
同目錄的配置檔案如下:
# paramiko連線使用的配置檔案,不是必須的,只是這樣可以避免過多的硬編碼 [ssh] hostname= port= username= password= private_key_path=