Як продовжити завдання, коли Fabric отримує помилку


94

Коли я визначаю завдання для запуску на декількох віддалених серверах, якщо завдання виконується на одному сервері і виходить із помилкою, 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 вашого файлового файлу, що стикається з винятком, виконання негайно зупиниться.

Зазвичай це бажана поведінка, але є багато винятків із правила, тому 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, і він працює нормально.
Chris Marinos

Можливо, я використовую Fabric == 1.9.0, і це не працює для мене
cevaris

Щойно протестували також на 1.9.0. Який результат ви отримуєте при спробі прикладу коду з мого оновленого коментаря?
Chris Marinos

Якщо ви не хочете друкувати попередження / помилки, ви також можете скористатися диспетчером контексту hide :with hide('everything'):
np8

13

Ви також можете встановити значення true для всього сценарію warn_only

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.

Ви також можете встановити його в за допомогою блоку. Дивіться документацію тут .


Дякую за це, але одне питання - чи можливий доступ / передача в поточному текстовому env dict, як визначено, коли сталося виняток? (Тож я можу надрукувати деякі конкретні налаштування за винятком.)
Брайан,

@Brian: Чи не могли б ви просто перевірити fabric.api.envвсередині вашого exceptблоку?
ArtOfWarfare

@ArtOfWarefare Ах, безглуздо, я намагався уникнути обгортання всіх своїх завдань у спробу / крім, а замість цього просто налаштував, 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)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.