介绍
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=