我正在为一个现有的系统添加一些新功能。有一个控制机器(一台本地 Linux 电脑)运行一些测试脚本,这些脚本会通过 SSH 将大量的命令远程发送给几台不同的机器。测试框架是用 Python 编写的,并使用 Fabric 访问不同的机器。
所有命令都通过一个通用调用函数来处理,简化如下:
def cmd(host, cmd, args):
...
with fabric.api.settings(host_string=..., user='root', use_ssh_config=True, disable_known_hosts=True):
return fabric.api.run('%s %s' % (cmd, args))
发送到每个机器的实际命令通常涉及在远程端运行一个现有的 Python 脚本。这些 Python 脚本会执行一些任务,其中包括调用外部命令(使用 system 和 subprocess)。在测试 PC 上调用的 run() 命令将在远程 Python 脚本完成后返回。
在某个时刻,我需要其中一个远程 Python 脚本来启动一个后台任务:使用 openvpn --config /path/to/config.openvpn 启动一个 openvon 服务器和客户端。在一个普通的 Python 脚本中,我只需使用 &:
system('openvpn --config /path/to/config.openvpn > /var/log/openvpn.log 2>&1 &')
当这个脚本通过 Fabric 远程调用时,必须显式使用 nohup、dtach、screen 等来在后台运行作业。我用以下方法使其工作:
system("nohup openvpn --config /path/to/config.openvpn > /var/log/openvpn.log 2>&1 < /dev/null &"
Fabric 常见问题解答对此进行了详细说明。
对于某些后台命令,它运行良好。
问题:不适用于所有类型的后台命令
这种技术不能用于我需要的所有命令。在某些脚本中,我需要启动一个后台 atop 命令(它是 top 的增强版),并将它的 stdout 重定向到一个文件中。
我的代码(注意:使用 atop -P 用于可解析的输出):
system('nohup atop -P%s 1 < /dev/null | grep %s > %s 2>&1 &' % (dataset, grep_options, filename))
当包含该命令的脚本通过 Fabric 远程调用时,atop 进程会被立即杀死。输出文件会生成,但它是空的。在通过 SSH 登录远程机器时调用相同的脚本可以正常工作,atop 命令会定期将数据转储到我的输出文件中。
一些谷歌搜索和挖掘让我了解到了一些关于使用 Fabric 的后台作业的有趣信息,但我的问题似乎只适用于某些类型的后台作业。我尝试过:
- 添加 sleep
- 使用 pty=False 运行
- 将 nohup 替换为 dtach -n:症状相同
- 我读过一些命令,比如 top 在用标准输入重定向到 /dev/null 的 Fabric 中失败,不太确定该怎么处理。我尝试了不同的组合或(非)重定向标准输入、标准输出和标准错误
看起来我已经黔驴技穷了。
Fabric 对于我们正在做的事情来说似乎有些过于复杂。我们甚至没有使用“fabfile”方法,因为它集成在一个 nose 框架中,我通过调用 nosetests 来运行它们。也许我应该放弃 Fabric,转而使用手动 SSH 命令,尽管我不喜欢因为不支持我较新模块之一而改变一个正常运行的系统这一想法。
2、解决方案 为了解决此问题,您可以使用以下解决方案:
- 编辑您的 Python 脚本并添加以下代码:import fcntl。
- 在您的 Python 脚本中,在循环中使用 fcntl 模块设置非阻塞文件对象。
- 在您的 Python 脚本中,使用 Fabric 库在远程主机上运行您的脚本。
以下是使用 Fabric 在远程主机上运行 Python 脚本的示例:
import fabric
def main():
# Set up the connection information for the remote host
host = 'remote_host'
user = 'username'
password = 'password'
port = 22
# Connect to the remote host
with fabric.api.settings(host_string=f'{user}@{host}', password=password, port=port):
# Run the Python script on the remote host
result = fabric.api.run('python script.py')
# Print the output of the script
print(result)
if __name__ == '__main__':
main()
请注意,您需要将 remote_host、user、password 和 port 替换为您自己的连接信息。
通过按照这些步骤操作,您应该能够在远程主机上使用 Fabric 运行 Python 脚本,而不会遇到任何问题。