0% found this document useful (0 votes)
92 views38 pages

Netbox Plugin Setup Tutorial

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
92 views38 pages

Netbox Plugin Setup Tutorial

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

NetBox Demo Plugin Tutorial

Install NetBox
Plugin development requires a local installation of NetBox. If you don't already have NetBox
installed, please consult the installation instructions.

Be sure to enable debugging in your NetBox configuration by setting DEBUG = True. This will
ensure that static assets can be served by the development server and return complete
tracebacks whenever there's a server error.

Warning: This guide requires NetBox v3.4 or later. Attempting to use an earlier NetBox release
will not work.

Clone the Git Repository


Next, we'll clone the demo git repository from GitHub. First, cd into your preferred location
(your home directory is probably fine), then clone the repo with git clone. We're checking out
the init branch, which will provide us with an empty workspace to start.
$ git clone --branch step00-empty https://github.com/netbox-community/netbox-plugin-demo

Cloning into 'netbox-plugin-demo'...

remote: Enumerating objects: 58, done.


..
Unpacking objects: 100% (58/58), done.

1. Setting Up NetBox Plugin Configuration

• First, we'll create a subdirectory to store our plugin's Python code, along with an
__init__.py file to define the PluginConfig.
$ mkdir netbox-plugin-demo/
$ cd netbox-plugin-demo/
$ mkdir netbox_access_lists
$ touch netbox_access_lists/__init__.py
• Next, open __init__.py in the text editor and Create the PluginConfig Class

from extras.plugins import PluginConfig

class NetBoxAccessListsConfig(PluginConfig):
name = 'netbox_access_lists'
verbose_name = 'Netbox Access Lists'
description = 'Manage simple ACLs in Netbox'
version = 0.2
base_url = 'access-lists'
min_version = "3.7.0"
max_version = "3.7.99"

config = NetBoxAccessListsConfig

• Create a README.md and enter the following content:


$ cd netbox-plugin-demo/
$ touch README.md

# NetBox Demo Plugin


## Access Lists Plugin

• To Install the Plugin, Create setup.py in the project's root directory.

from setuptools import setup, find_packages

setup(
name='netbox-access-lists',
version='0.2.0',
description='An example of Netbox Plugin',
install_requires=[],
packages=find_packages(),
include_package_data=True,
zip_safe=False,
)

• Activate the Virtual Environment and Run setup.py


cd / netbox / netbox
$ python3 setup.py develop
running develop
running egg_info
creating netbox_access_lists.egg-info
writing manifest file 'netbox_access_lists.egg-info/SOURCES.txt'
writing manifest file 'netbox_access_lists.egg-info/SOURCES.txt'
running build_ext

• Configure NetBox
Over in the NetBox installation path, open netbox/netbox/configuration.py and look for
the PLUGINS parameter;
# configuration.py
PLUGINS = [
'netbox_access_lists',
]

Save the file and run the NetBox development server (if not already running):
pip install -r requirements.txt

pip install -r base_requirements.txt

python netbox/manage.py runserver


2. Setting Up Models
• Create a file named models.py into the netbox_access_lists directory
$cd netbox_access_lists
$touch models.py

At the top of the file, Import Django’s module library and NetboxModel class.

We'll also import the PostgreSQL ArrayField.


from django.contrib.postgres.fields import ArrayField
from django.db import models
from netbox.models import NetBoxModel

We'll create two models: 


• AccessList: This will represent an access list, with a name and one or more rules
assigned to it.
• AccessListRule: This will be an individual rule with source/destination IP addresses,
port numbers, etc. assigned to an access list.

1. Access List Model

Fields

• name (CharField): The name of the access list. Maximum length is 100
characters.
• default_action (CharField): The default action for the access list, selected from
predefined choices (ActionChoices). Maximum length is 30 characters.
• comments (TextField): Optional comments or notes related to the access list.

Meta

• ordering: Orders instances by the name field.

Methods

• __str__(): Returns the name of the access list.


• get_default_action_color(): Retrieves the color associated with the
default_action choice from ActionChoices.
• get_absolute_url(): Generates the URL for viewing the access list detail page

class AccessList(NetBoxModel):
name = models.CharField(
max_length=100

2. Access List Rule Model

The AccessListRule model represents a rule within an access list. It includes detailed
attributes for defining network access rules.

Fields

• access_list (ForeignKey): A foreign key linking to the AccessList model. When an


access list is deleted, associated rules are also deleted.
• index (PositiveIntegerField): An integer that determines the order of the rule
within the access list.
• protocol (CharField): The network protocol for the rule, selected from
predefined choices (ProtocolChoices). Maximum length is 30 characters.
• source_prefix (ForeignKey): A foreign key linking to an IP prefix from the ipam
app. Represents the source IP prefix for the rule.
• source_ports (ArrayField): An array of integers representing the source ports.
Optional.
• destination_prefix (ForeignKey): A foreign key linking to an IP prefix from the
ipam app. Represents the destination IP prefix for the rule.
• destination_ports (ArrayField): An array of integers representing the destination
ports. Optional.
• action (CharField): The action taken by the rule, selected from predefined
choices (ActionChoices). Maximum length is 30 characters.
• description (CharField): Optional description of the rule. Maximum length is 500
characters.

Meta

• ordering: Orders instances by access_list and index.


• unique_together: Ensures that each combination of access_list and index is
unique.

Methods

• __str__(): Returns a string representation of the rule, including the access list
and index.
• get_protocol_color(): Retrieves the color associated with the protocol choice
from ProtocolChoices.
• get_action_color(): Retrieves the color associated with the action choice from
ActionChoices.
• get_absolute_url(): Generates the URL for viewing the access list rule detail
page.

class AccessListRule(NetBoxModel):
access_list = models.ForeignKey(
to=AccessList,
on_delete=models.CASCADE,
related_name='rules'
)

Define Field Choices


$ touch choices.py

Import the ChoiceSet class from utilities.choices:


from utilities.choices import ChoiceSet

A. Create a class for Action choices:

class ActionChoices(ChoiceSet):
key = 'AccessListRule.action'

PERMIT = 'permit',
DENY = 'deny',
REJECT = 'reject',
B. Create a class for Protocol choices:
class ProtocolChoices(ChoiceSet):
TCP = 'tcp',
UDP = 'udp',
ICMP = 'icmp',

Create Schema Migrations


$ python netbox/manage.py makemigrations netbox_access_lists --dry-run
Migrations for 'netbox_access_lists':
~/netbox-plugin-demo/netbox_access_lists/migrations/0001_initial.py
- Create model AccessList
- Create model AccessListRule

$ python netbox/manage.py makemigrations netbox_access_lists


Migrations for 'netbox_access_lists':
~/netbox-plugin-demo/netbox_access_lists/migrations/0001_initial.py
- Create model AccessList
- Create model AccessListRule

$ python netbox/manage.py migrate


Operations to perform:
Apply all migrations: admin, auth, circuits, contenttypes, dcim, django_rq,
extras, ipam, netbox_access_lists, sessions, social_django, taggit, tenancy,
users, virtualization, wireless
Running migrations:
Applying netbox_access_lists.0001_initial... OK

Create Some Objects


$ python netbox/manage.py nbshell
from netbox### NetBox interactive shell
### Python 3.8.12 | Django 4.0.3 | NetBox 3.2.0
### lsmodels() will show available models. Use help(<model>) for more info.
>>>

Excellent! We can now create access lists and rules in the database. The next few steps
will work on exposing this functionality in the NetBox user interface.

3. Creating Tables

We will create tables for AccessList and AccessListRule models, and also a bulk edit table
for AccessListRule.
$ cd netbox_access_lists/
$ edit tables.py
Start by importing necessary modules:
import django_tables2 as tables
from netbox.tables import NetBoxTable, ChoiceFieldColumn
from .models import AccessList, AccessListRule
from django.utils.translation import gettext as _

Create a table for the AccessList model:


class AccessListTable(NetBoxTable):
name = tables.Column(linkify=True)
default_action = ChoiceFieldColumn()

• name: Displays the name of the access list with a link.


• default_action: Uses ChoiceFieldColumn to display choices for default actions.
• rule_count: Displays the count of rules associated with the access list.

Create a table for the AccessListRule model:


class AccessListRuleTable(NetBoxTable):
access_list = tables.Column(
linkify=True
)

• access_list: Displays a link to the associated access list.


• index: Displays the rule index with a link.
• protocol: Uses ChoiceFieldColumn to display protocol choices.
• action: Uses ChoiceFieldColumn to display action choices.

Create a bulk edit table for the AccessListRule model:


class AccessListRuleBulkEditTable(NetBoxTable):
action = tables.Column(
verbose_name=_('action'),
linkify=True)

4. Creating Forms
Set Up the Forms Directory
Navigate to netbox_access_lists directory and create a new directory for
forms
$ cd netbox_access_lists/
$ mkdir forms

Create Form Files


Inside the forms directory, create the following files:
$ cd forms
$ touch __init__.py
$ touch bulk_edit.py
$ touch bulk_import.py
$ touch filtersets.py
$ touch model_forms.py
Structure:
forms/
├── __init__.py
├── bulk_edit.py
├── bulk_import.py
├── filtersets.py
└── model_forms.py

Open __init__.py for Forms Integeration


from .bulk_edit import *
from .bulk_import import *
from .model_forms import *
from .filtersets import *

❖ Next we’ll create model_forms.py

The model_forms.py file defines forms for handling data related to the AccessList and
AccessListRule models in the Django application.

It utilizes NetBoxModelForm for form creation and includes dynamic choice fields
for better flexibility.

Import the necessary components:


from netbox.forms import NetBoxModelForm
from utilities.forms.fields import CommentField
from ..models import AccessList, AccessListRule, ActionChoices, ProtocolChoices
from utilities.forms.fields import CommentField, DynamicModelChoiceField
from ipam.models import Prefix
from django.utils.translation import gettext_lazy as _

AccessListForm

A form for creating and updating AccessList instances. It includes fields for the name,
default action, and comments, along with tags for additional categorization
class AccessListForm(NetBoxModelForm):
comment = CommentField()
Components

• comment: A field for additional comments.


• fieldsets: Defines sections in the form for better organization:
• "Access-list": Contains the name and default_action fields.
• "Additional Fields": Contains the tags field.
• Meta Class: Links the form to the AccessList model and specifies the fields to be
included.

AccessListRuleForm

A form for creating and updating AccessListRule instances. It includes fields for access list
selection, network prefixes, protocol choices, and actions.

class AccessListRuleForm(NetBoxModelForm):
access_list = DynamicModelChoiceField(queryset=AccessList.objects.all())

By default, Django will create a "static" foreign key field for related objects. This renders
as a dropdown list that's pre-populated with all available objects. As you can imagine, in
a NetBox instance with many thousands of objects this can get rather unwieldy.
To avoid this, NetBox provides the DynamicModelChoiceField class. This renders foreign
key fields using a special dynamic widget backed by NetBox's REST API. This avoids the
overhead imposed by the static field, and allows the user to conveniently search for the
desired object.
🟢 Tip: The DynamicModelMultipleChoiceField class is also available for many-to-many
fields, which support the assignment of multiple objects.
We'll use DynamicModelChoiceField for the three foreign key fields in our form:
access_list, source_prefix, and destination_prefix. First, we must import the field class,
as well as the models of the related objects. AccessList is already imported, so we just
need to import Prefix from NetBox's ipam app. The beginning of forms.py should now
look like this:

Components

• access_list: A dynamic field for selecting an AccessList instance.


• source_prefix and destination_prefix: Dynamic fields for selecting network prefixes.
• fieldsets: Organizes the form into sections:
• "Rules": Includes fields related to rule configuration.
• "Choices": Includes fields for action and protocol.
• "Additional Fields": Contains description and tags.
• Meta Class: Links the form to the AccessListRule model and specifies the fields to be
included.

❖ Next we’ll create bulk_import.py

This documentation provides details about two bulk import forms used in the plugin for
handling AccessList and AccessListRule models. These forms are part of the NetBox
integration and are designed for importing data from CSV files into the application's
database.

AccessListBulkImportForm

The AccessListBulkImportForm is used to import bulk data into the AccessList model.
This form allows users to upload CSV files containing details of access lists, which are
then processed and stored in the database.

• Imports:

from netbox.forms import NetBoxModelImportForm


from ..models import AccessList, AccessListRule, ActionChoices, ProtocolChoices
from django import forms
from django.utils.translation import gettext_lazy as _
from utilities.forms.fields import CommentField, DynamicModelChoiceField, CSVModelChoiceField,
CSVChoiceField
from ipam.models import Prefix

• NetBoxModelImportForm from netbox.forms: Provides base functionality for


NetBox import forms.
• AccessList from models: The model into which data will be imported.
• forms from django: Django's form handling library.

class AccessListBulkImportForm(NetBoxModelImportForm):
class Meta:

Meta Information:
• model: Specifies that this form is for the AccessList model.
• fields: Lists the fields that can be imported via the form:
• id: The unique identifier of the access list.
• name: The name of the access list.
• default_action: The default action associated with the access list.
• comments: Any additional comments related to the access list.

AccessListRuleBulkImportForm

The AccessListRuleBulkImportForm is used to import bulk data into the AccessListRule


model. This form handles the importing of rules associated with access lists, including
details like source and destination prefixes and actions.

Imports:

• NetBoxModelImportForm from netbox.forms: Provides base functionality for


NetBox import forms.
• AccessList, AccessListRule, ActionChoices, ProtocolChoices from ..models: Models
and choices related to access lists and rules.
• forms from django: Django's form handling library.
• CommentField, DynamicModelChoiceField, CSVModelChoiceField, CSVChoiceField
from utilities.forms.fields: Custom form fields for handling CSV data.
• Prefix from ipam.models: Model representing IP address prefixes.

class AccessListRuleBulkImportForm(NetBoxModelImportForm):
action = CSVChoiceField(
label=_('Action'),
choices=ActionChoices,
help_text=_('Action')
)

Fields:

• action: CSVChoiceField
▪ label: 'Action'
▪ choices: Choices for the action field, imported from ActionChoices.
▪ help_text: 'Action'
• access_list: CSVModelChoiceField
▪ label: 'Access List'
▪ queryset: Fetches all AccessList objects.
▪ to_field_name: 'name'
▪ help_text: 'Access List'
• source_prefix: CSVModelChoiceField
▪ queryset: Fetches all Prefix objects.
▪ required: False
▪ to_field_name: 'prefix'
▪ help_text: 'Source Prefix'
• destination_prefix: CSVModelChoiceField
▪ queryset: Fetches all Prefix objects.
▪ required: False
▪ to_field_name: 'prefix'
▪ help_text: 'Destination Prefix'
❖ Next we’ll create bulk_edit.py file

This documentation provides details about two bulk edit forms used in the Plugin for
handling AccessList and AccessListRule models. These forms are used to edit multiple
records at once and are part of the NetBox integration.

AccessListBulkEditForm

The AccessListBulkEditForm allows users to edit multiple AccessList records at once. This
form is used to apply changes to the comments field of the access lists in bulk.

Imports:

• NetBoxModelBulkEditForm from netbox.forms: Base class providing bulk edit


functionality.
• AccessList from models: The model to which the bulk edits are applied.
• CommentField from utilities.forms.fields: A custom field for handling comments.
• forms from django: Django's form handling library.

class AccessListBulkEditForm(NetBoxModelBulkEditForm):
comments = CommentField()
AccessListRuleBulkEditForm

The AccessListRuleBulkEditForm allows users to edit multiple AccessListRule records at once.


This form is used to update the action and protocol fields of the access list rules in bulk.

Imports:

• NetBoxModelBulkEditForm from netbox.forms: Base class providing bulk edit


functionality.
• AccessListRule from ..models: The model to which the bulk edits are applied.
• ActionChoices, ProtocolChoices from models: Choices for the action and protocol
fields.
• forms from django: Django's form handling library.

class AccessListRuleBulkEditForm(NetBoxModelBulkEditForm):
fieldsets = (
( None,
(
'action',
'protocol', ),),)

Fields:
• action: forms.ChoiceField
▪ label: 'Action'
▪ choices: Choices for the action field, imported from ActionChoices.
▪ required: False (indicates that this field is optional)
• protocol: forms.ChoiceField
▪ label: 'Protocol'
▪ choices: Choices for the protocol field, imported from ProtocolChoices.
▪ required: False (indicates that this field is optional)

Meta Information:

a. fieldsets: Defines the layout of the form fields in the bulk edit interface. This
specifies that action and protocol should be grouped together.
b. model: Specifies that this form is for the AccessListRule model.

5. Views.py
The views.py file defines various views for managing AccessList and AccessListRule
objects in the Django application. These views include detail views, list views, edit views,
delete views, and bulk operations. Each view is associated with a specific form, table,
and filterset to handle CRUD operations efficiently.

Imports
from netbox.views import generic
from . import filtersets
from . import models, tables
from django.db.models import Count
from utilities.views import register_model_view
from .forms.model_forms import AccessListForm, AccessListRuleForm
from .forms.bulk_edit import AccessListRuleBulkEditForm, AccessListBulkEditForm
from .forms.bulk_import import AccessListBulkImportForm, AccessListRuleBulkImportForm
from .forms.filtersets import *

AccessListView
A view for displaying the details of a AccessList instance, including a table of related
AccessListRule instances.
@register_model_view(models.AccessList)
class AccessListView(generic.ObjectView):
queryset = models.AccessList.objects.all()

Components

• get_extra_context: Adds a table of related AccessListRule instances to the


context.
• Decorator: The @register_model_view decorator attaches a custom view to a
Django model in NetBox, allowing additional tabs or views for the model's
detail page. It generates a URL path and view name based on provided
parameters or defaults to the model's name. This facilitates easy integration
of new functionalities into existing model views.

AccessListListView

A view for listing all AccessList instances with an annotated rule count.
class AccessListListView(generic.ObjectListView):
queryset = AccessList.objects.annotate(
rule_count=Count('rules')
)

Components

• queryset: Annotates AccessList instances with a count of related rules.


• table: Specifies the table to display data.
• filterset: Provides filtering capabilities.

AccessListEditView

A view for editing an AccessList instance.


@register_model_view(models.AccessList, "edit")
class AccessListEditView(generic.ObjectEditView):
queryset = models.AccessList.objects.all()
Components

• form: Uses AccessListForm for editing.

AccessListDeleteView

A view for deleting an AccessList instance.


@register_model_view(models.AccessList, "delete")
class AccessListDeleteView(generic.ObjectDeleteView):

AccessListsBulkImportView

A view for bulk importing AccessList instances.


class AccessListsBulkImportView(generic.BulkImportView):
queryset = models.AccessList.objects.all()

AccessListBulkDeleteView

A view for bulk deleting AccessList instances.


class AccessListBulkDeleteView(generic.BulkDeleteView):
queryset = models.AccessList.objects.all()

AccessListBulkEditView

A view for bulk editing AccessList instances.


class AccessListBulkEditView(generic.BulkEditView):
queryset = models.AccessList.objects.all()

AccessListRuleView

Displays the details of a single AccessListRule instance.


@register_model_view(models.AccessListRule)
class AccessListRuleView(generic.ObjectView):

Components

• queryset: Retrieves all AccessListRule instances from the database.


AccessListRuleListView

Lists all AccessListRule instances with filtering and table display capabilities.
class AccessListRuleListView(generic.ObjectListView):
queryset = models.AccessListRule.objects.all()

Components

• queryset: Retrieves all AccessListRule instances.


• table: Uses AccessListRuleTable to display data in a tabular format.
• filterset: Applies AccessListRuleFilterSet to filter the list of rules.
• filterset_form: Provides the form for filtering.

AccessListRuleEditView

Allows editing of an AccessListRule instance.


@register_model_view(models.AccessListRule, "edit")
class AccessListRuleEditView(generic.ObjectEditView):

Components

• queryset: Retrieves all AccessListRule instances.


• form: Uses AccessListRuleForm for the editing form.
• Decorator: @register_model_view with "edit" action allows editing of the
model.

AccessListRuleDeleteView

Handles the deletion of an AccessListRule instance.

@register_model_view(models.AccessListRule, "delete")
class AccessListRuleDeleteView(generic.ObjectDeleteView):

Components

• queryset: Retrieves all AccessListRule instances.


• Decorator: @register_model_view with "delete" action allows deletion of the
model.

AccessListRuleImportView

Provides functionality for bulk importing AccessListRule instances.

class AccessListRuleImportView(generic.BulkImportView):
queryset = models.AccessListRule.objects.all()

• queryset: Retrieves all AccessListRule instances.


• model_form: Uses AccessListRuleBulkImportForm for handling the import
process.

AccessListRuleBulkEditView

Facilitates bulk editing of AccessListRule instances.


class AccessListRuleBulkEditView(generic.BulkEditView):
queryset = models.AccessListRule.objects.all()

Components

• queryset: Retrieves all AccessListRule instances.


• filterset: Applies AccessListRuleFilterSet for filtering.
• table: Uses AccessListRuleTable for displaying data.
• form: Uses AccessListRuleBulkEditForm for bulk editing.

AccessListRuleBulkDeleteView

Handles bulk deletion of AccessListRule instances.

class AccessListRuleBulkDeleteView(generic.BulkDeleteView):
queryset = models.AccessListRule.objects.all()

Components

• queryset: Retrieves all AccessListRule instances.


• table: Uses AccessListRuleTable for displaying data during bulk deletion.

Map Views to URLs

In the netbox_access_lists/ directory, create urls.py. This will define our view URLs.

Imports
from django.urls import path, include
from . import views
from utilities.urls import get_model_urls

app_name = "netbox_access_lists"

URL Patterns
urlpatterns = [
# Access lists
path('access-lists/', views.AccessListListView.as_view(), name='accesslist_list'),
path('access-lists/add/', views.AccessListEditView.as_view(), name='accesslist_add'),
path('access-lists/import/', views.AccessListsBulkImportView.as_view(), name='accesslist_import'),
path('access-lists/edit/', views.AccessListBulkEditView.as_view(), name='accesslist_bulk_edit'),
path('access-lists/delete/', views.AccessListBulkDeleteView.as_view(), name='accesslist_bulk_delete'),
path("access-lists/<int:pk>/", include(get_model_urls(app_name, "accesslist"))),

# Access list rules


path('rules/', views.AccessListRuleListView.as_view(), name='accesslistrule_list'),
path('rules/add/', views.AccessListRuleEditView.as_view(), name='accesslistrule_add'),
path('rules/import/', views.AccessListRuleImportView.as_view(), name='accesslistrule_import'),
path('rules/edit/', views.AccessListRuleBulkEditView.as_view(), name='accesslistrule_bulk_edit'),
path('rules/delete/', views.AccessListRuleBulkDeleteView.as_view(),
name='accesslistrule_bulk_delete'),
path("rules/<int:pk>/", include(get_model_urls(app_name, "accesslistrule"))),

Test the Views

Now for the moment of truth: Has all our work thus far yielded functional UI views?
Check that the development server is running, then open a browser and navigate to
http://localhost:8000/plugins/access-lists/access-lists/. You should see the access list list
view.

6. Templates

NetBox looks for templates within the templates/ directory (if it exists) within the plugin
root. Within this directory, create a subdirectory bearing the name of the plugin:

$ cd
netbox_access_lists /
$ mkdir - p
templates /netbox_access_lists /

Create the Access List Template


Begin by creating the file accesslist.html in the plugin's template directory.
$ touch templates/netbox_access_lists/accesslist.html

Although we need to create our own template, NetBox has done much of the work for
us, and provides a generic template that we can easily extend. At the top of the file, add
an extends tag:
{% extends 'generic/object.html' %}
{% load render_table from django_tables2 %}

{% block content %}
<div class="row mb-3">
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">Access List</h5>

Let's take a look at our new template! Navigate to the list view again (at
http://localhost:8000/plugins/access-lists/access-lists/), and follow the link through to a
particular access list. You should see something like the image below.
🟦 Note: If NetBox complains that the template still does not exist, you may need to
manually restart the development server (manage.py runserver).

This is nice, but it would be handy to include the access list's assigned rules on the page
as well.

Add a Rules Table

@register_model_view(models.AccessList)
class AccessListView(generic.ObjectView):
queryset = models.AccessList.objects.all()
def get_extra_context(self, request, instance):
table = tables.AccessListRuleTable(instance.rules.all())
table.configure(request)

return {
'rules_table': table,
}

This method does three things:


• Instantiate AccessListRuleTable with a queryset matching all rules assigned to
this access list
• Configure the table instance according to the current request (to honor user
preferences)
• Return a dictionary of context data referencing the table instance
This makes the table available to our template as the rules_table context variable. Let's
add it to our template.
First, we need to import the render_table tag from the django-tables2 library, so that
we can render the table as HTML. Add this at the top of the template, immediately
below the {% extends 'generic/object.html' %} line:

{% load render_table from django_tables2 %}

Then, immediately above the {% endblock content %} line at the end of the file, insert
the following template code:
<div class="row">
<div class="col col-md-12">
<div class="card">
<h5 class="card-header">Rules</h5>
<div class="card-body table-responsive">
{% render_table rules_table %}
</div>
</div>
</div>

After refreshing the access list view in the browser, you should now see the rules table
at the bottom of the page.
Create the AccessListRule Template

Speaking of rules, let's not forget about our AccessListRule model: It needs a
template too. Create accesslistrule.html alongside our first template:

$ edit templates/netbox_access_lists/accesslistrule.html

{% extends 'generic/object.html' %}

{% block content %}
<div class="row mb-3">
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">Access List Rule</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Access List</th>
<td>
<a href="{{ object.access_list.get_absolute_url }}">{{ object.access_list }}</a>
</td>
</tr>
<tr>
Feel free to experiment with different layouts and content before proceeding with the
next step.

7. Navigation

So far, we've been manually entering URLs to access our plugin's views. This obviously
will not suffice for regular use, so let's see about adding some links to NetBox's
navigation menu.

Adding Navigation Menu Items


Begin by creating navigation.py in the netbox_access_lists/ directory.

$ cd netbox_access_lists/
$ edit navigation.py
This document explains the configuration of menu items and buttons for the
netbox_access_lists plugin within the NetBox application. The code sets up navigation
menus and buttons for managing Access Lists and Access List Rules.

Components

1. Imports

• Classes for menu and button setup.


• Plugin settings.

2. Buttons:

• Add: Allows users to add new access lists or rules.


• Import: Enables importing data for access lists or rules.

3. Permissions:

• Buttons are controlled by permissions (add_accesslist, add_accesslistrule,


view_accesslist, view_accesslistrule) to restrict access based on user roles.

2. Access List Buttons:

accesslist_buttons = [
PluginMenuButton(
link='plugins:netbox_access_lists:accesslist_add',
PluginMenuButton(
link='plugins:netbox_access_lists:accesslist_import',

3. Access List Rule Buttons:

accesslistrule_buttons = [
PluginMenuButton(
link='plugins:netbox_access_lists:accesslistrule_add',
PluginMenuButton(
link='plugins:netbox_access_lists:accesslistrule_import',

4. Menu Items:
menu_items = (
PluginMenuItem(
link='plugins:netbox_access_lists:accesslist_list',
PluginMenuItem(
link='plugins:netbox_access_lists:accesslistrule_list',

8. Filter Sets:
Filters enable users to request only a specific subset of objects matching a query; when
filtering the sites list by status or region, for instance. NetBox employs the django-filters
library to build and apply filter sets for models. We can create filter sets to enable this
same functionality for our plugin as well.
$ cd netbox_access_lists/
$ touch filtersets.py

Create a Filter Set


At the top of this file, we'll import NetBox's NetBoxModelFilterSet class, which will serve
as the base class for our filter set, as well as our AccessListRule model. (In the i nterest
of brevity, we're only going to create a filter set for one model, but it should be clear how
to replicate this approach for the AccessList model as well.)

AccessListFilterSet

• Purpose: This filter set is used to filter and search through access lists.
• Fields Available for Filtering:
o id: The unique identifier of the access list.
o name: The name of the access list.
• Custom Search: Allows searching within the name field of the access lists to find
lists that contain the search term.

AccessListRuleFilterSet
• Purpose: This filter set allows you to filter and search through access list rules.
• Fields Available for Filtering:
o id: The unique identifier of the rule.
o access_list: The specific access list to which the rule belongs.
o index: The position or order of the rule within the access list.
o protocol: The protocol used by the rule.
o action: The action taken by the rule (e.g., permit or deny).
• Custom Search: Allows searching within the description field of the rules to find
rules that contain the search term.

Create a Filter Form


The filter set handles the "behind the scenes" process of filtering queries, but we also
need to create a form class to render the filter fields in the UI. We'll add this to
forms.py. First, import Django's forms module (which will provide the field classes we
need) and append NetBoxModelFilterSetForm to the existing import statement for
netbox.forms:

AccessListRuleFilterForm

• Purpose: Provides a user interface for filtering access list rules based on various
criteria.
• Fields:
• access_list: Dropdown menu to select an access list for filtering.
• index: Field to specify the rule’s index number.
• protocol: Dropdown menu to select one or multiple protocols.
• action: Dropdown menu to select the action (permit/deny) associated
with the rule.

AccessListFilterForm

• Purpose: Provides a user interface for filtering access lists based on criteria.
• Fields:
• name: Field to enter the name of the access list for filtering.
• action: Dropdown menu to select an action associated with the access
list.

9. REST API
The REST API enables powerful integration with other systems which exchange data with
NetBox. It is powered by the Django REST Framework (DRF), which is not a
componentof Django itself. In this tutorial, we'll see how we can extend NetBox's REST
API to serve our plugin.

Our API code will live in the api/ directory under netbox_access_lists/. Let's go ahead and
create that as well as an __init__.py file now:

Structure:

├── __init__.py

├── nested_serializers.py

├── serializers.py

├── urls.py

└── views.py

Create Model Serializers


cd netbox_access_lists/api
touch __init__.py
touch nested_serializers.py
touch serializers.py
touch urls.py
touch views.py

Imports
from rest_framework import serializers
from netbox.api.serializers import NetBoxModelSerializer
from ..models import AccessList, AccessListRule
from ipam.api.serializers import NestedPrefixSerializer
from .nested_serializers import NestedAccessListSerializer

Create AccessListSerializer

• Purpose: Serializes the AccessList model.


• Key Fields:
• url: Hyperlink to the access list's detail view.
• rule_count: Number of rules in the access list (read-only).
• Includes: Basic fields like id, name, default_action, and timestamps.

Create AccessListRuleSerializer

• Key Fields:
o url: Hyperlink to the rule's detail view.
o access_list, source_prefix, destination_prefix: Nested serializers for
related objects.
• Includes: Fields like id, index, protocol, source_prefix, and
destination_prefix.

• HyperlinkedIdentityField: Creates a URL link to the detailed view of the


resource. This is useful for providing links to specific API endpoints for resources.
• NestedAccessListSerializer and NestedPrefixSerializer: These are nested
serializers that include related objects in the serialized output. This approach
helps to represent relationships between models more comprehensively.
• NetBoxModelSerializer: A custom serializer base class that extends the DRF
ModelSerializer, providing additional functionality and default behavior specific
to NetBox models.

Create Nested Serializers


Begin by importing NetBox's WritableNestedSerializer class. This will serve as the base
class for our nested serializers.
from netbox.api.serializers import WritableNestedSerializer
from rest_framework import serializers
from ..models import AccessList, AccessListRule

Then, create two nested serializer classes, one for each of our plugin's models. Each of
these will have a url field and Meta child class like the regular serializers, however the
Meta.fields attribute for each is limited to a bare minimum of fields: id, url, display, and
a supplementary human-friendly identifier. Add these in serializers.py above the regular
serializers (because we need to define NestedAccessListSerializer before we can
reference it).

WritableNestedSerializer: Allows for nested object creation and updating

Create the Views


Next, we need to create views to handle the API logic. Just as serializers are roughly
analogous to forms, API views work similarly to the UI views that we created in step five.
However, because API functionality is highly standardized, view creation is substantially
simpler: We generally need only to create a single view set for each model. A view set is
a single class that can handle the view, add, change, and delete operations which each
require dedicated views under the UI.
Start by creating api/views.py and importing NetBox's NetBoxModelViewSet class, as
well as our plugin's models and filtersets modules, and our serializers.

AccessListViewSet

• Purpose: Handles CRUD operations for the AccessList model.


• Queryset:
o Retrieves AccessList objects with pre-fetched tags.
o Annotates each AccessList with a count of related rules (i.e., the number
of rules associated with each access list).
• Serializer: Uses AccessListSerializer to serialize the data for API responses.

AccessListRuleViewSet
• Filterset: Utilizes AccessListRuleFilterSet to provide filtering options for the
AccessListRule model.

Create the Endpoint URLs


Finally, we'll create our API endpoint URLs. This works a bit differently from UI views:
Instead of defining a series of paths, we instantiate a router and register each view set
to it.

Next, we'll define an app_name. This will be used to resolve API view names for
our plugin.
app_name = 'netbox_access_list'

Then, we create a NetBoxRouter instance and register each view with it using
our desired URL. These are the endpoints that will be available under
/api/plugins/access-lists/.
router = NetBoxRouter()
router.register('access-lists', views.AccessListViewSet)
router.register('access-list-rules', views.AccessListRuleViewSet)
Finally, we expose the router's urls attribute as urlpatterns so that it will be
detected by the plugins framework.
urlpatterns = router.urls
10. GraphQL API
In addition to its REST API, NetBox also features a GraphQL API. This can be used
to conveniently request arbitrary collections of data about NetBox objects.
NetBox's GraphQL API is built using the Graphene and graphene-django library.

Structure
cd graphql/
ls
__init__.py __pycache__ schema.py types.py

Let’s Create types.py

Create the Object Types


Subclass NetBoxObjectType to create two object type classes, one for each of our
models. Just like with the REST API serilizers, create a child Meta class on each
defining its model and fields. However, instead of explicitly listing each field by name, in
our case we can use the special value __all__ to indicate that we want to
include all available model fields. Additionally, declare filterset_class on
AccessListRuleType to attach the filter set.
class AccessListType(NetBoxObjectType):
class AccessListRuleType(NetBoxObjectType):

Create the Query


create schema.py
Then we need to create our query class. SubclassGraphene's ObjectType class
and define two fields for each model: an object field and a list field.
class AccessListQuery(ObjectField):
access_list = ObjectField(AccessListType)
access_list_list = ObjectListField(AccessListType)

access_list_rule = ObjectField(AccessListRuleType)
access_list_rule_list = ObjectListField(AccessListRuleType)

schema = AccessListQuery

To try out the GraphQL API, open <http://netbox:8000/graphql/> in a


browser and enter the following query:
query {
access_list_list {
id
name
rules {
index
action
description
}
}
}
11. Create Search Indexes
Our plugin has two models: AccessList and AccessListRule. We'd like users to be able to search
instances of both models using NetBox's global search feature. To enable this, we need
to declare and register a SearchIndex subclass for each model.
Begin by creating search.py in the plugin's root directory, alongside models.py.

$ cd netbox_access_lists/
$ edit search.py

AccessListIndex

• Purpose: Configures the search index for the AccessList model.


• Inherits from: SearchIndex, a base class provided by NetBox for search indexing.
• model: Specifies the model to be indexed, which is AccessList.
• fields:
o name: Indexed with a weight of 50, indicating its importance in search
relevance.
o comments: Indexed with a weight of 100, suggesting it's highly relevant for
search.
• display_attrs: Defines description as an attribute to be used for displaying search
results.

AccessListRuleIndex

• Purpose: Configures the search index for the AccessListRule model.


• Inherits from: SearchIndex.
• model: Specifies the model to be indexed, which is AccessListRule.
• fields:
o description: Indexed with a weight of 500, reflecting its high relevance for
search.
• display_attrs: Defines description as the attribute used for displaying search
results.
Register the Indexers
Finally, we need to register our indexers so that NetBox knows to run them.
At the top of search.py, import the register_search decorator. Then, use it
to wrap both of our index classes:
from netbox.search import SearchIndex, register_search
from .models import AccessList, AccessListRule

@register_search
class AccessListIndex(SearchIndex):

With our indexers now registered, we can run the reindex management command
to index any existing objects. (New objects created from this point forward will be
registered automatically upon creation.)

$ ./manage.py reindex netbox_access_lists


Reindexing 2 models.
Indexing models
netbox_access_lists.accesslist... 1 entries cached.
netbox_access_lists.accesslistrule... 3 entries cached.

This completes the plugin development tutorial. Well done! Now you're all set to make a plugin
of your own!

You might also like