Jak kontynuować zadanie, gdy Fabric otrzyma błąd

94

Kiedy zdefiniuję zadanie do uruchomienia na kilku zdalnych serwerach, jeśli zadanie zostanie uruchomione na serwerze pierwszym i zakończy się z błędem, Fabric zatrzyma i przerwie zadanie. Ale chcę, aby tkanina ignorowała błąd i uruchamiała zadanie na następnym serwerze. Jak mogę to zrobić?

Na przykład:

$ 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.
Mingo
źródło

Odpowiedzi:

146

Z dokumentów :

... Fabric domyślnie stosuje wzorzec zachowania „bezawaryjnego”: jeśli coś pójdzie nie tak, na przykład zdalny program zwraca niezerową wartość zwracaną lub kod Pythona twojego pliku fabfile napotka wyjątek, wykonanie zostanie natychmiast zatrzymane.

Zwykle jest to pożądane zachowanie, ale istnieje wiele wyjątków od reguły, dlatego Fabric zapewnia ustawienie env.warn_only, wartość logiczną. Domyślnie ma wartość False, co oznacza, że ​​wystąpienie błędu spowoduje natychmiastowe przerwanie programu. Jeśli jednak env.warn_only ma wartość True w momencie awarii - na przykład za pomocą menedżera kontekstu ustawień - Fabric wyemituje komunikat ostrzegawczy, ale będzie kontynuował wykonywanie.

Wygląda na to, że możesz sprawować precyzyjną kontrolę nad tym, gdzie błędy są ignorowane, używając settingsmenedżera kontekstu , na przykład:

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
Will McCutchen
źródło
13
Nie zapomnij zaimportowaćfrom fabric.api settings
cevaris
31

Począwszy od Fabric 1.5, istnieje ContextManager, który to ułatwia:

from fabric.api import sudo, warn_only

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

Aktualizacja: Ponownie potwierdziłem, że to działa w ipythonie, używając następującego kodu.

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')
Chris Marinos
źródło
Której wersji tkaniny używasz? Właśnie przetestowałem ponownie Fabric == 1.6.2 i działa dobrze.
Chris Marinos,
Prawdopodobnie używam Fabric == 1.9.0 i to nie działa dla mnie
cevaris
Właśnie przetestowano również w wersji 1.9.0. Jakie są twoje wyniki, gdy wypróbujesz przykładowy kod z mojego zaktualizowanego komentarza?
Chris Marinos,
Jeśli nie chcesz drukować ostrzeżeń / błędów, możesz również użyć menedżera kontekstu ukrywania :with hide('everything'):
np8
13

Możesz również ustawić warn_only całego skryptu na wartość true z

def local():
    env.warn_only = True
Rawkcy
źródło
10

Powinieneś ustawić abort_exceptionzmienną środowiskową i złapać wyjątek.

Na przykład:

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.

Możesz również ustawić go w bloku with. Zobacz dokumentację tutaj .

ArtOfWarfare
źródło
Dzięki za to, ale jedno pytanie - czy jest możliwy dostęp / przejście w aktualnym środowisku sieciowym zgodnie z definicją w momencie wystąpienia wyjątku? (Mogę więc wydrukować określone ustawienia z wyjątkiem.)
Brian,
@Brian: Nie mógłbyś po prostu sprawdzić fabric.api.envw swoim exceptbloku?
ArtOfWarfare
@ArtOfWarefare Ahh, głupi ja, starałem się uniknąć zawijania wszystkich moich zadań w próbie / z wyjątkiem, a zamiast tego po prostu skonfigurowałem env.abort_exception=MyExceptiontak, żebym mógł uruchomić własny błąd. To trochę "działa", jeśli używam funkcji zamiast klasy (spełnia wymaganie wywoływalne dla abort_exception), ale wciąż pracuję nad innymi problemami z tym podejściem.
Brian
@Brian: Więc w treści tej funkcji sprawdź, co fabric.api.envjest.
ArtOfWarfare
7

Przynajmniej w Fabric 1.3.2 możesz odzyskać wyjątek, przechwytując go SystemExit. Jest to przydatne, jeśli masz więcej niż jedno polecenie do uruchomienia w trybie wsadowym (np. Wdrożenie) i chcesz wyczyścić, jeśli jedno z nich zawiedzie.

zimbatm
źródło
+1: Testowane - działa to również w Fabric 1.9.0. Po złapaniu tego możesz sprawdzić SystemExitwiadomość lub kod użytkownika, aby uzyskać więcej informacji.
ArtOfWarfare
Nawet lepiej niż łapanie SystemExit, ustaw abort_exceptioninny wyjątek, aby przypadkowo nie złapać wyjątków, które nie mają nic wspólnego z Fabric. Zobacz moją odpowiedź na przykład: stackoverflow.com/a/27990242/901641
ArtOfWarfare
7

W Fabric 2.x można po prostu użyć invoke „s run z ostrzec = true argumentu. W każdym razie wywołanie jest zależne od Fabric 2.x :

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

Z poziomu zadania:

from invoke import task

@task
def my_task(c):
    c.run('bad command', warn=True)
Qlimax
źródło
-5

W moim przypadku na Fabric> = 1,4 ta odpowiedź była poprawna.

Możesz pominąć złe hosty, dodając to:

env.skip_bad_hosts = True

Lub przekroczyć --skip-bad-hostsflagę /

Christian Vielma
źródło