ANSIBLE SERIES: h.t.wrt* … tasks, plays e books: failures! (or error handling)
De tempos em tempos, a humanidade é testemunha de um curioso, engraçado e certo tipo de evento: erros que se transformam em felizes acertos! Não faz sentido pra você? Tudo bem, sem problemas! Eu lhe apresento 🥁🥁🥁 a penicilina e a coca-cola 🤭 rsrs! Então, voltando … O mesmo pode acontecer no Ansible. Eventualmente, um código de retorno com valor não-zero (diferente de zero), seja vindo de um comando ou de uma falha em determinado módulo, pode ser exatamente o que você busca no momento e por isso, não vai querer que o Ansible pare a execução naquele host e siga para o próximo. Não, muito pelo contrário. Tal código não-zero indica para você, e sua demanda/tarefa, um verdadeiro baita de um sucesso! Portanto, nada mais lógico do que tratá-lo … Correto? Bom, antes de iniciar os trabalhos, um exemplo básico de situação hipotética para ilustrar melhor: numa bela e ensolarada manhã, seu chefe propõe a você que, caso um host apresente uma falha, o Ansible pare/encerre instantaneamente a execução em todos os outros. Fim!
Agora sim, adiante! Vamos a aula de hoje, com o seu respectivo sumário listado a seguir:
- Ignorando comandos com falha;
- Ignorando erros de host inacessíveis;
- Redefinindo hosts inacessíveis;
- Manipuladores e falha;
- Definindo falha;
- Definindo “alterado”;
- Garantindo o sucesso para o comando e shell;
- Abortando uma reprodução em todos os hosts;
- Controle de erros em blocos;
07-a. Ignorando comandos com falhas
O comportamento padrão, quando uma task qualquer falha num host particular, é puro e simplesmente parar a execução das outras tarefas no mesmo, não importando a quantidade que faltava ou em qual task ele estava (ex: a primeira, a do meio, ou a última). Dessa forma, o Ansible pularia para o próximo host sem piscar duas vezes. Contudo, é totalmente possível continuar naquele host específico apesar dos apesares, ou melhor dizendo, da(s) falha(s). Basta utilizar a palavra-chave ignore_errors
:
- name: Do not count this as a failure
ansible.builtin.command: /bin/false
ignore_errors:
yes
Mas, atenção aqui! Tenha cuidado! Pois essa regra somente funcionará com tarefas capazes de retornar o valor 'failed'
(“falhou”). Sendo assim, o Ansible jamais/nunca ignora erros de: variáveis indefinidas, falhas de conexão, problemas de execução, pacotes ausentes ou até mesmo erros de sintaxe.
07-b. Ignorando erros de hosts inacessíveis
Em sua jornada diária, quantas e quantas vezes aquele seu colega sysadmin da infra/operação moveu uma VM de lugar, acrescentou uma nova regra de firewall, ou desligou um grupo de máquinas sem nenhum aviso prévio? Eu sei, talvez sua resposta seja: muitas, várias vezes … Certo? 😥 Mas calma! Por favor, peço que não brigue com ele! Afinal o dia-a-dia de um admin/ops pode ser um pouco cheio e meio estressante, de tanto deveres, rotinas e tarefas. E por isso, eis que trago a solução para os seus problemas (ou erros, neste caso). Se uma task abortar dado ao status do host ser ‘UNREACHABLE’, use ignore_unreachable
. Assim o Ansible ignora os erros da tarefa atual, mas permanecendo disponível para executar eventuais futuras tarefas no host que está inacessível no momento 🤩
À nível de task:
- name:This executes, fails, and the failure is ignored
ansible.builtin.command:/bin/true
ignore_unreachable:
yes - name:This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
À nível de playbook:
- hosts: allignore_unreachable:
yes tasks: - name:This executes, fails, and the failure is ignored
ansible.builtin.command:/bin/true
- name:This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true ignore_unreachable:no
07-c. Redefinindo hosts inacessíveis
Se o Ansible não puder se conectar a um host, ele marca esse host como ‘UNREACHABLE’ e o remove da lista de hosts ativos para a execução. Você pode usar meta: clear_host_errors
para reativar todos os hosts, para que as tarefas subsequentes possam tentar alcançá-los novamente. ( Top demais! 👌💯 )
07-d. Manipuladores e falhas
Ansible executa handlers
no final de cada play … Desde a nossa aula passada isso já é fato conhecido! PORÉM, e se uma tarefa notifica um manipulador, mas aí vem uma falha e faz o mesmo, logo em seguida ou posteriormente, na execução do playbook? O que acontece? Bem, o esperado e padrão é o manipulador não ser executado naquele host, o que pode deixar o host em um estado inesperado. Por exemplo, uma tarefa pode atualizar um arquivo de configuração e notificar um manipulador para reiniciar algum serviço. Se uma tarefa posterior na mesma execução falhar, o arquivo de configuração pode ser alterado, mas o serviço não será reiniciado.
Para evitar, reverter ou burlar (nomeie como quiser!) tal comportamento basta usar a opção de linha de comando --force-handlers
, incluindo force_handlers: True
em uma play, ou adicionando force_handlers = True
ao ansible.cfg. Quando os manipuladores são forçados, o Ansible executará todos os manipuladores notificados em todos os hosts, até mesmo hosts com tarefas com falha. (Observe que certos erros ainda podem impedir a execução do handler, como um host inacessível.)
07-e. Definindo uma falha
O Ansible permite definir o que “falha” significa em cada tarefa usando a condição failed_when
. Como acontece nas outras condicionais do Ansible, listas com várias condições failed_when são unidas com um and
implícito, o que refletindo na tarefa em si, já que a mesma só falha quando todas as condições são atendidas. Caso deseje o oposto disso, que é uma “falha” quando qualquer uma das condições for atendida, você deve definir as condições em uma string com um operador oR
explícito.
Você pode verificar se há falhas pesquisando uma palavra ou frase na saída de um comando:
- name: Fail task when the command error output prints FAILED ansible.builtin.command:/usr/bin/example-command -x -y -z
register:command_result
failed_when:
"'FAILED' in command_result.stderr"
Ou baseado no código de retorno:
- name: Fail task when both files are identical ansible.builtin.raw:diff foo/file1 bar/file2
register:diff_cmd
failed_when:
diff_cmd.rc == 0 or diff_cmd.rc >= 2
+ 1 exemplo: combinar múltiplas condições para definir uma “falha”
- name: Check if a file exists in temp and fail task if it does ansible.builtin.command:ls /tmp/this_should_not_be_here
register:result
failed_when:
-result.rc == 0
-'"No such" not in result.stdout'
07-f. Definindo “alterado”
O Ansible permite definir quando uma tarefa específica “mudou” um nó remoto usando a condição changed_when
. Isso permite determinar, com base em códigos de retorno ou saída, se uma alteração deve ser relatada nas estatísticas Ansible e se um manipulador deve ser acionado ou não. Como acontece com todas as condicionais em Ansible, listas de várias condições changed_when são unidas com um AND
implícito, o que significa que a tarefa só relata uma mudança quando todas as condições são atendidas. Se quiser relatar uma mudança quando qualquer uma das condições for atendida, você deve definir as condições em uma string com um operador OR
explícito. Por exemplo:
tasks: - name: Report 'changed' when the return code is not equal to 2 ansible.builtin.shell:/usr/bin/billybass --mode="take me to the river"
register:bass_result
changed_when:
"bass_result.rc != 2"
- name: This will never report 'changed' status ansible.builtin.shell:wall 'beep'
changed_when:
False
07-g. Garantindo o sucesso para o comando e shell
Os módulos de comando e shell se preocupam com os códigos de retorno, portanto, se você tiver um comando cujo código de saída bem-sucedido não seja zero, você pode fazer o seguinte:
tasks: - name: Run this command and ignore the resultansible.builtin.shell:
/usr/bin/somecommand
||
/bin/true
07-h. Abortando uma execução em todos os hosts
Às vezes, você deseja que uma falha em um único host, ou falhas em uma certa porcentagem de hosts, aborte a execução inteira em todos os hosts. Você pode interromper a execução da reprodução após a primeira falha acontecer com any_errors_fatal
. Para um controle mais refinado, você pode usar max_fail_percentage
para abortar a execução após uma determinada porcentagem de hosts falhar.
07-i. Controle de erros em blocos
Veja detalhes e exemplos em https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html#block-error-handling