-1

昨天我对 Paramiko 进行了两行代码更改,这不应该(据我所知)产生任何不利影响,然后我们的电话立即开始响起,并且收到了有关用户无法访问的报告的电子邮件他们 NFS 挂载的主目录是 zfs 文件系统。我询问了我的同事们的意见,每个人都很难理解为什么这个改变不仅没有完成我的计划,而且为什么它会为每个人都带来一切。

在使用 Paramiko 执行“zfs create”命令创建新用户的 NFS 主目录后,我使用 Python 的“time.sleep(5)”行给远程系统执行和处理命令的机会(有时特别是如果 NFS 服务器处于压力之下,命令可能需要一两秒才能真正生效)。

事实证明,我们似乎遇到了一个罕见的情况,即 5 秒的延迟是不够的。所以我决定把它从 Python 的“time.sleep(5)”函数改成使用 Paramiko 的“channel.recv_exit_status()”函数来等待退出状态码,这样花费的时间是多少是必需的(不是任意的秒数)。

以下是代码的区别(删除以减少确定 zfs 路径和用户名等平凡的东西):

原始版本

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("homedirserver.example.com")

# Create zfs share
command = "zfs create {0}/{1}".format(zfs_parent, username)
ssh.exec_command(command)

time.sleep(5)

# Confirm creation (this can fail without a time.sleep delay above)
command = "zfs list -H {0}/{1}".format(zfs_parent, username)
stdin, stdout, stderr = ssh.exec_command(command)
error = stderr.read().strip()
if error != "":
    # log error, raise RuntimeError

新版本

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("homedirserver.example.com")

# Create zfs share
command = "zfs create {0}/{1}".format(zfs_parent, username)
stdin, stdout, stderr = ssh.exec_command(command)  # <--- CHANGED
status = stdout.channel.recv_exit_status()          # <--- NEW

# Confirm creation
command = "zfs list -H {0}/{1}".format(zfs_parent, username)
stdin, stdout, stderr = ssh.exec_command(command)
error = stderr.read().strip()
if error != "":
    # log error, raise RuntimeError

似乎这样做时,“zfs create”命令作为文件服务器上的一个进程存在并且永远不会完成。 ps -eaf | grep "zfs create"将命令显示为文件服务器上的一个进程,该进程在我 Ctrl+C 退出原始调用程序之前一直存在。最令人惊讶的是,这似乎足以彻底破坏从该服务器导出的所有 zfs 文件系统。在 Ctrl+C 并等待几分钟后,一切恢复正常,人们停止报告 NFS 中断。

这是Paramiko 1.14.1Python 2.7.8。执行/调用机器是Solaris 11.2,远程文件服务器(进程挂起的地方)是Solaris 11.1

4

1 回答 1

1

让我们尝试以更安全的方式执行此操作:

import pipes
command = "zfs create {0}/{1}".format(pipes.quote(zfs_parent), pipes.quote(username))
ssh.set_combine_stderr(True)
stdin, stdout, _ = ssh.exec_command(command)
stdin.close()
stdout_text = stdout.read()
status = stdout.channel.recv_exit_status()

区别:

  • 我们明确地关闭了标准输入,所以进程不可能阻塞它。
  • 我们告诉 paramiko 我们不希望单独维护 stderr;标准错误内容将被重定向到标准输出。(这减少了一些关于读写顺序的极端情况;如果您需要跟踪此内容并且没有顺序/长度保证,事情需要更有趣)。
  • 我们还试图$(rm -rf /)通过使用pipes.quote().

当然,要完全确定发生了什么,您需要使用 sysdig 之类的工具来监控 ZFS 服务器在运行期间发生的情况(如果我上面的假设是正确的,您会看到该工具在写入时被阻止stdout 或 stderr 与内核交互足以中断文件系统的可用性),但如果您没有用于测试的非生产环境,这可能会有点不舒服。

于 2015-01-28T16:52:22.220 回答