Stage 4.3 - Source Of Truth

Return to Workshop

Stage 4 Source Of Truth

Estimated time to complete: 45 minutes

Stages

We will run through some of the concepts to understand how to use Nautobot as a source of truth to validate changes.

Using a source of truth (SoT) will define the intended or desired state of the network infrastructure and help validate changes before they are pushed to production.


Here are the requirements for Stage 4

Requirements

Here is a diagram of Stage 4. This shows all the technology we will be using in Stage 4.

It also defines the use cases we will be working on in Stage 4.

Diagram

Here is a summary of Stage 4

Stage 4 Summary

Let’s Build A Nautobot Server

Login to Server 2

On Server 2

Run the following command to bring up a Nautobot container

This is an all in one lab instance of Nautobot. This is not for Production.
docker run -itd --restart=always --name nautobot -p 8000:8000 --env NAPALM_USERNAME="admin" --env NAPALM_PASSWORD="admin" networktocode/nautobot-lab:1.6.2

This will spin up nautobot server in minutes

Create a superuser account after the container comes up

Run the following command

docker exec -it nautobot nautobot-server createsuperuser
cloud_user@ed26757f4b2c:~/nautobot$ docker exec -it nautobot nautobot-server createsuperuser
Username: support
Email address: knorton@presidio.com
Password:
Password (again):
Superuser created successfully.
cloud_user@ed26757f4b2c:~/nautobot$

Let’s Configure The Nautobot Server

Log into the Nautobot server

Open a web browser and go to:

http://server2:8000 (1)
1 Replace server2 with IP address or FQDN of your server 2

Click Login in the upper right-hand corner

Enter the superuser username and password you just created

Notice nothing is configured, but browse around

s4 25
Figure 1: Login To Nautobot

s4 26
Figure 2: Welcome To Nautobot

Let’s Configure A Nautobot Server

As you can see there is no data in the Source Of Truth (SOT)

Let’s add some data via the API and web interface

First, we need to create a token on the Nautobot server

Once you are logged into Nautobot

Go to the pull down of your account name in the upper right hand corner and select admin

Under Users → click on Tokens

Click add in the right hand corner

Select your username and click save

Copy the new token down to safe location

s4 27
Figure 3: Nautobot Token

Let’s hop onto server 1 (the server running the GitLab runner) and install some Ansible plugins for Nautobot

Login as a user

Install the following plugins – pynautobot and the nautobot ansible collection v4.5

Run the following command:

pip3 install pynautobot==1.5.0
ansible-galaxy collection install networktocode.nautobot:4.5.0

Here is the progression of the commands above

cloud_user@ed26757f4b1c:/home/cloud_user$ pip3 install pynautobot==1.5.0 (1)
Collecting pynautobot==1.5.0
  Downloading pynautobot-1.5.0-py3-none-any.whl (33 kB)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/lib/python3/dist-packages (from pynautobot==1.5.0) (1.25.8)
Collecting requests<3.0.0,>=2.30.0
  Downloading requests-2.31.0-py3-none-any.whl (62 kB)
     |████████████████████████████████| 62 kB 1.9 MB/s
Collecting charset-normalizer<4,>=2
  Downloading charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (141 kB)
     |████████████████████████████████| 141 kB 38.8 MB/s
Requirement already satisfied: certifi>=2017.4.17 in /usr/lib/python3/dist-packages (from requests<3.0.0,>=2.30.0->pynautobot==1.5.0) (2019.11.28)
Requirement already satisfied: idna<4,>=2.5 in /usr/lib/python3/dist-packages (from requests<3.0.0,>=2.30.0->pynautobot==1.5.0) (2.8)
Installing collected packages: charset-normalizer, requests, pynautobot
  WARNING: The script normalizer is installed in '/home/cloud_user/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed charset-normalizer-3.3.2 pynautobot-1.5.0 requests-2.31.0
cloud_user@ed26757f4b1c:~$ ansible-galaxy collection install networktocode.nautobot:4.5.0 (1)
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/networktocode-nautobot-4.5.0.tar.gz to /home/cloud_user/.ansible/tmp/ansible-local-137529gsphhq0t/tmp_4p3bt4j/networktocode-nautobot-4.5.0-34yakins
Installing 'networktocode.nautobot:4.5.0' to '/home/cloud_user/.ansible/collections/ansible_collections/networktocode/nautobot'
networktocode.nautobot:4.5.0 was installed successfully
1 DON’T Install the latest version of the nautobot ansible plugin

Let’s add the token to the environmental variables of the server

In production the recommendation would be to encrypt the token with a Vault application from Ansible or Hashicorp, but this is just for lab purposes

cloud_user@ed26757f4b1c:~/nautobot/add_device$ export NB_TOKEN="fbf4caacbd265dfc88aa430be6c31650a84179a7" (1)
cloud_user@ed26757f4b1c:~/nautobot/add_device$ export NB_HOST="ed26757f4b2c.mylabserver.com" (2)
cloud_user@ed26757f4b1c:~/nautobot/add_device$ printenv
SHELL=/bin/bash
NB_TOKEN=fbf4caacbd265dfc88aa430be6c31650a84179a7
NB_HOST=ed26757f4b2c.mylabserver.com
1 Replace your token in the command above
2 Replace your Nautobot server name in the command above

Then cd into the ~/network-automation/infra/nautobot/ directory to run the playbook

cd ~/network-automation/infra/nautobot/

This will add data to your Nautobot server

Run the following playbook

ansible-playbook ~/network-automation/infra/nautobot/add_info.yaml -vv

Here is the output from the playbook

cloud_user@ed26757f4b1c:~$ ansible-playbook ~/network-automation/infra/nautobot/add_info.yaml -vv
ansible-playbook [core 2.12.10]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/cloud_user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/cloud_user/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible-playbook
  python version = 3.8.10 (default, Nov 22 2023, 10:22:35) [GCC 9.4.0]
  jinja version = 2.10.1
  libyaml = True
Using /etc/ansible/ansible.cfg as config file
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.

PLAYBOOK: add_info.yaml ***************************************************************************************************************
1 plays in /home/cloud_user/network-automation/infra/nautobot/add_info.yaml

PLAY [ADD INFO TO NAUTOBOT] ***********************************************************************************************************
META: ran handlers

TASK [ADD SITE] ***********************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:7
changed: [localhost] => {"changed": true, "msg": "site Connecticut created", "site": {"asn": null, "comments": "", "computed_fields": "", "contact_email": "", "contact_name": "", "contact_phone": "", "created": "2023-12-18", "custom_fields": {}, "description": "", "display": "Connecticut", "facility": "", "id": "0627a5df-5138-4c4e-b263-b203f4085956", "last_updated": "2023-12-18T22:02:02.227340Z", "latitude": null, "longitude": null, "name": "Connecticut", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/sites/0627a5df-5138-4c4e-b263-b203f4085956/notes/", "physical_address": "", "region": null, "relationships": "", "shipping_address": "", "slug": "connecticut", "status": "active", "tags": [], "tenant": null, "time_zone": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/sites/0627a5df-5138-4c4e-b263-b203f4085956/"}}

TASK [ADD MANUFACTURER] ***************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:15
changed: [localhost] => {"changed": true, "manufacturer": {"computed_fields": "", "created": "2023-12-18", "custom_fields": {}, "description": "Arista", "display": "Arista", "id": "2c1dfb6d-4d46-4be0-aec0-c9cea0651d0c", "last_updated": "2023-12-18T22:02:03.618867Z", "name": "Arista", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/manufacturers/2c1dfb6d-4d46-4be0-aec0-c9cea0651d0c/notes/", "relationships": "", "slug": "arista", "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/manufacturers/2c1dfb6d-4d46-4be0-aec0-c9cea0651d0c/"}, "msg": "manufacturer Arista created"}

TASK [ADD LOCATION TYPE] **************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:23
changed: [localhost] => {"changed": true, "location_type": {"computed_fields": "", "content_types": [], "created": "2023-12-18", "custom_fields": {}, "description": "", "display": "Network Closets", "id": "d498e24c-e809-4972-a1d1-8cd0833ed7ff", "last_updated": "2023-12-18T22:02:04.677708Z", "name": "Network Closets", "nestable": false, "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/location-types/d498e24c-e809-4972-a1d1-8cd0833ed7ff/notes/", "parent": null, "relationships": "", "slug": "network-closets", "tree_depth": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/location-types/d498e24c-e809-4972-a1d1-8cd0833ed7ff/"}, "msg": "location_type Network Closets created"}

TASK [ADD DEVICE TYPE] ****************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:30
changed: [localhost] => {"changed": true, "device_type": {"comments": "", "computed_fields": "", "created": "2023-12-18", "custom_fields": {}, "display": "Arista ceos", "front_image": null, "id": "051d49d9-65df-4d5b-b126-5d8e5c50fb42", "is_full_depth": true, "last_updated": "2023-12-18T22:02:05.797213Z", "manufacturer": "2c1dfb6d-4d46-4be0-aec0-c9cea0651d0c", "model": "ceos", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/device-types/051d49d9-65df-4d5b-b126-5d8e5c50fb42/notes/", "part_number": "", "rear_image": null, "relationships": "", "slug": "ceos", "subdevice_role": null, "tags": [], "u_height": 1, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/device-types/051d49d9-65df-4d5b-b126-5d8e5c50fb42/"}, "msg": "device_type ceos created"}

TASK [ADD PLATFORM] *******************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:38
changed: [localhost] => {"changed": true, "msg": "platform EOS created", "platform": {"computed_fields": "", "created": "2023-12-18", "custom_fields": {}, "description": "", "display": "EOS", "id": "f5445430-3024-474f-b660-48d8c05181d1", "last_updated": "2023-12-18T22:02:06.874726Z", "manufacturer": "2c1dfb6d-4d46-4be0-aec0-c9cea0651d0c", "name": "EOS", "napalm_args": null, "napalm_driver": "", "network_driver": "", "network_driver_mappings": "", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/platforms/f5445430-3024-474f-b660-48d8c05181d1/notes/", "relationships": "", "slug": "eos", "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/platforms/f5445430-3024-474f-b660-48d8c05181d1/"}}

TASK [ADD DEVICE ROLE] ****************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:46
changed: [localhost] => {"changed": true, "device_role": {"color": "008000", "computed_fields": "", "created": "2023-12-18", "custom_fields": {}, "description": "", "display": "Switch", "id": "ab8891b0-9dd4-4624-a203-df73009b7aec", "last_updated": "2023-12-18T22:02:07.903963Z", "name": "Switch", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/device-roles/ab8891b0-9dd4-4624-a203-df73009b7aec/notes/", "relationships": "", "slug": "switch", "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/device-roles/ab8891b0-9dd4-4624-a203-df73009b7aec/", "vm_role": true}, "msg": "device_role Switch created"}

TASK [ADD ARISTA DEVICES] *************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:54
changed: [localhost] => (item={'name': 'clab-Arista-2s-3l-spine1', 'site': 'Connecticut', 'device_role': 'Switch', 'device_type': 'ceos', 'platform': 'EOS'}) => {"ansible_loop_var": "item", "changed": true, "device": {"asset_tag": null, "cluster": null, "comments": "", "computed_fields": "", "config_context": {}, "created": "2023-12-18", "custom_fields": {}, "device_redundancy_group": null, "device_redundancy_group_priority": null, "device_role": "ab8891b0-9dd4-4624-a203-df73009b7aec", "device_type": "051d49d9-65df-4d5b-b126-5d8e5c50fb42", "display": "clab-Arista-2s-3l-spine1", "face": null, "id": "27fb7618-61dd-4101-ab3e-d2740d88d790", "last_updated": "2023-12-18T22:02:09.486185Z", "local_context_data": null, "local_context_schema": null, "location": null, "name": "clab-Arista-2s-3l-spine1", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/27fb7618-61dd-4101-ab3e-d2740d88d790/notes/", "parent_device": null, "platform": "f5445430-3024-474f-b660-48d8c05181d1", "position": null, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "rack": null, "relationships": "", "secrets_group": null, "serial": "", "site": "0627a5df-5138-4c4e-b263-b203f4085956", "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/27fb7618-61dd-4101-ab3e-d2740d88d790/", "vc_position": null, "vc_priority": null, "virtual_chassis": null}, "item": {"device_role": "Switch", "device_type": "ceos", "name": "clab-Arista-2s-3l-spine1", "platform": "EOS", "site": "Connecticut"}, "msg": "device clab-Arista-2s-3l-spine1 created"}
changed: [localhost] => (item={'name': 'clab-Arista-2s-3l-spine2', 'site': 'Connecticut', 'device_role': 'Switch', 'device_type': 'ceos', 'platform': 'EOS'}) => {"ansible_loop_var": "item", "changed": true, "device": {"asset_tag": null, "cluster": null, "comments": "", "computed_fields": "", "config_context": {}, "created": "2023-12-18", "custom_fields": {}, "device_redundancy_group": null, "device_redundancy_group_priority": null, "device_role": "ab8891b0-9dd4-4624-a203-df73009b7aec", "device_type": "051d49d9-65df-4d5b-b126-5d8e5c50fb42", "display": "clab-Arista-2s-3l-spine2", "face": null, "id": "cc12f9c3-4820-425c-8122-a1d25a5c29a5", "last_updated": "2023-12-18T22:02:10.795711Z", "local_context_data": null, "local_context_schema": null, "location": null, "name": "clab-Arista-2s-3l-spine2", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/cc12f9c3-4820-425c-8122-a1d25a5c29a5/notes/", "parent_device": null, "platform": "f5445430-3024-474f-b660-48d8c05181d1", "position": null, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "rack": null, "relationships": "", "secrets_group": null, "serial": "", "site": "0627a5df-5138-4c4e-b263-b203f4085956", "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/cc12f9c3-4820-425c-8122-a1d25a5c29a5/", "vc_position": null, "vc_priority": null, "virtual_chassis": null}, "item": {"device_role": "Switch", "device_type": "ceos", "name": "clab-Arista-2s-3l-spine2", "platform": "EOS", "site": "Connecticut"}, "msg": "device clab-Arista-2s-3l-spine2 created"}
changed: [localhost] => (item={'name': 'clab-Arista-2s-3l-leaf1', 'site': 'Connecticut', 'device_role': 'Switch', 'device_type': 'ceos', 'platform': 'EOS'}) => {"ansible_loop_var": "item", "changed": true, "device": {"asset_tag": null, "cluster": null, "comments": "", "computed_fields": "", "config_context": {}, "created": "2023-12-18", "custom_fields": {}, "device_redundancy_group": null, "device_redundancy_group_priority": null, "device_role": "ab8891b0-9dd4-4624-a203-df73009b7aec", "device_type": "051d49d9-65df-4d5b-b126-5d8e5c50fb42", "display": "clab-Arista-2s-3l-leaf1", "face": null, "id": "79a848d1-794c-4666-9a45-85c6589305c9", "last_updated": "2023-12-18T22:02:12.203307Z", "local_context_data": null, "local_context_schema": null, "location": null, "name": "clab-Arista-2s-3l-leaf1", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/79a848d1-794c-4666-9a45-85c6589305c9/notes/", "parent_device": null, "platform": "f5445430-3024-474f-b660-48d8c05181d1", "position": null, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "rack": null, "relationships": "", "secrets_group": null, "serial": "", "site": "0627a5df-5138-4c4e-b263-b203f4085956", "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/79a848d1-794c-4666-9a45-85c6589305c9/", "vc_position": null, "vc_priority": null, "virtual_chassis": null}, "item": {"device_role": "Switch", "device_type": "ceos", "name": "clab-Arista-2s-3l-leaf1", "platform": "EOS", "site": "Connecticut"}, "msg": "device clab-Arista-2s-3l-leaf1 created"}
changed: [localhost] => (item={'name': 'clab-Arista-2s-3l-leaf2', 'site': 'Connecticut', 'device_role': 'Switch', 'device_type': 'ceos', 'platform': 'EOS'}) => {"ansible_loop_var": "item", "changed": true, "device": {"asset_tag": null, "cluster": null, "comments": "", "computed_fields": "", "config_context": {}, "created": "2023-12-18", "custom_fields": {}, "device_redundancy_group": null, "device_redundancy_group_priority": null, "device_role": "ab8891b0-9dd4-4624-a203-df73009b7aec", "device_type": "051d49d9-65df-4d5b-b126-5d8e5c50fb42", "display": "clab-Arista-2s-3l-leaf2", "face": null, "id": "19eba143-ead9-403c-b86d-060b37aafe58", "last_updated": "2023-12-18T22:02:13.805603Z", "local_context_data": null, "local_context_schema": null, "location": null, "name": "clab-Arista-2s-3l-leaf2", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/19eba143-ead9-403c-b86d-060b37aafe58/notes/", "parent_device": null, "platform": "f5445430-3024-474f-b660-48d8c05181d1", "position": null, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "rack": null, "relationships": "", "secrets_group": null, "serial": "", "site": "0627a5df-5138-4c4e-b263-b203f4085956", "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/19eba143-ead9-403c-b86d-060b37aafe58/", "vc_position": null, "vc_priority": null, "virtual_chassis": null}, "item": {"device_role": "Switch", "device_type": "ceos", "name": "clab-Arista-2s-3l-leaf2", "platform": "EOS", "site": "Connecticut"}, "msg": "device clab-Arista-2s-3l-leaf2 created"}
changed: [localhost] => (item={'name': 'clab-Arista-2s-3l-leaf3', 'site': 'Connecticut', 'device_role': 'Switch', 'device_type': 'ceos', 'platform': 'EOS'}) => {"ansible_loop_var": "item", "changed": true, "device": {"asset_tag": null, "cluster": null, "comments": "", "computed_fields": "", "config_context": {}, "created": "2023-12-18", "custom_fields": {}, "device_redundancy_group": null, "device_redundancy_group_priority": null, "device_role": "ab8891b0-9dd4-4624-a203-df73009b7aec", "device_type": "051d49d9-65df-4d5b-b126-5d8e5c50fb42", "display": "clab-Arista-2s-3l-leaf3", "face": null, "id": "bf1b5cf8-f51d-4d4a-9629-036cc2056bc2", "last_updated": "2023-12-18T22:02:15.031124Z", "local_context_data": null, "local_context_schema": null, "location": null, "name": "clab-Arista-2s-3l-leaf3", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/bf1b5cf8-f51d-4d4a-9629-036cc2056bc2/notes/", "parent_device": null, "platform": "f5445430-3024-474f-b660-48d8c05181d1", "position": null, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "rack": null, "relationships": "", "secrets_group": null, "serial": "", "site": "0627a5df-5138-4c4e-b263-b203f4085956", "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/devices/bf1b5cf8-f51d-4d4a-9629-036cc2056bc2/", "vc_position": null, "vc_priority": null, "virtual_chassis": null}, "item": {"device_role": "Switch", "device_type": "ceos", "name": "clab-Arista-2s-3l-leaf3", "platform": "EOS", "site": "Connecticut"}, "msg": "device clab-Arista-2s-3l-leaf3 created"}

TASK [ADD INTERFACE TO A DEVICE] ******************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:66
changed: [localhost] => {"changed": true, "interface": {"bridge": null, "cable": null, "cable_peer": null, "cable_peer_type": null, "computed_fields": "", "connected_endpoint": null, "connected_endpoint_reachable": null, "connected_endpoint_type": null, "count_ipaddresses": 0, "created": "2023-12-18", "custom_fields": {}, "description": "", "device": "bf1b5cf8-f51d-4d4a-9629-036cc2056bc2", "display": "vlan13", "enabled": true, "id": "d604fbf1-197b-4a0e-b8db-36271f825006", "label": "", "lag": null, "last_updated": "2023-12-18T22:02:16.631363Z", "mac_address": null, "mgmt_only": false, "mode": null, "mtu": null, "name": "vlan13", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/interfaces/d604fbf1-197b-4a0e-b8db-36271f825006/notes/", "parent_interface": null, "relationships": "", "tagged_vlans": [], "tags": [], "type": "virtual", "untagged_vlan": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/interfaces/d604fbf1-197b-4a0e-b8db-36271f825006/"}, "msg": "interface vlan13 created"}

TASK [ADD INTERFACE TO A DEVICE] ******************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:76
changed: [localhost] => {"changed": true, "interface": {"bridge": null, "cable": null, "cable_peer": null, "cable_peer_type": null, "computed_fields": "", "connected_endpoint": null, "connected_endpoint_reachable": null, "connected_endpoint_type": null, "count_ipaddresses": 0, "created": "2023-12-18", "custom_fields": {}, "description": "", "device": "bf1b5cf8-f51d-4d4a-9629-036cc2056bc2", "display": "vlan14", "enabled": true, "id": "e64ff347-f569-43c0-af26-1b196654381e", "label": "", "lag": null, "last_updated": "2023-12-18T22:02:18.211204Z", "mac_address": null, "mgmt_only": false, "mode": null, "mtu": null, "name": "vlan14", "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/interfaces/e64ff347-f569-43c0-af26-1b196654381e/notes/", "parent_interface": null, "relationships": "", "tagged_vlans": [], "tags": [], "type": "virtual", "untagged_vlan": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/dcim/interfaces/e64ff347-f569-43c0-af26-1b196654381e/"}, "msg": "interface vlan14 created"}

TASK [ADD IP ADDRESS] *****************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:86
changed: [localhost] => {"changed": true, "ip_address": {"address": "192.168.13.1/24", "assigned_object": null, "assigned_object_id": null, "assigned_object_type": null, "computed_fields": "", "created": "2023-12-18", "custom_fields": {}, "description": "", "display": "192.168.13.1/24", "dns_name": "", "family": 4, "id": "1e5b0fd5-f6dd-40c9-98b8-0faae3753e6f", "last_updated": "2023-12-18T22:02:19.370808Z", "nat_inside": null, "nat_outside": null, "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/ipam/ip-addresses/1e5b0fd5-f6dd-40c9-98b8-0faae3753e6f/notes/", "relationships": "", "role": null, "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/ipam/ip-addresses/1e5b0fd5-f6dd-40c9-98b8-0faae3753e6f/", "vrf": null}, "msg": "ip_address 192.168.13.1/24 created"}

TASK [ADD IP ADDRESS] *****************************************************************************************************************
task path: /home/cloud_user/network-automation/infra/nautobot/add_info.yaml:94
changed: [localhost] => {"changed": true, "ip_address": {"address": "192.168.14.1/24", "assigned_object": null, "assigned_object_id": null, "assigned_object_type": null, "computed_fields": "", "created": "2023-12-18", "custom_fields": {}, "description": "", "display": "192.168.14.1/24", "dns_name": "", "family": 4, "id": "45765efe-77a0-464f-a14a-b3f514208f5a", "last_updated": "2023-12-18T22:02:20.509881Z", "nat_inside": null, "nat_outside": null, "notes_url": "http://ed26757f4b2c.mylabserver.com:8000/api/ipam/ip-addresses/45765efe-77a0-464f-a14a-b3f514208f5a/notes/", "relationships": "", "role": null, "status": "active", "tags": [], "tenant": null, "url": "http://ed26757f4b2c.mylabserver.com:8000/api/ipam/ip-addresses/45765efe-77a0-464f-a14a-b3f514208f5a/", "vrf": null}, "msg": "ip_address 192.168.14.1/24 created"}
META: ran handlers
META: ran handlers

PLAY RECAP ****************************************************************************************************************************
localhost                  : ok=11   changed=11   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Go back to the Web Browser and refresh the Nautobot server

Click on Devices → Devices

You should see 5 devices

You just added 5 new devices using the Nautobot API

Check out the devices by clicking on a device

s4 28
Figure 4: Nautobot Devices

To support the new VLAN change

Click IPAM → IP Addresses and select 192.168.13.1/24

Click Edit in upper right-hand corner

Scroll down to Interface Assignment and select the following:

s4 29
Figure 5: Nautobot Interface Assignment

Click update and click IP Addresses and then click on the 192.168.14.1/24 IP Address and assign vlan14 to the same device under the Interface Assignment section

On the IP Address page you should see the checkboxes for assigned

s4 30
Figure 6: Nautobot IP Addresses

Now we have associated the two new VLANs to the Leaf3 switch


Next we need to add the SNMP change to Nautobot

Click on the Extensibility drop down and select Config Context

s4 31
Figure 7: Config Context

Add a new Config Context

Name it snmp and scroll down to the data section

Add the following JSON data into the data section and click Create:

{
    "config": {
        "communities": [
            {
                "acl_v4": "list3",
                "name": "netdevops",
                "view": "view1"
            }
        ],
        "contact": "admin",
        "hosts": [
            {
                "host": "host02",
                "user": "user1",
                "version": "2c"
            }
        ]
    }
}

This will add the SNMP configuration to each Switch

Click on a device and verify the SNMP Config Context


Instead of creating the VLAN and SNMP change using static data from the playbook and host_vars

Lets add the new VLANs and SNMP from the Source of Truth (SoT) Nautobot

This will allow us to query the SoT prior to successfully adding the changes to production

Now let’s create a couple Ansible Playbooks to grab the data from the SoT and create configurations from it for the change

The change will remain the same, open the change_sot folder and review the get_ip.yaml.

Notice the variables we will query in Nautobot

Check out the Nautobot Query - This introduces the GraphQL

---
- name: GET DEVICE INFORMATION FROM THE GRAPHQL API
  hosts: localhost
  vars:
  # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
    site_slug: "connecticut"
    switch_name: "clab-Arista-2s-3l-leaf3"
    vlan: "14"
    nautobot_host: "http://172.31.102.7:8000/"
    nautobot_token: "fbf4caacbd265dfc88aa430be6c31650a84179a7"
    nautobot_query: |
      query {
              devices(site: "{{ site_slug }}", name: "{{ switch_name }}") {
                name
                interfaces (name: "vlan{{ vlan }}"){
                  ip_addresses {
                    address
                     }
                   }
                 }
               }
  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
      template:
        src: ip_host_vars.j2
        dest: ./inventory/host_vars/{{ switch_name }}.yaml

Login to Nautobot Server and click on graphql in the lower right

s4 32
Figure 8: GraphQL

Cut and paste the following query at the line 32 of the left screen and hit the play button at the top

query {
             devices(site: "connecticut") {
                name
                interfaces {
                  ip_addresses {
                    address
            }
          }
      }
    }
s4 33
Figure 9: GraphQL Query

Let modify the query and hit the play button again to try the flexibility of GraphQL

Notice how we can modify query and receive new results

query {
             devices(site: "connecticut", name: "clab-Arista-2s-3l-leaf3") {
                name
                interfaces (name: "vlan14"){
                  ip_addresses {
                    address
            }
          }
      }
    }
s4 34
Figure 10: GraphQL Query

The next part of the Ansible Playbook to check out is the debug variable and the jinja2 template to create the host_vars yaml file

The devices variable will include the results of the GraphQL query

s4 35
Figure 11: GraphQL Query

Let modify the query to capture the SNMP info and hit the play button again to try the flexibility of GraphQL

Notice how we can modify query and receive new results

query {
              devices(site: "connecticut", name: ["clab-Arista-2s-3l-spine1", "clab-Arista-2s-3l-spine2"]) {
          name
          config_context
        }
      }
s4 36
Figure 12: GraphQL Query

Lets review the get_snmp.yaml and snmp_host_vars.j2 files

The devices variable will include the results of the GraphQL query

s4 37
Figure 13: get_snmp.yaml and snmp_host_vars.j2 files

all.yaml are the default variables used in the playbooks

Here is a good example of where you can simplify the information required from users

Users would only need to complete this file for the change

s4 38
Figure 14: all.yaml file and how it is related to get_ip.yaml and get_snmp.yaml files

This looks good so far, but you should never include a token or a password in file uploaded to a Git repository

So then how do you include a password in a playbook associated to a CI/CD pipeline?

---
- name: GET DEVICE INFORMATION FROM THE GRAPHQL API
  hosts: localhost
  vars:
  # FILL IN WITH PROPER REGION VALUE FOR YOUR SYSTEM
  nautobot_host: "http://172.31.102.7:8000/" (1)
  nautobot_token: "fbf4caacbd265dfc88aa430be6c31650a84179a7" (1)
  nautobot_query: |
    query {
     devices(site: "{{ site_slug }}", name: {{ snmp.switch_names.names | tojson }}) {
    name
    config_context
  }
}
  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
      template:
        src: snmp_host_vars.j2
        dest: ./inventory/host_vars/{{ item.name }}.yaml
      with_items:
        - "{{ switch_names }}"
1 We can use variables in GitLab

On the Gitlab Server in the Network Automation repository

Go to Settings → CI/CD → and expand variables

Click Add variable and add the variable using NB_TOKEN as the key, your token as the value and click Mask variable and click the Add variable button

Click Add variable and add the variable using NB_HOST as the key, your url of the Nautobot server with port 8000 (http://server2:8000) as the value and click Mask variable and click the Add variable button

Replace server2 with the FQDN of your server and include port 8000 (http://YourServer2:8000)
s4 39
Figure 15: GitLab Variables

Let’s Create A New Change

Create an Issue named Change with Nautobot and create a merge request

Copy and paste the following description:

- [ ] Query Nautobot to add new vlan 14
- [ ] Query Nautobot to add SNMP to the Spine Switches
s4 40
Figure 16: New Change With Nautobot

Go to a remote location where you have a copy of the remote repository from your Gitlab server

Let’s continue to use VS Code on your laptop

It is a good habit to first sync up changes with a remote repository before working on it

Run the following command:

git branch

Here is the progression of the command above

kennorton@C02G71AFMD6P-knorton :~/network-automation$ git branch
  1-network-change
  2-testing
* Master

Notice that I am starting out on the Master branch

If you are not on the master branch, run the following command:

git checkout master

It’s a good idea to check if your local repository is out of date from the remote repository

Run the following command:

git remote -v show origin

Here is the progression of the commands above

kennorton@C02G71AFMD6P-knorton :~/network-automation$ git remote -v show origin (1)
Username for 'http://ed26757f4b2c.mylabserver.com': knorton
Password for 'http://knorton@ed26757f4b2c.mylabserver.com':
* remote origin
  Fetch URL: http://ed26757f4b2c.mylabserver.com/knorton/network-automation.git
  Push  URL: http://ed26757f4b2c.mylabserver.com/knorton/network-automation.git
  HEAD branch: master
  Remote branches:
    3-change-with-nautobot               new (next fetch will store in remotes/origin)
    master                               tracked
    refs/remotes/origin/1-network-change stale (use 'git remote prune' to remove)
    refs/remotes/origin/2-testing        stale (use 'git remote prune' to remove)
  Local ref configured for 'git push':
    master pushes to master (local out of date)
1 This local repo is out of date and needs to be updated

Lets update the local repository if required

Below was performed from the VS Code terminal

Run the following command:

git pull origin master

Here is the progression of the commands above

kennorton@C02G71AFMD6P-knorton:~/network-automation$ git pull origin master
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 3), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), 2.14 KiB | 546.00 KiB/s, done.
From http://ed26757f4b2c.mylabserver.com/knorton/network-automation
 * branch            master     -> FETCH_HEAD
   7b37b11..6ccd843  master     -> origin/master
Updating 7b37b11..6ccd843
Fast-forward
 .gitlab-ci.yml   | 24 ++++++++++++++++++++++++
 tests/batfish.py |  2 +-
 2 files changed, 25 insertions(+), 1 deletion(-)

Notice the only branches are master, 1-network-change, and 2-testing

We can pull down the remote network change branch using the following command:

git fetch

But you need to create a new local branch in the local repository using the following command:

Your branch name maybe different

git branch 3-change-with-nautobot

Then let’s switch to that branch with the following command:

git checkout 3-change-with-nautobot

Here is a progression of the commands above

kennorton@C02G71AFMD6P-knorton network-automation % git branch --all
  1-network-change
  2-testing
* master
  remotes/origin/1-network-change
  remotes/origin/2-testing
  remotes/origin/master
kennorton@C02G71AFMD6P-knorton network-automation % git fetch
From http://ccoe-netdev-02.presidio-demo.com/knorton/network-automation
 * [new branch]      3-change-with-nautobot -> origin/3-change-with-nautobot
kennorton@C02G71AFMD6P-knorton network-automation % git branch
  1-network-change
  2-testing
* master
kennorton@C02G71AFMD6P-knorton network-automation % git branch 3-change-with-nautobot
kennorton@C02G71AFMD6P-knorton network-automation % git checkout 3-change-with-nautobot
Switched to branch '3-change-with-nautobot'
kennorton@C02G71AFMD6P-knorton network-automation %

In the get_ip.yaml and get_snmp.yaml file in the change_sot directory

Change the nautobot_token to include the following in both files:

nautobot_token: "{{ lookup('env', 'NB_TOKEN') }}"
nautobot_host: "{{ lookup('env', 'NB_HOST') }}"
s4 41
Figure 17: get_ip.yaml and get_snmp.yaml file

We have to include these changes to the CI/CD workflow in the .gitlab-ci.yml file

Now in your IDE update the change stage to change_with_sot and add the document and production stages

Also add the variables at the top of the .gitlab-ci.yml file

s4 42
Figure 18: .gitlab-ci.yaml update

Change the top of the .gitlab-ci.yml to reflect the following:

---
variables:
  NB_TOKEN: "$NB_TOKEN"
  NB_HOST: "$NB_HOST"

workflow:
  rules:
    - if: $CI_COMMIT_TAG
      when: never
    - if: $CI_COMMIT_BRANCH == 'master'

stages:
  - build
  - stage
  - change_with_sot
  - test
  - backup
  - document
  - production

Modify the change stage to reflect the following:

network_change:
  stage: change_with_sot
  before_script:
    - pip install pynautobot==1.5.0
    - ansible-galaxy collection install networktocode.nautobot:4.5.0
    - cd change_sot
    - mkdir inventory/host_vars
    - ansible-playbook get_snmp.yaml -v
    - ansible-playbook get_ip.yaml -v
  script:
    - ansible-playbook change.yaml -v

Lets add a document and production stages at the bottom of the .gitlab-ci.yml file to reflect the following:

Normally the push to production stage would include the changes to be pushed to production switches

Remember to save all your modified files
document_switches:
  stage: document
  before_script:
    - cd document
  script:
    - ansible-playbook document.yaml -v
    - docker container rm -f netdevops-nginx || true
    - docker rmi $(docker images -q --filter=reference="netdevops-nginx:1.0") || true
    - docker build -t netdevops-nginx:1.0 .
    - docker run -d -p 80:80 --name netdevops-nginx netdevops-nginx:1.0
  dependencies:
    - test_model_switches

push_to_production:
  stage: production
  script:
    - echo Pushing to production........
  dependencies:
    - document_switches

Now lets go push the changes to the remote repository

Run the following commands shown below:

git status
cd ~/network-automation
git add change_sot/get_snmp.yaml
git add change_sot/get_ip.yaml
git add .gitlab-ci.yml
git commit -m "Updated the gitlab-ci file to add the change with nautobot"
git branch
git push origin 3-change-with-nautobot
The name of your branch maybe different

Here is progression of the commands above:

kennorton@C02G71AFMD6P-knorton network-automation % git status
On branch 3-change-with-nautobot
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   .gitlab-ci.yml
        modified:   change_sot/get_ip.yaml
        modified:   change_sot/get_snmp.yaml

no changes added to commit (use "git add" and/or "git commit -a")
kennorton@C02G71AFMD6P-knorton network-automation % git add change_sot/get_snmp.yaml
kennorton@C02G71AFMD6P-knorton network-automation % git add change_sot/get_ip.yaml
kennorton@C02G71AFMD6P-knorton network-automation % git add .gitlab-ci.yml
kennorton@C02G71AFMD6P-knorton network-automation % git commit -m "Updated the gitlab-ci file to add the change with nautobot"

[3-change-with-nautobot 23a8eb8] Updated the gitlab-ci file to add the change with nautobot
 3 files changed, 6 insertions(+), 5 deletions(-)
kennorton@C02G71AFMD6P-knorton network-automation % git branch
  1-network-change
  2-testing
* 3-change-with-nautobot
  master
kennorton@C02G71AFMD6P-knorton network-automation % git push origin 3-change-with-nautobot
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 12 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 660 bytes | 660.00 KiB/s, done.
Total 6 (delta 5), reused 0 (delta 0), pack-reused 0
remote:
remote: To create a merge request for 3-change-with-nautobot, visit:
remote:   http://ccoe-netdev-02.presidio-demo.com/knorton/network-automation/-/merge_requests/new?merge_request%5Bsource_branch%5D=3-change-with-nautobot
remote:
To http://ccoe-netdev-02.presidio-demo.com/knorton/network-automation.git
   87eee58..23a8eb8  3-change-with-nautobot -> 3-change-with-nautobot

Log back onto the GitLab Server under the network-automation repository

Notice the Merge Request update – click Merge Requests and select the recent merge request

Under Activity you can review the changes

Click Mark as ready

Click Merge

s4 43
Figure 19: Merge Request

Here is a copy of the latest .gitlab-ci.yml file

---
variables:
  NB_TOKEN: "$NB_TOKEN"
  NB_HOST: "$NB_HOST"

workflow:
  rules:
    - if: $CI_COMMIT_TAG
      when: never
    - if: $CI_COMMIT_BRANCH == 'master'

stages:
  - build
  - stage
  - change_with_sot
  - test
  - backup
  - document
  - production


build_switches:
  stage: build
  before_script:
    - cd infra
  script:
    - sudo containerlab destroy -t ceos_2spine_3leaf.yaml || true
    - sudo -E CLAB_LABDIR_BASE=/var/clab containerlab deploy -t ceos_2spine_3leaf.yaml --reconfigure --max-workers 30 || true

staging_switches:
  stage: stage
  before_script:
    - cd build
  script:
    - sleep 60
    - pip install ansible-pylibssh
    - ansible-galaxy collection install arista.eos
    - ansible-playbook build.yaml -v

network_change:
  stage: change_with_sot
  before_script:
    - pip install pynautobot==1.5.0
    - ansible-galaxy collection install networktocode.nautobot:4.5.0
    - cd change_sot
    - mkdir inventory/host_vars
    - ansible-playbook get_snmp.yaml -v
    - ansible-playbook get_ip.yaml -v
  script:
    - ansible-playbook change.yaml -v

test_traditional_switches:
  stage: test
  before_script:
    - sleep 60
  script:
    - docker exec clab-Arista-2s-3l-client3 ifconfig eth1 192.168.14.8 netmask 255.255.255.0
    - docker exec clab-Arista-2s-3l-client3 route add default gw 192.168.14.1 eth1 || true
    - docker exec clab-Arista-2s-3l-client3 route delete default gw 172.20.20.1 eth0 || true
    - docker exec clab-Arista-2s-3l-client3 ping -c 5 192.168.14.1
    - docker exec clab-Arista-2s-3l-client3 traceroute 192.168.11.1

test_model_switches:
  stage: test
  before_script:
    - cd tests
    - docker rmi -f $(docker images -q --filter=reference="batfish/allinone:latest") || true
  script:
    - docker run -d --restart=always --name batfish -v batfish-data:/data -p 8888:8888 -p 9997:9997 -p 9996:9996 batfish/allinone || true
    - ansible-playbook snapshots.yaml -v 
    - python3 -m venv venv
    - source venv/bin/activate
    - pip install pybatfish
    - python3 batfish.py

backup_switches:
  stage: backup
  before_script:
    - cd backup
  script:
    - ansible-playbook playbooks/git_backup.yaml -v
  dependencies:
    - staging_switches

document_switches:
  stage: document
  before_script:
    - cd document
  script:
    - ansible-playbook document.yaml -v
    - docker container rm -f netdevops-nginx || true
    - docker rmi $(docker images -q --filter=reference="netdevops-nginx:1.0") || true
    - docker build -t netdevops-nginx:1.0 .
    - docker run -d -p 80:80 --name netdevops-nginx netdevops-nginx:1.0
  dependencies:
    - test_model_switches

push_to_production:
  stage: production
  script:
    - echo Pushing to production........
  dependencies:
    - document_switches

Under the build section

Click on the pipeline

s4 44
Figure 20: CI/CD Pipeline

When the pipeline completes the issue will automatically close

Go back into the issue and check the checkboxes if it was successful or repoen the issue

s4 45
Figure 21: Close The Issue

CONGRATULATIONS!! 🎉


End Result

At this point, You completed the following:

ind 5
Figure 22: Network Automation

Return to Workshop