Come continuare un'attività quando Fabric riceve un errore


94

Quando definisco un'attività da eseguire su diversi server remoti, se l'attività viene eseguita sul server uno ed esce con un errore, Fabric interromperà e interromperà l'attività. Ma voglio che il fabric ignori l'errore ed esegua l'attività sul server successivo. Come posso farlo fare?

Per esempio:

$ 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.

Risposte:


146

Dai documenti :

... Fabric ha come impostazione predefinita un modello di comportamento "fail-fast": se qualcosa va storto, come un programma remoto che restituisce un valore di ritorno diverso da zero o il codice Python del tuo fabfile incontra un'eccezione, l'esecuzione si interromperà immediatamente.

Questo è in genere il comportamento desiderato, ma ci sono molte eccezioni alla regola, quindi Fabric fornisce env.warn_only, un'impostazione booleana. Il valore predefinito è False, il che significa che una condizione di errore comporterà l'interruzione immediata del programma. Tuttavia, se env.warn_only è impostato su True al momento dell'errore, ad esempio con il gestore del contesto delle impostazioni, Fabric emetterà un messaggio di avviso ma continuerà l'esecuzione.

Sembra che tu possa esercitare un controllo dettagliato su dove gli errori vengono ignorati utilizzando il settingsgestore del contesto , qualcosa del genere:

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
Non dimenticare di importarefrom fabric.api settings
cevaris

31

A partire da Fabric 1.5, esiste un ContextManager che lo rende più semplice:

from fabric.api import sudo, warn_only

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

Aggiornamento: ho riconfermato che funziona in ipython utilizzando il seguente codice.

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')

Quale versione di tessuto stai usando? Ho appena ripetuto il test con Fabric == 1.6.2 e funziona bene.
Chris Marinos

Forse sto usando Fabric == 1.9.0 e non funziona per me
cevaris

Appena testato anche su 1.9.0. Qual è il tuo output quando provi il codice di esempio dal mio commento aggiornato?
Chris Marinos

Se non vuoi stampare gli avvisi / errori, puoi anche usare il gestore di contesto nascondi :with hide('everything'):
np8

13

Puoi anche impostare l'impostazione warn_only dell'intero script in modo che sia true con

def local():
    env.warn_only = True

10

Dovresti impostare il file abort_exception variabile di ambiente e catturare l'eccezione.

Per esempio:

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.

Puoi anche impostarlo in un blocco with. Consulta la documentazione qui .


Grazie per questo, ma una domanda: è possibile accedere / passare nel fabric env dict corrente come definito quando si è verificata l'eccezione? (Quindi posso stampare alcune impostazioni specifiche con l'eccezione.)
Brian

@ Brian: non potresti semplicemente controllare fabric.api.envall'interno del tuo exceptblocco?
ArtOfWarfare

@ArtOfWarefare Ah, stupido, stavo cercando di evitare di avvolgere tutte le mie attività in una prova / tranne e invece env.abort_exception=MyExceptionho semplicemente impostato il in modo da poter eseguire il mio fallimento. In un abort_exceptioncerto senso "funziona" se uso una funzione invece di una classe (soddisfa il richiamo richiamabile per ) ma sto ancora lavorando su altri problemi con questo approccio.
Brian

@ Brian: Quindi, all'interno del corpo di quella funzione, controlla cosa fabric.api.envè.
ArtOfWarfare

7

Almeno in Fabric 1.3.2, è possibile ripristinare l'eccezione rilevando l' SystemExiteccezione. È utile se si dispone di più di un comando da eseguire in un batch (come una distribuzione) e si desidera eseguire la pulizia se uno di essi non riesce.


+1: testato - funziona anche in Fabric 1.9.0. Dopo averlo rilevato, puoi controllare il SystemExitmessaggio o il codice di per maggiori dettagli.
ArtOfWarfare

Anche meglio che catturare SystemExit, impostare abort_exceptionun'eccezione diversa, in modo da non catturare accidentalmente eccezioni che non hanno nulla a che fare con Fabric. Vedere la mia risposta per un esempio: stackoverflow.com/a/27990242/901641
ArtOfWarfare

7

In Fabric 2.x puoi semplicemente usare l ' esecuzione di invoke con l' argomento warn = True . Ad ogni modo, invoke è una dipendenza di Fabric 2.x :

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

Dall'interno di un'attività:

from invoke import task

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

-5

Nel mio caso, su Fabric> = 1.4 questa risposta era quella corretta.

Puoi saltare i cattivi host aggiungendo questo:

env.skip_bad_hosts = True

O passando la --skip-bad-hostsbandiera /

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.