Demonstration templating a network configuration and pushing it a device
- Use and understand group variables to store the IP addresses we want.
- Use the Jinja2 template lookup plugin
- Demonstrate use of the network automation cli_config module
This step will cover creating Ansible variables for use in an Ansible Playbook. This exercise will use the following IP address schema for loopbacks addresses on rtr1 and rtr2:
Device | Loopback100 IP |
---|---|
rtr1 | 192.168.100.1/32 |
rtr2 | 192.168.100.2/32 |
Variable information can be stored in host_vars and group_vars. For this exercise create a folder named group_vars
:
[student1@ansible networking-workshop]$ mkdir ~/networking-workshop/group_vars
Now create a file in this directory name all.yml
using your text editor of choice. Both vim and nano are installed on the control node.
[student1@ansible networking-workshop]$ nano group_vars/all.yml
The interface and IP address information above must be stored as variables so that the Ansible playbook can use it. Start by making a simple YAML dictionary that stores the table listed above. Use a top level variable (e.g. nodes
) so that a lookup can be performed based on the inventory_hostname
:
nodes:
rtr1:
Loopback100: "192.168.100.1"
rtr2:
Loopback100: "192.168.100.2"
Copy the YAML dictionary we created above into the group_vars/all.yml file and save the file.
All devices are part of the group all by default. If we create a group named cisco only network devices belonging to that group would be able to access those variables.
Create a new template file named template.j2
:
[student1@ansible networking-workshop]$ nano template.j2
Copy the following into the template.j2 file:
{% for interface,ip in nodes[inventory_hostname].items() %}
interface {{interface}}
ip address {{ip}} 255.255.255.255
{% endfor %}
Save the file.
This step will explain and elaborate on each part of the newly created template.j2 file.
{% for interface,ip in nodes[inventory_hostname].items() %}
- Pieces of code in a Jinja template are escaped with
{%
and%}
. Theinterface,ip
breaks down the dictionary into a key namedinterface
and a value namedip
.
- The
nodes[inventory_hostname]
does a dictionary lookup in thegroup_vars/all.yml
file. The inventory_hostname is the name of the hostname as configured in Ansible's inventory host file. When the playbook is executed againstrtr1
inventory_hostname will bertr1
, when the playbook is executed againstrtr2
, the inventory_hostname will bertr2
and so forth.
The inventory_hostname variable is considered a magic variable which is automatically provided.
- The keyword
items()
returns a list of dictionaries. In this case the dictionary's key is the interface name (e.g. Loopback100) and the value is an IP address (e.g. 192.168.100.1)
interface {{interface}}
ip address {{ip}} 255.255.255.255
- Variables are rendered with the curly braces like this:
{{ variable_here }}
In this case the variable name key and value only exist in the context of the loop. Outside of the loop those two variables don't exist. Each iteration will re-assign the variable name to new values based on what we have in our variables.
Finally:
{% endfor %}
- In Jinja we need to specify the end of the loop.
Create the Ansible Playbook config.yml:
[student1@ansible networking-workshop]$ nano config.yml
Copy the following Ansible Playbook to the config.yml file:
---
- name: configure network devices
hosts: rtr1,rtr2
gather_facts: false
tasks:
- name: configure device with config
cli_config:
config: "{{ lookup('template', 'template.j2') }}"
- This Ansible Playbook has one task named configure device with config
- The cli_config module is vendor agnostic. This module will work identically for an Arista, Cisco and Juniper device. This module only works with the network_cli connection plugin.
- The cli_config module only requires one parameter, in this case config which can point to a flat file, or in this case uses the lookup plugin. For a list of all available lookup plugins visit the documentation
- Using the template lookup plugin requires two parameters, the plugin type template and the corresponding template name template.j2.
Execute the Ansible Playbook:
[student1@ansible networking-workshop]$ ansible-playbook config.yml
The output should look as follows.
[student1@ansible ~]$ ansible-playbook config.yml
PLAY [rtr1,rtr2] ********************************************************************************
TASK [configure device with config] ********************************************************************************
changed: [rtr1]
changed: [rtr2]
PLAY RECAP ********************************************************************************
rtr1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
rtr2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Use the command show ip int br
to verify the IP addresses have been confirmed on the network devices.
[student1@ansible networking-workshop]$ ssh rtr1
rtr1#show ip int br | include Loopback100
Loopback100 192.168.100.1 YES manual up up
- The Jinja2 template lookup plugin can allow us to template out a device configuration.
- The
*os_config
(e.g. ios_config) and cli_config modules can source a jinja2 template file, and push directly to a device. If you want to just render a configuration locally on the control node, use the template module. - Variables are mostly commonly stored in group_vars and host_vars. This short example only used group_vars.
The finished Ansible Playbook is provided here for an answer key: config.yml.
The provided Ansible Jinja2 template is provided here: template.j2.
You have completed lab exercise 4
Click here to return to the Ansible Network Automation Workshop