暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

​Python使用Paramiko 实现ssh远程操作

IT那活儿 2023-08-09
2500
点击上方“IT那活儿”公众号,关注后了解更多内容,不管IT什么活儿,干就完了!!!



前 言



在日常运维过程中,经常会遇见需要在不同的主机上批量执行某些复杂操作的需求,如果单纯的使用bash、expect等工具来写脚本,不仅脚本编写的难度大,功能实现复杂或者是无法实现,同时脚本的可阅读性和可维护性也很难维持在一个合理的水平,这时就需要采用一些更加现代和高效的方式。

Paramiko 是一个用于实现 SSH 协议的 Python 库,它提供了一种简单而强大的方式来执行远程命令、传输文件和管理 SSH 会话。本文将介绍如何使用 Paramiko 在 Python 中进行 SSH 操作。




安装 Paramiko



在开始之前,我们需要先安装 Paramiko。在确保Python 环境可以访问互联网的条件下,可以使用 pip 包管理器在命令行中运行以下命令来安装 Paramiko:

pip install paramiko
如果当前Python 环境无法访问互联网,那就只能采取下载whl包或者自行编译安装的方式实现了,可以参考paramiko的官方页面(https://www.paramiko.org/)

2.1 连接到远程主机

首先,我们需要建立与远程主机的 SSH 连接。以下是一个完整连接远程主机的示例代码:
1. #导入paramiko包
 2. import paramiko
 3.
 4. # SSH 连接参数
 5. host = "your_host"
 6. username = "your_username"
 7. password = "your_password"
 8.
 9. # 创建 SSH 客户端
10. ssh = paramiko.SSHClient()
11.
12. # 自动添加远程主机的 SSH 密钥
13. ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
14.
15. # 连接到远程主机
16. ssh.connect(host, username=username, password=password)
17.
18. # 关闭 SSH 连接
19. ssh.close()

在上述代码中,我们使用 paramiko.SSHClient 创建了一个 SSH 客户端对象,并设置了自动添加远程主机的 SSH 密钥。然后使用 connect 方法传入主机地址、用户名和密码进行身份验证并连接到远程主机。连接成功后,你可以继续执行其他操作。别忘了在执行完其他操作之后关闭ssh连接,请保持良好的编程习惯

2.2 执行远程命令

连接到远程主机后就可以执行远程命令并获取其输出。以下是执行远程命令的示例代码:
1. # 创建 SSH 客户端
 2. ssh = paramiko.SSHClient()
 3.  
 4. # 连接到远程主机
 5. ssh.connect(host, username=username, password=password)
 6.  
 7. # 执行远程命令
 8. command = "ls -lrt"
 9. stdin, stdout, stderr = ssh.exec_command(command)
10.  
11. # 获取命令输出
12. output = stdout.read().decode("utf-8")
13.  
14. # 关闭 SSH 连接
15. ssh.close()

在上述代码中,我们使用 exec_command 方法执行了一个远程命令,这里示例命令是 ls -lrt,可以根据需要更改为其他命令。然后再通过 stdout 变量获取命令的标准输出,并使用 read 方法读取输出内容。

2.3 传输文件

除了执行远程命令以外,Paramiko 还提供了远程文件传输的功能,通过sftp协议进行文件传输。以下是传输文件的示例代码:
1. # 创建 SSH 客户端
2. ssh = paramiko.SSHClient()
3.
4. # 连接到远程主机
5. ssh.connect(host, username=username, password=password)
6.
7. # 本地文件路径和远程文件路径
8. local_path = "/path/to/local/file.txt"
9. remote_path = "/path/to/remote/file.txt"
10.
11. # 上传文件
12. sftp = ssh.open_sftp()
13. sftp.put(local_path, remote_path)
14. sftp.close()
15.
16. # 下载文件
17. sftp = ssh.open_sftp()
18. sftp.get(remote_path, local_path)
19. sftp.close()
20.
21. #关闭 SSH 连接
22. ssh.close()

在上述代码中,我们使用 `open_sftp` 方法打开一个 SFTP 会话,通过 `put` 方法将本地文件上传到远程主机,或者通过 `get` 方法从远程主机下载文件到本地。




进阶内容



通过上述简单的示例,我想大家一定都学会了如何在python中使用paramiko模块实现远程主机上的各种操作,但是大家在实践过程中一定会发现一个重要的问题,那就是在真实的运维任务中,很多操作实际上并不都是在单用户的条件下实现的,通常需要频繁切换用户同时执行多条命令,但是上述paramiko的各种操作并不能够实现用户的切换和运行环境、命令的连续交互。

为了实现这个目标,我们需要利用paramiko提供的交互式 shell 会话能力

3.1 交互式 shell

要创建交互式 shell 会话,我们可以使用 Paramiko 的 invoke_shell() 方法。下面是一个示例代码:
1. channel = ssh.invoke_shell()
在这个例子中,我们使用 invoke_shell() 方法创建了一个交互式 shell 会话,并将其赋值给 channel 变量。

3.2 执行多条命令并获取完整结果

在交互式 shell 会话中,我们可以发送多条命令(包括切换用户,以及输入密码等各种交互操作),并获取每条命令的完整执行结果。下面是一个示例代码:
1. channel.send('command1\n')
2. output = channel.recv(65535).decode()
3. print(output)
4.
5. channel.send('command2\n')
6. output = channel.recv(65535).decode()
7. print(output)
8.
9. channel.send('command3\n')
10. output = channel.recv(65535).decode()
11. print(output)

在这个例子中,我们使用 send() 方法发送每条命令,并使用 recv() 方法接收每条命令的执行结果。recv() 方法的参数指定了要接收的最大字节数,通常可以设置一个足够大的值来确保完整接收输出。
每次发送命令后,我们使用 recv() 方法获取输出,并将其解码为字符串。然后,我们可以根据需要对输出进行处理,比如打印、保存到文件等操作。
但是这里又存在一个显而易见的问题,由于网络延迟的存在,我们在获取命令结果时,如果采用上述示例代码中的逻辑,往往只能获取部分执行结果,为了结果这个问题,我们需要适当设置延迟时间来确保完整获取每条命令的输出。下面是一个示例代码:
1. def send_command(channel, command):
2. channel.send(command + '\n')
3. time.sleep(1) # 设置适当的延迟时间以确保数据完整性
4.
5. def receive_output(channel):
6. output = ''
7. while not channel.recv_ready():
8. time.sleep(0.1)
9. while channel.recv_ready():
10. output += channel.recv(1024).decode()
11. time.sleep(0.1)
12. return output
13.
14. commands = ['command1', 'command2', 'command3']
15. for command in commands:
16. send_command(channel, command)
17. output = receive_output(channel)
18. print(output)

在这个例子中,我们定义了两个辅助函数:send_command() 和 receive_output()。send_command() 函数用于发送命令,并在每次发送后等待适当的延迟时间,以确保命令发送完整。receive_output() 函数用于接收命令的执行结果,并将接收到的数据拼接起来,直到数据接收完整。
但是上述代码中依旧存在隐患,那就是当网络延迟过大时,设置的延迟时间过小,仍然会导致命令结果不完整。
实际上,paramiko有提供类似channel.recv_ready和channel.recv_exit_status的方法来实时判断结果是否输出完成,前者channel.recv_ready检测channel的发送状态,如果可发送新的命令则返回true,后者channel.recv_exit_status用来判断命令是否执行完毕,执行完毕返回true。使用这两个函数可以修改上述示例,代码如下:
1. def send_command(channel, command):
2.     channel.send(command + '\n')
3.  
4. def receive_output(channel):
5.     output = ''
6.     while channel.recv_ready():
7.         output += channel.recv(1024).decode()
8.     return output
9.  
10. commands = ['command1', 'command2', 'command3']
11. for command in commands:
12.     send_command(channel, command)
13.  
14.     # 等待命令执行完毕
15.     while not channel.exit_status_ready():
16.         time.sleep(0.1)
17.  
18.     output = receive_output(channel)
19.     print(output)

但是在实际的生产过程中,我不止一次的发现这两个方法并不能准确的判断命令执行是否完成,因此,在实际生产过程中,我是通过不停的区获取命令执行结果,拼接后判断例如当前用户的用户名,当前主机的主机名,以及类似”~ >”、”#”这样的关键字来进行判断命令执行是否完成。

总 结:

本文介绍了如何使用 Python 的 Paramiko 库实现 SSH 操作。Paramiko 提供了一个简单而强大的方式来管理 SSH 会话,使得远程操作变得更加方便和灵活。通过 Paramiko,我们可以执行远程命令、传输文件,甚至实现交互式终端功能。这对于管理远程服务器、自动化任务和部署应用程序等场景都非常有用。
但需要注意的是,在实际使用中,要确保远程主机的 SSH 服务已启用,并且具备正确的连接参数,如主机地址、用户名和密码。此外,由于近些年来对于安全性的考虑,我们在使用ssh连接的时候可以尽量的去使用公钥身份验证来代替密码验证以提高安全性。
通过本文的介绍和示例代码,希望读者能够快速上手并开始在Python中进行SSH操作。同时,希望本文对您理解和应用Paramiko有所帮助,让您能够更好地管理远程主机并执行远程操作。

END


本文作者:李双修(上海新炬中北团队)

本文来源:“IT那活儿”公众号

文章转载自IT那活儿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论