Cisco APIC-EM as an Ansible dynamic-inventory

February 10, 2016

APIC-EM is a product of Cisco which delivers SDN to the Enterprise WAN, Campus and Access networks. It provides centralized automation of policy-based application profiles. Through programmability, automated network control helps IT rapidly respond to new business opportunities. APIC-EM can be downloaded at no additional charge using a free membership to the Cisco DevNet community. On the DevNet community there are also a lot of tutorials and sandbox learning labs.
It can discover devices in your network using ip-ranges or CDP-neighbors.

discovery

After discovering devices APIC-EM manages the inventory and triggers data collections periodically. For discovering the so called southbound-API of the APIC is used to connect via CLI to the devices and gather facts. In the future there will be additional southbound mechanisms for connecting to devices (like REST or NX-API).
Every device can be tagged with tags or marked with a location. In our example we will set only the location of some devices to the value location1.

location

If you like to manage network-devices with Ansible and your network consists primarily of Cisco equipment APIC-EM is perfect as a dynamic inventory. My python script uses code snippets of this project but does not function as a separate Ansible module. It is a regular Ansible dynamic inventory which can be specified as an inventory when calling playbooks. A dynamic inventory is built with the help of a python script and has to return a specially formatted JSON hash/dictionary of all the groups to be managed. So the script has to return something like this to the Ansible-playbook (This is only a static example. Normally the python script creates this structure dynamically after querying APIC-EM):

{
    "location1"   : {
        "hosts"   : [ "device1", "device2", "device3" ]
    },
    "location2"   : {
	"hosts"   : [ "device4" ]
    },
    "location3"   : {
	"hosts"   : [ "device5" ]
    },
    "_meta" : {
       "hostvars" : {
          "device1"     : { "device_ip" : 192.168.2.1 },
          "device2"     : { "device_ip" : 192.168.2.2 },
	  "device3"	: { "device_ip" : 192.168.2.3 },
          "device4"	: { "device_ip" : 192.168.2.4 },
	  "device5"	: { "device_ip" : 192.168.2.5 }
       }
    } 
}

Our dynamic inventory script will query the APIC-EM REST-API (the northbound API) and the received results will be formatted correctly to an interpretable JSON hash/dictionary. A very cool thing about the script is the fact that it only returns devices with the reachable state. So you can be sure that the returned devices were reachable a few minutes ago by APIC-EM. The script can be found on my github project ansible-apicem-dynamic-inventory. dynamic-inventory

The playbook regex.yml we are executing uses the dynamic inventory and as an example calls the module ntc_show_command (with show clock) which is part of the amazing project ntc-ansible. The module connects to network-devices with the help of netmiko, executes show clock, parses the output with googles textfsm and returns it to Ansible where we can now use the JSON-structured data. The used playbook regex.yml outputs the data for demonstration purposes with the debug command:

---

- name: GET STRUCTURED DATA BACK FROM CLI DEVICES
  hosts: all 
  connection: local
  gather_facts: False

  tasks:

    - name: GET DATA
      ntc_show_command:
        connection: ssh
        platform: cisco_ios
        command: 'show clock'
        host: "{{ device_ip }}"
        username: "{{ username }}"
        password: "{{ secret }}"
      register:  mydata

    - name: DISPLAY mydata
      debug: msg="{{ mydata.response }}"

Username and password are variables of a vault-file I use with my Ansible-scripts. device_ip is one of the variables received from APIC-EM. The following example shows how to call the Ansible-playbook and displays the debugging output after using the playbook with APIC-EM as dynamic inventory. I limit the hosts to our defined location1 which currently contains 3 devices:

» ansible-playbook regex.yml -i dynamic-inventory --limit "location1" --ask-vault
Vault password:

PLAY [GET STRUCTURED DATA BACK FROM CLI DEVICES] *******************************

TASK [GET DATA] ****************************************************************
ok: [device1]
ok: [device2]
ok: [device3]

TASK [DISPLAY mydata] **********************************************************
ok: [device1] => {
    "msg": [
        {
            "day": "9",
            "dayweek": "Tue",
            "month": "Feb",
            "time": "21:52:50.265",
            "timezone": "MET",
            "year": "2016"
        }
    ]
}
ok: [device2] => {
    "msg": [
        {
            "day": "9",
            "dayweek": "Tue",
            "month": "Feb",
            "time": "21:52:50.332",
            "timezone": "MET",
            "year": "2016"
        }
    ]
}
ok: [device3] => {
    "msg": [
        {
            "day": "9",
            "dayweek": "Tue",
            "month": "Feb",
            "time": "21:52:50.271",
            "timezone": "MET",
            "year": "2016"
        }
    ]
}

PLAY RECAP *********************************************************************
device1     : ok=2    changed=0    unreachable=0    failed=0
device2     : ok=2    changed=0    unreachable=0    failed=0
device3      : ok=2    changed=0    unreachable=0    failed=0

Summarized my project structure is:

  • The dynamic inventory script: dynamic-inventory/dynamic.py
  • The vault yaml with user/password for my IOS devices: group-vars/all/vault.yml
  • The ntc_show_command Ansible module: library/ntc_show_command.py
  • The ntc_template for show clock: ntc_templates/cisco_ios_show_clock.template
  • The playbook: regex.yml
Back...