Ansible ACI
BONUS

Ansible is an open source community project by RedHat and is the simplest way to automate your IT. Ansible can be used across entire IT teams, ranging from systems administrators to network administrators to developers and managers. Ansible provides an enterprise-ready, task-based, agentless architecture automation solution for not only servers and software, but also networking starting in Ansible 2.1. Further, the Ansible backend makes extensive use of Python. Cisco is a major supported vender. Ansible modules supporting Cisco ACI were added in Ansible 2.4.0, with additional modules available through Cisco's DataCenter GitHub.

Step 1 - Activate virtual environment for requests library

Return to your Terminal window and re-activate the virtual environment for the requests library to use what was previously installed.


cd ~/ltraci-3225
workon acilab

Step 2 - Install Ansible

Like PIP, Ansible requires the Extra Packages for Enterprise Linux (EPEL) to be installed, thus, you must first sudo yum -y install epel-release on CentOS, it's just ready to install on Ubuntu, or pip can be used. Install Ansible via pip:


    pip install ansible==2.7.6

Verify Ansible Version

Verify Ansible was installed by checking the version. You'll be working with the latest Ansible release, Ansible 2.7.6.


    ansible --version

Upon a successful installation and verification of the Ansible version you're output should look as follows:


ansible 2.7.6
 config file = /etc/ansible/ansible.cfg
 configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
 ansible python module location = /usr/lib/python2.7/site-packages/ansible
 executable location = /usr/bin/ansible
 python version = 2.7.5 (default, Aug 4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]

Step 3 - Ansible Playbooks

Ansible calls it's configuration and orchestration framework "playbooks" and is a collections of "play(s)" or tasks for configuration management and deployment to a device or multiple devices in a synchronous or asynchronous fashion. While playbooks can be represented as a single file, Ansible best practices recommend a particular directory structure for playbooks that we'll be building below. This directory structure lets you organize files into smaller configuration files for better reuse, particularly into "roles." Roles in are Ansible are a way to automatically load certain variables, tasks, etc depending on a categorization. A role can be defined for example by device type. Before creating the configuration tasks using Ansible modules, lets create the appropriate directory structure.

In your Terminal window you should be in your user home directory. Create a directory called playbooks, then change directory into your newly created Playbooks directory. Create another directory called aci that will be used to contain the ACI playbook you will be writing and change into that directory.


    cd ~
    mkdir ~/playbooks && cd $_    


    mkdir ~/playbooks/aci && cd $_ 

Within the aci directory create two more directories; group_vars and roles.


    mkdir ~/playbooks/aci/group_vars
    mkdir ~/playbooks/aci/roles

We're going to use Ansible Galaxy to create our roles within the roles directory. Ansible Galaxy is the official community for downloading or creating roles.


    cd ~/playbooks/aci/roles


ansible-galaxy init apic


    >- apic was created successfully 

A command called tree has been yum installed using sudo yum -y install tree (no need to install) to view the directory structure created by Ansible Galaxy. We'll mainly be working within the defaults, tasks, and vars directories. The defaults and vars directories contain variables to be used by the tasks, and as you probably guessed, the tasks will be placed within the tasks directory.


    tree && cd ~/playbooks/aci/

Step 4 - YAML

Under the majority of the role directories you'll notice a ".yml" file. Playbooks are written in a very simple markup language called YAML, which stands for Yet Another Markup Language. YAML is used as it's even easier to read than data structure formats such as XML or JSON that we've previously examined.

YAML files optionally begin with --- at the top of the file to signify the start of the file. Following the file start, comes the Ansible modules written in YAML syntax. YAML syntax for Ansible modules are expressed as a list of key/value pairs. Much like you've dealt with key/value paris thus far in this lab, this is a dictionary. A list simply begins with a "- " (a dash followed by a space) while as in previous examples, dictionaries use ":". Below you can see an example of YAML syntax that contains a list of dictionaries and where the dictionaries contain lists:


- name: CREATE ACI TENANT
  aci_tenant:
    tenant: "{{ tenant_name }}"
    descr: "{{ descr }}"
    host: "{{ inventory_hostname }}"
    username: "{{ user }}"
    password: "{{ pass }}"
    validate_certs: no

- name: CREATE ACI BD
  aci_bd:
    tenant: "{{ tenant_name }}"
    bd: "{{ bd_name }}"
    vrf: "{{ vrf_name }}"
    arp_flooding: True
    l2_unknown_unicast: proxy
    l3_unknown_multicast: flood
    multi_dest: bd-flood
    host: "{{ inventory_hostname }}"
    username: "{{ user }}"
    password: "{{ pass }}"
    validate_certs: no

Step 5 - Ansible Variables

In the above example, you were introduced to two things. 1) You were introduced to and which are actually existing Ansible Network modules. Network modules for tasks will be examined in the next section. 2) You were introduced to variables and how to use them.

Ansible allows you to define and reference variables in your playbook tasks using Jinja2 templating; a templating language for Python. A YAML gotcha for Jinja2 is that if you start the line with a Jinja2 templated variable, for example the above, then the entire line must be in quotes. Variables can be defined in various locations and Ansible has a precedence system for understanding what and when a variable would be overridden if used in more than one location. The precedence for a variable can be seen below. As an example, a variable in the role defaults directory would be overridden by a variable in the playbook global_vars/all directory and file. Further, both of these are overridden by the local vars directory within the specific role, role vars.

  • role defaults (defined in role/defaults/main.yml)
  • inventory file or script group vars
  • inventory group_vars/all
  • playbook group_vars/all
  • inventory group_vars/*
  • playbook group_vars/*
  • inventory file or script host vars
  • inventory host_vars/*
  • playbook host_vars/*
  • host facts
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • role (and include_role) params
  • include params
  • include_vars
  • set_facts / registered vars
  • extra vars (always win precedence)

As an example, each of these network modules have basic requirements for arguments to access the device, albeit, some of these parameters may not apply to the network operating system you work with:

  • host - defines the hostname or IP address of the remote host
  • port - defines the port to connect to
  • username - defines the username to use to authenticate the connection
  • password - defines the password to use to authenticate the connection
  • transport - defines the type of connection transport to build
  • authorize - enables privilege escalation for devices that require it
  • auth_pass - defines the password, if needed, for privilege escalation

By default, the transport for all modules is set to use the "cli". The Ansible ACI network modules do not make utilize the cli, but rather replace the transport with protocol; either http or https. A username and password need to be specified per task as well; much like when a request was preformed in Postman or Python.

Ansible Global Vars

Copy the below YAML into the your Terminal window to create the all file in group_vars and populate the contents of the file for the ansible_connection, username, password, and protocol to be used in the ACI tasks. group_vars/all is where you place universal variables that apply for all devices.


cat <<EOF >> ~/playbooks/aci/group_vars/all
---
ansible_connection: local
user: aciproglab04
pass: cisco.123
EOF

Ansible APIC Role Vars

For each role you're now going to create the variables to be used per device and commonly across all devices specified within the role. The device specific variables can be defined under the host_vars directory for each device - we did not create this in this lab. The common variables to be used across all devices in that role will be defined under roles/apic/vars/main.yml.

Copy the below YAML into the apic role vars directory main.yml file. Remember, each of these are a dictionary with key/value pairs or a dictionary that contains a list of dictionaries.

Note: You will leverage a pre-existing Tenant, Ansible_Tenant and pre-existing VRF, Ansible_VRF that your user has access to.


cat <<EOF >> ~/playbooks/aci/roles/apic/vars/main.yml

tenant_name: aciproglab04
vrf_name: ansible_lab04-vrf 
bd_name: ansible_lab04-bd 
gateway_ip: 10.1.1.1 
subnet_mask: 24 
app_profile_name: ansible_lab04-ap 
epg_name: ansible_lab04-epg
EOF

Step 6 - Ansible ACI Tasks

The Ansible documentation for each network module provides a synopsis of what function the module performs and a table of parameters or keys. The tabulated parameters inform the user which parameters are required for the module to function as a task, which parameters are optional, and what the defaults are for parameters. There are many network modules that exist to configure Tenants, VRFs, BDs, APs, EPGs, Contracts, etc. There's even a network module to perform direct requests using the class or MO URL structure as you have done throughout this lab. This module could be used to perform any task that doesn't have an existing network module.

The Ansible ACI Network modules you'll be using for this section can be found below. These will be used to introduce you to Ansible tasks to create your own Tenant, VRF, BD, AP, and EPG.

  • aci_vrf
  • aci_bd
  • aci_ap
  • aci_epg

Again, you will leverage a pre-existing Tenant, that your user has access to. There is however a Tenant module available:

  • aci_tenant

Copy the tasks below to create your Tenant and the before mentioned constructs:


cat <<EOF >> ~/playbooks/aci/roles/apic/tasks/main.yml

- name: CREATE ACI TENANT VRF 
  aci_vrf: 
    vrf: "{{ vrf_name }}" 
    tenant: "{{ tenant_name }}" 
    hostname: "{{ inventory_hostname }}" 
    username: "{{ user }}" 
    password: "{{ pass }}" 
    use_ssl: yes
    validate_certs: false
  
- name: CREATE ACI TENANT BD 
  aci_bd: 
    tenant: "{{ tenant_name }}" 
    bd: "{{ bd_name }}" 
    vrf: "{{ vrf_name }}" 
    host: "{{ inventory_hostname }}" 
    username: "{{ user }}" 
    password: "{{ pass }}" 
    use_ssl: yes
    validate_certs: false
  
- name: CREATE ACI TENANT BD SUBNET 
  aci_bd_subnet: 
    tenant: "{{ tenant_name }}" 
    bd: "{{ bd_name }}" 
    gateway: "{{ gateway_ip }}" 
    mask: "{{ subnet_mask }}" 
    host: "{{ inventory_hostname }}" 
    username: "{{ user }}" 
    password: "{{ pass }}" 
    use_ssl: yes
    validate_certs: false
  
- name: CREATE ACI TENANT APP PROFILE 
  aci_ap: 
    ap: "{{ app_profile_name }}" 
    tenant: "{{ tenant_name }}" 
    hostname: "{{ inventory_hostname }}" 
    username: "{{ user }}" 
    password: "{{ pass }}" 
    use_ssl: yes
    validate_certs: false
  
- name: CREATE ACI TENANT APP PROFILE EPG 
  aci_epg: 
    epg: "{{ epg_name }}" 
    ap: "{{ app_profile_name }}" 
    tenant: "{{ tenant_name }}" 
    bd: "{{ bd_name }}" 
    hostname: "{{ inventory_hostname }}" 
    username: "{{ user }}" 
    password: "{{ pass }}" 
    use_ssl: yes
    validate_certs: false

EOF

Step 7 - Ansible Host Files

Ansible refers to it's host file as an inventory file. The inventory file has a default location in /etc/ansible/hosts, but can also be specified directly within a playbook locally and used with the -i hosts option, where hosts happens to be the inventory file name. Within the inventory file, you can simply list all the devices/hosts or make use of group names using brackets which classifies devices you are controlling at what times and for what purpose.

Copy or type the below inventory host file.


cat <<EOF >> ~/playbooks/aci/hosts
# hosts file for Ansible playbook
[apic]
10.0.226.41
EOF

Step 8 - Ansible Main Playbook

As previously mentioned, Ansible calls it's configuration and orchestration framework "playbooks" and is a collections of "play(s)" or tasks for configuration management and deployment to a device or multiple devices. While playbooks can be represented as a single file, Ansible best practices recommend a particular directory structure for playbooks that you built using roles for better organization and reuse. You built all of this over the last three sections. You now need to build the main playbook file, which would look like the below:

The below playbook file you will use for this lab designates the following behaviors, for each role ‘x’:

Copy or type the main playbook YAML file.


cat <<EOF >> ~/playbooks/aci/site.yml
---
# main playbook
- hosts: apic
  roles:
    - role: apic
EOF

Step 9 - Ansible Playbook Execution

To execute an Ansible playbook you simply just use ansible-playbook. You are going to use our own host file, so you must specify -i hosts, where -i is for inventory and hosts is the inventory file name. Lastly, you must specify the playbook file, site.yml.


ansible-playbook -i hosts site.yml

Alternatively, you can add -vvv for verbose debugging output for each task that is executed.

Upon successful execution of this playbook, you will have a Tenant with a VRF, BD, AP, and EPG deployed to the ACI fabric using Ansible. Feel free to login to the APIC and verify.

If not already from the previous section, login to the ACI fabric using your user, aciproglab04, and password, cisco.123. Click Tenants, then ALL TENANTS. In the Tenant view, locate your tenant: . Double click your tenant to navigate to it.

Expand the menus to verify the VRF, BD, Application Profile, and EPG created.