Bookmark this page

Chapter 7.  Automating Network Administration Tasks

Abstract

Goal

Automate common network administration tasks, discussing recommended practices and approaches to cross-vendor automation.

Objectives
  • Configure connectivity to managed network devices, gather device information, and generate a dynamic report that documents the state of your network infrastructure.

  • Generate new device configurations using Jinja2 templates, and use templates to manage existing devices.

  • Simplify tasks by using vendor-independent network resource modules and the ansible.netcommon Ansible Content Collection.

  • Perform rolling network configuration updates, and automatically recover from errors.

Sections
  • Gaining Infrastructure Awareness (and Guided Exercise)

  • Generating Configuration Settings from Jinja2 Templates (and Guided Exercise)

  • Simplifying Tasks with Platform-independent Modules (and Guided Exercise)

  • Performing Rolling Configuration Updates (and Guided Exercise)

Gaining Infrastructure Awareness

Objectives

  • Configure connectivity to managed network devices, gather device information, and generate a dynamic report that documents the state of your network infrastructure.

Collect Information about Your Current Environment

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.

Collecting and Using Facts to Gain Infrastructure Awareness

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:
      - all

If 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_lines

Creating and Using Backup Files to Gain Infrastructure Awareness

Usually, 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: json

If 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:

  1. 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.

  2. 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 1
  register: slurped_file

- name: Display decoded slurped_file['content']
  ansible.builtin.debug:
    var: slurped_file['content'] | b64decode | from_json 2

- name: Set json_content
  ansible.builtin.set_fact: 3
    json_content: >
      {{ slurped_file['content'] | b64decode | from_json }}

- name: Show json_content['configuration']['interfaces']
  ansible.builtin.debug:
    var: >-
      json_content['configuration']['interfaces'] 4

1

This line specifies the path to a backup file.

2

This line uses the content key in the slurped file is Base64 encoded. Use the b64decode filter to decode the value of the content key. Skip using the from_json filter if the file is not a JSON file. For most backup files, this content matches the content of the ansible_facts['net_config'] variable.

3

This task uses the ansible.builtin.set_fact module to make it easier for additional tasks to work with the data.

4

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 json_content['configuration']['routing-options'] variable to display routing options or the json_content['configuration']['snmp'] variable to display the Simple Network Management Protocol (SNMP) configuration.

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.

Saving Configuration Settings Using the Resource Manager Role

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: ./

Important

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.yaml

After 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.

Generating Dynamic Reports

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.

Note

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.

Figure 7.1: Example web report

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.

Revision: do457-2.3-7cfa22a