Bagaimana melanjutkan tugas saat Fabric menerima kesalahan

94

Saat saya menentukan tugas untuk dijalankan di beberapa server jarak jauh, jika tugas berjalan di server satu dan keluar dengan kesalahan, Fabric akan menghentikan dan membatalkan tugas. Tetapi saya ingin membuat fabric mengabaikan kesalahan dan menjalankan tugas di server berikutnya. Bagaimana saya bisa membuatnya melakukan ini?

Sebagai contoh:

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

Jawaban:

146

Dari dokumen :

... Fabric default ke pola perilaku "gagal-cepat": jika terjadi kesalahan, seperti program jarak jauh yang mengembalikan nilai kembalian bukan nol atau kode Python fabfile Anda menghadapi pengecualian, eksekusi akan segera dihentikan.

Biasanya ini adalah perilaku yang diinginkan, tetapi ada banyak pengecualian untuk aturan tersebut, jadi Fabric menyediakan env.warn_only, setelan Boolean. Defaultnya adalah False, yang berarti kondisi kesalahan akan mengakibatkan program segera dibatalkan. Namun, jika env.warn_only disetel ke True pada saat terjadi kegagalan - dengan, katakanlah, pengelola konteks pengaturan - Fabric akan mengeluarkan pesan peringatan tetapi terus menjalankan.

Sepertinya Anda dapat melakukan kontrol yang sangat cermat atas tempat kesalahan diabaikan dengan menggunakan settingspengelola konteks , seperti ini:

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
sumber
13
Jangan lupa untuk mengimporfrom fabric.api settings
cevaris
31

Mulai Fabric 1.5, ada ContextManager yang membuatnya lebih mudah:

from fabric.api import sudo, warn_only

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

Pembaruan: Saya menegaskan kembali bahwa ini berfungsi di ipython menggunakan kode berikut.

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
sumber
Versi kain apa yang Anda gunakan? Saya baru saja menguji ulang dengan Fabric == 1.6.2, dan itu berfungsi dengan baik.
Chris Marinos
Mungkin, saya menggunakan Fabric == 1.9.0 dan tidak berhasil untuk saya
cevaris
Baru saja diuji pada 1.9.0 juga. Apa output Anda ketika Anda mencoba kode contoh dari komentar saya yang diperbarui?
Chris Marinos
Jika Anda tidak ingin mencetak peringatan / kesalahan, Anda juga dapat menggunakan manajer konteks sembunyikan :with hide('everything'):
np8
13

Anda juga dapat menyetel seluruh setelan warn_only skrip menjadi true dengan

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

Anda harus menyetel abort_exceptionvariabel lingkungan dan menangkap pengecualian.

Sebagai contoh:

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.

Anda juga dapat mengaturnya di blok dengan. Lihat dokumentasinya di sini .

ArtOfWarfare
sumber
Terima kasih untuk ini, tetapi satu pertanyaan - apakah mungkin akses / lewati di fabric env dict seperti yang ditentukan ketika pengecualian terjadi? (Jadi saya dapat mencetak beberapa pengaturan khusus dengan pengecualian.)
Brian
@ Brian: Tidak bisakah Anda memeriksa di fabric.api.envdalam exceptblok Anda ?
ArtOfWarfare
@ArtOfWarefare Ahh konyol saya Saya mencoba untuk menghindari membungkus semua tugas saya dalam percobaan / kecuali dan sebagai gantinya hanya mengatur env.abort_exception=MyExceptionsehingga saya bisa menjalankan kegagalan saya sendiri. Ini semacam "berfungsi" jika saya menggunakan fungsi alih-alih kelas (memenuhi persyaratan yang dapat dipanggil untuk abort_exception) tetapi saya masih mengerjakan beberapa masalah lain dengan pendekatan itu.
Brian
@ Brian: Jadi di dalam tubuh fungsi itu periksa apa fabric.api.env.
ArtOfWarfare
7

Setidaknya di Fabric 1.3.2, Anda dapat memulihkan pengecualian tersebut dengan menangkap SystemExitpengecualian tersebut. Itu berguna jika Anda memiliki lebih dari satu perintah untuk dijalankan dalam satu batch (seperti penerapan) dan ingin membersihkan jika salah satunya gagal.

zimbatm
sumber
+1: Diuji - ini juga berfungsi di Fabric 1.9.0. Setelah menangkap ini, Anda dapat memeriksa SystemExitpesan atau kode itu untuk lebih jelasnya.
ArtOfWarfare
Bahkan lebih baik daripada menangkap SystemExit, setel abort_exceptionke pengecualian yang berbeda, sehingga Anda tidak secara tidak sengaja menangkap pengecualian yang tidak ada hubungannya dengan Fabric. Lihat jawaban saya untuk contoh: stackoverflow.com/a/27990242/901641
ArtOfWarfare
7

Di Fabric 2.x Anda hanya dapat menggunakan Invoke 's run dengan memperingatkan = True argumen. Bagaimanapun, pemanggilan adalah ketergantungan dari Fabric 2.x :

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

Dari dalam tugas:

from invoke import task

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

Dalam kasus saya, pada Fabric> = 1.4 jawaban ini adalah yang benar.

Anda dapat melewati host yang buruk dengan menambahkan ini:

env.skip_bad_hosts = True

Atau mengoper --skip-bad-hostsbendera /

Christian Vielma
sumber