Automating Nautobot - Lab 05¶
Building a Site Report with Ansible and Nautobot Data¶
- Task 1 - Use Data from the REST API to Build a Markdown Report with Ansible
- Task 2 - Use Data from the GraphQL API to Add More Information to the Report
Task 1 - Use Data from the REST API to Build a Markdown Report with Ansible¶
Nautobot, being the source of truth for a good part of your device data, can also be used in combination with Ansible and Jinja templates to quickly create all sorts of text reports with dynamic information.
Here you will create a playbook that uses both the REST API and the GraphQL API to retrieve data about a Site and its Devices in order to produce a Site Report.
Step 1-1¶
In the /home/ntc/ansible/ folder, create a new file named pb_report.yml. Open it in your editor of choice and add the contents below.
---
- name: GET SITE INFORMATION FROM THE REST API
hosts: localhost
vars:
site_slug: "ams01" # FILL IN WITH PROPER SITE VALUE FOR YOUR SYSTEM
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
tasks:
- name: LOOK UP SITE INFO IN NAUTOBOT
set_fact:
site_response: "{{ query('networktocode.nautobot.lookup',
'sites',
api_filter='slug='+site_slug,
api_endpoint=nautobot_host,
token=nautobot_token,
validate_certs=False) }}"
- debug:
var: site_response
verbosity: 1
- name: SET ONLY THE USEFUL INFORMATION INTO THE VAR
set_fact:
site_info: "{{ site_response[0]['value'] }}"
- debug:
var: site_info
verbosity: 1
- name: RENDER THE SITE REPORT
hosts: localhost
tasks:
- name: RENDER THE SITE REPORT
template:
src: site_report.j2
dest: site_report.md
Note: It has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Step 1-2¶
Let's take a moment to understand what the first part of this playbook does:
- It defines a
site_slugwhich will serve as the input for which Site to create are report for. - It also defines the connection parameters for API access. These are specified in the playbook here directly for the sake of simplicity in the lab environment.
- A query lookup plugin from the Nautobot collection is used to send the query to the Nautobot REST API. This could also very easily be replaced by the generic
urimodule from Ansible.- The only "interesting" parameter is the
api_filterwhich tells the lookup what to query for - in this case, it will get a specific site based on itsslugvalue. - The lookup plugin returns a specific
key/valuedata structure which can be viewed when executing the playbook with a-vparameter to trigger thedebugmodule.
- The only "interesting" parameter is the
- Right now the
debugtasks are there to assist you in understanding what data is retrieved and to link it to the Jinja template later.
Step 1-3¶
In the /home/ntc/ansible/ folder, create a new folder named templates. In it, create a new file named site_report.j2. Open it in your editor of choice and add the contents below.
ntc@nautobot-1:~/labs/ansible$ mkdir templates
ntc@nautobot-1:~/labs/ansible$ touch templates/site_report.j2
# Site report for {{ site_info['name'] }}
- **Tenant**: {{ site_info['tenant']['name'] }}
- **Status**: {{ site_info['status']['label'] }}
- **Region**: {{ site_info['region']['name'] }}
- **Facility**: {{ site_info['facility'] }}
- **Site Type**: {{ site_info['custom_fields']['site_type'] }}
## Statistics
| Type | Count |
|:-----|:------|
| Devices | {{ site_info['device_count']}} |
| Racks | {{ site_info['rack_count']}} |
| Prefixes | {{ site_info['prefix_count']}} |
| VLANs | {{ site_info['vlan_count']}} |
| Circuits | {{ site_info['circuit_count']}} |
| VMs | {{ site_info['virtualmachine_count']}} |
Note: The template extracts interesting (at least for the report!) data from the
site_infofact and formats it with a little Markdown syntax. While the table won't look like much in text format, when the report is rendered as HTML, it will look great!
Step 1-4¶
Execute the pb_report.yml playbook. Spend some time here and run it with a -v for verbosity - this will cause the debug tasks to fire and show you the data being fed into the Jinja template.
# Normal execution to generate the output report.
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_report.yml
# Debugs visible execution to assist in troubleshooting.
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_report.yml -v
Step 1-5¶
Take a look at the freshly generated site_report.md file.
ntc@nautobot-1:~/labs/ansible$ cat site_report.md
# Site report for AMS01
- **Tenant**: Nautobot Airports
- **Status**: Active
- **Region**: Netherlands
- **Facility**: Amsterdam Airport Schiphol
- **Site Type**: POP
## Statistics
| Type | Count |
|:-----|:------|
| Devices | 11 |
| Racks | 8 |
| Prefixes | 39 |
| VLANs | 16 |
| Circuits | 4 |
| VMs | 0 |
If you are working in modern editors like VSCode, you should have a Markdown Preview function that shows you the HTML rendered output. In VSCode you can access it through the Command Palette -> Markdown: Open Preview.
It should look like this:

Task 2 - Use Data from the GraphQL API to Add More Information to the Report¶
The report looks pretty good so far, but what if you could also have details about the site's devices in the same file? For this second chunk of the report, let's use the GraphQL API.
Sometimes you might need to mix and match - for example the Site counts printed in the report above are only exposed via the REST API call.
Since devices are more complex objects and they also have a lot of relationships with other objects, it's a lot easier to build a GraphQL query to get all the data needed.
Step 2-1¶
Remember that GraphQL query built at the end of the previous lab? This is where it comes in handy. This is the new play code that you're adding to the pb_report.yml playbook.
Let's take it step by step:
- The new information here is the GraphQL query - it's added as a play variable. The
|is necessary to define a multiline string in YAML. - Since the Nautobot collection provides a
query_graphqlmodule, it is actually very easy to send the query - the module even registers the returned data in the Ansible host vars for you! - Since the top-level object in the query is
devices, that's the key under which the data coming from the API will be found. The play is setup with adebugtask should you want to see that raw data as provided by the API.
- name: GET DEVICE INFORMATION FROM THE GRAPHQL API
hosts: localhost
tags: gql
vars:
site_slug: "ams01" # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
nautobot_query: |
query {
devices(site: "{{ site_slug }}") {
name
status {
name
}
device_type {
manufacturer {
name
}
part_number
}
device_role {
name
}
primary_ip4 {
interface {
name
}
host
}
}
}
tasks:
- name: GET SITE DEVICES INFORMATION FROM NAUTOBOT
networktocode.nautobot.query_graphql:
url: "{{ nautobot_host }}"
token: "{{ nautobot_token }}"
validate_certs: false
query: "{{ nautobot_query }}"
update_hostvars: true
- debug:
var: devices
verbosity: 1
Note: If you want an easier way to check out the data returned by the API, copy the GraphQL query and paste it into the Nautobot GraphiQL web interface! You will need to replace the
{{ site_slug }}part with an actual value (likeams01) for it to work though.
Step 2-2¶
For reference, the full pb_report.yml playbook should now be as follows:
---
- name: GET SITE INFORMATION FROM THE REST API
hosts: localhost
vars:
site_slug: "ams01" # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
tasks:
- name: LOOK UP SITE INFO IN NAUTOBOT
set_fact:
site_response: "{{ query('networktocode.nautobot.lookup',
'sites',
api_filter='slug='+site_slug,
api_endpoint=nautobot_host,
token=nautobot_token,
validate_certs=False) }}"
- debug:
var: site_response
verbosity: 1
- name: SET ONLY THE USEFUL INFORMATION INTO THE VAR
set_fact:
site_info: "{{ site_response[0]['value'] }}"
- debug:
var: site_info
verbosity: 1
- name: GET DEVICE INFORMATION FROM THE GRAPHQL API
hosts: localhost
tags: gql
vars:
site_slug: "ams01" # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
nautobot_host: "https://REPLACE_WITH_NAUTOBOT_IP/"
nautobot_token: "REPLACE_WITH_NAUTOBOT_TOKEN"
nautobot_query: |
query {
devices(site: "{{ site_slug }}") {
name
status {
name
}
device_type {
manufacturer {
name
}
part_number
}
device_role {
name
}
primary_ip4 {
interface {
name
}
host
}
}
}
tasks:
- name: GET SITE DEVICES INFORMATION FROM NAUTOBOT
networktocode.nautobot.query_graphql:
url: "{{ nautobot_host }}"
token: "{{ nautobot_token }}"
validate_certs: false
query: "{{ nautobot_query }}"
update_hostvars: true
- debug:
var: devices
verbosity: 1
- name: RENDER THE SITE REPORT
hosts: localhost
tasks:
- name: RENDER THE SITE REPORT
template:
src: site_report.j2
dest: site_report.md
Note: It has placeholders for the Nautobot IP address and its token. Make sure you replace them with the ones provided by your instructor on the day!
Note: The GraphQL play comes in after the REST play and before the template rendering!
Step 2-3¶
The Jinja template also needs some additions, update the site_report.j2 file so it looks as follows (adding the ## Devices section at the end):
# Site report for {{ site_info['name'] }}
- **Tenant**: {{ site_info['tenant']['name'] }}
- **Status**: {{ site_info['status']['label'] }}
- **Region**: {{ site_info['region']['name'] }}
- **Facility**: {{ site_info['facility'] }}
- **Site Type**: {{ site_info['custom_fields']['site_type'] }}
## Statistics
| Type | Count |
|:-----|:------|
| Devices | {{ site_info['device_count']}} |
| Racks | {{ site_info['rack_count']}} |
| Prefixes | {{ site_info['prefix_count']}} |
| VLANs | {{ site_info['vlan_count']}} |
| Circuits | {{ site_info['circuit_count']}} |
| VMs | {{ site_info['virtualmachine_count']}} |
## Devices
| Hostname | Role | Vendor | Model | IPv4 | Interface | Status |
|:-----|:-----|:-----|:-----|:-----|:-----|:-----|
{% for device in devices %}
| {{ device['name'] -}}
| {{ device['device_role']['name'] -}}
| {{ device['device_type']['manufacturer']['name'] -}}
| {{ device['device_type']['part_number'] -}}
| {{ device['primary_ip4']['host'] | default("None") -}}
| {{ device['primary_ip4']['interface']['name'] | default("None") -}}
| {{ device['status']['name'] }}
{% endfor %}
Step 2-4¶
Finally, save and run the updated playbook!
# Normal execution to generate the output report.
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_report.yml
# Debugs visible execution to assist in troubleshooting.
ntc@nautobot-1:~/labs/ansible$ ansible-playbook pb_report.yml -v
Note: If you ever want to run the playbook for another site without changing it, you can override the
site_slugfrom the command line:ansible-playbook pb_report.yml -e site_slug=atl01.
Step 2-5¶
The newly generated report should look like this:
ntc@nautobot-1:~/labs/ansible$ cat site_report.md
# Site report for AMS01
- **Tenant**: Nautobot Airports
- **Status**: Active
- **Region**: Netherlands
- **Facility**: Amsterdam Airport Schiphol
- **Site Type**: POP
## Statistics
| Type | Count |
|:-----|:------|
| Devices | 11 |
| Racks | 8 |
| Prefixes | 39 |
| VLANs | 16 |
| Circuits | 4 |
| VMs | 0 |
## Devices
| Hostname | Role | Vendor | Model | IPv4 | Interface | Status |
|:-----|:-----|:-----|:-----|:-----|:-----|:-----|
| ams01-dist-01| distribution| Cisco| WS-C6509-E| None| None| Active
| ams01-edge-01| edge| Arista| DCS-7280CR2-60| 10.11.128.1| Loopback0| Active
| ams01-edge-02| edge| Arista| DCS-7280CR2-60| 10.11.128.2| Loopback0| Active
| ams01-leaf-01| leaf| Arista| DCS-7150S-24| 10.11.128.3| Loopback0| Active
| ams01-leaf-02| leaf| Arista| DCS-7150S-24| 10.11.128.4| Loopback0| Active
| ams01-leaf-03| leaf| Arista| DCS-7150S-24| 10.11.128.5| Loopback0| Active
| ams01-leaf-04| leaf| Arista| DCS-7150S-24| 10.11.128.6| Loopback0| Active
| ams01-leaf-05| leaf| Arista| DCS-7150S-24| 10.11.128.7| Loopback0| Active
| ams01-leaf-06| leaf| Arista| DCS-7150S-24| 10.11.128.8| Loopback0| Active
| ams01-leaf-07| leaf| Arista| DCS-7150S-24| 10.11.128.9| Loopback0| Active
| ams01-leaf-08| leaf| Arista| DCS-7150S-24| 10.11.128.10| Loopback0| Active
Step 2-6¶
If you are working in modern editors like VSCode, you should have a Markdown Preview function that shows you the HTML rendered output. In VSCode you can access it through the Command Palette -> Markdown: Open Preview.
It should look like this:

Step 2-7¶
If you cannot get the preview to work, you can also see a final render of the report here on GitHub.