Ansible msg error

Разберём плейбук который будет проверять наличие установленных пакетов: Проверяем используя shell: - name: Check install java shell: java -version 2>&1 | grep version | awk '{print $1}' | sed 's/"//g' # команда вернёт java или openjdk register: java_result #записываем результат в данную переменную - debug: var: java_result # вывод записанного результата - name: Install default … Читать далее ansible. Работа с проверками (debug/when/jinja2) →

Раз­бе­рём плей­бук кото­рый будет про­ве­рять нали­чие уста­нов­лен­ных паке­тов:

Про­ве­ря­ем исполь­зуя shell:

— name: Check install java

  shell: java -version 2>&1 | grep version | awk ‘{print $1}‘ | sed ‘s/«//g’  # коман­да вер­нёт java или openjdk

  register: java_result   #запи­сы­ва­ем резуль­тат в дан­ную переменную

— debug:

    var: java_result    # вывод запи­сан­но­го результата

— name: Install default packages

  yum:

      name: «{{item}}«

      state: present

  with_items:

      — java-1.8.0-openjdk

  when: java_result.stdout != «java» and java_result.stdout != «openjdk»  # когда stdout у запи­сан­ной ранее пере­мен­ной НЕ равен java или openjdk

                                                                       # то тогда будет уста­нов­ле­на java-1.8.0-openjdk

===========================================================================

Про­ве­ря­ем нали­чие 2х уста­нов­лен­ных паке­тов python и tcpdump  исполь­зу­ем модуль package_facts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

— hosts: 123.456.789.000

  user: ansible

  become: true

  gather_facts: yes

  ignore_errors: yes

  become_method: sudo

  tasks:

  — name: «Check if python and tcpdump is installed»

    package_facts:

     manager: «auto»

  — name: «python or tcpdump is installed»

    debug:

     msg: «Packpage FOUND»                 #Дан­ное сооб­ще­ние будет выве­де­но если выпол­нит­ся усло­вие ниже

    when:

     «‘python’ in ansible_facts.packages»   #Если пакет python содер­жит­ся в ansible_facts.packages

     «‘tcpdump’ in ansible_facts.packages»  #Если пакет tcpdump содер­жит­ся в ansible_facts.packages

  — name: «python or tcpdump is NOT installed» 

    debug:

      msg: «Packpage NOT found»             #Дан­ное сооб­ще­ние будет выве­де­но если выпол­нит­ся усло­вие ниже

    when: «‘python’ not in ansible_facts.packages or ‘tcpdump’ not in ansible_facts.packages»   # если пакет python или tcpdump не установлены

========================================

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

— hosts: 10.242.144.142

  user: ansible

  become: true

  gather_facts: yes

  ignore_errors: yes

  become_method: sudo

  tasks:

  — name: «Check if python and tcpdump is installed»

    package_facts:

     manager: «auto»

    register: version

#  — debug:

#      var: ansible_facts.packages

  — name: list some packages

    yum: list=git

    register: out_some

  — debug: var=out_some.results[1].version

  — name: «python or tcpdump is installed»

    debug:

     msg: «Packpage FOUND«

    when:

     «‘python’ in ansible_facts.packages»

     «‘tcpdump’ in ansible_facts.packages»

  — name: «python or tcpdump is NOT installed»

    debug:

      msg: «Packpage NOT found»

    when: «‘python’ not in ansible_facts.packages or ‘tcpdump’ not in ansible_facts.packages»

====================================

Делегирование задачи другому хосту

Ино­гда тре­бу­ет­ся выпол­нить зада­чу на опре­де­лён­ном узле, но в кон­тек­сте дру­го­го узла. Напри­мер, во вре­мя обнов­ле­ния узла может воз­ник­нуть необ­хо­ди­мость отклю­чить для него мони­то­ринг, нахо­дя­щий­ся на отдель­ном сер­ве­ре. Для это­го исполь­зу­ет­ся управ­ля­ю­щая дирек­ти­ва delegate_to. При­ве­дём пример:

— name: disable nagios alerts <span class=«hljs-keyword»>for</span> <span class=«hljs-keyword»>this</span> host webserver service

  nagios: action=disable_alerts host={{inventory_hostname}} services=dnsserver

  delegate_to: mon_host.example.com

Резуль­та­том выпол­не­ния этой зада­чи будет отклю­че­ние сооб­ще­ний для сер­ви­са dnsserver в Nagios.

====================================================

Узнать опре­де­ле­на ли (суще­ству­ет ли) пере­мен­ная в Ansible:

tasks:

— shell: echo «The variable ‘foo’ is defined: ‘{{ foo }}‘»

  when: foo is defined

— fail: msg=»The variable ‘bar’ is not defined»

  when: bar is undefined

Про­ве­рить явля­ет­ся ли пере­мен­ная в Ansible пустой:

tasks:

— fail: msg=»The variable ‘bar’ is empty»

  when: bar|length == 0

— shell: echo «The variable ‘foo’ is not empty: ‘{{ foo }}‘»

  when: foo|length &gt; 0

Убе­дить­ся что пере­мен­ная в Ansible опре­де­ле­на и не явля­ет­ся пустой:

tasks:

— shell: echo «The variable ‘foo’ is defined and not empty»

  when: (foo is defined) and (foo|length &gt; 0)

— fail: msg=»The variable ‘bar’ is not defined or empty»

  when: (bar is not defined) or (bar|length == 0)

Узнать име­ет ли пере­мен­ная в Ansible зна­че­ние True или False:

tasks:

— shell: echo «The variable ‘foo’ is ‘True'»

  when: foo|bool == True

— shell: echo «The variable ‘bar’ is ‘False'»

  when: bar|bool == False

выпол­не­ние зада­чи на опре­де­лён­ной груп­пе из группы:
cat /etc/ansible/hosts
[nfs:children]
nfsmaster
nfsclient

[nfsmaster]
192.168.1.112
[nfsclient]
192.168.1.111
192.168.1.110

— name: Create a directory if it does not exist

  file:

    path: /share

    state: directory

    mode: 0755

    owner: root

    group: root

  when: inventory_hostname in groups[‘nfsmaster’]

Печать Переменных & Вывод Всех Переменных — Playbook

Во вре­мя отлад­ки плей­бу­ка в Ansible, полез­но знать как, отоб­ра­зить задан­ные пере­мен­ные или извест­ные о хосте факты.

В Ansible, для печа­ти сооб­ще­ний из плей­бу­ка, а так­же для выво­да зна­че­ний пере­мен­ных, мы можем исполь­зо­вать модуль debug.

В сле­ду­ю­щих при­ме­рах я пока­зы­ваю, как напе­ча­тать кон­крет­ные пере­мен­ные в Ansible и как выве­сти спи­сок всех извест­ных фак­тов и пере­мен­ных в Ansible с помо­щью плейбука.

Debug Ansible Playbook — Печать Переменных

Выве­сти зна­че­ние переменной:

— name: «Ansible | Print a variable»

  debug:

    msg: «The operating system is {{ ansible_distribution }}«

Напе­ча­тать пере­мен­ную толь­ко если она определена:

— name: «Ansible | Print a variable if defined»

  debug:

    msg: «The ‘foo’ variable value is {{ foo }}«

  when: foo is defined

— name: «Ansible | Print a message if a variable is undefined«

  debug:

    msg: «The ‘bar’ variable is undefined»

  when: bar is not defined

Печать несколь­ких переменных:

— name: «Ansible | Print multiple variable»

  debug:

    msg: |

      The role is {{ host_role }}

      The environment is {{ host_environment }}

Выве­сти спи­сок всех извест­ных фак­тов и переменных:

— name: «Ansible | List all known variables and facts»

  debug:

    var: hostvars[inventory_hostname]

Jinja2: Проверка Существования и Значения Переменной

В Jinja2-тем­плей­тах явля­ет­ся хоро­шей прак­ти­кой зара­нее про­ве­рять суще­ству­ет ли пере­мен­ная и какое у нее значение.

Суще­ству­ет несколь­ко полез­ных про­ве­рок, кото­рые вы може­те сде­лать с помо­щью встро­ен­ных в Jinja2 тестов и филь­тров.

В этой ста­тье я пока­жу, как про­ве­рить, суще­ству­ет ли пере­мен­ная, явля­ет­ся ли она пустой или она уста­нов­ле­на в зна­че­ние True.

Проверка Переменной в Jinja2

Про­ве­рить суще­ству­ет ли (опре­де­ле­на ли) переменная:

{% if variable is defined %}

    variable is defined

{% else %}

    variable is not defined

{% endif %}

Про­ве­рить явля­ет­ся ли пустой переменная:

{% if variable|length %}

    variable is not empty

{% else %}

    variable is empty

{% endif %}

Про­ве­рить уста­нов­ле­на ли в зна­че­ние true переменная:

{% if variable is sameas true %}

    variable is true

{% else %}

    variable is not true

{% endif %}

Про­ве­рить что пере­мен­ная суще­ству­ет и не явля­ет­ся пустой:

{% if variable is defined and variable|length %}

    variable is defined and not empty

{% else %}

    variable is not defined or empty

{% endif %}

Про­ве­рить что пере­мен­ная суще­ству­ет и уста­нов­ле­на в зна­че­ние true:

{% if variable is defined and variable is sameas true %}

    variable is defined and true

{% else %}

    variable is not defined or not set to true

{% endif %}

======================================================================================

Ещё немно­го о WHEN

Использование когда с фактами

Вы може­те исполь­зо­вать услов­ные выра­же­ния when для запус­ка зада­чи, толь­ко если выпол­ня­ет­ся опре­де­лен­ное усло­вие. Для демон­стра­ции создай­те новую кни­гу вос­про­из­ве­де­ния с име­нем ubuntu-server.yml со сле­ду­ю­щим содержимым:

[codesyntax lang=»php»]

— name: Using when with facts 

  hosts: all

  tasks:

    — name: Detect Ubuntu Servers

      debug:

        msg: «This is an Ubuntu Server.»

      when:  ansible_facts[‘distribution’] == «Ubuntu»

[/codesyntax]

Теперь запу­сти­те playbook:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

[des@andrey]$ ansible-playbook ubuntu-servers.yml

PLAY [Using when with facts] *******************************************

TASK [Gathering Facts] *********************************************************

ok: [node4]

ok: [node1]

ok: [node3]

ok: [node2]

TASK [Detect Ubuntu Servers] ***************************************************

skipping: [node1]

skipping: [node2]

skipping: [node3]

ok: [node4] =&gt; {

    «msg»: «Это сер­вер Ubuntu.»

}

PLAY RECAP *********************************************************************

node1                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    

node2                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    

node3                      : ok=1    changed=0    unreachable=0    failed=0    skipped=1    

node4                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0

Обра­ти­те вни­ма­ние, как мы исполь­зо­ва­ли факт Ansible ansible_facts[‘distribution’] в усло­вии when, что­бы про­ве­рить, на каких узлах рабо­та­ет Ubuntu. Так­же обра­ти­те вни­ма­ние, что вам не нуж­но заклю­чать пере­мен­ные в фигур­ные скоб­ки при исполь­зо­ва­нии услов­ных выра­же­ний when.

В выход­ных дан­ных playbook обра­ти­те вни­ма­ние, как TASK [Detect Ubuntu Servers] про­пу­ще­ны пер­вые три узла, посколь­ку все они рабо­та­ют под CentOS и рабо­та­ют толь­ко на node4, посколь­ку он рабо­та­ет под управ­ле­ни­ем Ubuntu.

Использование when с регистрами

Вы так­же може­те исполь­зо­вать услов­ные выра­же­ния с заре­ги­стри­ро­ван­ны­ми пере­мен­ны­ми. Напри­мер, сле­ду­ю­щая кни­га centos-servers.yml пока­жет, на каких узлах рабо­та­ет CentOS:

[codesyntax lang=»php»]

— name: Using when with registers

  hosts: all

  tasks:

    — name: Save the contents of /etc/os-release

      command: cat /etc/os-release

      register: os_release

    — name: Detect CentOS Servers

      debug:

        msg: «Запуск CentOS…»

      when: os_release.stdout.find(‘CentOS’) != -1

[/codesyntax]

Плей­бук сна­ча­ла начи­на­ет­ся с сохра­не­ния содер­жи­мо­го фай­ла /etc/os-release в регистр os_release . Затем вто­рая зада­ча отоб­ра­жа­ет сооб­ще­ние «Запуск CentOS…», толь­ко если сло­во «CentOS» встре­ча­ет­ся в   стан­дарт­ном выво­де os_release .

Иди­те впе­ред и запу­сти­те playbook:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

[des@andrey]$ ansible-playbook centos-servers.yml

PLAY [Using when with registers] ***********************************************

TASK [Gathering Facts] *********************************************************

ok: [node4]

ok: [node1]

ok: [node3]

ok: [node2]

TASK [Save the contents of /etc/os-release] ************************************

changed: [node4]

changed: [node1]

changed: [node2]

changed: [node3]

TASK [Detect CentOS Servers] ***************************************************

ok: [node1] =&gt; {

    «msg»: «Запуск CentOS…»

}

ok: [node2] =&gt; {

    «msg»: «Запуск CentOS…»

}

ok: [node3] =&gt; {

    «msg»: «Запуск CentOS…»

}

skipping: [node4]

PLAY RECAP *********************************************************************

node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    

node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    

node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    

node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=1

Обра­ти­те вни­ма­ние, как рабо­та­ет TASK [Detect CentOS Servers] толь­ко на пер­вых трех узлах и про­пус­ка­ет­ся node4 (Ubuntu).

Тестирование нескольких условий с помощью when

Вы так­же може­те про­ве­рить несколь­ко усло­вий одно­вре­мен­но, исполь­зуя логи­че­ские опе­ра­то­ры. Напри­мер, сле­ду­ю­щая перезагрузка-centos8.yml Playbook исполь­зу­ет логи­че­ский и опе­ра­то­ра для пере­за­груз­ки сер­ве­ров, рабо­та­ю­щих под управ­ле­ни­ем CentOS вер­сии 8:

[codesyntax lang=»php»]

— name: Reboot Servers

  hosts: all

  tasks:

    — name: Reboot CentOS 8 servers

      reboot:

        msg: «Сер­вер перезагружается…»

      when: ansible_facts[‘distribution’] == «CentOS» and ansible_facts[‘distribution_major_version’] == «8»

[/codesyntax]

Вы так­же може­те исполь­зо­вать логи­че­ский опе­ра­тор или для запус­ка зада­чи, если выпол­ня­ет­ся какое-либо из усло­вий. Напри­мер, сле­ду­ю­щая зада­ча будет пере­за­гру­жать сер­ве­ры , на кото­рых рабо­та­ет CentOS или RedHat :

[codesyntax lang=»php»]

  tasks:

    — name: Reboot CentOS and RedHat Servers 

      reboot:

        msg: «Сер­вер перезагружается…»

      when: ansible_facts[‘distribution’] == «CentOS» or ansible_facts[‘distribution’] == «RedHat»

[/codesyntax]

Использование when с петлями

Если вы объ­еди­ни­те услов­ный опе­ра­тор when с цик­лом, Ansible про­ве­рит усло­вие для каж­до­го эле­мен­та в цик­ле отдельно.

Напри­мер, сле­ду­ю­щий сце­на­рий print-even.yml напе­ча­та­ет все чет­ные чис­ла в диа­па­зоне (1,11):

[codesyntax lang=»php»]

— name: Print Some Numbers

  hosts: node1

  tasks:

    — name: Print Even Numbers

      debug:

        msg: Number {{ item }} is Even.

      loop: «{{ range(1,11) | list }}«

      when: item % 2 == 0

[/codesyntax]

Про­дол­жай­те и запу­сти­те playbook, что­бы уви­деть спи­сок всех чет­ных чисел в диа­па­зоне (1,11):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

[des@andrey]$ ansible-playbook print-even.yml

PLAY [Print Some Numbers] **********************************

TASK [Gathering Facts] ****************************

ok: [node1]

TASK [Print Even Numbers] ******************************

skipping: [node1] =&gt; (item=1)

ok: [node1] =&gt; (item=2) =&gt; {

    «msg»: «Чис­ло 2 четное.»

}

skipping: [node1] =&gt; (item=3)

ok: [node1] =&gt; (item=4) =&gt; {

    «msg»: «Чис­ло 4 четное.»

}

skipping: [node1] =&gt; (item=5)

ok: [node1] =&gt; (item=6) =&gt; {

    «msg»: «Чис­ло 6 четное.»

}

skipping: [node1] =&gt; (item=7)

ok: [node1] =&gt; (item=8) =&gt; {

    «msg»: «Чис­ло 8 четное.»

}

skipping: [node1] =&gt; (item=9)

ok: [node1] =&gt; (item=10) =&gt; {

    «msg»: «Чис­ло 10 четное.»

}

PLAY RECAP ***********************************

node1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0

Использование when с переменными

Вы так­же може­те исполь­зо­вать услов­ные опе­ра­то­ры when с ваши­ми соб­ствен­ны­ми опре­де­лен­ны­ми пере­мен­ны­ми. Помни­те, что услов­ные выра­же­ния тре­бу­ют логи­че­ских вход­ных дан­ных; то есть, для сра­ба­ты­ва­ния усло­вия тест дол­жен быть истин­ным, поэто­му вам нуж­но исполь­зо­вать фильтр bool с небу­ле­вы­ми переменными.

Что­бы про­де­мон­стри­ро­вать это, взгля­ни­те на сле­ду­ю­щий сбор­ник сце­на­ри­ев isfree.yml :

[codesyntax lang=»php»]

— name:

  hosts: node1

  vars:

    weekend: true

    on_call: «no»

  tasks:

    — name: Run if «weekend» is true and «on_call» is false

      debug:

        msg: «Вы свободны!»

      when: weekend and not on_call | bool

[/codesyntax]

Обра­ти­те вни­ма­ние, что здесь я исполь­зо­вал фильтр bool для пре­об­ра­зо­ва­ния зна­че­ния on_call в его логи­че­ский экви­ва­лент (no -> false).

Кро­ме того, вы долж­ны знать, что not false – это исти­на, и поэто­му в этом слу­чае все усло­вие будет оце­ни­вать­ся как true; ты свободен!

Вы так­же може­те про­ве­рить, уста­нов­ле­на ли пере­мен­ная или нет; напри­мер, сле­ду­ю­щая зада­ча будет выпол­нять­ся, толь­ко если опре­де­ле­на пере­мен­ная car :

[codesyntax lang=»php»]

tasks:

    — name: Run only if you got a car

      debug:

        msg: «Давай отпра­вим­ся в путешествие…»

      when: car is defined

[/codesyntax]

Сле­ду­ю­щая зада­ча исполь­зу­ет сбой моду­ля потер­петь неуда­чу , если клю­чи пере­мен­ная не определена:

[codesyntax lang=»php»]

tasks:

    — name: Fail if you got no keys

      fail:

        msg: «Эта игра тре­бу­ет неко­то­рых ключей»

      when: keys is undefined

[/codesyntax]

Обработка исключений с помощью блоков

Пого­во­рим об обра­бот­ке исключений.

Группировка задач с блоками

Вы може­те исполь­зо­вать бло­ки для груп­пи­ров­ки свя­зан­ных задач. Для демон­стра­ции взгля­ни­те на сле­ду­ю­щую кни­гу install-apache.yml :

[codesyntax lang=»php»]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

— name: Install and start Apache Play

  hosts: webservers

  tasks:

    — name: Install and start Apache

      block:

         — name: Install httpd

           yum:

             name: httpd

             state: latest

         — name: Start and enable httpd

           service:

             name: httpd

             state: started

             enabled: yes

    — name: This task is outside the block

      debug:

        msg: «Сей­час я нахо­жусь за пре­де­ла­ми блока…»

[/codesyntax]

Playbook запус­ка­ет­ся на хостах груп­пы веб-сер­ве­ров и име­ет один блок с име­нем Уста­но­вить и запу­стить Apache, кото­рый вклю­ча­ет в себя две задачи:

  1. Уста­но­вить httpd
  2. Запу­стить и вклю­чить httpd

Пер­вая зада­ча Install httpd исполь­зу­ет модуль yum для уста­нов­ки паке­та httpd apache. Вто­рая зада­ча исполь­зу­ет слу­жеб­ный модуль для запус­ка и раз­ре­ша­ет запуск httpd при загрузке.

Обра­ти­те вни­ма­ние, что в playbook есть тре­тья зада­ча, кото­рая не при­над­ле­жит бло­ку Install and start Apache.

Теперь запу­сти­те playbook, что­бы уста­но­вить и запу­стить httpd на узлах веб-серверов:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

[des@andrey]$ ansible-playbook install-apache.yml

PLAY [Install and start Apache Play] *******************************************

TASK [Gathering Facts] *********************************************************

ok: [node3]

ok: [node2]

TASK [Install httpd] ***********************************************************

changed: [node2]

changed: [node3]

TASK [Start and enable httpd] **************************************************

changed: [node3]

changed: [node2]

TASK [This task is outside the block] ******************************************

ok: [node2] =&gt; {

    «msg»: «Сей­час я нахо­жусь за пре­де­ла­ми квартала…»

}

ok: [node3] =&gt; {

    «msg»: «Сей­час я нахо­жусь за пре­де­ла­ми квартала…»

}

PLAY RECAP *********************************************************************

node2                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0    

node3                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0

Вы так­же може­те выпол­нить спе­ци­аль­ную коман­ду, что­бы убе­дить­ся, что httpd дей­стви­тель­но запу­щен и работает:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

[des@andrey]$ ansible webservers -m command -a «systemctl status httpd»

node3 | CHANGED | rc=0 &gt;&gt;

httpd.service The Apache HTTP Server

   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)

   Active: active (running) since Tue 2020-11-03 19:35:13 UTC; 1min 37s ago

     Docs: man:httpd.service(8)

Main PID: 47122 (httpd)

   Status: «Running, listening on: port 80″

    Tasks: 213 (limit: 11935)

   Memory: 25.1M

   CGroup: /system.slice/httpd.service

           ├─47122 /usr/sbin/httpd -DFOREGROUND

           ├─47123 /usr/sbin/httpd -DFOREGROUND

           ├─47124 /usr/sbin/httpd -DFOREGROUND

           ├─47125 /usr/sbin/httpd -DFOREGROUND

           └─47126 /usr/sbin/httpd -DFOREGROUND

Nov 03 19:35:13 node3 systemd[1]: Starting The Apache HTTP Server…

Nov 03 19:35:13 node3 systemd[1]: Started The Apache HTTP Server.

Nov 03 19:35:13 node3 httpd[47122]: Server configured, listening on: port 80

node2 | CHANGED | rc=0 &gt;&gt;

httpd.service The Apache HTTP Server

   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)

   Active: active (running) since Tue 2020-11-03 19:35:13 UTC; 1min 37s ago

     Docs: man:httpd.service(8)

Main PID: 43695 (httpd)

   Status: «Running, listening on: port 80″

    Tasks: 213 (limit: 11935)

   Memory: 25.1M

   CGroup: /system.slice/httpd.service

           ├─43695 /usr/sbin/httpd -DFOREGROUND

           ├─43696 /usr/sbin/httpd -DFOREGROUND

           ├─43697 /usr/sbin/httpd -DFOREGROUND

           ├─43698 /usr/sbin/httpd -DFOREGROUND

           └─43699 /usr/sbin/httpd -DFOREGROUND

Nov 03 19:35:13 node2 systemd[1]: Starting The Apache HTTP Server…

Nov 03 19:35:13 node2 systemd[1]: Started The Apache HTTP Server.

Nov 03 19:35:13 node2 httpd[43695]: Server configured, listening on: port 80

Обработка сбоев с помощью блоков

Вы так­же може­те исполь­зо­вать бло­ки для обра­бот­ки оши­бок задач, исполь­зуя раз­де­лы rescue и always. Это очень похо­же на обра­бот­ку исклю­че­ний в язы­ках про­грам­ми­ро­ва­ния, таких как try-catch в Java или try-except в Python.

Вы може­те исполь­зо­вать раз­дел вос­ста­нов­ле­ния, что­бы вклю­чить все зада­чи, кото­рые вы хоти­те запу­стить в слу­чае сбоя одной или несколь­ких задач в блоке.

Для демон­стра­ции давай­те взгля­нем на сле­ду­ю­щий пример:

[codesyntax lang=»php»]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

tasks:

  — name: Handling error example

    block:

      — name: run a command

        command: uptime

      — name: run a bad command

        command: blabla

      — name: This task will not run

        debug:

          msg: «Я не запус­каю пото­му что выше­при­ве­ден­ная зада­ча провалилась.»

    rescue:

      — name: Runs when the block failed

        debug:

          msg: «Блок не удал­ся; давай­те попро­бу­ем испра­вить это здесь…»

[/codesyntax]

Обра­ти­те вни­ма­ние, как вто­рая зада­ча в бло­ке run a bad command гене­ри­ру­ет ошиб­ку, и, в свою оче­редь, тре­тья зада­ча в бло­ке нико­гда не полу­ча­ет воз­мож­но­сти запу­стить. Зада­чи внут­ри спа­са­тель­ной сек­ции будут рабо­тать , пото­му что вто­рая зада­ча в бло­ке не удалась.

Вы так­же може­те исполь­зо­вать ignore_errors: yes, что­бы гаран­ти­ро­вать, что Ansible про­дол­жит выпол­не­ние задач в playbook, даже если зада­ча не удалась:

[codesyntax lang=»php»]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

tasks:

  — name: Handling error example

    block:

      — name: run a command

        command: uptime

      — name: run a bad command

        command: blabla

        ignore_errors: yes

      — name: This task will run

        debug:

          msg: «Запуск пото­му что выше­ука­зан­ные ошиб­ки зада­чи были проигнорированы.»

    rescue:

      — name: This will not run

        debug:

          msg: «Ошиб­ки были про­игно­ри­ро­ва­ны! … не соби­ра­ет­ся запускаться.»

[/codesyntax]

Обра­ти­те вни­ма­ние, что в этом при­ме­ре вы про­игно­ри­ро­ва­ли ошиб­ки во вто­рой зада­че в бло­ке run a bad command, и поэто­му тре­тья зада­ча была запу­ще­на. Кро­ме того, спа­са­тель­ный раз­дел не будет рабо­тать , как вы игно­ри­ро­ва­ли ошиб­ку во вто­рой зада­че в блоке.

Вы так­же може­те доба­вить в блок раз­дел все­гда. Зада­чи в раз­де­ле все­гда будут выпол­нять­ся все­гда, неза­ви­си­мо от того, про­изо­шел сбой бло­ка или нет.

Что­бы про­де­мон­стри­ро­вать это, взгля­ни­те на сле­ду­ю­щую кни­гу вос­про­из­ве­де­ния handle-errors.yml, в кото­рой есть все три раз­де­ла бло­ка (block, rescue, always):

[codesyntax lang=»php»]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

— name: Handling Errors with Blocks

  hosts: node1

  tasks:

    — name: Handling Errors Example

      block:

        — name: run a command

          command: uptime

        — name: run a bad command

          command: blabla

        — name: This task will not run

          debug:

            msg: «Я не запус­ка­юсь пото­му что выше­при­ве­ден­ная зада­ча провалилась!»

      rescue:

        — name: Runs when the block fails

          debug:

            msg: «Блок про­ва­лил­ся! давай­те попро­бу­ем испра­вить это здесь…»

      always:

        — name: This will always run

          debug:

            msg: «Неза­ви­си­мо от того, про­ва­лил­ся блок или нет ... Я все­гда буду запус­кать­ся.!«

[/codesyntax]

Иди­те впе­ред и запу­сти­те playbook:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

[des@andrey]$ ansible-playbook handle-errors.yml

PLAY [Handling Errors with Blocks] *********************************************

TASK [Gathering Facts] *********************************************************

ok: [node1]

TASK [run a command] ***********************************************************

changed: [node1]

TASK [run a bad command] *******************************************************

fatal: [node1]: FAILED! =&gt; {«changed»: false, «cmd»: «blabla», «msg»: «[Errno 2] No such file or directory: b’blabla’: b’blabla'», «rc»: 2}

TASK [Runs when the block fails] ***********************************************

ok: [node1] =&gt; {

    «msg»: «Block failed! let’s try to fix it here …»

}

TASK [This will always run] ****************************************************

ok: [node1] =&gt; {

    «msg»: «Whether the block has failed or not … I will always run!»

}

PLAY RECAP *********************************************************************

node1                      : ok=4    changed=1    unreachable=0    failed=0    skipped=0

Как вы виде­те; спа­са­тель­ный раз­дел ли рабо­тать как 2 – й зада­ча в бло­ке про­изо­шел сбой, и вы не игно­ри­ро­вать ошиб­ки. Кро­ме того, все­гда выпол­нял­ся (и будет все­гда) раз­дел всегда.

Запуск задач при изменении с помощью обработчиков

Посмот­рим, как менять обра­бот­чи­ки и запус­кать задачи.

Запуск вашего первого обработчика

Вы може­те исполь­зо­вать обра­бот­чи­ки для запус­ка задач при изме­не­нии на ваших управ­ля­е­мых узлах. Что­бы про­де­мон­стри­ро­вать это, взгля­ни­те на сле­ду­ю­щий сбор­ник handler-example.yml:

[codesyntax lang=»php»]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

— name: Simple Handler Example

  hosts: node1

  tasks:

    — name: Create engineers group

      group:

        name: engineers

      notify: add destroyer

    — name: Another task in the play

      debug:

        msg: «I am just another task.»

  handlers:

    — name: add destroyer

      user:

        name: destroyer

        groups: engineers

        append: yes

[/codesyntax]

Пер­вая зада­ча Create engineers group­со­зда­ет груп­пу инже­не­ров, а так­же уве­дом­ля­ет обра­бот­чи­ка add destroyer.

Давай­те запу­стим сце­на­рий, что­бы уви­деть, что произойдет:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

[des@andrey]$ ansible-playbook handler-example.yml

PLAY [Simple Handler Example] **************************************************

TASK [Gathering Facts] *********************************************************

ok: [node1]

TASK [Create engineers group] **************************************************

changed: [node1]

TASK [Another task in the play] ************************************************

ok: [node1] =&gt; {

    «msg»: «I am just another task.»

}

RUNNING HANDLER [add destroyer] ***************************************************

changed: [node1]

PLAY RECAP *********************************************************************

node1                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0

Обра­ти­те вни­ма­ние, что созда­ние инже­не­ров вызва­ло изме­не­ние на node1 и в резуль­та­те запу­сти­ло add destroyerобработчик.

Вы так­же може­те запу­стить быст­рую спе­ци­аль­ную коман­ду, что­бы убе­дить­ся, что поль­зо­ва­тель destroyer дей­стви­тель­но явля­ет­ся чле­ном груп­пы инженеров:

[des@andrey]$ ansible node1 -m command -a «id destroyer»

node1 | CHANGED | rc=0 &gt;&gt;

uid=1000(destroyer) gid=1000(destroyer) groups=1000(destroyer),4(adm),190(systemd-journal),1004(engineers)

Плей­бу­ки и моду­ли Ansible явля­ют­ся идем­по­тент­ны­ми, что озна­ча­ет, что если на управ­ля­е­мых узлах про­изо­шло изме­не­ние кон­фи­гу­ра­ции; он боль­ше не повторится!

Что­бы пол­но­стью понять кон­цеп­цию идем­по­тент­но­сти Ansible; запу­сти­те playbook handler-example.yml еще раз:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

[des@andrey]$ ansible-playbook handler-example.yml

PLAY [Simple Handler Example] **************************************************

TASK [Gathering Facts] *********************************************************

ok: [node1]

TASK [Create engineers group] **************************************************

ok: [node1]

TASK [Another task in the play] ************************************************

ok: [node1] =&gt; {

    «msg»: «I am just another task.»

}

PLAY RECAP *********************************************************************

node1                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0

Как вы виде­те; на этот раз Create engineers group зада­ча не вызва­ла и не сооб­щи­ла об изме­не­нии, пото­му что груп­па инже­не­ров уже суще­ству­ет на node1 и, как резуль­тат; обра­бот­чик add destroyer не запустился.

Контроль, когда сообщать об изменении

Вы може­те исполь­зо­вать клю­че­вое сло­во changed_when, что­бы ука­зать, когда зада­ча долж­на сооб­щать об изме­не­нии. Что­бы про­де­мон­стри­ро­вать это, взгля­ни­те на сле­ду­ю­щий сце­на­рий control-change.yml:

[codesyntax lang=»php»]

— name: Control Change

  hosts: node1

  tasks:

    — name: Run the date command

      command: date

      notify: handler1

    — name: Run the uptime command

      command: uptime

  handlers:

     — name: handler1

       debug:

         msg: «I can handle dates»

[/codesyntax]

Обра­ти­те вни­ма­ние, как пер­вая зада­ча Run the date command­за­пус­ка­ет handler1. А теперь запу­сти­те playbook:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

[des@andrey]$ ansible-playbook control-change.yml

PLAY [Control Change] **********************************************************

TASK [Gathering Facts] *********************************************************

ok: [node1]

TASK [Run the date command] ****************************************************

changed: [node1]

TASK [Run the uptime command] **************************************************

changed: [node1]

RUNNING HANDLER [handler1] *****************************************************

ok: [node1] =&gt; {

    «msg»: «I can handle dates»

}

PLAY RECAP *********************************************************************

node1                      : ok=4    changed=2    unreachable=0    failed=0    skipped=0

Обе зада­чи Run the date command и Run the uptime command заре­ги­стри­ро­ван­ные изме­не­ния, и обра­бот­чик handler1 был запу­щен. Вы може­те утвер­ждать, что выпол­не­ние команд date и uptime на самом деле ниче­го не меня­ет на управ­ля­е­мом узле, и вы совер­шен­но правы!

Теперь давай­те отре­дак­ти­ру­ем playbook, что­бы зада­ча не сооб­ща­ла об изменениях:

[codesyntax lang=»php»]

— name: Control Change

  hosts: node1

  tasks:

    — name: Run the date command

      command: date

      notify: handler1

      changed_when: false

    — name: Run the uptime command

      command: uptime

  handlers:

     — name: handler1

       debug:

         msg: «I can handle dates»

[/codesyntax]

Теперь сно­ва запу­сти­те playbook:

[des@andrey]$ ansible-playbook control-change.yml

PLAY [Control Change] **********************************************************

TASK [Gathering Facts] *********************************************************

ok: [node1]

TASK [Run the date command] ****************************************************

ok: [node1]

TASK [Run the uptime command] **************************************************

changed: [node1]

PLAY RECAP *********************************************************************

node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0

Как види­те, на этот раз зада­ча не сооб­щи­ла об изме­не­нии, и в резуль­та­те handler1 не был запущен.

Настройка сервисов с обработчиками

Обра­бот­чи­ки осо­бен­но полез­ны, когда вы редак­ти­ру­е­те кон­фи­гу­ра­ции служб с помо­щью Ansible. Это пото­му, что вы хоти­те пере­за­пу­стить служ­бу толь­ко при изме­не­нии ее кон­фи­гу­ра­ции службы.

Что­бы про­де­мон­стри­ро­вать это, взгля­ни­те на сле­ду­ю­щую инструк­цию configure-ssh.yml :

[codesyntax lang=»php»]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

— name: Configure SSH 

  hosts: all

  tasks:

     — name: Edit SSH Configuration

       blockinfile:

         path: /etc/ssh/sshd_config

         block: |

            MaxAuthTries 4

            Banner /etc/motd

            X11Forwarding no

       notify: restart ssh

  handlers:

    — name: restart ssh

      service:

        name: sshd

        state: restarted

[/codesyntax]

Обра­ти­те вни­ма­ние, что мы исполь­зо­ва­ли модуль blockinfile для встав­ки несколь­ких строк тек­ста в файл кон­фи­гу­ра­ции /etc/ssh/sshd_config. Зада­ча так­же вызы­ва­ет обра­бот­чик restart ssh по изменению.

Иди­те впе­ред и запу­сти­те playbook:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

[des@andrey]$ ansible-playbook configure-ssh.yml

PLAY [Configure SSH] ***********************************************************

TASK [Gathering Facts] *********************************************************

ok: [node4]

ok: [node3]

ok: [node1]

ok: [node2]

TASK [Edit SSH Configuration] **************************************************

changed: [node4]

changed: [node2]

changed: [node3]

changed: [node1]

RUNNING HANDLER [restart ssh] **************************************************

changed: [node4]

changed: [node3]

changed: [node2]

changed: [node1]

PLAY RECAP *********************************************************************

node1                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    

node2                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    

node3                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0    

node4                      : ok=3    changed=2    unreachable=0    failed=0    skipped=0

Все хоро­шо! Теперь давай­те быст­ро взгля­нем на послед­ние несколь­ко строк в файле/etc/ssh/sshd_config:

[des@andrey]$ ansible node1 -m command -a «tail -5 /etc/ssh/sshd_config»

node1 | CHANGED | rc=0 &gt;&gt;

# BEGIN ANSIBLE MANAGED BLOCK

MaxAuthTries 4

Banner /etc/motd

X11Forwarding no

# END ANSIBLE MANAGED BLOCK

Уди­ви­тель­но! Точ­но так, как вы ожи­да­ли. Имей­те в виду, что если вы повтор­но запу­сти­те playbook configure-ssh.yml, Ansible не будет редак­ти­ро­вать (или добав­лять) файл /etc/ssh/sshd_config. Вы може­те попро­бо­вать это сами.

Мы так­же реко­мен­ду­ем вам взгля­нуть на стра­ни­цы доку­мен­та­ции blockinfile и lineinfile, что­бы понять раз­ли­чия и исполь­зо­ва­ние каж­до­го модуля:

[des@andrey]$ ansible-doc blockinfile

[des@andrey]$ ansible-doc lineinfile

Topics

  • Error Handling In Playbooks

    • Ignoring Failed Commands

    • Resetting Unreachable Hosts

    • Handlers and Failure

    • Controlling What Defines Failure

    • Overriding The Changed Result

    • Aborting the play

    • Using blocks

Ansible normally has defaults that make sure to check the return codes of commands and modules and
it fails fast – forcing an error to be dealt with unless you decide otherwise.

Sometimes a command that returns different than 0 isn’t an error. Sometimes a command might not always
need to report that it ‘changed’ the remote system. This section describes how to change
the default behavior of Ansible for certain tasks so output and error handling behavior is
as desired.

Ignoring Failed Commands¶

Generally playbooks will stop executing any more steps on a host that has a task fail.
Sometimes, though, you want to continue on. To do so, write a task that looks like this:

- name: this will not be counted as a failure
  command: /bin/false
  ignore_errors: yes

Note that the above system only governs the return value of failure of the particular task,
so if you have an undefined variable used or a syntax error, it will still raise an error that users will need to address.
Note that this will not prevent failures on connection or execution issues.
This feature only works when the task must be able to run and return a value of ‘failed’.

Resetting Unreachable Hosts¶

New in version 2.2.

Connection failures set hosts as ‘UNREACHABLE’, which will remove them from the list of active hosts for the run.
To recover from these issues you can use meta: clear_host_errors to have all currently flagged hosts reactivated,
so subsequent tasks can try to use them again.

Handlers and Failure¶

When a task fails on a host, handlers which were previously notified
will not be run on that host. This can lead to cases where an unrelated failure
can leave a host in an unexpected state. For example, a task could update
a configuration file and notify a handler to restart some service. If a
task later on in the same play fails, the service will not be restarted despite
the configuration change.

You can change this behavior with the --force-handlers command-line option,
or by including force_handlers: True in a play, or force_handlers = True
in ansible.cfg. When handlers are forced, they will run when notified even
if a task fails on that host. (Note that certain errors could still prevent
the handler from running, such as a host becoming unreachable.)

Controlling What Defines Failure¶

Ansible lets you define what “failure” means in each task using the failed_when conditional. As with all conditionals in Ansible, lists of multiple failed_when conditions are joined with an implicit and, meaning the task only fails when all conditions are met. If you want to trigger a failure when any of the conditions is met, you must define the conditions in a string with an explicit or operator.

You may check for failure by searching for a word or phrase in the output of a command:

- name: Fail task when the command error output prints FAILED
  command: /usr/bin/example-command -x -y -z
  register: command_result
  failed_when: "'FAILED' in command_result.stderr"

or based on the return code:

- name: Fail task when both files are identical
  raw: diff foo/file1 bar/file2
  register: diff_cmd
  failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2

In previous version of Ansible, this can still be accomplished as follows:

- name: this command prints FAILED when it fails
  command: /usr/bin/example-command -x -y -z
  register: command_result
  ignore_errors: True

- name: fail the play if the previous command did not succeed
  fail:
    msg: "the command failed"
  when: "'FAILED' in command_result.stderr"

You can also combine multiple conditions for failure. This task will fail if both conditions are true:

- name: Check if a file exists in temp and fail task if it does
  command: ls /tmp/this_should_not_be_here
  register: result
  failed_when:
    - result.rc == 0
    - '"No such" not in result.stdout'

If you want the task to fail when only one condition is satisfied, change the failed_when definition to:

failed_when: result.rc == 0 or "No such" not in result.stdout

If you have too many conditions to fit neatly into one line, you can split it into a multi-line yaml value with >:

- name: example of many failed_when conditions with OR
  shell: "./myBinary"
  register: ret
  failed_when: >
    ("No such file or directory" in ret.stdout) or
    (ret.stderr != '') or
    (ret.rc == 10)

Overriding The Changed Result¶

When a shell/command or other module runs it will typically report
“changed” status based on whether it thinks it affected machine state.

Sometimes you will know, based on the return code
or output that it did not make any changes, and wish to override
the “changed” result such that it does not appear in report output or
does not cause handlers to fire:

tasks:

  - shell: /usr/bin/billybass --mode="take me to the river"
    register: bass_result
    changed_when: "bass_result.rc != 2"

  # this will never report 'changed' status
  - shell: wall 'beep'
    changed_when: False

You can also combine multiple conditions to override “changed” result:

- command: /bin/fake_command
  register: result
  ignore_errors: True
  changed_when:
    - '"ERROR" in result.stderr'
    - result.rc == 2

Aborting the play¶

Sometimes it’s desirable to abort the entire play on failure, not just skip remaining tasks for a host.

The any_errors_fatal option will end the play and prevent any subsequent plays from running. When an error is encountered, all hosts in the current batch are given the opportunity to finish the fatal task and then the execution of the play stops. any_errors_fatal can be set at the play or block level:

- hosts: somehosts
  any_errors_fatal: true
  roles:
    - myrole

- hosts: somehosts
  tasks:
    - block:
        - include_tasks: mytasks.yml
      any_errors_fatal: true

for finer-grained control max_fail_percentage can be used to abort the run after a given percentage of hosts has failed.

Using blocks¶

Most of what you can apply to a single task (with the exception of loops) can be applied at the Blocks level, which also makes it much easier to set data or directives common to the tasks.
Blocks also introduce the ability to handle errors in a way similar to exceptions in most programming languages.
Blocks only deal with ‘failed’ status of a task. A bad task definition or an unreachable host are not ‘rescuable’ errors:

tasks:
- name: Handle the error
  block:
    - debug:
        msg: 'I execute normally'
    - name: i force a failure
      command: /bin/false
    - debug:
        msg: 'I never execute, due to the above task failing, :-('
  rescue:
    - debug:
        msg: 'I caught an error, can do stuff here to fix it, :-)'

This will ‘revert’ the failed status of the outer block task for the run and the play will continue as if it had succeeded.
See Blocks error handling for more examples.

Ansible Error handling – In this lesson, you will learn the different ways of how to handle failures in Ansible tasks by using the ignore_errors, force_handlers, Ansible blocks, Ansible rescue, and Ansible always directives in a playbook.

Contents

  • How Can You Handle Error In Ansible
  • Specifying Task Failure Conditions
  • Managing Changed Status
  • Using Ansible Blocks
  • Using Ansible Blocks With Rescue and Always Statement

How Can Error Handling Be Done In Ansible

Ansible plays and tasks are executed in the order they are defined in a playbook, and by default, if a task fails, the other tasks will not be executed in that order. However, this behavior can be changed with the use of a keyword called “ignore_errors: true“.

This keyword can be added to a play or a task as the case may be. If it is added to a play, it means that all the errors in the tasks associated to a play will be ignored. More so, if it is added to a task, it means all the errors in the task will be ignored.

Well, we learnt about handlers in one of our previous lessons, what about handlers? Yes, this is also applicable to handlers, handlers error can be handled by the keyword, “force_handlers:yes”.

If a task that is supposed to notify a handler fails, the handlers will not be executed. This behavior can also be changed by using the keyword, “force_handler: yes” .

As usual, let’s understand better with examples.

If we were to install the httpd package and the autofs package using a playbook, the name argument will consist of the “httpd” value and the “autofs” value.

Now, we are going to write a playbook, and we will intentionally make an error by making the name argument of autofs containing “autos“.

1. Create a playbook

[lisa@drdev1 ~]$ vim playbook7.yml
- name: Install basic package
  hosts: hqdev1.tekneed.com
  tasks:
     - name: install autofs
       yum:
         name: autos
         state: present
       ignore_errors: true

     - name: install httpd
       yum:
         name: httpd
         state: present

2. Run the playbook

[lisa@drdev1 ~]$ ansible-playbook playbook7.yml

PLAY [Install basic package] ***********************************************************
........

ansible error handling

This playbook we run should have resulted in an error or task failure, and the second task shouldn’t have run because the first task failed, but because we used the “ignore_errors” keyword, the task did not fail and the play run.

Also, remember that you can choose to use the “ignore_errors” keyword at a play level or at a task level. In our case, it was used at the task level.

Again, let’s see an example of how we can use the force_handlers keyword to forcefully run a task with handlers.

1. create a playbook

- name: restarting httpd using handlers
  hosts: hqdev1.tekneed.com
  force_handlers: yes
  tasks:
    - name: restart httpd
      service:
        name: httpd
        state: restarted
      notify: restart httpd

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

The force_handler directive will always force handlers to run whether it is called or not. Note that the keyword can only be used at a play level

2. Run the playbook

[lisa@drdev1 ~]$ ansible-playbook playbook3.yml

PLAY [restarting httpd using handlers] ********************************
......

Because the force_handlers directive is set to yes, the handler will always run.

Specifying Task Failure conditions

Ansible may run a task/command successfully, however may be a failure due to the final result a user desires to get. In this sense, one can specify a condition for tasks to fail or in other words, you are at liberty to determine what a failure is.

Let’s see how this can be done by using the “failed_when” directive. The failed_when directive, from its word, simply means the task should fail when a condition is met or not met.

Let’s use the playbook below as an example,

[lisa@drdev1 ~]$ vim playbook5.yml
- name: Web page fetcher
  hosts: hqdev1.tekneed.com
  tasks:
    - name: connect to website
      uri:
        url: https://tekneed.com
        return_content: true
      register: output

    - name: verify content
      debug:
        msg: "verifying content"
      failed_when:
        - '"this content" not in output.content'
        - '"other content" not in output.content'

This is what this playbook will do. The uri module will interact with the webserver and fetch the page, https://www.tekneed.com.
More so, With the true value for the return_content argument, the body of the response of https://tekneed.com will be returned as content, and output will be captured with the aid of the register directive.

For the second task, the content “verifying content” will be printed by the debug module with the aid of the msg argument, and with the “failed_when” directive, the task will fail when the string, “this content” and “other content” is not in the captured output.

Run the playbook

[lisa@drdev1 ~]$ ansible-playbook playbook5.yml

PLAY [Web page fetcher] ************************************************************
.......

Alternatively, the “fail” module can be used to specify when a task fails. This module can only be very useful if the “when” keyword is used to specify the exact failure condition.

Let’s use the same example we used above, but this time around use the “fail” module and “when” keyword

[lisa@drdev1 ~]$ vim playbook5.yml
- name: Web page fetcher
  hosts: hqdev1.tekneed.com
  tasks:
    - name: connect to website
      uri:
        url: https://tekneed.com
        return_content: true
      register: output

    - name: verify content
      fail:
        msg: "verifying content"
      when:
        - '"this content" not in output.content'
        - '"other content" not in output.content'

Run the playbook

[lisa@drdev1 ~]$ ansible-playbook playbook5.yml

PLAY [Web page fetcher] ************************************************************
.......

Managing Changed Status

Managing a changed status can be useful in avoiding unexpected results while running a playbook. Some tasks may even report a changed status and nothing in the real sense has really changed. It could just be that information was retrieved.

In some cases, you may not want a task to result in a changed status. In this case, one needs to use the “changed_when: false” keyword. The playbook will only report “ok” or “failed” status, and will never report a changed status.

An example of such task can be seen below.

- name: copy in nginx conf
  template: src=nginx.conf.j2
            dest=/etc/nginx/nginx.conf

- name: validate nginx conf
  command: nginx -t
  changed_when: false

One can also specify a condition when a task should change, an example of such task is seen in the playbook below.

- name: Web page fetcher
  hosts: hqdev1.tekneed.com
  tasks:
    - name: connect to website
      uri:
        url: https://tekneed.com
        return_content: true
      register: output

    - name: verify content
      fail:
        msg: "verifying content"
      changed_when: "'success' in output.stdout"

Using Ansible Blocks

Blocks are used to group tasks, specific tasks that are related, and can be very useful with a conditional statement.

If tasks are grouped conditionally, and the conditions is/are true, all the tasks will be executed. You should also know that block is a directive in Ansible and not a module, hence the block directive and the when directive will be at the same indentation level.

Let’s see how blocks can be used with examples.

create a playbook

[lisa@drdev1 ~]$ vim playbook8.yml
- name: setting up httpd
  hosts: localhost
  tasks:
    - name: Install start and enable httpd
      block:
      - name: install httpd
        yum:
          name: httpd
          state: present
      - name: start and enable httpd
        service:
          name: httpd
          state: started
          enabled: true
      when: ansible_distribution == "Red Hat"

This playbook will group only two tasks in a block(install and enable httpd). The first task name is “install httpd” while the second task name is “start and enable httpd“. These tasks will only execute if the condition is true, which is, the OS ansible will execute against is/are Red Hat.

Run the playbook

[lisa@drdev1 ~]$ ansible-playbook playbook8.yml

PLAY [setting up httpd] ********************************************************
.......

With this kind of playbook, if the condition fails, other tasks will not be executed. Let’s see how we can use block with rescue and always if we don’t want this type of condition.

Using Ansible block With rescue and always Statement

Apart from blocks being used to group different tasks, they can also be used specifically for error handling with the rescue keyword.

It works in a way that; if a task that is defined in a block fails, the tasks defined in the rescue section will be executed. This is also similar to the ignore_errors keyword.

More so, there is also an always section, this section will always run either the task fails or not. This is also similar to the ignore_errors keyword.

Let’s understand better with examples.

create a playbook.

[lisa@drdev1 ~]$ vim playbook9.yml
- name: setting up httpd
  hosts: localhost
  tasks:
    - name: Install the latest httpd and restart
      block:
        - name: install httpd
          yum:
            name: htt
            state: latest
      rescue:
        - name: restart httpd
          service:
            name: httpd
            state: started
        - name: Install autofs
          yum:
            name: autofs
      always:
        - name: restart autofs
          service:
            name: autofs
            state: started

This playbook will group four tasks in a block(install the latest httpd and restart).
The first task in the block section will fail because the name of the package is incorrect. However, the second and third tasks will run because they are in the rescue section. More so, the fourth task will run because it is in the always section.

Note that you can have as many tasks you want in the block section, rescue section or always section

Run the playbook

[lisa@drdev1 ~]$ ansible-playbook playbook9.yml

PLAY [setting up httpd] *********************************************************
......

Class Activity

create a playbook that contains 1 play with tasks using block, always and rescue statements

If you like this article, you can support us by

1. sharing this article.

2. Buying the article writer a coffee (click here to buy a coffee)

3. Donating to push our project to the next level. (click here to donate)

If you need personal training, send an email to info@tekneed.com

Click To Watch Video On Ansible Error Handling

RHCE EX294 Exam Practice Question On Ansible Error Handling

Suggested: Managing Layered Storage With Stratis – Video

Your feedback is welcomed. If you love others, you will share with others

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Another version of this product is already installed как исправить
  • Another launcher is already running как исправить
  • Another instance of this program is already running error
  • Another instance is already running как исправить ошибку
  • Another instance is already running euro truck simulator 2 как исправить

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии