Pynetbox - NetBox Python API client part 3 - updates and deletions

Time for another post in the series on Pynetbox, awesome PythonAPI client for NetBox. In part 1 we learned how to retrieve information from NetBox, and how to work with objects returned by Pynetbox. In part 2 we've gone through object creation, where we learned how to check what fields are required and how to fill them to make NetBox happy.

In this post we will talk about updating existing objects and deleting objects we no longer need.

Unlike first two posts this one will be much shorter but should be no less exciting!

Posts in the Pynetbox series:

Contents

Software versions in this blog post

This blog post is based on the following versions of software:

  • NetBox v2.4.8
  • Pynetbox v4.0.6

Examples are run with Python v3.7.0 but should work with any version of Python3.

NOTE Previous posts used Pynetbox v3.4.7 but in the later releases a few bugs were fixed so I moved to v4.0.6.

Setup and prerequisites

I assume you already have NetBox installed and that machine running pynetbox has access to it. Before installing pynetbox you will also need to create API token, if you don't have one already.

I covered API token creation in the first post in the series. If you need to creat one follow the link before proceeding: https://ttl255.com/pynetbox-netbox-python-api-client-p1-getting-info/#setup-prereq

Example 1 - Making updates to an object]

To start off this post, we'll get right into it and we'll do some updates!

First step to making updates is to retrieve object we want to modify.

Once we retrieved the object, making updates to it is very simple. You just need to assign a new value to the attribute of the object and save it!

Yup, as simple as that.

It's action time, we'll retrieve an object and then we'll try to modify it.

rtr = nb.dcim.devices.get(name="rtr-inet-seoul-01")

print("Device name: ", rtr.name)
print("Current tenant: ", rtr.tenant)
print("Current serial number: ", rtr.serial)
print("Current asset tag: ", rtr.asset_tag)
Device name:  rtr-inet-seoul-01
Current tenant:  None
Current serial number:  
Current asset tag:  None

We retrieved device object and confirmed tenant, serial, and asset tags attributes currently have no values assigned.

Let's set a few attributes and try saving.

# Retrieve tenant we want to assign to our router
rtr_tenant = nb.tenancy.tenants.get(name="RogNet Corp", group="internal")

# Set attributes to new values
rtr.tenant = rtr_tenant
rtr.serial = "436F6F6C21"
rtr.asset_tag = "0027182"
rtr.save()

# Retrieve same router again just to prove it was updated
rtr_modified = nb.dcim.devices.get(name="rtr-inet-seoul-01")

print("Device name: ", rtr_modified.name)
print("New tenant: ", rtr_modified.tenant)
print("New serial number: ", rtr_modified.serial)
print("New asset tag: ", rtr_modified.asset_tag)
Device name:  rtr-inet-seoul-01
New tenant:  RogNet Corp
New serial number:  436F6F6C21
New asset tag:  0027182

Worked like a charm. Requested fields were updated with values we provided. You probably noticed that I assigned an actual object to the tenant attribute, and not an id value. Pynetbox allows us to do that, making things much simpler. We don't have to retrieve object's id and set attribute to that value, just assigning object itself will work.

Updating attributes one by one certainly works, but it can get a bit unwieldy if we have more than 2 or 3 attributes to update. Perhaps there is a better way?

Example 2 - Updating multiple values with dictionary

Well, yes, there is! We can update the object by passing a dictionary to an update() method. This is equivalent to manually assigning values to multiple attributes and calling save(). In the case of update() however, save() is called for us so that we don't have to remember to do it manually.

Let's make similar changes to another device object, but this time we'll pass a dictionary with attributes/values we want to change.

# Retrieve router object for update with dictionary
rtr2 = nb.dcim.devices.get(name="sw-mgmt-kampala-01")

print("Device name: ", rtr2.name)
print("Current tenant: ", rtr2.tenant)
print("Current serial number: ", rtr2.serial)
print("Current asset tag: ", rtr2.asset_tag)
Device name:  sw-mgmt-kampala-01
Current tenant:  None
Current serial number:  
Current asset tag:  None

Again, no values set on the attributes we're about to modify.

# Retrieve tenant for rtr2
rtr2_tenant = nb.tenancy.tenants.get(name="Uganda Press", group="customers")

rtr2_update_dict = dict(
    tenant=rtr2_tenant,
    serial="4E69636521",
    asset_tag="0057721",
)

# Nicely ask PynetBox to update our object
rtr2.update(rtr2_update_dict)

# Retrieve same router again just to prove it was updated
rtr2_modified = nb.dcim.devices.get(name="sw-mgmt-kampala-01")

print("Device name: ", rtr2_modified.name)
print("New tenant: ", rtr2_modified.tenant)
print("New serial number: ", rtr2_modified.serial)
print("New asset tag: ", rtr2_modified.asset_tag)
Device name:  sw-mgmt-kampala-01
New tenant:  Uganda Press
New serial number:  4E69636521
New asset tag:  0057721

And it worked beautifully. We passed our crafted dictionary to the update() method and then showed that object has been indeed modified just the way we wanted.

So that's it for the updates. As you can see making those is easy and straightforward but we should be used to this by now with Pynetbox lending us its helping hand.

Time to move onto deletions.

Example 3 - Deleting objects

Deletions are even easier to do than updates. We first retrieve object from NetBox and then call method delete() on it. Yup, that's really it, does what it says on the tin.

Let's go through a quick example of creating and deleting an object:

# Retrieve info needed for creating our device
ndev_dtype = nb.dcim.device_types.get(slug="asr-1002-x")
ndev_drole = nb.dcim.device_roles.get(slug="internet-edge")
ndev_site = nb.dcim.sites.get(slug="seoul-dc-01")

try:
    result = nb.dcim.devices.create(
        name="bad-isp-delete",
        device_type=ndev_dtype.id,
        device_role=ndev_drole.id,
        site=ndev_site.id,
    )
except pynetbox.RequestError as e:
    print(e.error)

rtr = nb.dcim.devices.get(name="bad-isp-delete")

print("RTR Name:", rtr.name)
RTR Name: bad-isp-delete

Looking good, object is in NetBox , but this router happens to connect to a poor ISP so we decided to decommission it and need to update NetBox accordingly.

Begone bad ISP!

rtr.delete()

Dit it work?

rtr_ = nb.dcim.devices.get(name="bad-isp-delete")
print("NetBox result:", rtr_)
NetBox result: None

And it's gone. Great, we can now deploy it in a different location, hopefully with a much better ISP.

It's worth pointing out that the originally retrieved object is still in the memory if you need access to its attributes or want to re-create things deleted by accident.

# Object is still in the memory
pprint(dict(rtr))
{'asset_tag': None,
 'cluster': None,
 'comments': '',
 'created': '2019-01-13',
 'custom_fields': {'TKTREF': None},
 'device_role': {'id': 6,
                 'name': 'Internet edge',
                 'slug': 'internet-edge',
                 'url': 'http://localhost/api/dcim/device-roles/6/'},
 'device_type': {'id': 5,
                 'manufacturer': {'id': 1,
                                  'name': 'Cisco',
                                  'slug': 'cisco',
                                  'url': 'http://localhost/api/dcim/manufacturers/1/'},
                 'model': 'ASR 1002-X',
                 'slug': 'asr-1002-x',
                 'url': 'http://localhost/api/dcim/device-types/5/'},
 'display_name': 'bad-isp-delete',
 'face': None,
 'id': 50,
 'last_updated': '2019-01-13T20:50:27.244711Z',
 'local_context_data': None,
 'name': 'bad-isp-delete',
 'parent_device': None,
 'platform': None,
 'position': None,
 'primary_ip': None,
 'primary_ip4': None,
 'primary_ip6': None,
 'rack': None,
 'serial': '',
 'site': {'id': 2,
          'name': 'seoul-dc-01',
          'slug': 'seoul-dc-01',
          'url': 'http://localhost/api/dcim/sites/2/'},
 'status': {'label': 'Active', 'value': 1},
 'tags': [],
 'tenant': None,
 'vc_position': None,
 'vc_priority': None,
 'virtual_chassis': None}

So that's it. I appreciate that this post was a bit shorther than the previous two, but fear not! We will be returning with posts featuring more examples of using Pynetbox that will hopefully provide some inspiration for your own NetBox adventures. Some of the things I plan to show are automatic assignment of prefixes and IP addresses. We'll also have a look at custom fields and tags and possibly more.

References: