Create a new workflow job template and modify the playbooks used to resolve issues, improve efficiency, and handle errors.
Outcomes
Create a workflow job template with "On Success" and "On Failure" nodes.
Use loops to write efficient tasks.
Use conditions to control where tasks run.
Use blocks with rescue and always sections to handle playbook errors.
As the student user on the workstation machine, use the lab command to prepare your system for this exercise.
This command creates Git repositories, creates some automation controller resources, and enables the Network Configuration Protocol (NETCONF) system service on the Juniper Junos managed node.
[student@workstation ~]$ lab start task-review
Instructions
This exercise provides you with playbooks to configure interface settings on a Cisco IOS XE and a Juniper Junos managed node in your environment. The playbooks have associated job templates created in automation controller for you to use to create a workflow job template. The playbooks do not work and you must fix them. After updating the playbooks, verify that the workflow job template runs successfully.
Navigate to https://controller.lab.example.com to access the automation controller web UI.
Log in as admin using redhat as the password.
Create a workflow job template called Configure Interfaces with the following information.
In the following steps, you create nodes for the workflow job template.
Table 5.4. Workflow Template
| Field | Value |
|---|---|
Configure Interfaces
| |
Configure interfaces on network devices
| |
Default
| |
Common
|
Start the workflow for the workflow job template by synchronizing the project. Create the first node using the following information:
Using the workflow visualizer for the workflow job template, click . Create the first node using information from the "Workflow Node #1" table. Select the project and specify the node alias. When finished, click .
![]() |
The symbol under the node in the workflow visualizer indicates it is a project sync node.
The blue line connecting and the node indicates that this step is always performed.
Create a node that runs if the node succeeds. This new node must launch the job template. Create the second node using the following information:
Using the workflow visualizer for the workflow job template, hover over the node and then click .
Select and then click .
Create the second node using information from the "Workflow Node #2" table. Select the job template and then click .
![]() |
The symbol under the node in the workflow visualizer indicates it is a job template node.
The green line connecting the node and the node indicates that this step is performed only on success.
Create a node that runs if the node succeeds. This new node must launch the job template. Create the third node using the following information:
Create a node that runs if the node fails. This new node must launch the job template. Create the fourth node using the following information:
After this step, the completed workflow job template should appear as follows:
![]() |
Using the workflow visualizer for the workflow job template, hover over the node and then click .
Select and then click .
Create the fourth node using information from the "Workflow Node #4" table. Select the job template and then click .
The red line connecting the node and the node indicates that this step is performed only on failure.
Click to exit the workflow visualizer.
Run the workflow job template. Notice that the node fails, which causes the node to run.
Repair the issues in the add_interfaces.yml playbook that caused the node in your workflow job to fail.
Clone the git@git.lab.example.com:student/interfaces Git repository into the /home/student/git-repos directory.
Change to the exercise branch and then edit the tasks in the add_interfaces.yml playbook.
Any task using a module from the cisco.ios collection should only run on IOS devices, and any task using a module from the junipernetworks.junos collection should only run on Junos devices.
You can view connection variables for the ios group in the ~/git-repos/inventories/group_vars/ios/connection.yml file and connection variables for the junos group in the ~/git-repos/inventories/group_vars/junos/connection.yml file.
Use either VS Code or the git clone command to clone the git@git.lab.example.com:student/interfaces Git repository into the /home/student/git-repos directory:
[student@workstation ~]$cd ~/git-repos/[student@workstation git-repos]$git clone \git@git.lab.example.com:student/interfaces
Use either VS Code or the git checkout exercise command to switch to the exercise branch:
[student@workstation git-repos]$cd interfaces[student@workstation interfaces]$git checkout exerciseSwitched to branch 'exercise' Your branch is up to date with 'origin/exercise'.
Edit the add_interfaces.yml playbook to add the when: ansible_network_os == "cisco.ios.ios" case statement to the tasks that use the cisco.ios modules:
...output omitted... - name: Create loopback interfacewhen: ansible_network_os == 'cisco.ios.ios'cisco.ios.ios_interfaces: ...output omitted... - name: Assign IP to loopback interfacewhen: ansible_network_os == 'cisco.ios.ios'cisco.ios.ios_l3_interfaces:
Edit the add_interfaces.yml playbook to add the when: ansible_network_os == "junipernetworks.junos.junos" case statement to the task that uses the junipernetworks.junos module:
...output omitted...
- name: Add IP addresses
when: ansible_network_os == 'junipernetworks.junos.junos'
junipernetworks.junos.junos_l3_interfaces:
...output omitted...The existing add_interfaces.yml playbook only adds one interface to each managed node.
Extend the functionality of the add_interfaces.yml playbook by using loops.
Add a loop to the Create loopback interface task that iterates over the interface names in the ~/git-repos/inventories/group_vars/ios/interfaces.yml file.
Add a second loop to the Assign IP to loopback interface task to iterate over the addresses in the ~/git-repos/inventories/group_vars/ios/interfaces.yml file.
Add a third loop to the Add IP addresses task to iterate over the addresses in the ~/git-repos/inventories/group_vars/junos/interfaces.yml file.
Each task in the playbook correctly references the interfaces variable in the ~/git-repos/inventories/group_vars/junos/interfaces.yml file. However, the existing tasks only reference the first list item for each managed node.
Stage and commit the modified files and use a descriptive commit message. Push your changes to the remote Git repository.
Inspect the ~/git-repos/inventories/group_vars/ios/interfaces.yml file:
---
interfaces:
iosxe1.lab.example.com:
- name: Loopback100
address: 10.10.10.100/32
enabled: true
- name: Loopback101
address: 10.10.10.101/32
enabled: true
- name: Loopback102
address: 10.10.10.102/32
enabled: true
- name: Loopback103
address: 10.10.10.103/32
enabled: trueUpdate the ~/git-repos/interfaces/add_interfaces.yml playbook to add a loop to the Create loopback interfaces task.
Update the interface name, enabled state, and description parameters.
The updated task contains the following content:
- name: Create loopback interfaces
when: ansible_network_os == 'cisco.ios.ios'
cisco.ios.ios_interfaces:
config:
- name: "{{ item['name'] }}"
enabled: "{{ item['enabled'] }}"
description: >-
{{ item['description'] |
default(omit) }}
state: replaced
loop: "{{ interfaces[inventory_hostname] }}"Add a loop to the Assign IP to loopback interface task and then update the interface name and address parameters.
The updated task contains the following content:
- name: Assign IP to loopback interface
when: ansible_network_os == 'cisco.ios.ios'
cisco.ios.ios_l3_interfaces:
config:
- name: "{{ item['name'] }}"
ipv4:
- address: "{{ item['address'] }}"
state: replaced
loop: "{{ interfaces[inventory_hostname] }}"Inspect the ~/git-repos/inventories/group_vars/junos/interfaces.yml file:
---
interfaces:
junos1.lab.example.com:
- name: fxp0
address: 172.25.250.205/24
- name: fxp0
address: 172.25.250.206/24
- name: fxp0
address: 172.25.250.207/24
- name: fxp0
address: 172.25.250.208/24Add a loop to the Add IP addresses task and then update the interface name and address parameters.
The updated task contains the following content:
- name: Add IP addresses
when: ansible_network_os == 'junipernetworks.junos.junos'
junipernetworks.junos.junos_l3_interfaces:
config:
- name: "{{ item['name'] }}"
ipv4:
- address: "{{ item['address'] }}"
loop: "{{ interfaces[inventory_hostname] }}"The final playbook should contain the following content:
---
- name: Configure loopback interfaces
hosts:
- iosxe1.lab.example.com
- junos1.lab.example.com
gather_facts: false
tasks:
- name: Create loopback interface
when: ansible_network_os == 'cisco.ios.ios'
cisco.ios.ios_interfaces:
config:
- name: "{{ item['name'] }}"
enabled: "{{ item['enabled'] }}"
description: >-
{{ item['description'] |
default(omit) }}
state: replaced
loop: "{{ interfaces[inventory_hostname] }}"
- name: Assign IP to loopback interface
when: ansible_network_os == 'cisco.ios.ios'
cisco.ios.ios_l3_interfaces:
config:
- name: "{{ item['name'] }}"
ipv4:
- address: "{{ item['address'] }}"
state: replaced
loop: "{{ interfaces[inventory_hostname] }}"
- name: Add IP addresses
when: ansible_network_os == 'junipernetworks.junos.junos'
junipernetworks.junos.junos_l3_interfaces:
config:
- name: "{{ item['name'] }}"
ipv4:
- address: "{{ item['address'] }}"
loop: "{{ interfaces[inventory_hostname] }}"Use either VS Code or the CLI to add, commit, and push the changes to the remote repository.
Use a descriptive commit message, such as Improving add_interfaces.yml playbook.
If you choose to use the CLI, then use the git add, git commit, and git push commands:
[student@workstation interfaces]$git add add_interfaces.yml[student@workstation interfaces]$git commit -m \"Improving add_interfaces.yml playbook"[exercise 96b349d] Improving add_interfaces.yml playbook ...output omitted... [student@workstation interfaces]$git push -u origin exercise...output omitted... To git.lab.example.com:student/interfaces.git 6563170..d4a96cb exercise -> exercise Branch 'exercise' set up to track remote branch 'exercise' from 'origin'.
Run the workflow job template. Notice that the node succeeds but the node fails.
Repair the issue in the ping_check.yml playbook that caused the node in your workflow job to fail.
Edit the tasks in the ping_check.yml playbook in the exercise branch of the repository.
Tasks in the IOS tasks block should only run on IOS devices, the Show ios_ping task should only run if the Ping IOS hosts task fails, and the Report IOS host status task should always run.
Similarly, the tasks in the Junos tasks block should only run on Junos devices, the Show junos_ping task should only run if the Ping Junos hosts task fails, and the Report Junos host status task should always run.
Stage and commit the modified files and use a descriptive commit message.
Push your changes to the remote Git repository.
When finished, either close the opened directory in VS Code or return to the /home/student directory from the CLI.
Add a when condition statement to the IOS tasks block so that the tasks only run on hosts where the ansible_network_os variable is set to the cisco.ios.ios value.
The updated block contains the following content:
...output omitted...
tasks:
- name: IOS tasks
when: ansible_network_os == 'cisco.ios.ios'
block:
- name: Ping IOS hosts
cisco.ios.ios_ping:
dest: "{{ item }}"
...output omitted...Move the Show ios_ping task into a rescue block for the IOS tasks block:
...output omitted... - name: IOS tasks when: ansible_network_os == 'cisco.ios.ios' block: - name: Ping IOS hosts cisco.ios.ios_ping: dest: "{{ item }}" loop: >- {{ interfaces[inventory_hostname] | map(attribute='address') | ansible.utils.ipaddr('address') }} register: ios_ping - name: Report IOS host status ansible.builtin.debug: msg: >- "The host {{ item['item'] }} had {{ item['packet_loss'] }} packet loss." loop: "{{ ios_ping['results'] | flatten(1) }}"rescue:- name: Show ios_pingansible.builtin.debug:var: ios_ping['results'] | selectattr('failed', '==', true)...output omitted...
Move the Report IOS host status task into an always block for the IOS tasks block:
...output omitted... - name: IOS tasks when: ansible_network_os == 'cisco.ios.ios' block: - name: Ping IOS hosts cisco.ios.ios_ping: dest: "{{ item }}" loop: >- {{ interfaces[inventory_hostname] | map(attribute='address') | ansible.utils.ipaddr('address') }} register: ios_ping rescue: - name: Show ios_ping ansible.builtin.debug: var: ios_ping['results'] | selectattr('failed', '==', true)always:- name: Report IOS host statusansible.builtin.debug:msg: >-"The host {{ item['item'] }} had{{ item['packet_loss'] }} packet loss."loop: "{{ ios_ping['results'] | flatten(1) }}"...output omitted...
Add a when condition statement to the Junos tasks block so the tasks only run on hosts where the ansible_network_os variable is set to the junipernetworks.junos.junos value.
The updated block contains the following content:
...output omitted...
tasks:
- name: Junos tasks
when: ansible_network_os == 'junipernetworks.junos.junos'
block:
- name: Ping Junos hosts
junipernetworks.junos.junos_ping:
dest: "{{ item }}"
...output omitted...Move the Show junos_ping task into a rescue block for the Junos tasks block:
...output omitted... - name: Junos tasks when: ansible_network_os == 'junipernetworks.junos.junos' block: - name: Ping Junos hosts vars: ansible_connection: ansible.netcommon.network_cli junipernetworks.junos.junos_ping: dest: "{{ item }}" loop: >- {{ interfaces[inventory_hostname] | map(attribute='address') | ansible.utils.ipaddr('address') }} register: junos_ping - name: Report Junos host status ansible.builtin.debug: msg: >- "The host {{ item['item'] }} had {{ item['packet_loss'] }} packet loss." loop: "{{ junos_ping['results'] | flatten(1) }}"rescue:- name: Show junos_pingansible.builtin.debug:var: junos_ping['results'] | selectattr('failed', '==', true)...output omitted...
Move the Report Junos host status task into an always block for the Junos tasks block:
...output omitted... - name: Junos tasks when: ansible_network_os == 'junipernetworks.junos.junos' block: - name: Ping Junos hosts vars: ansible_connection: ansible.netcommon.network_cli junipernetworks.junos.junos_ping: dest: "{{ item }}" loop: >- {{ interfaces[inventory_hostname] | map(attribute='address') | ansible.utils.ipaddr('address') }} register: junos_ping rescue: - name: Show junos_ping ansible.builtin.debug: var: junos_ping['results'] | selectattr('failed', '==', true)always:- name: Report Junos host statusansible.builtin.debug:msg: >-"The host {{ item['item'] }} had{{ item['packet_loss'] }} packet loss."loop: "{{ junos_ping['results'] | flatten(1) }}"
The final playbook should contain the following content:
---
- name: Ping new IP addresses
hosts:
- iosxe1.lab.example.com
- junos1.lab.example.com
gather_facts: false
tasks:
- name: IOS tasks
when: ansible_network_os == 'cisco.ios.ios'
block:
- name: Ping IOS hosts
cisco.ios.ios_ping:
dest: "{{ item }}"
loop: >-
{{ interfaces[inventory_hostname] |
map(attribute='address') |
ansible.utils.ipaddr('address') }}
register: ios_ping
rescue:
- name: Show ios_ping
ansible.builtin.debug:
var: ios_ping['results'] | selectattr('failed', '==', true)
always:
- name: Report IOS host status
ansible.builtin.debug:
msg: >-
"The host {{ item['item'] }} had
{{ item['packet_loss'] }} packet loss."
loop: "{{ ios_ping['results'] | flatten(1) }}"
- name: Junos tasks
when: ansible_network_os == 'junipernetworks.junos.junos'
block:
- name: Ping Junos hosts
vars:
ansible_connection: ansible.netcommon.network_cli
junipernetworks.junos.junos_ping:
dest: "{{ item }}"
loop: >-
{{ interfaces[inventory_hostname] |
map(attribute='address') |
ansible.utils.ipaddr('address') }}
register: junos_ping
rescue:
- name: Show junos_ping
ansible.builtin.debug:
var: junos_ping['results'] | selectattr('failed', '==', true)
always:
- name: Report Junos host status
ansible.builtin.debug:
msg: >-
"The host {{ item['item'] }} had
{{ item['packet_loss'] }} packet loss."
loop: "{{ junos_ping['results'] | flatten(1) }}"Use either VS Code or the CLI to add, commit, and push the changes to the remote repository.
Use a descriptive commit message, such as Improving ping_check.yml playbook.
If you choose to use the CLI, then use the git add, git commit, and git push commands:
[student@workstation interfaces]$git add ping_check.yml[student@workstation interfaces]$git commit -m \"Improving ping_check.yml playbook"[exercise 96b349d] Improving ping_check.yml playbook ...output omitted... [student@workstation interfaces]$git push -u origin exercise...output omitted... Branch 'exercise' set up to track remote branch 'exercise' from 'origin'.
If you completed this step and the previous steps using VS Code, then click → to close the /home/student/git-repos/interfaces directory.
If you completed this step and the previous steps using the CLI, then run the cd command to return to the /home/student directory:
[student@workstation interfaces]$ cdRun the workflow job template. Notice that the node and the node both succeed.