Skip to content

Lab 12 - Pynautobot Basics

Lab Overview

In this lab we will use the pynautobot python package to interact with the Nautobot ReST API.

Table of Contents

Task 1 - Prepare the environment

Step 1-1 - Login via ssh to your pod

Connect via ssh to your Nautobot pod as the ntc user. Use the password provided by the instructor.

ssh ntc@{{ your pod }}

Verify you are logged in as the ntc user.

ntc@ntc-nautobot:~# whoami
ntc

Step 1-2 - Create and activate a virtual environment

A virtual environment will isolate any packages we install and can easily be activated, deactivated or deleted.

Create the virtual environment

ntc@ntc-nautobot:~# python3 -m venv pynautobot_venv

Activate the virtual environment

ntc@ntc-nautobot:~# source pynautobot_venv/bin/activate

Step 1-3 - Install pynautobot

ntc@ntc-nautobot:~# pip install pynautobot
Collecting pynautobot
  Downloading pynautobot-1.5.0-py3-none-any.whl (33 kB)
Collecting urllib3<1.27,>=1.21.1
  Downloading urllib3-1.26.16-py2.py3-none-any.whl (143 kB)
     |████████████████████████████████| 143 kB 51.7 MB/s
Collecting requests<3.0.0,>=2.30.0
  Downloading requests-2.31.0-py3-none-any.whl (62 kB)
     |████████████████████████████████| 62 kB 816 kB/s
Collecting charset-normalizer<4,>=2
  Downloading charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (199 kB)
     |████████████████████████████████| 199 kB 61.3 MB/s
Collecting idna<4,>=2.5
  Downloading idna-3.4-py3-none-any.whl (61 kB)
     |████████████████████████████████| 61 kB 78 kB/s
Collecting certifi>=2017.4.17
  Downloading certifi-2023.7.22-py3-none-any.whl (158 kB)
     |████████████████████████████████| 158 kB 78.9 MB/s
Installing collected packages: urllib3, charset-normalizer, idna, certifi, requests, pynautobot
Successfully installed certifi-2023.7.22 charset-normalizer-3.2.0 idna-3.4 pynautobot-1.5.0 requests-2.31.0 urllib3-1

After pynautobot has successfully installed, enter the python shell.

ntc@ntc-nautobot:~# python3
Python 3.8.10 (default, May 26 2023, 14:05:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Before we get started with pynautobot, use urlib3 to disable warnings about request security. This is not a requirement for using pynautobot, but in our lab environment disabling the warnings will prevent unnecessary message noise while we interact with pynautobot.

>>> import urllib3
>>> urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

Task 2 - Pynautobot basics

Step 2-1 - Create a client

Import pynautobot so we can use it!

import pynautobot

By providing domain and authentication details to a pynautobot client, we can run commands against the API without having to re-enter this information every time. The function arguments have been broken out into variables for increased readability, but if you want everything can be put into a single call to the api method.

You will need a Token for the client, this can be found in the Admin section of Nautobot as discussed in a previous lab. Don't forget to put the url and token values into a string by using quotes.

>>> url = "https://{{ your pod }}/"
>>> token = "1234123412341234123412341234123412341234"
>>> nautobot = pynautobot.api(url=url, token=token)

Next we need to disable SSL verification so pynautobot will work in our lab.

>>> nautobot.http_session.verify = False

Step 2-2 - Get Nautobot data using pynautobot

Use the client to assign all the Sites to a variable

>>> sites = nautobot.dcim.sites.all()
Print the contents of sites
>>> sites

[<pynautobot.core.response.Record ('AMS01') at 0x7fa1bc6ac0d0>, <pynautobot.core.response.Record ('ANG01') at 0x7fa1bc69f580>, <pynautobot.core.response.Record ('ATL01') at 0x7fa1bc69fac0>, <pynautobot.core.response.Record ('ATL02') at 0x7fa1bc6a8a60>, <pynautobot.core.response.Record ('AUS01') at 0x7fa1bc69f5e0>, <pynautobot.core.response.Record ('AZD01') at 0x7fa1bc405460>, <pynautobot.core.response.Record ('BKK01') at 0x7fa1bc4055b0>, <pynautobot.core.response.Record ('BRE01') at 0x7fa1bc405700>, <pynautobot.core.response.Record ('CAN01') at 0x7fa1bc405850>, <pynautobot.core.response.Record ('CDG01') at 0x7fa1bc4059a0>, <pynautobot.core.response.Record ('CDG02') at 0x7fa1bc405af0>, <pynautobot.core.response.Record ('DEL01') at 0x7fa1bc405c40>, <pynautobot.core.response.Record ('DEN01') at 0x7fa1bc405d90>, <pynautobot.core.response.Record ('DFW01') at 0x7fa1bc405ee0>, <pynautobot.core.response.Record ('DFW02') at 0x7fa1bc405fd0>, <pynautobot.core.response.Record ('DXB01') at 0x7fa1bc405370>, <pynautobot.core.response.Record ('DXB02') at 0x7fa1bc391310>, <pynautobot.core.response.Record ('FRA01') at 0x7fa1bc391460>, <pynautobot.core.response.Record ('HKG01') at 0x7fa1bc3915b0>, <pynautobot.core.response.Record ('HND01') at 0x7fa1bc391700>, <pynautobot.core.response.Record ('HND02') at 0x7fa1bc391850>, <pynautobot.core.response.Record ('HOU01') at 0x7fa1bc3919a0>, <pynautobot.core.response.Record ('ICN01') at 0x7fa1bc391af0>, <pynautobot.core.response.Record ('JFK01') at 0x7fa1bc391c40>, <pynautobot.core.response.Record ('Jersey City') at 0x7fa1bc391d90>, <pynautobot.core.response.Record ('KOL01') at 0x7fa1bc391ee0>, <pynautobot.core.response.Record ('LAX01') at 0x7fa1bc391fd0>, <pynautobot.core.response.Record ('LAX02') at 0x7fa1bc3911c0>, <pynautobot.core.response.Record ('LAX03') at 0x7fa1bc325310>, <pynautobot.core.response.Record ('LHR01') at 0x7fa1bc325460>, <pynautobot.core.response.Record ('LHR02') at 0x7fa1bc3255b0>, <pynautobot.core.response.Record ('LON01') at 0x7fa1bc325700>, <pynautobot.core.response.Record ('MAD01') at 0x7fa1bc325850>, <pynautobot.core.response.Record ('NYM01') at 0x7fa1bc3259a0>, <pynautobot.core.response.Record ('New York City') at 0x7fa1bc325af0>, <pynautobot.core.response.Record ('ORD01') at 0x7fa1bc325c40>, <pynautobot.core.response.Record ('ORD02') at 0x7fa1bc325d90>, <pynautobot.core.response.Record ('PEK01') at 0x7fa1bc325ee0>, <pynautobot.core.response.Record ('PEK02') at 0x7fa1bc325fd0>, <pynautobot.core.response.Record ('PVG01') at 0x7fa1bc3251c0>, <pynautobot.core.response.Record ('PVG02') at 0x7fa1bc334310>, <pynautobot.core.response.Record ('SIN01') at 0x7fa1bc334460>, <pynautobot.core.response.Record ('SLC01') at 0x7fa1bc3345b0>, <pynautobot.core.response.Record ('SMF01') at 0x7fa1bc334700>, <pynautobot.core.response.Record ('Weehawken') at 0x7fa1bc334850>]
Use a for loop to print out some information about each Site. If you are new to python, note that the indentation is important. Use TAB to indent lines.
>>> for site in sites:
...     print(f"Site Name: {site.name}")
...     print(f"Site Status: {site.status}")
Site Name: AMS01
Site Status: Active
Site Name: ANG01
Site Status: Active
Site Name: ATL01
Site Status: Active
Site Name: ATL02
Site Status: Active
Site Name: AUS01
Site Status: Active
Site Name: AZD01
Site Status: Active
Site Name: BKK01
Site Status: Active
Site Name: BRE01
Site Status: Active
Site Name: CAN01
Site Status: Active
...
Assign a specific Site to a variable.
>>> site_hou01 = nautobot.dcim.sites.get(name="HOU01")
Confirm the site has been retrieved.
>>> site_hou01
<pynautobot.core.response.Record ('HOU01') at 0x7fa1bc69f0a0>
Get all the Sites from a particular region by filtering the request.
us_sites = nautobot.dcim.sites.filter(region="united-states")
Use a for loop to print out some information about each Site.
>>> for site in us_sites:
...     print(f"Site Name: {site.name}")
...     print(f"Site Status: {site.status}")
        print(f"Site Region: {site.region}")
Site Name: ANG01
Site Status: Active
Site Region: United States
Site Name: ATL01
Site Status: Active
Site Region: United States
Site Name: ATL02
Site Status: Active
Site Region: United States
Site Name: AZD01
Site Status: Active
Site Region: United States
Site Name: BRE01
Site Status: Active
Site Region: United States
Site Name: DEN01
Site Status: Active
Site Region: United States
Site Name: DFW01
Site Status: Active
Site Region: United States
Site Name: DFW02
Site Status: Active
Site Region: United States
Site Name: HOU01
Site Status: Active
Site Region: Texas
...

Step 2-3 - Update Nautobot data using pynautobot

In a previous step we assigned the HOU01 site to a variable. Let's check the status.

>>> site_hou01.status.value
'active'
The Site Status is Active, Update it to Planned.
>>> site_hou01.status = "planned"
Check the Site Status.
>>> site_hou01.status.value
That didn't work! Why? We haven't actually saved the changes we made to the Nautobot database, we have only modified the variable in memory.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'value'
Save the Site to send the updates to Nautobot.
>>> site_hou01.save()
Oh no, that didn't work either! In a previous lab we updated the regex rules for site names and HOU01 doesn't meet the requirement. We could update the name and slug here, or disable the regex rule.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ntc/pynautobot_venv/lib/python3.8/site-packages/pynautobot/core/response.py", line 392, in save
    if req.patch({i: serialized[i] for i in diff}):
  File "/home/ntc/pynautobot_venv/lib/python3.8/site-packages/pynautobot/core/query.py", line 400, in patch
    return self._make_call(verb="patch", data=data)
  File "/home/ntc/pynautobot_venv/lib/python3.8/site-packages/pynautobot/core/query.py", line 276, in _make_call
    raise RequestError(req)
pynautobot.core.query.RequestError: The request failed with code 400 Bad Request: {'name': ['Site names must be in the format AAA000 or AAA0000. The name must begin with exactly 3 upper case letters followed by 3 or 4 numbers between 0 and 9.']}
Rules are rules, so let's update the name and slug here.
>>> site_hou01.name = "HOU001"
>>> site_hou01.slug = "hou001"
Save the Site again. A response of True means the save was successful.
>>> site_hou01.save()
True
Get the data from the database again to update the variable with the new data. I'm also creating a new variable so the variable name matches the Site name.
>>> site_hou001 = nautobot.dcim.sites.get(name="HOU001")
Print out the updated Site data. You can also verify this data in the Nautobot GUI.
>>> site_hou001.name
'HOU001'
>>> site_hou001.slug
'hou001'
>>> site_hou001.status.value
'planned'

Step 2-4 - Send a GraphQL query with pynautobot

To make the response more readable, import json.

>>> import json

Create the query. Note the triple quotes which are used for multi-line strings in python.

>>> query = """
... query  {
...   devices {
...     name
...   }
... }
... """
Send the request.
device_names = nautobot.graphql.query(query=query)
Print the json data.
>>> print(json.dumps(device_names.json, indent=2))
{
  "data": {
    "devices": [
      {
        "name": "ams01-dist-01"
      },
      {
        "name": "ams01-edge-01"
      },
      {
        "name": "ams01-edge-02"
      },
      {
        "name": "ams01-leaf-01"
      },
      {
        "name": "ams01-leaf-02"
      },
      {
        "name": "ams01-leaf-03"
      },
      {
        "name": "ams01-leaf-04"
      },
      {
        "name": "ams01-leaf-05"
      },
      {
        "name": "ams01-leaf-06"
      },
      {
        "name": "ams01-leaf-07"
      },
      {
        "name": "ams01-leaf-08"
      },
      {
        "name": "ang01-dist-01"
      },
      {
        "name": "ang01-edge-01"
      },
      {
        "name": "ang01-edge-02"
      },
      ...