Abstract
| Goal |
Automate common network administration tasks, discussing recommended practices and approaches to cross-vendor automation. |
| Objectives |
|
| Sections |
|
Configure connectivity to managed network devices, gather device information, and generate a dynamic report that documents the state of your network infrastructure.
You can collect information about your current environment in multiple ways. Depending on your goal, you might use one or more of the following methods:
Collect facts about your managed nodes using vendor-specific *_facts modules, such as the cisco.ios.ios_facts, junipernetworks.junos.junos_facts, and arista.eos.eos_facts modules.
Create backup files of the configuration of your managed nodes using vendor-specific *_config modules, such as the cisco.ios.ios_config, junipernetworks.junos.junos_config, and arista.eos.eos_config modules.
Save configuration information about your managed nodes using the network.base.resource_manager role.
You might use the *_facts modules in plays where you need to access current configuration settings for your managed nodes, but where you do not necessarily need to save this information.
To collect a full set of facts, disable fact gathering at the play level with gather_facts: false, and then add a task such as the following:
- name: Collect facts
cisco.ios.ios_facts:
gather_subset:
- allIf a task gathered configuration facts, then you might use the following task to display those facts:
- name: Display ansible_facts['net_config']
ansible.builtin.debug:
var: ansible_facts['net_config']You might use filters to process the ansible_facts['net_config'] variable, such as the regex_findall filter to search for a pattern, or the split filter to convert the variable into a list where each line is a list item:
- name: Display SNMP configuration
ansible.builtin.debug:
var: ansible_facts['net_config'] | regex_findall('^snmp.*$', multiline=true)
- name: Display configuration as a list
vars:
separator: "\n"
ansible.builtin.debug:
var: ansible_facts['net_config'] | split(separator)To make it easier to use a variable in another playbook task, you might use the ansible.builtin.set_fact module.
The following example uses that module to set the snmp_config_lines variable.
Another task could then use the snmp_config_lines variable.
In this example, the ansible.builtin.debug module displays the snmp_config_lines variable if the variable contains at least one list item:
- name: Set snmp_config_lines
ansible.builtin.set_fact:
snmp_config_lines: >-
{{ ansible_facts['net_config'] |
regex_findall('^snmp.*$', multiline=true) }}
- name: Display snmp_config_lines
when: snmp_config_lines | length > 0
ansible.builtin.debug:
var: snmp_config_linesUsually, the content of a backup file matches the content in the ansible_facts['net_config'] variable.
When that is true, you might find it easier to use gathered configuration facts rather than creating and processing backup files.
For Juniper Junos managed nodes, the content of a backup file does not match the content in the ansible_facts['net_config'] variable, and the content of that variable can be difficult to process.
The junipernetworks.junos.junos_config module provides the backup_format option, which is not found in other *_config modules.
Changing the format of backup files to something different from the default set format might help with processing the backup files.
If you create backup files using the JSON format, then you can process those backup files using the from_json filter.
The following example creates a backup file in JSON format for each Juniper Junos managed node:
- name: Create a backup file
junipernetworks.junos.junos_config:
backup: true
backup_options:
filename: "{{ inventory_hostname }}.json"
backup_format: jsonIf you decide to process the content of backup files, then you might use the ansible.builtin.slurp module.
This module works for both local and remote files.
Using the ansible.builtin.slurp module typically involves two steps:
Use the ansible.builtin.slurp module to read file content.
Add the register keyword to the task so that the task captures the file content.
Decode the captured content so that you can use the content in another task.
You might use the ansible.builtin.debug module to display the output of applied filters and then use the ansible.builtin.set_fact module after verifying that the applied filters have the desired output.
- name: Slurp backup file
ansible.builtin.slurp:
src: /backups/junos1.lab.example.com.json
register: slurped_file
- name: Display decoded slurped_file['content']
ansible.builtin.debug:
var: slurped_file['content'] | b64decode | from_json
- name: Set json_content
ansible.builtin.set_fact:
json_content: >
{{ slurped_file['content'] | b64decode | from_json }}
- name: Show json_content['configuration']['interfaces']
ansible.builtin.debug:
var: >-
json_content['configuration']['interfaces'] 
This line specifies the path to a backup file. | |
This line uses the | |
This task uses the | |
This line displays configured interfaces and is specific to Juniper Junos managed nodes.
By using a backup file in JSON format, you can display configuration information, such as the interfaces on the managed node.
Additionally, you might use the |
For backup files that do not use JSON format, you might use filters such as the regex_findall, regex_search, or split filters for additional data processing.
You can use the resource_manager role from the network.base collection to generate YAML files that contain information about the infrastructure of your managed nodes.
The following example defines the action variable with the persist value.
This action causes the role to save infrastructure information about your managed nodes.
- name: Network Resource Manager
ansible.builtin.include_role:
name: network.base.resource_manager
vars:
action: persist
inventory_directory: ./The persist action expects to find an inventory file named inventory.yaml in the directory defined by the inventory_directory variable.
By default, the inventory_directory variable has the ./inventory value.
If the inventory file in your Ansible project uses the INI format, then you can redirect the output of the ansible-navigator inventory command to create a YAML version of the inventory file:
[user@host project]$ansible-navigator inventory -i inventory --list \--yaml > inventory.yaml
The previous command generates a file with content similar to the following example:
all:
children:
ios:
hosts:
iosxe1.lab.example.com:
ansible_become: true
ansible_become_method: enable
ansible_connection: ansible.netcommon.network_cli
ansible_network_os: cisco.ios.ios
ansible_ssh_private_key_file: ~/.ssh/lab_rsa
ansible_user: student
iosxe2.lab.example.com:
ansible_become: true
ansible_become_method: enable
ansible_connection: ansible.netcommon.network_cli
ansible_network_os: cisco.ios.ios
ansible_ssh_private_key_file: ~/.ssh/lab_rsa
ansible_user: student
ungrouped: {}If your Ansible project already defines host variables in the host_vars directory or group variables in the group_vars directory, then you can delete the variable information from the inventory.yaml file.
After removing the variables, the previous example consists of the following content:
all:
children:
ios:
hosts:
iosxe1.lab.example.com:
iosxe2.lab.example.com:
ungrouped: {}After creating an inventory.yaml file, update your ansible-navigator.yml file or your ansible.cfg file to point to the new inventory file.
You can then delete the previous inventory file.
When you use the persist action of the network.base.resource_manager role, the play creates the host_vars directory in the directory specified by the inventory_directory variable.
In the host_vars directory, the play creates a directory for each managed node and then creates YAML files for the collected resources.
For the iosxe1.lab.example.com managed node, you might see the following files:
[user@host project]$ tree host_vars/iosxe1.lab.example.com/
host_vars/iosxe1.lab.example.com/
├── acls.yaml
├── hostname.yaml
├── interfaces.yaml
├── l2_interfaces.yaml
├── l3_interfaces.yaml
├── lacp.yaml
├── lacp_interfaces.yaml
├── ospf_interfaces.yaml
├── static_routes.yaml
└── vlans.yamlAfter you create these files, you can use the content in these files as regular host variables.
Based on the content of the l3_interfaces.yaml file, you might use the following task to display the IPv4 addresses for the GigabitEthernet1 interface:
- name: Show IPv4 addresses for the GigabitEthernet1 interface
ansible.builtin.debug:
var: >-
hostvars[inventory_hostname]['l3_interfaces'] |
selectattr('name', '==', 'GigabitEthernet1') |
map(attribute='ipv4') |
flatten |
map(attribute='address')This task might result in the following output:
TASK [Show the IPv4 address for the GigabitEthernet1 interface] ****************
ok: [iosxe1.lab.example.com] => {
"hostvars[inventory_hostname]['l3_interfaces'] | selectattr('name', '==', 'GigabitEthernet1') | map(attribute='ipv4') | flatten | map(attribute='address')": [
"172.25.250.20/24"
]
}As with the other methods for gaining infrastructure awareness, you might use the ansible.builtin.set_fact module to make it easier for additional tasks to work with specific pieces of data.
The preceding section discussed methods for collecting information about your current environment and how you might extract pieces of information for further use. Now that you can collect and extract information about your infrastructure, you can generate reports.
You might integrate the extracted infrastructure information into existing reports, or you might create new reports. You can decide on what type of report to create and what information should be included in the report. Although you might create dynamic reports that use simple text files, you might also create interactive reports that you publish to a web server.
Creating a dynamic report for a web server requires additional knowledge related to items such as HTML, XML, CSS, and graphic design.
The following screen capture shows an interactive web page with Layer 1, Layer 2, and Layer 3 information for a couple of network devices. Users can click various web page elements to either reveal or hide information about each network device. This is just one example and you might choose to do something completely different.
![]() |
Creating a dynamic web report might require creating multiple content collections or roles so that the report displays the desired information in the desired format.
The network.toolkit collection available from the https://github.com/network-automation/toolkit Git repository provides a good starting point.