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.
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.
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 example shows how to use ignore_errors in a task to continue playbook execution on the managed node even if the task fails.
If the virtual LAN (VLAN) with ID 150 does not exist, then the cisco.ios.ios_command module fails, but having ignore_errors set to true enables execution to continue.
tasks:
- name: Displaying VLAN id 150 information
cisco.ios.ios_command:
commands:
- show vlan id 150
ignore_errors: trueYou can use the failed_when keyword on a task to specify the conditions that indicate a failed task.
This is often used with command modules that might successfully execute a command on the managed node, but where the command output indicates a failure.
For example, you can execute a command on the managed node that outputs a message that you can consider an error, and then use that message to define the failed state for the task.
The following example shows how you can use the failed_when keyword in a task:
tasks:
- name: Displaying configured VLANs
cisco.ios.ios_command:
commands: show vlans
register: vlan_result
failed_when: "'No Virtual LANs configured' in vlan_result['stdout'][0]"You can use the ansible.builtin.fail module to force a task failure.
You can also use that module to provide a custom failure message for the task.
For example, you can modify the previous example as follows, where the tasks produce the failure and send a customized message when you run the playbook:
tasks:
- name: Displaying configured VLANs
cisco.ios.ios_command:
commands: show vlans
register: vlan_result
- name: Reporting command failure
ansible.builtin.fail:
msg: "Missing VLAN configuration"
when: "'No Virtual LANs configured' in vlan_result['stdout'][0]"Using the when: true condition with the ansible.builtin.fail module causes the playbook to terminate immediately at the point where the task is defined.
When a task makes a change to a managed node, it reports a task result of changed.
However, sometimes tasks are performed outside of the managed node and you might not want this change to be reported on the managed node.
You can use the changed_when keyword to control how a task reports that it has changed something on the managed node.
If you set the value of the changed_when keyword to false, then the task never reports a changed state.
For example, the Copying the vlan configuration to a file task in the following example gathers information from the managed node, but does not change any configuration on the managed node:
tasks:
- name: Gather IOS interface facts
cisco.ios.ios_facts:
gather_network_resources: vlans
- name: Copying the vlan configuration to a file
ansible.builtin.copy:
content: "{{ ansible_network_resources['vlans'] | to_nice_yaml }}"
dest: "{{ inventory_hostname }}_vlan.yml"
mode: 0600When you run the playbook for the first time, the task reports a changed result for each managed node against which the task runs:
TASK [Copying the vlan configuration to a file] ********************************changed: [iosxe1]changed: [iosxe2]changed: [iosxe3]
The following example shows how to use the changed_when keyword in a task to never report the changed result:
tasks:
- name: Gather IOS interface facts
cisco.ios.ios_facts:
gather_network_resources: vlans
- name: Copying the vlan configuration to a file
ansible.builtin.copy:
content: "{{ ansible_network_resources['vlans'] | to_nice_yaml }}"
dest: "{{ inventory_hostname }}_vlan.yml"
mode: 0600
changed_when: falseThe task where you added the changed_when keyword with the false value can still have the ok, failed, or skipped result when you run the playbook.
In playbooks, you can use blocks to group tasks that should be executed as a single unit. It is a good practice to add a name to each block to document the purpose of the block.
Use the block keyword to create a block and then add tasks to the block section.
You can use the when keyword to apply a condition to the entire block rather than to individual tasks.
tasks:
- name: Configure DNS for Juniper Junos managed nodes
when: ansible_network_os == 'junipernetworks.junos.junos'
block:
- name: Enable netconf service on port 830
vars:
ansible_connection: ansible.netcommon.network_cli
junipernetworks.junos.junos_netconf:
netconf_port: 830
state: present
- name: Configure DNS settings
junipernetworks.junos.junos_system:
domain_search:
- lab.example.com
- example.com
name_servers:
- 172.25.250.220Blocks also enable error handling using the block keyword in combination with the rescue and always keywords for the grouped tasks.
The rescue and always keywords have the same indentation in your playbook as the block keyword.
If any task in a block section fails, then the playbook runs tasks in the rescue section.
After running tasks in the block section, and possibly tasks in the rescue section, the playbook runs tasks in the always section.
To summarize:
block
Defines the main tasks to run.
rescue
Defines the tasks to run if any task in the block section fails.
always
Defines the tasks that always run independently of the success or failure of tasks defined in the block or rescue sections.
If you define variables or when conditions for a block, then those settings apply to all sections of the block.
As an example of using blocks for error handling, suppose that you have a group of Cisco IOS managed nodes that were not fully configured to use the 172.25.250.220 DNS server.
You want to gather a list of misconfigured managed nodes and then configure them to use the 172.25.250.220 DNS sever.
The following example show how to accomplish this by using blocks:
tasks:
- name: Verify DNS for IOS managed nodes
block:
- name: Verifying initial DNS configuration
cisco.ios.ios_command:
commands: show ip name-server
register: ios_dns
failed_when: ios_dns['stdout'][0] != '172.25.250.220'
rescue:
- name: Add the misconfigured server to the list
ansible.builtin.lineinfile:
path: misconfigured_servers.txt
line: "{{ inventory_hostname }}"
create: yes
- name: Configure DNS settings in failed managed nodes
cisco.ios.ios_system:
name_servers: 172.25.250.220
always:
- name: Verifying DNS configuration
cisco.ios.ios_command:
commands: show ip name-server
register: final_dns
- name: Displaying final DNS configuration
ansible.builtin.debug:
var: final_dns['stdout']Start by reviewing the expected settings.
Each task in the | |
The | |
The first task in the | |
The second task in the | |
Tasks in the |