Skip to content

Using NautobotGPT

This document describes common use-cases and scenarios for NautobotGPT. Even though these examples show just the initial response for the sake of brevity, you are encouraged to continue the conversation by asking follow-up questions.

Note: Due to the nature of how Large Language Models work, the responses from NautobotGPT are not going to be the same every time. This is because the model is trained on a large dataset of text and code, and the responses may not always be 100% accurate.

Use Case 1: Learning Nautobot Concepts

Regardless of your experience level with Nautobot, NautobotGPT can help you learn about Nautobot concepts and data models. Take for example, if you are new to Nautobot and want to learn how Nautobot the Device Data model.

Open a new chat and you can use the following prompt to get started.

Question

Assuming that I have a basic understanding of Python and I am new to Nautobot, help me understand the Device data models in Nautobot.

Is there any order in which I need to create these objects?

Sample Response from NautobotGPT

Absolutely, let's break down the Device data models in Nautobot and the typical creation order you need to follow.


High-Level Understanding

Nautobot uses a series of interrelated data models to represent devices and their context in a network. The "Device" model is central, but to model real environments, you first set up objects (like locations and types) that devices depend on.


Below are the core models you'll work with (ordered for dependency and practical creation):

  1. Location

    • Represents physical places (data centers, rooms, racks, etc.).
    • In Nautobot v2+: There is no "Site" model; use Locations (with a hierarchy).
    • Required fields: name, location_type, status.
  2. LocationType

    • Defines the type of a location (e.g., "Data Center", "Rack", "Room").
    • This is necessary before you create Locations.
  3. DeviceRole

    • Categorizes devices functionally (e.g., "Core Switch", "Access Switch", "Firewall").
  4. DeviceType

    • Represents the hardware make/model.
    • Points to a Manufacturer.
  5. Manufacturer

    • Manufacturer/vendor of device types (e.g., Cisco, Arista).
  6. Device

    • The main object for network devices (switches, routers, etc.).
    • References all the above models.
  7. Rack

    • (Optional, but common.) Represents a physical rack, and devices may reference a rack and a position within it.
  8. Interfaces

    • Connections on a device (Ethernet ports, etc.).
    • Created after devices since they reference a specific device.

Order of Creation (Best Practice)

To create devices in Nautobot, follow this order for smooth operations:

  1. LocationType: Create all required types.
  2. Location: Create the physical locations with the appropriate types.
  3. Rack: If you want to use racks, define them next (these live within locations).
  4. Manufacturer: Add your hardware vendors.
  5. DeviceType: Add specific device hardware models, referencing manufacturers.
  6. DeviceRole: Define the functional roles.
  7. Device: Now create your actual device objects, referencing all the above.
  8. Interface: Once the device exists, define its interfaces and connections.

Example Workflow

Imagine adding a new "Core Switch" in a datacenter:

  1. LocationType: Data Center, Floor, Rack.
  2. Location: Add "NYC Data Center" (type: Data Center), "1st Floor", "Rack 101".
  3. Manufacturer: Cisco.
  4. DeviceType: Catalyst 9500, linked to Cisco.
  5. DeviceRole: Core Switch.
  6. Device: Place a new device in "Rack 101", role "Core Switch", type "Catalyst 9500".
  7. Interface: Add Ethernet and SFP ports to the new device.

Dependency Diagram

Manufacturer ┐
DeviceType ──┘
                  ┌───────────── LocationType
LocationType ───────────┐
Location ───────────────┘
Rack (optional) ─────────
DeviceRole
Device (central object)
Interface (depends on Device)

Validation & Next Steps

  • Before Creating Devices: Ensure referenced objects (manufacturer, device type, location, locationtype, device role) exist.
  • Nautobot may error if you try to create a device that references missing objects.

Summary

  • Order matters: Create dependencies (types, locations, manufacturers, etc.) first.
  • Nautobot's UI and API will guide you and prevent errors, but following this sequence avoids common pitfalls.
  • Once devices exist, you can build out interfaces and connections, and layer in more advanced models (IP addresses, cables, etc.). If you want code examples, or how to create any object via Nautobot's UI, API, or a job, just ask!

Use Case 2: Developing Nautobot Jobs

If you are new to Nautobot and want to learn how to develop Nautobot Jobs, you can ask NautobotGPT to help you write a simple job that lists all devices in Nautobot.

Open a new chat and you can use the following prompt to get started.

Question

Assuming that I have a basic understanding of Python and I am new to Nautobot, help me write a simple Nautobot job that lists all devices.

Sample Response from NautobotGPT

Certainly! Here's a step-by-step guide and example to help you write a simple Nautobot job that lists all devices. This example is for Nautobot v2.x.


High-Level Summary

You want to create a Nautobot Job that, when run, retrieves and lists all devices in your Nautobot instance. I'll provide a sample Job definition and explain each step.


Steps

  1. Create the Job File Save your Job in the recommended path: /opt/nautobot/jobs/list_devices.py. (Remember: After you add this file, you must run nautobot-server post_upgrade before the Job will appear in the Nautobot UI.)
  2. Write the Job Code Here's a functional sample Job:
from nautobot.apps.jobs import Job, register_jobs
from nautobot.dcim.models import Device
class ListAllDevices(Job):
    """A simple job to list all devices in Nautobot"""
    class Meta:
        name = "List All Devices"
        description = "List all Device objects in Nautobot."
    def run(self):
        devices = Device.objects.all()
        output = []
        for device in devices:
            # Here we list basic info: name, device type, status, and location
            output.append({
                "name": device.name,
                "device_type": str(device.device_type) if device.device_type else "Unknown",
                "status": str(device.status) if device.status else "Unknown",
                "location": str(device.location) if device.location else "Unknown"
            })
            self.logger.info(
                "Device: %s | Type: %s | Status: %s | Location: %s",
                device.name,
                device.device_type,
                device.status,
                device.location
            )
        return output
register_jobs(ListAllDevices)

Explanation

  • Imports:
    • Import Job and register_jobs from nautobot.apps.jobs (this is the correct path for Nautobot v2.x).
    • Import the Device model for access to device records.
  • Class Definition:
    • Subclass Job, and add some Meta details (for display).
    • No inputs in this Job, since we're just listing all devices.
  • run method:
    • Query all Device objects.
    • Assemble a list of relevant device details (name, type, status, location).
    • Log each device for visibility during job execution.
    • Return the device summary list as output.
  • register_jobs(ListAllDevices):
    • Required to register this Job with Nautobot.

Setup and Validation

  1. Save the Job file to /opt/nautobot/jobs/list_devices.py.
  2. Run:
    nautobot-server post_upgrade
    
    This loads the new job.
  3. Navigate to the "Jobs" section in the Nautobot UI; find and run "List All Devices".
  4. Check Output: The job should print each device in the log and provide a summary table in the results.

Next Steps / Testing

  • Try customizing which device fields you display.
  • Experiment adding input variables to filter the devices (e.g., by location or status).
  • Explore additional methods (like exporting results to a file using self.create_file). Let me know if you'd like to extend this job, for example, to filter by location or status!

Use Case 3: Troubleshooting Nautobot Issues

Troubleshooting Nautobot issues can be a complex and time-consuming process. NautobotGPT can help you troubleshoot issues by providing you with a list of potential issues and solutions. In this example, we will troubleshoot an issue where we are trying to get a list of all devices in Nautobot, but we are getting an error because we are using the wrong API endpoint for Nautobot v2.

Open a new chat and you can use the following prompt to get started.

Question

I am trying to get a list of all devices in Nautobot, but I am getting an error, here is my sample output:

python t-shoot-api.py
Devices found: 50
Device: ams01-asw-01, Site: None
Device: ams01-asw-02, Site: None
Device: ams01-dsw-01, Site: None
Device: ams01-dsw-01:2, Site: None
Device: ams01-pdu-01, Site: None
Device: ams01-pdu-02, Site: None
Device: ams01-rt-01, Site: None
Device: ams01-rt-02, Site: None
Device: ams01-rt-02_sdwan_test, Site: None
Device: ams01-wifi-ap-01, Site: None
Device: ang01-asw-01, Site: None
Device: ang01-asw-02, Site: None
Device: ang01-dsw-01, Site: None
Device: ang01-dsw-01:2, Site: None
Device: ang01-pdu-01, Site: None
Device: ang01-pdu-02, Site: None
Device: ang01-rt-01, Site: None
Device: ang01-rt-02, Site: None
Device: ang01-wifi-ap-01, Site: None
Device: any01-asw-01, Site: None
Device: any01-asw-02, Site: None
Device: any01-dsw-01, Site: None
Device: any01-dsw-01:2, Site: None
Device: any01-pdu-01, Site: None
Device: any01-pdu-02, Site: None
Device: any01-rt-01, Site: None
Device: any01-rt-02, Site: None
Device: any01-wifi-ap-01, Site: None
Device: atb01-asw-01, Site: None
Device: atb01-asw-02, Site: None
Device: atb01-asw-03, Site: None
Device: atb01-asw-04, Site: None
Device: atb01-asw-05, Site: None
Device: atb01-asw-06, Site: None
Device: atb01-asw-07, Site: None
Device: atb01-dsw-01, Site: None
Device: atb01-dsw-01:2, Site: None
Device: atb01-pdu-01, Site: None
Device: atb01-pdu-02, Site: None
Device: atb01-rt-01, Site: None
Device: atb01-rt-02, Site: None
Device: atb01-wifi-ap-01, Site: None
Device: atl01-asw-01, Site: None
Device: atl01-asw-02, Site: None
Device: atl01-asw-03, Site: None
Device: atl01-asw-04, Site: None
Device: atl01-asw-05, Site: None
Device: atl01-asw-06, Site: None
Device: atl01-asw-07, Site: None
Device: atl01-dsw-01, Site: None

And here is my code:

import requests

NAUTOBOT_URL = "https://demo.nautobot.com/api/"
RESOURCE = "dcim/devices/"
API_TOKEN = "YOUR_TOKEN_HERE"

def fetch_devices():
    url = f"{NAUTOBOT_URL}{RESOURCE}"
    headers = {"Authorization": f"Token {API_TOKEN}"}
    resp = requests.get(url, headers=headers)
    if resp.status_code != 200:
        print("Request failed with status:", resp.status_code)
        return []
    try:
        data = resp.json()
    except ValueError:
        print("Response content is not valid JSON.")
        return []
    return data.get("results", [])

def main():
    devices = fetch_devices()
    print(f"Devices found: {len(devices)}")
    for device in devices:
        device_name = device.get("name")
        site = device.get("site")
        print(f"Device: {device_name}, Site: {site}")

if __name__ == "__main__":
    main()

Sample Response from NautobotGPT

High-Level Analysis

You are querying the Nautobot API for devices and printing their names and associated site. All your device output has Site: None, suggesting site is either missing from the API response or not being parsed correctly. The likely cause is that the devices endpoint is returning a nested object for site, not a simple string, and in Nautobot v2+ there is no "site" but "location" (migration from NetBox to Nautobot v2+ removed the Site model in favor of Location).

Errors/Issues

  1. You're looking for a "site" field, but in Nautobot v2 and later, it's "location".
  2. The "location" field is likely a nested object (dict), not a simple string. You need to extract the relevant "name" or "display" field.
  3. Your code only gets the first page of results: Nautobot API is paginated by default. You'll only receive up to 50 (or 100) devices unless you iterate through all pages.

Corrected Code

Below is the improved code, adapted for Nautobot v2.x:

import requests
NAUTOBOT_URL = "https://demo.nautobot.com/api/"
RESOURCE = "dcim/devices/"
API_TOKEN = "YOUR_TOKEN_HERE"
def fetch_devices():
    url = f"{NAUTOBOT_URL}{RESOURCE}"
    headers = {"Authorization": f"Token {API_TOKEN}"}
    devices = []
    while url:
        resp = requests.get(url, headers=headers)
        if resp.status_code != 200:
            print("Request failed with status:", resp.status_code)
            break
        try:
            data = resp.json()
        except ValueError:
            print("Response content is not valid JSON.")
            break
        devices.extend(data.get("results", []))
        url = data.get("next")
    return devices
def main():
    devices = fetch_devices()
    print(f"Devices found: {len(devices)}")
    for device in devices:
        device_name = device.get("name")
        # Use "location" instead of "site" for Nautobot v2+
        location = device.get("location")
        if location:
            # location may be a dict with fields like {"id": 12, "name": "DC1", ...}
            location_name = location.get("name") or location.get("display") or str(location)
        else:
            location_name = None
        print(f"Device: {device_name}, Location: {location_name}")
if __name__ == "__main__":
    main()

Key Changes:

  • Use "location" instead of "site" (Nautobot v2+).
  • Handle nested dict for "location" to print the location's name.
  • Paginate through all results using the "next" value.

Validation Steps

  • Run the script after inserting your real Nautobot API token.
  • Check if devices now show correct locations (not "None").
  • Validate the total count matches the expected number of devices in Nautobot.
  • If you have custom_fields or other nesting, handle similarly by checking for dict content.

Tip:

If you need to support both old NetBox/Nautobot v1 and new Nautobot v2+, you could check for both site and location fields, but for modern Nautobot, location is the proper one.

Let me know if you have any questions or get another error!

Use Case 4: Troubleshooting Existing Nautobot Jobs

In this use case, we will get help with troubleshooting a Nautobot Job that is not working as expected.

Open a new chat and you can use the following prompt to get started.

Question

I'm refactoring a job to use kwargs to clean up and standardize my code while also trying to add before_start() and after_return() functions. I'm misunderstanding the kwargs dict somehow because it looks serialized into the id string when I try to use the device object in before_start() rather than being the object itself. I thought the kwargs should be automatically deserialized, is that the case? Here's snippets from my code for reference:

class MyJob(Job):
    device = ObjectVar(model=Device)

... ... ...

def before_start(self, task_id, args, kwargs):
    self.logger.info("Before start! The provided kwargs are `%s`", kwargs)
    # device = kwargs["device"]
    device = kwargs.get("device")
    self.logger.info(f"Establishing Netmiko connection with: {device.name}")

.... .... ....

def run(self, **kwargs):
    device = kwargs["device"]

I get the same object str has no attribute 'name' when the info log tries to run {device.name} when I try both device = kwargs["device} or device = kwargs.get("device"), and I can see 'device': 'device_uuid', in the log that prints the kwargs. What am I doing wrong?

Sample Response from NautobotGPT

High-level summary:

You are correctly observing that in the Nautobot Job framework, particularly within lifecycle hooks like before_start(), the kwargs you receive do not contain deserialized model objects. Instead, they contain the raw, usually string-serialized, ID/PK values (or sometimes UUIDs) corresponding to the user's input. The actual deserialization to ORM objects only happens for arguments to the run() method, and even then only when the variable type supports it (like ObjectVar).

Detailed Explanation & Solution:

1. Understanding kwargs in before_start() vs run()

  • In before_start(), the kwargs dict gives you the raw representation of input fields, not deserialized model instances.
    • For example, you will get: {"device": "74b9d5cd-xxxx-yyyy-zzzz-e7a3ee1bc3d3"}
  • In run(self, **kwargs), for ObjectVar, Nautobot will automatically look up the actual object and pass it as the value in kwargs.
    • So here, you'll get: device = Device.objects.get(pk="74b9d5cd-xxxx-yyyy-zzzz-e7a3ee1bc3d3") (Nautobot does this under the hood).

2. Why?

At the time Nautobot runs before_start(), the job hasn't been fully instantiated or validated, and the form processing isn't complete, so Nautobot passes through the raw input.

3. How to fix / What to do in before_start()?

If you want to use the actual Device object in before_start(), you need to do the lookup yourself:

from nautobot.dcim.models import Device
def before_start(self, task_id, args, kwargs):
    device_id = kwargs.get("device")
    device = Device.objects.get(pk=device_id)
    self.logger.info("Establishing Netmiko connection with: %s", device.name)
  • Replace device.name with this extra query.
  • Do not use f-strings when using self.logger.info, per Nautobot logging guidelines.

4. Why is it automatic in run()?

Because Nautobot uses a form-to-model translation based on the input types declared in your Job (such as ObjectVar). It injects the real objects into kwargs in run(), but not in the earlier lifecycle hooks.

5. Summary Table:

Method What is in kwargs?
before_start() Raw input values (ID, UUID, string, etc.)
run(**kwargs) Deserialized values (ORM objects for ObjectVar, etc.)

Validation and Testing

  • In before_start, always resolve any ObjectVars using a manual ORM lookup.
  • You can confirm by printing/logging type(kwargs["device"]) in each hook.

Example Refactored Code

from nautobot.apps.jobs import Job, ObjectVar, register_jobs
from nautobot.dcim.models import Device
class MyJob(Job):
    device = ObjectVar(model=Device, description="Select a device")
    def before_start(self, task_id, args, kwargs):
        device_id = kwargs.get("device")
        device = Device.objects.get(pk=device_id)
        self.logger.info("Before start! Device selected: %s", device.name)
    def run(self, device, **kwargs):
        # `device` is already a Device object
        self.logger.info("Run method: Device selected: %s", device.name)
        return f"Job completed for device {device.name}"
register_jobs(MyJob)
  • before_start resolves the device manually.
  • run receives the real object.

Key Takeaway

Nautobot Jobs' before_start() and after_return() methods receive raw input values, not deserialized objects—you must manually fetch models if you need them there.

run() method gets the real thing, as long as your Job variables use the right types.

If you have multiple ObjectVars or need to handle cases where the referenced object might not exist, add exception handling (like catching Device.DoesNotExist).

Let me know if you want a more robust example or have more variables/questions!