Как продолжить задачу, когда Fabric получает ошибку

94

Когда я определяю задачу для запуска на нескольких удаленных серверах, если задача выполняется на сервере 1 и завершается с ошибкой, 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.
Минго
источник

Ответы:

146

Из документов :

... По умолчанию Fabric использует шаблон поведения «безотказно»: если что-то пойдет не так, например, удаленная программа, возвращающая ненулевое возвращаемое значение, или код Python вашего fabfile обнаружит исключение, выполнение будет немедленно остановлено.

Обычно это желаемое поведение, но есть много исключений из правила, поэтому 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
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 09
Только что тестировал и на 1.9.0. Что вы получите, когда попробуете пример кода из моего обновленного комментария?
Крис Маринос
Если вы не хотите печатать предупреждения / ошибки, вы также можете использовать менеджер контекста скрытия :with hide('everything'):
np8
13

Вы также можете установить значение true для параметра warn_only всего скрипта с помощью

def local():
    env.warn_only = True
Rawkcy
источник
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. См. Документацию здесь .

Искусство Войны
источник
Спасибо за это, но один вопрос - возможен ли доступ / проход в текущей структуре env dict, как определено, когда произошло исключение? (Так что я могу распечатать некоторые конкретные настройки за исключением.)
Брайан
@ Брайан: Не могли бы вы просто проверить fabric.api.envвнутри своего exceptблока?
ArtOfWarfare
@ArtOfWarefare Ах, глупый я, я пытался избежать обертывания всех моих задач в try / except и вместо этого просто настроил, env.abort_exception=MyExceptionчтобы я мог запустить свой собственный сбой. Это вроде как «работает», если я использую функцию вместо класса (удовлетворяет вызываемому запросу abort_exception), но я все еще работаю над некоторыми другими проблемами с этим подходом.
Брайан,
@Brian: Итак, внутри этой функции проверьте, что fabric.api.envесть.
ArtOfWarfare
7

По крайней мере, в Fabric 1.3.2 вы можете восстановить исключение, перехватив SystemExitисключение. Это полезно, если у вас есть более одной команды для запуска в пакете (например, развертывание) и вы хотите очистить, если одна из них не работает.

Зимбатм
источник
+1: Протестировано - это также работает в Fabric 1.9.0. Уловив это, вы можете проверить SystemExitсообщение или код пользователя для получения более подробной информации.
ArtOfWarfare
Даже лучше, чем перехват SystemExit, установить abort_exceptionдругое исключение, чтобы случайно не перехватить исключения, не имеющие ничего общего с Fabric. См. Мой ответ для примера: stackoverflow.com/a/27990242/901641
ArtOfWarfare
7

В ткани 2.x вы можете просто использовать Invoke «s бежать с предупредит = True аргумент. В любом случае, invoke - это зависимость Fabric 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)
Qlimax
источник
-5

В моем случае на Fabric> = 1.4 этот ответ был правильным.

Вы можете пропустить плохие хосты, добавив это:

env.skip_bad_hosts = True

Или передать --skip-bad-hostsфлаг /

Кристиан Вьельма
источник