Cómo continuar una tarea cuando Fabric recibe un error

94

Cuando defino una tarea para que se ejecute en varios servidores remotos, si la tarea se ejecuta en el servidor uno y sale con un error, Fabric detendrá y abortará la tarea. Pero quiero hacer que la tela ignore el error y ejecute la tarea en el siguiente servidor. ¿Cómo puedo hacer que haga esto?

Por ejemplo:

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

Respuestas:

146

De los documentos :

... Fabric tiene por defecto un patrón de comportamiento "fallido": si algo sale mal, como un programa remoto que devuelve un valor de retorno distinto de cero o el código Python de su fabfile que encuentra una excepción, la ejecución se detendrá inmediatamente.

Este suele ser el comportamiento deseado, pero hay muchas excepciones a la regla, por lo que Fabric proporciona env.warn_only, una configuración booleana. El valor predeterminado es Falso, lo que significa que una condición de error provocará que el programa se cancele inmediatamente. Sin embargo, si env.warn_only se establece en True en el momento de la falla, por ejemplo, con el administrador de contexto de configuración, Fabric emitirá un mensaje de advertencia pero continuará ejecutándose.

Parece que puede ejercer un control detallado sobre dónde se ignoran los errores utilizando el settingsadministrador de contexto , algo así:

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
fuente
13
No te olvides de importarfrom fabric.api settings
cevaris
31

A partir de Fabric 1.5, existe un ContextManager que facilita esto:

from fabric.api import sudo, warn_only

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

Actualización: volví a confirmar que esto funciona en ipython usando el siguiente código.

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
fuente
¿Qué versión de tela estás usando? Acabo de volver a probar con Fabric == 1.6.2 y funciona bien.
Chris Marinos
Posiblemente, estoy usando Fabric == 1.9.0 y no me funciona
cevaris
También probado en 1.9.0. ¿Cuál es su resultado cuando prueba el código de ejemplo de mi comentario actualizado?
Chris Marinos
Si no desea imprimir las advertencias / errores, también se puede utilizar la piel gestor de contexto:with hide('everything'):
NP8
13

También puede establecer la configuración warn_only del script completo para que sea verdadera con

def local():
    env.warn_only = True
Rawkcy
fuente
10

Debe establecer la abort_exceptionvariable de entorno y detectar la excepción.

Por ejemplo:

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.

También puede configurarlo en un bloque with. Consulte la documentación aquí .

ArtOfWarfare
fuente
Gracias por esto, pero una pregunta: ¿es posible acceder / pasar en el entorno de tejido actual tal como se definió cuando ocurrió la excepción? (Entonces puedo imprimir algunas configuraciones específicas con la excepción).
Brian
@Brian: ¿No podrías simplemente comprobar fabric.api.envdentro de tu exceptcuadra?
ArtOfWarfare
@ArtOfWarefare Ahh, tonto de mí, estaba tratando de evitar envolver todas mis tareas en un intento / excepto y, en su lugar, configuré el env.abort_exception=MyExceptionpara poder ejecutar mi propio error. En cierto modo, "funciona" si uso una función en lugar de una clase (satisface la solicitud deabort_exception ) pero todavía estoy trabajando en algunos otros problemas con ese enfoque.
Brian
@Brian: Entonces, dentro del cuerpo de esa función, verifique qué fabric.api.enves.
ArtOfWarfare
7

En Fabric 1.3.2 al menos, puede recuperar la excepción detectando la SystemExitexcepción. Eso es útil si tiene más de un comando para ejecutar en un lote (como una implementación) y desea limpiar si uno de ellos falla.

zimbatm
fuente
+1: probado: esto también funciona en Fabric 1.9.0. Después de detectar esto, puede verificar el SystemExitmensaje o código de para obtener más detalles.
ArtOfWarfare
Incluso mejor que detectar SystemExit, establecer abort_exceptionuna excepción diferente, para que no detecte accidentalmente excepciones que no tienen nada que ver con Fabric. Vea mi respuesta para ver un ejemplo: stackoverflow.com/a/27990242/901641
ArtOfWarfare
7

En Fabric 2.x puedes usar invocación 's carrera con la verdadera advertir = argumento. De todos modos, invoke es una dependencia de Fabric 2.x :

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

Desde dentro de una tarea:

from invoke import task

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

En mi caso, en Fabric> = 1.4 esta respuesta fue la correcta.

Puede omitir hosts defectuosos agregando esto:

env.skip_bad_hosts = True

O pasar la --skip-bad-hostsbandera /

Christian Vielma
fuente