Arista + Ansible – Getting Started

The Ansible 2.1 release made it easier than ever to manage Arista switches.  The following article describes how to leverage Ansible for EOS configuration management.

The Basics

If you’re brand new to Ansible, it might be helpful to take a spin through their Overview and Getting Started just to familiarize yourself with some of the basic concepts.

The Ansible documentation has a great introduction to Ansible for Networking – definitely check it out before reading on.

EOS Modules 

Ansible modules do all of the heavy-lifting, and there’s a module to do just about anything you could possible think of, from copying a file all the way to managing OpenStack or vSphere.  The Ansible 2.1 release introduced four new modules that were specifically designed to work with Arista EOS.  They are:

  • eos_eapi – helps you manage your eAPI configuration
  • eos_config – allows you to specify configuration lines right from your playbook
  • eos_command – run any command on EOS (best for ‘show’ commands)
  • eos_template – send static configuration or generate configuration from a Jinja template. This module has been deprecated as of 2.2 and eos_config should be used instead.

These modules provide the tools to manage any section of the running configuration.  Aside from the running configuration, you can use the eos_command module to do all sorts of things like gather statistics, ensure services are running, check if interfaces are up, mlag is active or even upgrade EOS and install packages – almost anything is possible.

Note:  It’s best to migrate playbooks to use these new modules instead of the modules that were shipped with the arista.eos role.  That role has been deprecated by the modules in Ansible 2.1.

Connection Basics

The traditional connection method for Ansible is SSH.  Typically, when managing a server, you would copy the Ansible Control Host’s SSH key to the server’s authorized_keys for a password-less authentication.  When you run a playbook, Ansible would SSH to the server, copy the module and variables down to the server where the module would be executed.  The output of the module would be collected and sent back to the Ansible Control Host.  A sample task to install Apache on a server would look like:

- name: install the latest version of Apache
  yum: name=httpd state=latest

This model changes a bit when connecting to EOS.  Instead of SSH, EOS can be managed via CLI(ssh) or eAPI(http/s).  The difference in procedure is that we use connection: local in the playbook.  By doing this, the module is not copied to the Arista switch, rather, it stays on the Ansible Control Host and executes locally.  The module itself uses connection parameters that are passed in as arguments to the module to know how to connect to the switch.  So a task that leverages eos_command to get the current EOS version would look like:

- name: Gather info from Show Version
  eos_command:
    commands: 'show version'
    host: dc1-spine1
    username: admin
    password: admin
    transport: eapi
    use_ssl: true

Notice the additional arguments defined in the task to help the module understand how to connect to EOS. Having all of these additional arguments can be cumbersome, so we can use the ‘provider’ argument to wrap them up into one dictionary. This dictionary can live inside group_vars/all or any other host_var or group_var file. For example:

# group_vars/all.yaml
eos_connection:
  host: "{{ inventory_hostname }}"
  username: admin
  password: admin
  transport: eapi
  use_ssl: true

This is handy because as Ansible executes the playbook for each host, it will substitute the inventory_hostname, ie the name that’s listed in the hosts file.  This would change the task to:

- name: Gather info from Show Version
  eos_command:
    commands: 'show version'
    provider: '{{ eos_connection }}'

Note that the former solution, the arista.eos role, required pyeapi and an eapi.conf file.  These new modules remove this dependency which helps you get up and running even faster!

Configuration Management

The easiest way to manage the running configuration on your Arista EOS switch is via eos_template.  This module allows you to leverage the power and flexibility of Jinja to quickly build EOS switch configuration.  Let’s demonstrate using a basic example (download the eos_central_demo source files to follow along).

Say we wanted to implement the following part of a configuration with Ansible:

eos_running-config

Here we have two leaf switches with somewhat similar configuration.  They both require the same Vlans to be configured, but their Ethernet configuration varies somewhat.  We can create a simple Vlan and Ethernet jinja template to manage this configuration.  When creating a template, it’s best to determine what pieces are unique, and which are common.  The unique pieces can become part of our data model, and the common pieces can be statically defined in our template.  Take a look at the following illustration to see how we can start building a data model.

eos_data-model

 

First, we identify the parts of the configuration that are unique. For our ‘vlan’ object, we use attributes ‘vlanid’ and ‘name’.  The top-right box is a YAML interpretation of this dictionary model.  For our Ethernet interface we have the attributes ‘name’, ‘description’ and ‘address.’  These dictionary attributes can be called anything and may vary depending upon your needs.  What’s nice about this approach is that our models are extremely readable, intuitive and vendor-agnostic.

Given this model, we can now create the Jinja templates. Below, we create a ‘vlans’ list. This list contains unique ‘vlan’ entries. At the bottom right we use a Jinja loop to iterate through the ‘vlans’ list and create the corresponding running-configuration.

vlan_jinja

 

Next, let’s create the Jinja template for the Ethernet interface following the same principles:

eth_template

 

Now that we have the building blocks in place, let’s create a playbook to execute these templates.  First we need a ‘hosts’ file to list our inventory:

#hosts
[pod1_leafs]
leaf-a
leaf-b

Since both leaf switches have the same Vlans configured, let’s keep the ‘vlans’ object in a group variable file:

Check out file ‘pod1_leafs’ under group_vars, which contains:

vlans:
 - vlanid: 2
   name: production
 - vlanid: 3
   name: app

Then create host-specific variable files where our data models will reside:

Check out leaf-a file under host_vars

interfaces:
 - name: Ethernet1
   description: [BGP]Spine1
   address: 10.1.1.1/31
 - name: Ethernet2
   description: [BGP]Spine2
   address: 10.1.2.1/31

Also leaf-b file under host_vars

interfaces:
 - name: Ethernet1
   description: [BGP]Spine1
   address: 10.1.1.2/31
 - name: Ethernet2
   description: [BGP]Spine2
   address: 10.1.2.2/31

Then we can create a simple playbook, configure_eos.yaml:

---
- hosts: pod1_leafs
  connection: local
  gather_facts: no

  tasks:
  - name: Configure Arista Vlans
    eos_config:
      src: vlans.j2
      provider: "{{ eos_connection }}"

  - name: ConfigureArista Eth Interfaces
    eos_config:
      src: intf.j2
      provider: "{{ eos_connection }}"

 

We can run this playbook with the following command:

ansible-playbook -i hosts configure_eos.yaml -v
No config file found; using defaults
PLAY [pod1_leafs] **************************************************************

TASK [Configure Arista Vlans] **************************************************
changed: [leaf-b] => {"changed": true, "responses": [{}, {}, {}, {}], "updates": ["vlan 3", "name app", "vlan 2", "name production"]}
changed: [leaf-a] => {"changed": true, "responses": [{}, {}, {}, {}], "updates": ["vlan 3", "name app", "vlan 2", "name production"]}

TASK [ConfigureArista Eth Interfaces] ******************************************
changed: [leaf-b] => {"changed": true, "responses": [{}, {}, {}, {}, {}, {}, {}, {}], "updates": ["interface Ethernet1", "description [BGP]Spine1", "no switchport", "ip address 10.1.1.1/31", "interface Ethernet2", "description [BGP]Spine2", "no switchport", "ip address 10.1.2.1/31"]}
changed: [leaf-a] => {"changed": true, "responses": [{}, {}, {}, {}, {}, {}, {}, {}], "updates": ["interface Ethernet1", "description [BGP]Spine1", "no switchport", "ip address 10.1.1.2/31", "interface Ethernet2", "description [BGP]Spine2", "no switchport", "ip address 10.1.2.2/31"]}

PLAY RECAP *********************************************************************
leaf-a                     : ok=2    changed=2    unreachable=0    failed=0
leaf-b                     : ok=2    changed=2    unreachable=0    failed=0

 

See how with one level of verbosity we can see the exact commands Ansible sent to the switch in the ‘updates’ list. Run the same command again and see what commands it sends.

Here’s a quick breakdown of what happens when we run this command:

playbook_procedure

As we execute each task, the eos_config module is used to gather the existing running-configuration, then compare it against the configuration generated from the Jinja template.  If there are incongruities, the module will send the required commands to update the configuration on the Arista switch.

This looks something like:

proto

Hopefully this post helped illustrate the very simple, yet powerful, approach Ansible takes to help manage EOS switch configuration.  This is just the tip of the iceberg!  Feel free to ask questions below, or contact your Arista account team to learn more!

Check out the resources below for even more help!