前言
作为Devops的开发人员,在开发系统的时候不可必要的需要ssh远程访问服务器。那么在python库中可以采用两个常用的库:paramiko
、ansible
。
在win10的系统下,本来想要python3直接调用ansible库进行远程执行的,但是很可惜,ansible是基于linux系统的ssh服务进行远程调用,不太兼容windows。那么下面来使用paramiko
库,直接手写一个ssh远程调用。
介绍
paramiko 遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接,可以实现远程文件的上传,下载或通过ssh远程执行命令。
项目地址:https://github.com/paramiko/paramiko
官方文档:http://docs.paramiko.org/
使用pip3安装
pip3 install paramiko
安装过程如下:
代码语言:javascript复制D:pythonProjectlocust_auto_test>pip3 install paramiko
Collecting paramiko
Using cached https://files.pythonhosted.org/packages/17/9f/7430d1ed509e195d5a5bb1a2bda6353a4aa64eb95491f198a17c44e2075c/paramiko-2.5.0-py2.py3-none-any.whl
Collecting bcrypt>=3.1.3 (from paramiko)
Downloading https://files.pythonhosted.org/packages/09/91/1b9e566c9aafe40eb89b31a7d322c7c070a3249bd2f3e50c8828fe4418d7/bcrypt-3.1.6-cp37-cp37m-win_amd64.whl
Requirement already satisfied: cryptography>=2.5 in d:python37libsite-packages (from paramiko) (2.7)
Collecting pynacl>=1.0.1 (from paramiko)
Using cached https://files.pythonhosted.org/packages/fc/e7/179847c0dce637c59cea416c75b8de1ec1e862358c7369ad99c1fad00158/PyNaCl-1.3.0-cp37-cp37m-win_amd64.whl
Requirement already satisfied: cffi>=1.1 in d:python37libsite-packages (from bcrypt>=3.1.3->paramiko) (1.12.1)
Requirement already satisfied: six>=1.4.1 in d:python37libsite-packages (from bcrypt>=3.1.3->paramiko) (1.12.0)
Requirement already satisfied: asn1crypto>=0.21.0 in d:python37libsite-packages (from cryptography>=2.5->paramiko) (0.24.0)
Requirement already satisfied: pycparser in d:python37libsite-packages (from cffi>=1.1->bcrypt>=3.1.3->paramiko) (2.19)
Installing collected packages: bcrypt, pynacl, paramiko
Successfully installed bcrypt-3.1.6 paramiko-2.5.0 pynacl-1.3.0
D:pythonProjectlocust_auto_test>
测试是否安装成功,如下:
代码语言:javascript复制D:pythonProjectlocust_auto_test>ipython3
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import paramiko
In [2]:
可以看到导入并没有出错,所以下面可以正常使用这个库了。
在本次实验中,最核心的功能就是远程执行ssh命令,所以首先来实验一下这个功能。
使用ipython3远程执行ssh命令
代码语言:javascript复制D:pythonProjectlocust_auto_test>ipython3
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import paramiko
# 设置ssh访问信息
In [2]: remote_ip = '192.168.196.129'
In [3]: remote_ssh_port = 22
In [5]: ssh_password = '********'
In [6]: ssh_username = 'root'
In [7]: ssh = paramiko.SSHClient()
# 设置连接策略
In [8]: ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# ssh连接服务器
In [9]: ssh.connect( hostname = remote_ip, port = remote_ssh_port, username = ssh_username, password = ssh_password )
# 远程ssh执行shell命令
In [10]: stdin, stdout, stderr = ssh.exec_command("df -h | grep dev")
# 打印查看磁盘信息的结果
In [11]: print(stdout.readlines())
['/dev/mapper/centos-root 17G 9.7G 7.3G 58% /n', 'devtmpfs 899M 0 899M 0% /devn', 'tmpfs 911M 0 911M 0% /dev/shmn',
'/dev/sda1 1014M 142M 873M 14% /bootn']
In [12]:
# 执行完毕之后,结果只会打印一次
In [14]: stdin, stdout, stderr = ssh.exec_command("df -h | grep dev")
In [15]: for line in stdout.readlines():
...: print(line)
...:
/dev/mapper/centos-root 17G 9.7G 7.3G 58% /
devtmpfs 899M 0 899M 0% /dev
tmpfs 911M 0 911M 0% /dev/shm
/dev/sda1 1014M 142M 873M 14% /boot
## 执行一个存在等待时长的shell命令
In [24]: stdin, stdout, stderr = ssh.exec_command("df -h | grep dev && echo '123' && sleep 10 && echo 'sleep complete'")
## 发现应该是在执行打印的时候,才是真正执行shell命令。
In [25]: for line in stdout.readlines():
...: print(line)
...:
/dev/mapper/centos-root 17G 9.7G 7.3G 58% /
devtmpfs 899M 0 899M 0% /dev
tmpfs 911M 0 911M 0% /dev/shm
/dev/sda1 1014M 142M 873M 14% /boot
123
sleep complete
In [26]:
# 执行一个查看日志的远程ssh命令
In [11]: stdin, stdout, stderr = ssh.exec_command("tail -f /root/test_log/test.log")
In [12]: for line in stdout.readlines():
...: print(line)
...:
# 发现就算写入新的信息进去,是不会持续打印出新的信息的。
# 也就是验证了这个ssh执行时一次性的执行结果。
# 关闭ssh连接
In [16]: ssh.close()
上传文件功能
代码语言:javascript复制In [2]: import os
In [10]: import paramiko
## 设置sftp连接信息
In [11]: remote_ip = '192.168.196.129'
In [12]: remote_ssh_port = 22
In [13]: ssh_password = '***********'
In [14]: ssh_username = 'root'
## 创建sftp连接
In [16]: t = paramiko.Transport((remote_ip, remote_ssh_port))
In [17]: t.connect(username=ssh_username, password=ssh_password)
In [18]: sftp = paramiko.SFTPClient.from_transport(t)
## 执行上传sftp
In [26]: sftp.put('D:\pythonProject\locust_auto_test\paramiko_test\file1.txt', '/root/test_log/file1.txt')
Out[26]: <SFTPAttributes: [ size=18 uid=0 gid=0 mode=0o100644 atime=1560329096 mtime=1560329096 ]>
In [27]:
## 关闭sftp连接
In [28]: t.close()
到远程服务器查看上传好的文件,如下:
代码语言:javascript复制[root@centos7 test_log]# ls
file1.txt
[root@centos7 test_log]# cat file1.txt
测试上传文件[root@centos7 test_log]#
[root@centos7 test_log]#
执行下载文件
首先在远程Centos7将file1.txt文件拷贝一份为file2.txt,用于下载该文件。
代码语言:javascript复制[root@centos7 test_log]# cp file1.txt file2.txt
[root@centos7 test_log]#
[root@centos7 test_log]# ls
file1.txt file2.txt
[root@centos7 test_log]#
执行下载文件功能如下:
代码语言:javascript复制## 创建sftp连接
In [29]: t = paramiko.Transport((remote_ip, remote_ssh_port))
In [30]: t.connect(username=ssh_username, password=ssh_password)
In [31]: sftp = paramiko.SFTPClient.from_transport(t)
## 通过sftp查看远程服务器该路径有什么文件
In [32]: sftp.listdir('/root/test_log')
Out[32]: ['file1.txt', 'file2.txt']
## 设置本地路径
In [35]: local_dir = 'D:\pythonProject\locust_auto_test\paramiko_test\file2.txt'
## 设置远程路径
In [36]: remote_dir = '/root/test_log/file2.txt'
## 下载远程路径的文件到本地路径
In [37]: sftp.get(remote_dir,local_dir)
## 查看本地路径是否已有file2.txt,可以看到已经成功下载下来了。
In [38]: os.listdir(os.getcwd())
Out[38]: ['file1.txt', 'file2.txt', 'test1.py']
上面我写windows下的路径都是直接写了个全路径,是为了方便理解,下面可以使用命令来设置这些路径。
代码语言:javascript复制In [41]: local_dir = os.path.join(os.getcwd(),'file2.txt')
In [42]: sftp.get(remote_dir,local_dir)
In [43]: os.listdir(os.getcwd())
Out[43]: ['file1.txt', 'file2.txt', 'test1.py']
In [44]:
当时由于windows与linux获取当前路径的拼接方式不同,所以linux路径我还是直接使用字符串写远程路径的方式。
上面基本上已经将功能都完成了,下一步就可以将这些方法都封装到一个工具类中。
封装工具类方法
代码语言:javascript复制import paramiko
import os
class ParamikoHelper():
def __init__(self,remote_ip, remote_ssh_port, ssh_password, ssh_username ):
self.remote_ip = remote_ip
self.remote_ssh_port = remote_ssh_port
self.ssh_password = ssh_password
self.ssh_username = ssh_username
def connect_ssh(self):
try:
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(hostname=self.remote_ip, port=self.remote_ssh_port, username=self.ssh_username,
password=self.ssh_password)
except Exception as e:
print(e)
return self.ssh
def close_ssh(self):
try:
self.ssh.close()
except Exception as e:
print(e)
def exec_shell(self, shell):
ssh = self.connect_ssh()
try:
stdin, stdout, stderr = ssh.exec_command(shell)
return stdin, stdout, stderr
except Exception as e:
print(e)
def sftp_put_file(self, file, local_dir, remote_dir):
try:
t = paramiko.Transport((self.remote_ip, self.remote_ssh_port))
t.connect(username=self.ssh_username, password=self.ssh_password)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.put(os.path.join(local_dir, file), remote_dir)
t.close()
except Exception:
print("connect error!")
def sftp_get_file(self, file, local_dir, remote_dir):
try:
t = paramiko.Transport((self.remote_ip, self.remote_ssh_port))
t.connect(username=self.ssh_username, password=self.ssh_password)
sftp = paramiko.SFTPClient.from_transport(t)
sftp.get(remote_dir, os.path.join(local_dir, file))
t.close()
except Exception:
print("connect error!")
def main():
remote_ip = '192.168.196.129'
remote_ssh_port = 22
ssh_password = '**************'
ssh_username = 'root'
ph = ParamikoHelper(remote_ip=remote_ip,remote_ssh_port=remote_ssh_port,ssh_password=ssh_password,ssh_username=ssh_username)
# 远程执行ssh命令
shell = "df -h | grep dev"
stdin, stdout, stderr = ph.exec_shell(shell)
for line in stdout.readlines():
print(line)
ph.close_ssh()
# 上传文件file2.txt到远程服务器上
file = 'file2.txt'
remote_dir = '/root/test_log/' file
local_dir = os.getcwd()
ph.sftp_put_file(file=file, local_dir=local_dir, remote_dir=remote_dir)
# 下载文件file3.txt
file = 'file3.txt'
remote_dir = '/root/test_log/' file
local_dir = os.getcwd()
ph.sftp_get_file(file=file, local_dir=local_dir, remote_dir=remote_dir)
if __name__ == '__main__':
main()