Welcome to Ansible with Arista devices’s How To!¶
Overview¶
About Ansible modules for Arista EOS automation¶
Arista EOS modules are part of the core modules list of ansible and do not require any additional 3rd part modules to be installed on your server. They are maintained by Ansible Network Team and provides options to manage configuration and get status as well.
A complete list of available eos module is available on Ansible documentation website
Available How-To:¶
Do not forget to install requirements if you want to run tests
- All ansible content is under ansible-content folder.
List of documents:
- Manage
eos
configuration with ansible: eos_config module - Getting status of Arista devices with Ansible: eos_command module
- Jinja2 & YML structures: templating engine
Installation & Requirements¶
Please refer to installation before running content of this repository
Ask question or report issue¶
Please open an issue on Github this is the fastest way to get an answer.
Contribute¶
Contributing pull requests are gladly welcomed for this repository. If you are planning a big change, please start a discussion first to make sure we’ll be able to merge it.
License¶
Project is published under BSD License.
Documentation structure¶
Overview¶
About Ansible modules for Arista EOS automation¶
Arista EOS modules are part of the core modules list of ansible and do not require any additional 3rd part modules to be installed on your server. They are maintained by Ansible Network Team and provides options to manage configuration and get status as well.
A complete list of available eos module is available on Ansible documentation website
Available How-To:¶
Do not forget to install requirements if you want to run tests
- All ansible content is under ansible-content folder.
List of documents:
- Manage
eos
configuration with ansible: eos_config module - Getting status of Arista devices with Ansible: eos_command module
- Jinja2 & YML structures: templating engine
Installation & Requirements¶
Please refer to installation before running content of this repository
Ask question or report issue¶
Please open an issue on Github this is the fastest way to get an answer.
Contribute¶
Contributing pull requests are gladly welcomed for this repository. If you are planning a big change, please start a discussion first to make sure we’ll be able to merge it.
License¶
Project is published under BSD License.
Installation and Lab Environment¶
Requirements¶
Repository requires to install some requirements to be consumed:
- Docker daemon
- docker-topo script
- cEOS-LAB image
- ansible software
In the meantime, it is recommended to run this repository in a virtual-environment. To start such environment, use following commands:
$ python3 -m pip install virtualenv
$ python3 -m virtualenv ansible_training
$ cd ansible_training
$ source bin/activate
Docker and docker-topo¶
Docker installation is platform specific and you should use following links:
Then, install docker-topo
from pip
:
$ python3 -m pip install git+https://github.com/networkop/docker-topo.git
Get cEOS-LAB image¶
With your Arista login, go to download page and download cEOS-LAB image on your laptop. Then, add ceos images to docker:
$ docker import cEOS-lab.tar.xz ceosimage:latest
Run docker topology¶
Once docker-topo is installed, run the docker topology with following commands:
# Clone repository locally
$ git clone https://github.com/titom73/ansible-arista-module-howto.git
# Enter repository
$ cd ansible-arista-module-howto/
# Build docker topology
$ docker-topo --create ansible-demo-topology.yaml
Install requirements¶
Install ansible with pip
:
$ pip install -r requirements.txt
Then, check ansible version (version might have changed until we release this document):
$ ansible --version
ansible 2.7.8
config file = /Users/tgrimonet/Projects/ansible-demo/ansible.cfg
configured module search path = ['/Users/tgrimonet/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /Users/tgrimonet/.venv/ansible-demo/lib/python3.7/site-packages/ansible
executable location = /Users/tgrimonet/.venv/ansible-demo/bin/ansible
python version = 3.7.2 (default, Jan 13 2019, 12:50:01) [Clang 10.0.0 (clang-1000.11.45.5)]
Collecting status from Arista EOS devices¶
Ansible provides 2 different modules to collect information from EOS devices:
eos_facts
: Collect facts from devices such has Hardware, EOS version, configuration.eos_command
: Collect result of any command you send to a device.
Collecting facts from devices.¶
This module collects all device facts from remote device. All keys found on device are prepend with ansible_net_
. In addition of ](https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values) collecting by Ansible, this module also collects following keys:
- All IPv4 addresses configured on the device
- All IPv6 addresses configured on the device
- The current active config from the device
- All file system names available on the device
- The fully qualified domain name of the device
- The list of fact subsets collected from the device
- The configured hostname of the device
- The image file the device is running
- A hash of all interfaces running on the system
- The available free memory on the remote device in Mb
- The total memory on the remote device in Mb
- The model name returned from the device
- The list of LLDP neighbors from the remote device
- The serial number of the remote device
- The operating system version running on the remote device
Collect all facts and display some variables¶
Below is a playbook example that grab all facts, register them into a variable named facts
and then display output
---
- name: Run commands on remote LAB devices
hosts: lab
connection: network_cli
gather_facts: false
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Collect all facts from device
eos_facts:
provider: "{{arista_credentials}}"
gather_subset:
- all
register: facts
- name: Display result
debug:
msg: "Model is {{facts.ansible_facts.ansible_net_model}} and it is running {{facts.ansible_facts.ansible_net_version}}"
Output of this playbook is quite easy to:
$ ansible-playbook pb.get.commands.yaml --limit ceos1
[...]
TASK [Display result] ********
ok: [ceos01] => {
"msg": "Model is cEOS and it is running 4.21.1.1F"
}
[...]
Save device configuration from facts¶
To save configuration, we can collect only configuration fact from device and save output to a file like in playbook below:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: false
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Create output directory (if missing)
file:
path: "{{playbook_dir}}/config"
state: directory
- name: Collect all facts from device
eos_facts:
provider: "{{arista_credentials}}"
gather_subset:
- config
register: facts
- name: copy collected configuration in configuration/text directory
copy:
content: "{{ facts.ansible_facts.ansible_net_config }}"
dest: "{{ playbook_dir }}/config/{{ inventory_hostname }}.conf"
Playbook will create a folder named config
if it is missing and then save config files:
$ ansible-playbook pb.get.commands.yaml
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos01]
ok: [ceos02]
TASK [Create output directory (if missing)] ********
ok: [ceos01]
ok: [ceos02]
TASK [Collect all facts from device] ********
ok: [ceos01]
ok: [ceos02]
TASK [copy collected configuration in configuration/text directory] ********
changed: [ceos01]
changed: [ceos02]
PLAY RECAP ********
ceos01 : ok=4 changed=1 unreachable=0 failed=0
ceos02 : ok=4 changed=1 unreachable=0 failed=0
And from your shell perspective:
$ tree -L 2
.
├── ansible.cfg
├── authentication.yaml
├── config
│ ├── ceos1.conf
│ └── ceos2.conf
├── group_vars
├── host_vars
│ ├── ceos1
│ └── ceos2
Collecting result of commands¶
Collect full command result:¶
Another module to collect status of EOS devices is eos_command
module. It gives you a method to run commandon any Arista device and save result for further tasks.
Next playbook will monitor status of BGP peers:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: false
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Collect BGP Status
eos_command:
commands:
- enable
- show ip bgp summary
provider: "{{arista_credentials}}"
register: bgp_status
- name: Display result
debug:
var: bgp_status
And the result is (truncated):
$ ansible-playbook pb.collect.status.bgp.yaml
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
ok: [ceos2]
TASK [Collect BGP Status] ********
ok: [ceos2]
ok: [ceos1]
TASK [Display result] ********
ok: [ceos1] => {
"bgp_status": {
"changed": false,
"failed": false,
"stdout": [
{},
{
"vrfs": {
"default": {
"asn": 65001,
[...]
PLAY RECAP ********
ceos1 : ok=3 changed=0 unreachable=0 failed=0
ceos2 : ok=3 changed=0 unreachable=0 failed=0
Filter results using loop¶
Because Output is very verbose, we can reduce a bit to focus only on status of any configured peers by using with_dict
loop management:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: false
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Collect BGP Status
eos_command:
commands:
- enable
- show ip bgp summary
provider: "{{arista_credentials}}"
register: bgp_status
- name: Display result
debug:
msg: "Peering with {{item.key}} is {{item.value.peerState}}"
with_dict: "{{bgp_status.stdout[1].vrfs.default.peers}}"
And output is quite shorter than previous try:
$ ansible-playbook pb.collect.status.bgp.yaml
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
ok: [ceos2]
TASK [Collect BGP Status] ********
ok: [ceos2]
ok: [ceos1]
TASK [Display result] ********
ok: [ceos1] => (item={'key': '10.0.0.2', 'value': {'msgSent': 33, 'inMsgQueue': 0, 'prefixReceived': 0, 'upDownTime': 1552640949.496501, 'version': 4, 'msgReceived': 32, 'prefixAccepted': 0, 'peerState': 'Established', 'outMsgQueue': 0, 'underMaintenance': False, 'asn': 65002}}) => {
"msg": "Peering with 10.0.0.2 is Established"
}
ok: [ceos2] => (item={'key': '10.0.0.1', 'value': {'msgSent': 32, 'inMsgQueue': 0, 'prefixReceived': 0, 'upDownTime': 1552640948.972306, 'version': 4, 'msgReceived': 32, 'prefixAccepted': 0, 'peerState': 'Established', 'outMsgQueue': 0, 'underMaintenance': False, 'asn': 65001}}) => {
"msg": "Peering with 10.0.0.1 is Established"
}
PLAY RECAP ********
ceos1 : ok=3 changed=0 unreachable=0 failed=0
ceos2 : ok=3 changed=0 unreachable=0 failed=0
Wait for a specific result¶
Waiting for a specific result is useful to set a task to fail or success depending on a change propagating in the network.
In this example, we are going to put one device in maintenance mode. To do that, we use a playbook to configure device in maintenance mode using eos_config and then waiting for result of this change with command show maintenance
---
- name: Run commands on remote LAB devices
hosts: ceos1
connection: local
gather_facts: false
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure device hostname from lines
eos_config:
provider: "{{arista_credentials}}"
lines:
- quiesce
parents:
- maintenance
- unit System
- name: Wait for device to change to maintenance mode (10sec)
eos_command:
provider: "{{arista_credentials}}"
commands: show maintenance
wait_for: result[0]['units']['System']['state'] eq 'underMaintenance'
interval: 2
retries: 5
In case of these 10 seconds (5 tries with 2 seconds in between) are not enough, then, task fails:
$ ansible-playbook pb.collect.waitfor.maintenance.yaml
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
TASK [Configure device hostname from lines] ********
ok: [ceos1]
TASK [Wait for device to change to maintenance mode (10sec)] ********
fatal: [ceos1]: FAILED! => {"changed": false, "failed_conditions": ["result[0]['units']['System']['state'] eq 'underMaintenance'"], "msg": "One or more conditional statements have not been satisf
ied"}
PLAY RECAP ********
ceos1 : ok=2 changed=0 unreachable=0 failed=1
But when device changes to underMaintenance
, then, task is a success:
$ ansible-playbook pb.collect.waitfor.maintenance.yaml
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
TASK [Configure device hostname from lines] ********
ok: [ceos1]
TASK [Wait for device to change to maintenance mode (10sec)] ********
ok: [ceos1]
PLAY RECAP ********
ceos1 : ok=3 changed=0 unreachable=0 failed=0
Configure Arista EOS with Ansible¶
eos_config
is a core module managed by Ansible network team. As this module is part of the core, there is no need to install additional Ansible module with ansible-galaxy
As eos_*
modules are part of core engine, we can use ansible options to run tests and capture configuration changes when running playbooks:
--check
option: When ansible-playbook is executed with--check
it will not make any changes on remote systems. Modules will report what changes they would have made rather than making them.--diff
option: When this flag is supplied and the module supports this, Ansible will report back the changes made or, if used with –check, the changes that would have been made.
These options are generally called dry-run mode.
Apply lines of configurations to devices.¶
Example playbooks: ‘pb.config.lines.simple.yaml’
Basic lines of configuration¶
To push lines of configuraiton to device, a small playbook should be like this:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure device hostname from lines
eos_config:
provider: "{{arista_credentials}}"
lines:
- "hostname MyHostname"
- "ntp server 1.1.1.1"
In this playbook, we use an authentication provider for ARISTA products as described in the home page
When applying this playbook, output is like below:
using--diff
option display changes applied by tasks
$ ansible-playbook pb.config.lines.simple.yaml --diff
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
ok: [ceos2]
TASK [Configure device hostname from lines] ********
--- system:/running-config
+++ session:/********_1551450161-session-config
@@ -2,7 +2,9 @@
!
transceiver qsfp default-mode 4x10G
!
-hostname CEOS2
+hostname MyHostname
+!
+ntp server 1.1.1.1
!
spanning-tree mode mstp
!
changed: [ceos2]
--- system:/running-config
+++ session:/********_1551450161-session-config
@@ -2,7 +2,7 @@
!
transceiver qsfp default-mode 4x10G
!
-hostname CEOS1
+hostname MyHostname
!
ntp server 1.1.1.1
!
changed: [ceos1]
PLAY RECAP ********
ceos1 : ok=2 changed=1 unreachable=0 failed=0
ceos2 : ok=2 changed=1 unreachable=0 failed=0
Dynamic lines of configuration¶
Even if it is not the easiest way to play with, you can include Jinja2 variables in your configuration lines. These Jinja2 variables shall be defined previously in your host_vars
and / or group_vars
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure device hostname from lines
eos_config:
provider: "{{arista_credentials}}"
lines:
- "hostname {{inventory_hostname}}-ansible"
- "ntp server 1.1.1.1"
Below is output of a change when running this specific playbooK with --diff
TASK [Configure device hostname from lines] ********
--- system:/running-config
+++ session:/********_1551450491-session-config
@@ -2,7 +2,7 @@
!
transceiver qsfp default-mode 4x10G
!
-hostname MyHostname
+hostname ceos2-automation
!
ntp server 1.1.1.1
!
Lines of configuration within a block¶
Assuming we want to configure an interface, we have to first enter to this block and then push configuration like this:
Arista interace configuration:
!
interface Ethernet 1
description My Wonderful description
!
To achieve this configuration with ansible module, syntax should be like this:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure interface description
eos_config:
provider: "{{arista_credentials}}"
lines:
- description My Wonderful description
parents: interface Ethernet 1
replace: block
With a --diff
, we can that description is applied to interface Ethernet 1
[...]
changed: [ceos1]
--- system:/running-config
+++ session:/********_1551451836-session-config
@@ -13,6 +13,7 @@
!
interface Ethernet1
+ description My Wonderful description
!
interface Ethernet2
!
[...]
PLAY RECAP ***
ceos1 : ok=4 changed=1 unreachable=0 failed=0
ceos2 : ok=4 changed=1 unreachable=0 failed=0
And the result is quite simple:
ceos1-automation#show interfaces description
Interface Status Protocol Description
Et1 up up My Wonderful description
Et2 up up
Apply configuration file to device¶
When playing with jinja2 templates, it is easier to generate a file and then push it to devices instead of applying line by line.
eos_config
support a mechanism to push a config files from your server to remote devices. This file can be either a plain text file or a JINJA2 template. In case of a template, rendering will be done first by ansible and then result will be applied on devices.
Apply a config file¶
To apply a plain text config, following playbook should be used:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure interface description
eos_config:
provider: "{{arista_credentials}}"
src: inputs/generic-config.cfg
Config file can be a complete Arista configuration or just a snippet of the configuration you want to update.
In this example, inputs/generic-config.cfg
has following content:
interface Ethernet2
description Description from generic configuration block
!
And running playbook pb.config.file.yaml
with --diff
option shows all changes:
$ ansible-playbook pb.config.file.yaml --diff
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
ok: [ceos2]
TASK [Configure interface description] ********
--- system:/running-config
+++ session:/********_1551711841-session-config
@@ -13,6 +13,7 @@
interface Ethernet1
!
interface Ethernet2
+ description Description from generic configuration block
!
no ip routing
!
changed: [ceos2]
--- system:/running-config
+++ session:/********_1551711841-session-config
@@ -13,6 +13,7 @@
interface Ethernet1
!
interface Ethernet2
+ description Description from generic configuration block
!
no ip routing
!
changed: [ceos1]
PLAY RECAP ********
ceos1 : ok=2 changed=1 unreachable=0 failed=0
ceos2 : ok=2 changed=1 unreachable=0 failed=0
Apply a template¶
eos_config
also supports a JINJA2 template as an input file. In this scenario, ansible will run template rendering locally and then will push result to devices. It means that your configuration will have some content specific per device and / or collected in a previous task.
This section will not describe JINJA2 syntax. A specific page is available with some hints about jinja2 syntax and YAML structures.
Following template creates a basic SNMP configuration with generic fields. Only thing is chassis-id
will be set with inventory_hostname
defined in our inventory.ini
file.
snmp-server chassis-id {{inventory_hostname}}
snmp-server contact demo@acme.com
snmp-server location "cEOS Virtual lab"
Playbook is very close to playbook in Apply a config file
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure SNMP information with template
eos_config:
provider: "{{arista_credentials}}"
src: "inputs/template-config.j2"
The main difference is src
field where you put a template file instead of a plan text configuration.
As you can see in extract below, content is built on a per device approach:
$ ansible-playbook pb.config.template.yaml --diff
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
ok: [ceos2]
TASK [Configure SNMP information with template] ********
--- system:/running-config
+++ session:/********_1551713015-session-config
@@ -3,6 +3,10 @@
transceiver qsfp default-mode 4x10G
!
hostname ceos1
+!
+snmp-server chassis-id ceos1
+snmp- server contact demo@acme.com
+snmp-server location "cEOS Virtual lab"
!
spanning-tree mode mstp
!
changed: [ceos1]
--- system:/running-config
+++ session:/********_1551713015-session-config
@@ -3,6 +3,10 @@
transceiver qsfp default-mode 4x10G
!
hostname ceos2
+!
+snmp-server chassis-id ceos2
+snmp-server contact demo@acme.com
+snmp-server location "cEOS Virtual lab"
!
spanning-tree mode mstp
!
changed: [ceos2]
PLAY RECAP ********
ceos1 : ok=2 changed=1 unreachable=0 failed=0
ceos2 : ok=2 changed=1 unreachable=0 failed=0
Intended configuration approach¶
Pushing intended configuration is probably the best approach in an automation workflow: your device configuration does not deviate from your expectations as complete configuration is pushed by ansible.
To work in this approach, you need to build complete configuration using either a complete template or by assembling rendered templates. Once it is done, you can push and replace configuration with following syntax:
tasks:
- name: Load a config in an intended way
eos_config:
provider: "{{arista_credentials}}"
src: "{{ lookup('file', 'inputs/{{inventory_hostname}}-master.cfg') }}"
replace: 'config'
This approach can also be applied with template:
tasks:
- name: Intended configuration management
eos_config:
provider: "{{arista_credentials}}"
src: "device-configuration.j2"
replace: "config"
And finally, how to save config¶
eos_config
module can automatically save config to the startup_config. It provides 3 different options to do that. The save
action is started by either save or save_when keywords:
- save: automatically save the running-config to startup-config after any execution of the task.
- save_when: gives an option to select when ansible save running-config to startup-config:
always
: Equal to save keywordmodified
: The running-config will only be copied to the startup-config if it has changed since the last save to startup-configchanged
: (ansible >= 2.5) The running-config will only be copied to the startup-config if the task has made a changenever
: Well, running will never be copied to startup-config. Can be useful for validation purpose
So the last playbook should be like this one:
---
- name: Run commands on remote LAB devices
hosts: all
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: Configure SNMP information with template
eos_config:
provider: "{{arista_credentials}}"
src: "inputs/template-config.j2"
save_when: changed
Get diff between running and intended configuration¶
In cas we generate complete configuration, it is nice to capture the deviation between our target (generated configuration) and running configuration on devices.
So to do that, eos_config
has a special option named diff_against: intended
and then we defined what we named intended configuration.
---
- name: Run commands on remote LAB devices
hosts: ceos1
connection: local
gather_facts: no
pre_tasks:
- include_vars: "authentication.yaml"
tasks:
- name: diff the running config against a master config
eos_config:
provider: "{{arista_credentials}}"
diff_against: intended
intended_config: "{{ lookup('file', 'inputs/{{inventory_hostname}}-master.cfg') }}"
Output of the pb.config.intended.yaml playbook is the following:
$ ansible-playbook pb.configure.intended.yaml --check --diff
PLAY [Run commands on remote LAB devices] ********
TASK [include_vars] ********
ok: [ceos1]
TASK [diff the running config against a master config] ********
--- before
+++ after
@@ -1,16 +1,10 @@
transceiver qsfp default-mode 4x10G
-hostname ceos1
-snmp-server chassis-id ceos1
-snmp-server contact demo@acme.com
-snmp-server location "cEOS Virtual lab"
+hostname CEOS1-TEST
spanning-tree mode mstp
no aaa root
username ******** privilege 15 secret sha512 $6$j80NtRkV0CMlgXPS$a0.JbwuO1NMvIthS4eu6dEMHIV9gNGRRFf5SI6qNu5g4I3zxinnVrSMyj8EkQ1V/x7ORAWwe5CpYmgQME2jad1
interface Ethernet1
interface Ethernet2
- description My Wonderful description
- switchport trunk allowed vlan 12
- switchport mode trunk
no ip routing
management api http-commands
no shutdown
changed: [ceos1]
PLAY RECAP ********
ceos1 : ok=2 changed=1 unreachable=0 failed=0
As you can see, we have a complete view of the configuration deviation between running and intended.
Note: this module requires to be run with –check flag.
YAML Structures in Jinja2¶
Data structure to use YAML
with JINJA2
templates. YAML
keeps data stored as a map containing keys and values associated to those keys.
Basic¶
Generic key allocation¶
YAML
input data structure
---
time_zone: Europe/Paris
hostname: myDevice
comment: “This device is a fake one”
JINJA2
to consume YAML
data structure
{% if time_zone is defined %}
clock timezone {{ time_zone }};
{% endif %}
Structured key allocation¶
YAML will consider lines prefixed with more spaces than parent key are contained inside it
---
routing_policy:
communities:
myCommunity: 99:1
tenant2: 99:2
Data structure similar to this YAML representation is :
routing_policy = {
"communities": {
"myCommunity": "99:1",
"tenant2": "99:2"
}
}
List Management¶
YAML List¶
Assuming following data structure in YAML
---
ntp_servers:
- 8.8.8.8
- 4.4.4.4
Access data from JINJA2 can be done with the following code:
{% for ntp_server in ntp_servers %}
ntp server {{ ntp_server }};
{% endfor %}
Data structure similar to this YAML representation is :
ntp_servers = ['8.8.8.8', '4.4.4.4']
YAML Dictionary¶
Assuming following data structure in YAML
---
vlans:
10: descr1
20: descr2
30: descr3
Access data from JINJA2 can be done with the following code:
{% for vlan_id, descr in vlans.items() %}
vlan {{ vlan_id }}
description {{ descr }}
{% endfor %}
Data structure similar to this YAML representation is :
vlans = {10: "descr1", 20: "descr2", 30: "descr3"}
YAML Maps¶
Assuming following data structure in YAML
---
interfaces:
- name: "ge-0/0/0"
descr: "Blah"
- name: "ge-0/0/1"
descr: "comment"
Access data from JINJA2 can be done with the following code:
{% for interface in interfaces %}
interface {{interface.name}} {
description {{interface.descr}};
{% endfor %}
Data structure similar to this YAML representation is :
interfaces = [{"name": "ge-0/0/0", "descr": "Blah"},
{"name": "ge-0/0/1", "descr": "comment"}]
Advanced Jinja2 syntax¶
Update variable in a Loop¶
Syntax below allows user to update value of a variable within a loop and access to it after in a different jinja2 block:
{% set ns = namespace (dev_state = "disable") %}
{% for portname, portlist in topo[inventory_hostname].iteritems() %}
{% if portlist.state == "enable" %}
{% set ns.dev_state = "enable" %}
{% endif %}
{% endfor %}
status: {{ns.dev_state|default("enable")}}
Use IPaddr within Jinja2 template¶
Assuming following yaml
definition:
id: 10
underlay:
networks:
loopbacks: 10.0.0.0/16
Jinja gives option to build IP address within loopback
network with following syntax where id
is the idth in the network:
loopback_ip: {{ underlay.networks.loopbacks | ipaddr(id) | ipaddr('address') }}
Another example:
{{ tenant.interconnect_prefix | ipaddr(6)}}
combined with interconnect_prefix: 172.25.10.16/28
resulst in 172.25.10.22
Manage Jinja2 rendering indentation¶
Jinja2 can manage whitespace and tabular indentation with lstrip_blocks
and trim_blocks
options:
trim_blocks
: If this is set toTrue
the first newline after a block is removed (block, not variable tag!). Defaults toFalse
.lstrip_blocks
: If this is set toTrue
leading spaces and tabs are stripped from the start of a line to a block. Defaults toFalse
.
To manage these options, just put this line in your template:
#jinja2: lstrip_blocks: "True (or False)", trim_blocks: "True (or False)"
...
Example
Using this template:
{% for host in groups['webservers'] %}
{% if inventory_hostname in hostvars[host]['ansible_fqdn'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ hostvars[host]['ansible_fqdn'] }} {{ hostvars[host]['inventory_hostname'] }} MYSELF
{% else %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ hostvars[host]['ansible_fqdn'] }} jcs-server{{ loop.index }} {{ hostvars[host]['inventory_hostname'] }}
{% endif %}
{% endfor %}
lstrip_block=False
Rendering is similar to :
172.16.25.1 spine1
172.16.25.3 spine2
172.16.25.4 spine3
lstrip_block=true
Rendering should be:
172.16.25.1 spine1
172.16.25.3 spine2
172.16.25.4 spine3
Loop management¶
Jinja2 has built-in option to manage loop information:
loop.index
: The current iteration of the loop. (1 indexed)
loop.index0
: The current iteration of the loop. (0 indexed)
loop.revindex
: The number of iterations from the end of the loop (1 indexed)
loop.revindex0
: The number of iterations from the end of the loop (0 indexed)
loop.first
: True if first iteration.
loop.last
: True if last iteration.
loop.length
: The number of items in the sequence.
loop.cycle
: A helper function to cycle between a list of sequences. See the explanation below.
loop.depth
: Indicates how deep in deep in a recursive loop the rendering currently is. Starts at level 1
loop.depth0
: Indicates how deep in deep in a recursive loop the rendering currently is. Starts at level 0