Fabric收到错误时如何继续执行任务


94

当我定义一个任务在多个远程服务器上运行时,如果该任务在一个服务器上运行并退出并出现错误,Fabric将停止并中止该任务。但我想让Fabric忽略该错误并在下一台服务器上运行该任务。我怎样才能做到这一点?

例如:

$ fab site1_service_gw
[site1rpt1] Executing task 'site1_service_gw'

[site1fep1] run: echo 'Nm123!@#' | sudo -S route
[site1fep1] err:
[site1fep1] err: We trust you have received the usual lecture from the local System
[site1fep1] err: Administrator. It usually boils down to these three things:
[site1fep1] err:
[site1fep1] err:     #1) Respect the privacy of others.
[site1fep1] err:     #2) Think before you type.
[site1fep1] err:     #3) With great power comes great responsibility.
[site1fep1] err: root's password:
[site1fep1] err: sudo: route: command not found

Fatal error: run() encountered an error (return code 1) while executing 'echo 'Nm123!@#' | sudo -S route '

Aborting.

Answers:


146

文档

... Fabric默认为“快速失败”行为模式:如果发生任何错误,例如远程程序返回非零返回值或fabfile的Python代码遇到异常,则执行将立即停止。

这通常是所需的行为,但是该规则有很多例外,因此Fabric提供了布尔值env.warn_only。默认为False,这意味着错误情况将导致程序立即中止。但是,如果在发生故障时将env.warn_only设置为True(例如使用设置上下文管理器),则Fabric将发出警告消息,但会继续执行。

看起来您可以使用settings上下文管理器对忽略错误的位置进行细粒度控制,如下所示:

from fabric.api import settings

sudo('mkdir tmp') # can't fail
with settings(warn_only=True):
    sudo('touch tmp/test') # can fail
sudo('rm tmp') # can't fail

13
别忘了导入from fabric.api settings
cevaris 2014年

31

从Fabric 1.5开始,有一个ContextManager使此操作更容易:

from fabric.api import sudo, warn_only

with warn_only():
    sudo('mkdir foo')

更新:我再次确认使用下面的代码可以在ipython中工作。

from fabric.api import local, warn_only

#aborted with SystemExit after 'bad command'
local('bad command'); local('bad command 2')

#executes both commands, printing errors for each
with warn_only():
    local('bad command'); local('bad command 2')

您正在使用哪个版本的面料?我刚刚用Fabric == 1.6.2重新测试了,它工作正常。
克里斯·马里诺斯

可能是我正在使用Fabric == 1.9.0,它对我不起作用
cevaris

刚刚在1.9.0上进行了测试。当您尝试从我的更新注释中获取示例代码时,输​​出是什么?
克里斯·马里诺斯

如果您不想打印警告/错误,也可以使用hide上下文管理器:with hide('everything'):
np8

13

您还可以将整个脚本的warn_only设置设置为true

def local():
    env.warn_only = True

10

您应该设置abort_exception环境变量并捕获异常。

例如:

from fabric.api        import env
from fabric.operations import sudo

class FabricException(Exception):
    pass

env.abort_exception = FabricException
# ... set up the rest of the environment...

try:
    sudo('reboot')
except FabricException:
    pass  # This is expected, we can continue.

您也可以在with块中进行设置。请参阅此处的文档。


谢谢您,但是有一个问题-发生异常时,是否可以按定义访问/传递当前的结构环境?(因此,除了例外情况,我可以打印一些特定设置。)
Brian

@布赖恩:你不能只是检查fabric.api.env你的内except块?
ArtOfWarfare 2015年

@ArtOfWarefare啊,愚蠢的我,我试图避免将我的所有任务包装在try / except中,而是仅仅进行了设置,env.abort_exception=MyException这样我就可以自行执行失败。如果我使用函数而不是类(满足的可调用req abort_exception),那是“有效的”,但是我仍在使用该方法解决其他一些问题。
布赖恩

@Brian:所以在该函数的主体内部检查一下fabric.api.env
ArtOfWarfare 2015年

7

至少在Fabric 1.3.2中,您可以通过捕获SystemExit异常来恢复异常。如果您有多个命令要批量运行(例如部署),并且如果其中一个失败则希望清除,这将很有帮助。


+1:已测试-在Fabric 1.9.0中也可以使用。捕捉到此信息后,您可以查看SystemExit的消息或代码以获取更多详细信息。
ArtOfWarfare 2014年

最好SystemExitabort_exceptioncatch 设置为其他异常,而不是catch,这样您就不会意外捕获与Fabric无关的异常。参见我的示例示例:stackoverflow.com/a/27990242/901641
ArtOfWarfare 2015年

7

Fabric 2.x中,您可以仅使用带有warn = True参数的invokerun。无论如何,invokeFabric 2.x的依赖项:

from invoke import run
run('bad command', warn=True)

从任务中:

from invoke import task

@task
def my_task(c):
    c.run('bad command', warn=True)

-5

就我而言,在Fabric> = 1.4上,此答案是正确的。

您可以通过添加以下内容来跳过不良主机:

env.skip_bad_hosts = True

或通过--skip-bad-hosts标志/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.