您的位置:首页 > 科技 > 能源 > 山西建设工程集团有限公司_深圳猎头公司_99个创意营销方案_网站页面怎么优化

山西建设工程集团有限公司_深圳猎头公司_99个创意营销方案_网站页面怎么优化

2024/12/23 15:46:54 来源:https://blog.csdn.net/peanutfish/article/details/142665144  浏览:    关键词:山西建设工程集团有限公司_深圳猎头公司_99个创意营销方案_网站页面怎么优化
山西建设工程集团有限公司_深圳猎头公司_99个创意营销方案_网站页面怎么优化

ONLY FOR SELF STUDY, NO COMMERCIAL USAGE!!!


Chapter 4. Implementing Task Control

Writing Loops and Conditional Tasks

Task Iteration with Loops

Using loops makes it possible to avoid writing multiple tasks that use the same module.

To iterate a task over a set of items, you can use the loop keyword. You can configure loops to repeat a task using each item in a list, the contents of each of the files in a list, a generated sequence of numbers, or using more complicated structures.

Simple Loops

A simple loop iterates a task over a list of items. The loop keyword is added to the task, and takes as a value the list of items over which the task should be iterated. The loop variable item holds the value used during each iteration.

Consider the following snippet that uses the ansible.builtin.service module twice to ensure that two network services are running:

- name: Postfix is runningansible.builtin.service:name: postfixstate: started- name: Dovecot is runningansible.builtin.service:name: dovecotstate: started

These two tasks can be rewritten to use a simple loop so that only one task is needed to ensure that both services are running:

- name: Postfix and Dovecot are runningansible.builtin.service:name: "{{ item }}"state: startedloop:- postfix- dovecot

The loop can use a list provided by a variable.

In the following example, the mail_services variable contains the list of services that need to be running.

vars:mail_services:- postfix- dovecottasks:- name: Postfix and Dovecot are runningansible.builtin.service:name: "{{ item }}"state: startedloop: "{{ mail_services }}"
Loops over a List of Dictionaries

The loop list does not need to be a list of simple values.

In the following example, each item in the list is actually a dictionary. Each dictionary in the example has two keys, name and groups, and the value of each key in the current item loop variable can be retrieved with the item['name'] and item['groups'] variables, respectively.

- name: Users exist and are in the correct groupsuser:name: "{{ item['name'] }}"state: presentgroups: "{{ item['groups'] }}"loop:- name: janegroups: wheel- name: joegroups: root

The outcome of the preceding task is that the user jane is present and a member of the group wheel, and that the user joe is present and a member of the group root.

Earlier-style Loop Keywords

Before Ansible 2.5, most playbooks used a different syntax for loops. Multiple loop keywords were provided, which used the with_ prefix, followed by the name of an Ansible look-up plug-in (an advanced feature not covered in detail in this course). This syntax for looping is very common in existing playbooks, but will probably be deprecated at some point in the future.

Some examples are listed in the following table:

Table 4.1. Earlier-style Ansible Loops

Loop keywordDescription
with_itemsBehaves the same as the loop keyword for simple lists, such as a list of strings or a list of dictionaries. Unlike loop, if lists of lists are provided to with_items, they are flattened into a single-level list. The item loop variable holds the list item used during each iteration.
with_fileRequires a list of control node file names. The item loop variable holds the content of a corresponding file from the file list during each iteration.
with_sequenceRequires parameters to generate a list of values based on a numeric sequence. The item loop variable holds the value of one of the generated items in the generated sequence during each iteration.

The following playbook shows an example of the with_items keyword:

  vars:data:- user0- user1- user2tasks:- name: "with_items"ansible.builtin.debug:msg: "{{ item }}"with_items: "{{ data }}"

Important note:

Since Ansible 2.5, the recommended way to write loops is to use the loop keyword.

However, you should still understand the earlier syntax, especially with_items, because it is widely used in existing playbooks. You are likely to encounter playbooks and roles that continue to use with_* keywords for looping.

The Ansible documentation contains a good reference on how to convert the earlier loops to the new syntax, as well as examples of how to loop over items that are not simple lists. See the “Migrating from with_X to loop” section of the Ansible User Guide.

Using Register Variables with Loops

The register keyword can also capture the output of a task that loops. The following snippet shows the structure of the register variable from a task that loops:

[student@workstation loopdemo]$ cat loop_register.yml
---
- name: Loop Register Testgather_facts: falsehosts: localhosttasks:- name: Looping Echo Taskansible.builtin.shell: "echo This is my item: {{ item }}"loop:- one- tworegister: echo_results- name: Show echo_results variableansible.builtin.debug:var: echo_results

Running the preceding playbook yields the following output:

[student@workstation loopdemo]$ ansible-navigator run -m stdout loop_register.ymlPLAY [Loop Register Test] ******************************************************TASK [Looping Echo Task] *******************************************************
changed: [localhost] => (item=one)
changed: [localhost] => (item=two)TASK [Show echo_results variable] **********************************************
ok: [localhost] => {"echo_results": {"changed": true,"msg": "All items completed","results": [{"ansible_loop_var": "item","changed": true,"cmd": "echo This is my item: one","delta": "0:00:00.004519","end": "2022-06-29 17:32:54.065165","failed": false,...output omitted..."item": "one","msg": "","rc": 0,"start": "2022-06-29 17:32:54.060646","stderr": "","stderr_lines": [],"stdout": "This is my item: one","stdout_lines": ["This is my item: one"]},{"ansible_loop_var": "item","changed": true,"cmd": "echo This is my item: two","delta": "0:00:00.004175","end": "2022-06-29 17:32:54.296940","failed": false,...output omitted..."item": "two","msg": "","rc": 0,"start": "2022-06-29 17:32:54.292765","stderr": "","stderr_lines": [],"stdout": "This is my item: two","stdout_lines": ["This is my item: two"]}],"skipped": false}
}
...output omitted...

In the preceding example, the results key contains a list. In the next example, the playbook is modified so that the second task iterates over this list:

# new_loop_register.yml
---
- name: Loop Register Testgather_facts: falsehosts: localhosttasks:- name: Looping Echo Taskansible.builtin.shell: "echo This is my item: {{ item }}"loop:- one- tworegister: echo_results- name: Show stdout from the previous task.ansible.builtin.debug:msg: "STDOUT from previous task: {{ item['stdout'] }}"loop: "{{ echo_results['results'] }}"

After running the preceding playbook, you see the following output:

PLAY [Loop Register Test] ******************************************************TASK [Looping Echo Task] *******************************************************
changed: [localhost] => (item=one)
changed: [localhost] => (item=two)TASK [Show stdout from the previous task.] *************************************
ok: [localhost] => (item={'changed': True, 'stdout': 'This is my item: one', 'stderr': '', 'rc': 0, 'cmd': 'echo This is my item: one', 'start': '2022-06-29 17:41:15.558529', 'end': '2022-06-29 17:41:15.563615', 'delta': '0:00:00.005086', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'echo This is my item: one', '_uses_shell': True, 'warn': False, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['This is my item: one'], 'stderr_lines': [], 'failed': False, 'item': 'one', 'ansible_loop_var': 'item'}) => {"msg": "STDOUT from previous task: This is my item: one"
}
ok: [localhost] => (item={'changed': True, 'stdout': 'This is my item: two', 'stderr': '', 'rc': 0, 'cmd': 'echo This is my item: two', 'start': '2022-06-29 17:41:15.810566', 'end': '2022-06-29 17:41:15.814932', 'delta': '0:00:00.004366', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'echo This is my item: two', '_uses_shell': True, 'warn': False, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['This is my item: two'], 'stderr_lines': [], 'failed': False, 'item': 'two', 'ansible_loop_var': 'item'}) => {"msg": "STDOUT from previous task: This is my item: two"
}
...output omitted...
Running Tasks Conditionally

Ansible can use conditionals to run tasks or plays when certain conditions are met.

The following scenarios illustrate the use of conditionals in Ansible.

  • Define a hard limit in a variable (for example, min_memory) and compare it against the available memory on a managed host.
  • Capture the output of a command and evaluate it to determine whether a task completed before taking further action. For example, if a program fails, then a batch is skipped.
  • Use Ansible facts to determine the managed host network configuration and decide which template file to send (for example, network bonding or trunking).
  • Evaluate the number of CPUs to determine how to properly tune a web server.
  • Compare a registered variable with a predefined variable to determine if a service changed. For example, test the MD5 checksum of a service configuration file to see if the service is changed.
Conditional Task Syntax

The when statement is used to run a task conditionally. It takes as a value the condition to test. If the condition is met, the task runs. If the condition is not met, the task is skipped.

One of the simplest conditions that can be tested is whether a Boolean variable is true or false. The when statement in the following example causes the task to run only if run_my_task is true.

---
- name: Simple Boolean Task Demohosts: allvars:run_my_task: truetasks:- name: httpd package is installedansible.builtin.dnf:name: httpdwhen: run_my_task

Note:

Boolean variables can have the value true or false.

In Ansible content, you can express those values in other ways(NTOE: the newest YAML only allow to use true/false for boolean ):

  • True, yes, or 1

  • False, no, or 0

Starting with Ansible Core 2.12, strings are always treated by when conditionals as true Booleans if they contain any content.

Therefore, if the run_my_task variable in the preceding example were written as shown in the following example then it would be treated as a string with content and have the Boolean value true, and the task would run. This is probably not the behavior that you want.

  run_my_task: "false"

If it had been written as shown in the next example, however, it would be treated as the Boolean value false and the task would not run:

  run_my_task: false

To ensure that this is the case, you could rewrite the previous when condition to convert an accidental string value to a Boolean and to pass Boolean values unchanged:

      when: run_my_task | bool

The next example is a bit more sophisticated, and tests whether the my_service variable has a value. If it does, the value of my_service is used as the name of the package to install. If the my_service variable is not defined, then the task is skipped without an error.

---
- name: Test Variable is Defined Demohosts: allvars:my_service: httpdtasks:- name: "{{ my_service }} package is installed"ansible.builtin.dnf:name: "{{ my_service }}"when: my_service is defined

The following table shows some operations that you can use when working with conditionals:

Table 4.2. Example Conditionals

OperationExample
Equal (value is a string)ansible_facts['machine'] == "x86_64"
Equal (value is numeric)max_memory == 512
Less thanmin_memory < 128
Greater thanmin_memory > 256
Less than or equal tomin_memory <= 256
Greater than or equal tomin_memory >= 512
Not equal tomin_memory != 512
Variable existsmin_memory is defined
Variable does not existmin_memory is not defined
Boolean variable is true. The values of 1, True, or yes evaluate to true.memory_available
Boolean variable is false. The values of 0, False, or no evaluate to false.not memory_available
First variable’s value is present as a value in second variable’s listansible_facts['distribution'] in supported_distros

The last entry in the preceding table might be confusing at first. The following example illustrates how it works.

In the example, the ansible_facts['distribution'] variable is a fact determined during the Gathering Facts task, and identifies the managed host’s operating system distribution. The supported_distros variable was created by the playbook author, and contains a list of operating system distributions that the playbook supports. If the value of ansible_facts['distribution'] is in the supported_distros list, the conditional passes and the task runs.

---
- name: Demonstrate the "in" keywordhosts: allgather_facts: truevars:supported_distros:- RedHat- Fedoratasks:- name: Install httpd using dnf, where supportedansible.builtin.dnf:name: httpstate: presentwhen: ansible_facts['distribution'] in supported_distros

Importantyaml

Observe the indentation of the when statement. Because the when statement is not a module variable, it must be placed outside the module by being indented at the top level of the task.

Testing Multiple Conditions

One when statement can be used to evaluate multiple conditionals. To do so, conditionals can be combined with either the and or or keywords, and grouped with parentheses.

The following snippets show some examples of how to express multiple conditions.

  • If a conditional statement should be met when either condition is true, then use the or statement. For example, the following condition is met if the machine is running either Red Hat Enterprise Linux or Fedora:

    when: ansible_facts['distribution'] == "RedHat" or ansible_facts['distribution'] == "Fedora"
    
  • With the and operation, both conditions have to be true for the entire conditional statement to be met. For example, the following condition is met if the remote host is a Red Hat Enterprise Linux 9.0 host, and the installed kernel is the specified version:

    when: ansible_facts['distribution_version'] == "9.0" and ansible_facts['kernel'] == "5.14.0-70.13.1.el9_0.x86_64"
    

    The when keyword also supports using a list to describe a list of conditions. When a list is provided to the when keyword, all the conditionals are combined using the and operation. The example below demonstrates another way to combine multiple conditional statements using the and operator:

    when:- ansible_facts['distribution_version'] == "9.0"- ansible_facts['kernel'] == "5.14.0-70.13.1.el9_0.x86_64"
    

    This format improves readability, a key goal of well-written Ansible Playbooks.

  • You can express more complex conditional statements by grouping conditions with parentheses. This ensures that they are correctly interpreted.

    For example, the following conditional statement is met if the machine is running either Red Hat Enterprise Linux 9 or Fedora 34. This example uses the greater-than character (>) so that the long conditional can be split over multiple lines in the playbook, to make it easier to read.

    when: >( ansible_facts['distribution'] == "RedHat" andansible_facts['distribution_major_version'] == "9" )or( ansible_facts['distribution'] == "Fedora" andansible_facts['distribution_major_version'] == "34" )
    
Combining Loops and Conditional Tasks

You can combine loops and conditionals.

In the following example, the ansible.builtin.dnf module installs the mariadb-server package if there is a file system mounted on / with more than 300 MiB free. The ansible_facts['mounts'] fact is a list of dictionaries, each one representing facts about one mounted file system. The loop iterates over each dictionary in the list, and the conditional statement is not met unless a dictionary is found that represents a mounted file system where both conditions are true.

- name: install mariadb-server if enough space on rootansible.builtin.dnf:name: mariadb-serverstate: latestloop: "{{ ansible_facts['mounts'] }}"when: item['mount'] == "/" and item['size_available'] > 300000000

Important

When you use when with loop for a task, the when statement is checked for each item.

The following example also combines conditionals and register variables. This playbook restarts the httpd service only if the postfix service is running:

---
- name: Restart HTTPD if Postfix is Runninghosts: alltasks:- name: Get Postfix server statusansible.builtin.command: /usr/bin/systemctl is-active postfix register: result- name: Restart Apache HTTPD based on Postfix statusansible.builtin.service:name: httpdstate: restartedwhen: result.rc == 0
References

Loops — Ansible Documentation

Tests — Ansible Documentation

Conditionals — Ansible Documentation

What Makes A Valid Variable Name — Variables — Ansible Documentation

For more information on the change to Boolean handling in conditionals in community Ansible 5 (and Ansible Core 2.12) and later, see https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_5.html#deprecated

Example
# playbook.yml 
# - The first task installs the MariaDB required packages only when hosts are Redhat, and the second task ensures that the MariaDB service is running.---
- name: Test example for control flowhosts: database_prodvars:mariadb_packages:- mariadb-server- python3-PyMySQLtasks:- name: Installing MariaDBansible.builtin.dnf:name: "{{ item }}"state: presentloop: "{{ mariadb_packages }}"when: ansible_facts['distribution'] == "RedHat"- name: Starting MariaDBansible.builtin.service:name: mariadbstate: startedenabled: true

Implementing Handlers

Ansible Handlers

Handlers are tasks that respond to a notification triggered by other tasks. Tasks only notify their handlers when the task changes something on a managed host. Each handler is triggered by its name after the play’s block of tasks.

If no task notifies the handler by name then the handler does not run. If one or more tasks notify the handler, the handler runs once after all other tasks in the play have completed. Because handlers are tasks, administrators can use the same modules in handlers that they would use for any other task.

Normally, handlers are used to reboot hosts and restart services.

Important:

Always use unique names for your handlers. You might have unexpected results if more than one handler uses the same name.

Handlers can be considered as inactive tasks that only get triggered when explicitly invoked using a notify statement. The following snippet shows how the Apache server is only restarted by the restart apache handler when a configuration file is updated and notifies it:

tasks:- name: copy demo.example.conf configuration templateansible.builtin.template:src: /var/lib/templates/demo.example.conf.templatedest: /etc/httpd/conf.d/demo.example.confnotify:- restart apachehandlers:- name: restart apacheansible.builtin.service:name: httpdstate: restarted

In the previous example, the restart apache handler is triggered when notified by the template task that a change happened. A task might call more than one handler in its notify section. Ansible treats the notify statement as an array and iterates over the handler names:

tasks:- name: copy demo.example.conf configuration templateansible.builtin.template:src: /var/lib/templates/demo.example.conf.templatedest: /etc/httpd/conf.d/demo.example.confnotify:- restart mysql- restart apachehandlers:- name: restart mysqlansible.builtin.service:name: mariadbstate: restarted- name: restart apacheansible.builtin.service:name: httpdstate: restarted
Describing the Benefits of Using Handlers

As discussed in the Ansible documentation, there are some important things to remember about using handlers:

  • Handlers always run in the order specified by the handlers section of the play. They do not run in the order in which they are listed by notify statements in a task, or in the order in which tasks notify them.
  • Handlers normally run after all other tasks in the play complete. A handler called by a task in the tasks part of the playbook does not run until all tasks under tasks have been processed. (Some minor exceptions to this exist.)
  • Handler names exist in a per-play namespace. If two handlers are incorrectly given the same name, only one of them runs.
  • Even if more than one task notifies a handler, the handler runs one time. If no tasks notify it, the handler does not run.
  • If a task that includes a notify statement does not report a changed result (for example, a package is already installed and the task reports ok), the handler is not notified. Ansible notifies handlers only if the task reports the changed status.

Important:

Handlers are meant to perform an extra action when a task makes a change to a managed host. They should not be used as a replacement for normal tasks.

References

Handlers: running operations on change — Ansible Documentation

Example
# Handlers example: the configure_webapp.yml playbook file. This playbook installs and configures a web application server. When the web application server configuration changes, the playbook triggers a restart of the appropriate service.---
- name: Web application server is deployedhosts: webappvars:packages:- nginx- php-fpm- firewalldweb_service: nginxapp_service: php-fpmfirewall_service: firewalldfirewall_service_rules:- httpweb_config_src: files/nginx.conf.standardweb_config_dst: /etc/nginx/nginx.confapp_config_src: files/php-fpm.conf.standardapp_config_dst: /etc/php-fpm.conftasks:- name: Installing Web application serveransible.builtin.dnf:name: "{{ item }}"state: presentloop: "{{ packages }}"- name: Starting servcices...ansible.builtin.service:name: "{{ item }}"state: startedenabled: trueloop: - "{{ web_service }}"- "{{ app_service }}"- "{{ firewall_service }}"- name: Going through firewallansible.posix.firewalld:service: "{{ item }}"permanent: trueimmediate: truestate: enabledloop: "{{ firewall_service_rules }}"- name: Downlaoding config files and restart web servicesansible.builtin.copy:src: "{{ web_config_src }}"dest: "{{ web_config_dst }}"mode: "0644"notify:- restart web service- name: Downlaoding config files and restart web servicesansible.builtin.copy:src: "{{ app_config_src }}"dest: "{{ app_config_dst }}"mode: "0644"notify:- restart app servicehandlers:- name: restart web serviceansible.builtin.service:name: "{{ web_service }}" state: restarted- name: restart app serviceansible.builtin.service:name: "{{ app_service }}" state: restarted

Results:

# First RUN:
[student@workstation control-handlers]$ ansible-navigator run -m stdout configure_webapp.ymlPLAY [Web application server is deployed] **************************************TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]TASK [Installing Web application server] ***************************************
ok: [servera.lab.example.com] => (item=nginx)
ok: [servera.lab.example.com] => (item=php-fpm)
ok: [servera.lab.example.com] => (item=firewalld)TASK [Starting servcices...] ***************************************************
ok: [servera.lab.example.com] => (item=nginx)
ok: [servera.lab.example.com] => (item=php-fpm)
ok: [servera.lab.example.com] => (item=firewalld)TASK [Going through firewall] **************************************************
ok: [servera.lab.example.com] => (item=http)TASK [Downlaoding config files and restart web services] ***********************
changed: [servera.lab.example.com]TASK [Downlaoding config files and restart web services] ***********************
changed: [servera.lab.example.com]RUNNING HANDLER [restart web service] ******************************************
changed: [servera.lab.example.com]RUNNING HANDLER [restart app service] ******************************************
changed: [servera.lab.example.com]PLAY RECAP *********************************************************************
servera.lab.example.com    : ok=8    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   #Second RUN: (No handler running as no files changed during 2nd time run)
[student@workstation control-handlers]$ ansible-navigator run -m stdout configure_webapp.ymlPLAY [Web application server is deployed] **************************************TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]TASK [Installing Web application server] ***************************************
ok: [servera.lab.example.com] => (item=nginx)
ok: [servera.lab.example.com] => (item=php-fpm)
ok: [servera.lab.example.com] => (item=firewalld)TASK [Starting servcices...] ***************************************************
ok: [servera.lab.example.com] => (item=nginx)
ok: [servera.lab.example.com] => (item=php-fpm)
ok: [servera.lab.example.com] => (item=firewalld)TASK [Going through firewall] **************************************************
ok: [servera.lab.example.com] => (item=http)TASK [Downlaoding config files and restart web services] ***********************
ok: [servera.lab.example.com]TASK [Downlaoding config files and restart web services] ***********************
ok: [servera.lab.example.com]PLAY RECAP *********************************************************************
servera.lab.example.com    : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
[student@workstation control-handlers]$ 

Handling Task Failure

Ansible evaluates the return code of each task to determine whether the task succeeded or failed. Normally, when a task fails Ansible immediately skips all subsequent tasks.

However, sometimes you might want to have play execution continue even if a task fails. For example, you might expect that a particular task could fail, and you might want to recover by conditionally running some other task. A number of Ansible features can be used to manage task errors.

Ignoring Task Failure

By default, if a task fails, the play is aborted. However, this behavior can be overridden by ignoring failed tasks. You can use the ignore_errors keyword in a task to accomplish this.

The following snippet shows how to use ignore_errors in a task to continue playbook execution on the host even if the task fails. For example, if the notapkg package does not exist then the ansible.builtin.dnf module fails, but having ignore_errors set to true allows execution to continue.

- name: Latest version of notapkg is installedansible.builtin.dnf:name: notapkgstate: latestignore_errors: true
Forcing Execution of Handlers After Task Failure

Normally when a task fails and the play aborts on that host, any handlers that had been notified by earlier tasks in the play do not run. If you set the force_handlers: true keyword on the play, then notified handlers are called even if the play aborted because a later task failed.

Important:

If you have ignore_errors: true set on a task or for the task’s play, if that task fails the failure is ignored. In that case, the play keeps running and handlers still run, even if you have force_handlers: false set, unless some other error causes the play to fail.

The following snippet shows how to use the force_handlers keyword in a play to force execution of the notified handler even if a subsequent task fails:

---
- hosts: allforce_handlers: truetasks:- name: a task which always notifies its handleransible.builtin.command: /bin/truenotify: restart the database- name: a task which fails because the package doesn't existansible.builtin.dnf:name: notapkgstate: latesthandlers:- name: restart the databaseansible.builtin.service:name: mariadbstate: restarted

Important:

Remember that handlers are notified when a task reports a changed result but are not notified when it reports an ok or failed result.

If you set force_handlers: true on the play, then any handlers that have been notified are run even if a later task failure causes the play to fail. Otherwise, handlers are not run at all when a play fails.

Setting force_handlers: true on a play does not cause handlers to be notified for tasks that report ok or failed; it only causes the handlers to run that have already been notified before the point at which the play failed.

Specifying Task Failure Conditions

You can use the failed_when keyword on a task to specify which conditions indicate that the task has failed. This is often used with command modules that might successfully execute a command, but where the command’s output indicates a failure.

For example, you may check for failure by searching for a word or phrase in the output of a command. The following example shows one way that you can use the failed_when keyword in a task:

tasks:- name: Run user creation scriptansible.builtin.shell: /usr/local/bin/create_users.shregister: command_resultfailed_when: "'Password missing' in command_result.stdout"

The ansible.builtin.fail module can also be used to force a task failure. You could instead write that example as two tasks:

tasks:- name: Run user creation scriptansible.builtin.shell: /usr/local/bin/create_users.shregister: command_resultignore_errors: true- name: Report script failureansible.builtin.fail:msg: "The password is missing in the output"when: "'Password missing' in command_result.stdout"

You can use the ansible.builtin.fail module to provide a clear failure message for the task. This approach also enables delayed failure, which means that you can run intermediate tasks to complete or roll back other changes.

or it can also based on the return code

- name: Fail task when both files are identicalansible.builtin.raw: diff foo/file1 bar/file2register: diff_cmdfailed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2

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 doesansible.builtin.command: ls /tmp/this_should_not_be_hereregister: resultfailed_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 ORansible.builtin.shell: "./myBinary"register: retfailed_when: >("No such file or directory" in ret.stdout) or(ret.stderr != '') or(ret.rc == 10)
Specifying When a Task Reports “Changed” Results

When a task makes a change to a managed host, it reports the changed state and notifies handlers. When a task does not need to make a change, it reports ok and does not notify handlers.

Use the changed_when keyword to control how a task reports that it has changed something on the managed host. For example, the ansible.builtin.command module in the next example validates the httpd configuration on a managed host.

This task validates the configuration syntax, but nothing is actually changed on the managed host. Subsequent tasks can use the value of the httpd_config_status variable.

It normally would always report changed when it runs. To suppress that change report, changed_when: false is set so that it only reports ok or failed.

  - name: Validate httpd configurationansible.builtin.command: httpd -tchanged_when: falseregister: httpd_config_status

The following example uses the ansible.builtin.shell module and only reports changed if the string “Success” is found in the output of the registered variable. If it does report changed, then it notifies the handler.

tasks:- ansible.builtin.shell:cmd: /usr/local/bin/upgrade-databaseregister: command_resultchanged_when: "'Success' in command_result.stdout"notify:- restart_databasehandlers:- name: restart_databaseansible.builtin.service:name: mariadbstate: restarted
Ansible Blocks and Error Handling

In playbooks, blocks are clauses that logically group tasks, and can be used to control how tasks are executed. For example, a task block can have a when keyword to apply a conditional to multiple tasks:

- name: block examplehosts: alltasks:- name: installing and configuring DNF versionlock pluginblock:- name: package needed by dnfansible.builtin.dnf:name: python3-dnf-plugin-versionlockstate: present- name: lock version of tzdataansible.builtin.lineinfile:dest: /etc/yum/pluginconf.d/versionlock.listline: tzdata-2016j-1state: presentwhen: ansible_distribution == "RedHat"

Blocks also allow for error handling in combination with the rescue and always statements. If any task in a block fails, then rescue tasks are executed to recover.

After the tasks in the block clause run, as well as the tasks in the rescue clause if there was a failure, then tasks in the always clause run.

To summarize:

  • block: Defines the main tasks to run.
  • rescue: Defines the tasks to run if the tasks defined in the block clause fail.
  • always: Defines the tasks that always run independently of the success or failure of tasks defined in the block and rescue clauses.

The following example shows how to implement a block in a playbook.

  tasks:- name: Upgrade DBblock:- name: upgrade the databaseansible.builtin.shell:cmd: /usr/local/lib/upgrade-databaserescue:- name: revert the database upgradeansible.builtin.shell:cmd: /usr/local/lib/revert-databasealways:- name: always restart the databaseansible.builtin.service:name: mariadbstate: restarted

The when condition on a block clause also applies to its rescue and always clauses if present.

References

Error Handling in Playbooks — Ansible Documentation

Error Handling — Blocks — Ansible Documentation

Example
---
- name: How to handle errors examplehosts: databasesvars:web_package: httpddb_package: mariadb-serverdb_service: mariadbtasks:- name: Running date cmdansible.builtin.command: /usr/bin/dateregister: command_resultchanged_when: false       # the above 'date' command task will not reported 'changed' if we set this to false- name: Display the cmd resultansible.builtin.debug:var: command_result.stdout- name: Doing block thingsblock: - name: Installing web packageansible.builtin.dnf:name: "{{ web_package }}"state: presentfailed_when: web_package == "httpd"   # Force to report failure if the 'web_package' name is httpdrescue:  # this will run only there is failure in above 'block'  - name: Installing db packageansible.builtin.dnf:name: "{{ db_package }}"state: presentalways:- name: Anyway Starting DB serveransible.builtin.service:name: "{{ db_service }}"state: startedenabled: true

Result: Look carefully at the output. The failed_when keyword changes the status that the task reports after the task runs; it does not change the behavior of the task itself.

However, the reported failure might change the behavior of the rest of the play. Because that task was in a block and reported that it failed, the Install mariadb-server package task in the block’s rescue section was run.

[student@workstation control-errors]$ ansible-navigator run -m stdout playbook.ymlPLAY [How to handle errors example] ********************************************TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]TASK [Running date cmd] ********************************************************
>>ok: [servera.lab.example.com]<<TASK [Display the cmd result] **************************************************
ok: [servera.lab.example.com] => {"command_result.stdout": "Wed Aug 28 10:42:15 AM EDT 2024"
}TASK [Installing web package] **************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, >>"failed_when_result": true<<, "msg": "Nothing to do", "rc": 0, "results": []}TASK [Installing db package] ***************************************************
ok: [servera.lab.example.com]TASK [Anyway Starting DB server] ***********************************************
ok: [servera.lab.example.com]PLAY RECAP *********************************************************************
servera.lab.example.com    : ok=5    changed=0    unreachable=0    >>failed=0<<    skipped=0    >>rescued=1<<    ignored=0   

Chapter4 TEST

Requests:

  1. Under the #Fail fast message comment, add a task that uses the ansible.builtin.fail module. Provide an appropriate name for the task.

    This task should only be executed when the remote system does not meet the following minimum requirements:

    • Has at least the amount of RAM specified by the min_ram_mb variable. The min_ram_mb variable is defined in the vars.yml file and has a value of 256.
    • Is running Red Hat Enterprise Linux.
  2. Under the #Install all packages comment, add a task named Ensure required packages are present to install the latest version of any missing packages. Required packages are specified by the packages variable, which is defined in the vars.yml file.

  3. Under the #Enable and start services comment, add a task to start services. All services specified by the services variable, which is defined in the vars.yml file, should be started and enabled. Provide an appropriate name for the task.

  4. Under the #Block of config tasks comment, add a task block to the play. This block contains two tasks:

    • A task to ensure that the directory specified by the ssl_cert_dir variable exists on the remote host. This directory stores the web server’s certificates.

    • A task to copy all files specified by the web_config_files variable to the remote host. Examine the structure of the web_config_files variable in the vars.yml file. Configure the task to copy each file to the correct destination on the remote host.

      This task should trigger the Restart web service handler if any of these files are changed on the remote server.

    Additionally, a debug task is executed if either of the two tasks above fail. In this case, the task prints the following message: One or more of the configuration changes failed, but the web service is still active.

    Provide an appropriate name for all tasks.

  5. The play configures the remote host to listen for standard HTTPS requests. Under the #Configure the firewall comment, add a task to configure firewalld.

    Ensure that the task configures the remote host to accept standard HTTP and HTTPS connections. The configuration changes must be effective immediately and persist after a reboot. Provide an appropriate name for the task.

  6. Define the Restart web service handler.

    When triggered, this task should restart the web service defined by the web_service variable, defined in the vars.yml file.

# vars.yml
min_ram_mb: 256web_service: httpd
web_package: httpd
ssl_package: mod_sslfw_service: firewalld
fw_package: firewalldservices:- "{{ web_service }}"- "{{ fw_service }}"packages:- "{{ web_package }}"- "{{ ssl_package }}"- "{{ fw_package }}"ssl_cert_dir: /etc/httpd/conf.d/sslweb_config_files:- src: server.keydest: "{{ ssl_cert_dir }}"- src: server.crtdest: "{{ ssl_cert_dir }}"- src: ssl.confdest: /etc/httpd/conf.d- src: index.htmldest: /var/www/html
# playbook.yml
---
- name: Playbook Control Labhosts: webserversvars_files: vars.ymltasks:#Fail fast message- name: Show failed system requirements messageansible.builtin.fail:msg: "The {{ inventory_hostname }} did not meet minimum reqs."when: >ansible_facts['memtotal_mb'] < min_ram_mb oransible_facts['distribution'] != "RedHat"#Install all packages- name: Ensure required packages are presentansible.builtin.dnf:name: "{{ packages }}"state: latest#Enable and start services- name: Ensure services are started and enabledansible.builtin.service:name: "{{ item }}"state: startedenabled: trueloop: "{{ services }}"#Block of config tasks- name: Setting up the SSL cert directory and config filesblock:- name: Create SSL cert directoryansible.builtin.file:path: "{{ ssl_cert_dir }}"state: directory- name: Copy config filesansible.builtin.copy:src: "{{ item['src'] }}"dest: "{{ item['dest'] }}"loop: "{{ web_config_files }}"notify: Restart web servicerescue:- name: Configuration error messageansible.builtin.debug:msg: >One or more of the configurationchanges failed, but the web serviceis still active.#Configure the firewall- name: Ensure web server ports are openansible.posix.firewalld:service: "{{ item }}"immediate: truepermanent: truestate: enabledloop:- http- https#Add handlershandlers:- name: Restart web serviceansible.builtin.service:name: "{{ web_service }}"state: restarted

output:

[student@workstation control-review]$ ansible-navigator run \
> -m stdout playbook.ymlPLAY [Playbook Control Lab] ****************************************************TASK [Gathering Facts] *********************************************************
ok: [serverb.lab.example.com]TASK [Show failed system requirements message] *********************************
skipping: [serverb.lab.example.com]TASK [Ensure required packages are present] ************************************
changed: [serverb.lab.example.com]TASK [Ensure services are started and enabled] *********************************
changed: [serverb.lab.example.com] => (item=httpd)
ok: [serverb.lab.example.com] => (item=firewalld)TASK [Create SSL cert directory] ***********************************************
changed: [serverb.lab.example.com]TASK [Copy config files] *******************************************************
changed: [serverb.lab.example.com] => (item={'src': 'server.key', 'dest': '/etc/httpd/conf.d/ssl'})
changed: [serverb.lab.example.com] => (item={'src': 'server.crt', 'dest': '/etc/httpd/conf.d/ssl'})
changed: [serverb.lab.example.com] => (item={'src': 'ssl.conf', 'dest': '/etc/httpd/conf.d'})
changed: [serverb.lab.example.com] => (item={'src': 'index.html', 'dest': '/var/www/html'})TASK [Ensure web server ports are open] ****************************************
changed: [serverb.lab.example.com] => (item=http)
changed: [serverb.lab.example.com] => (item=https)RUNNING HANDLER [Restart web service] ******************************************
changed: [serverb.lab.example.com]PLAY RECAP *********************************************************************
serverb.lab.example.com    : ok=7    changed=6    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Verify Web service

[student@workstation control-review]$ curl -k -vvv https://serverb.lab.example.com
*   Trying 172.25.250.11:443...
* Connected to serverb.lab.example.com (172.25.250.11) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/pki/tls/certs/ca-bundle.crt
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Unknown (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=US; L=Default City; O=Red Hat; OU=Training; CN=serverb.lab.example.com
*  start date: Nov 13 15:52:18 2018 GMT
*  expire date: Aug  9 15:52:18 2021 GMT
*  issuer: C=US; L=Default City; O=Red Hat; OU=Training; CN=serverb.lab.example.com
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
* TLSv1.2 (OUT), TLS header, Unknown (23):
> GET / HTTP/1.1
> Host: serverb.lab.example.com
> User-Agent: curl/7.76.1
> Accept: */*
> 
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Unknown (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* TLSv1.2 (IN), TLS header, Unknown (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Fri, 30 Aug 2024 15:21:47 GMT
< Server: Apache/2.4.51 (Red Hat Enterprise Linux) OpenSSL/3.0.1
< Last-Modified: Fri, 30 Aug 2024 14:35:03 GMT
< ETag: "24-620e77f28279e"
< Accept-Ranges: bytes
< Content-Length: 36
< Content-Type: text/html; charset=UTF-8
< 
Configured for both HTTP and HTTPS.
* Connection #0 to host serverb.lab.example.com left intact

TO BE CONTINUED…

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com