You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: tutorial/step01-initial-setup.md
+4-7
Original file line number
Diff line number
Diff line change
@@ -76,9 +76,7 @@ Back in the project's root (one level up from `__init__.py`), create a file name
76
76
Manage simple access control lists in NetBox
77
77
```
78
78
79
-
You'll notice that our reference repo names this file `README.md`: The `md` extension tells tools which support it to render the file as Markdown for better readability.
80
-
81
-
:warning:**Warning:** Be sure to create this file in the project root and _not_ within the `netbox_access_lists` directory.
79
+
:green_circle:**Tip:** You'll notice that we've given our `README` file a `md` extension. This tells tools which support it to render the file as Markdown for better readability.
82
80
83
81
## Install the Plugin
84
82
@@ -93,17 +91,16 @@ setup(
93
91
name='netbox-access-lists',
94
92
version='0.1',
95
93
description='An example NetBox plugin',
96
-
license='Apache 2.0',
97
94
install_requires=[],
98
95
packages=find_packages(),
99
96
include_package_data=True,
100
97
zip_safe=False,
101
98
)
102
99
```
103
100
104
-
:warning:**Warning:** Be sure to create this file in the project root and _not_ within the `netbox_access_lists` directory.
101
+
:warning:**Warning:** Be sure to create `setup.py` in the project root and _not_ within the `netbox_access_lists` directory.
105
102
106
-
This file will call the `setup()` function provided by `setuptools` to install our code. There are plenty of additional arguments that can be passed, but for our example this is sufficient.
103
+
This file will call the `setup()` function provided by Python's [`setuptools`](https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/) library to install our code. There are plenty of additional arguments that can be passed, but for our example this is sufficient.
107
104
108
105
:green_circle:**Tip:** There are alternative methods for installing Python code which work just as well; free free to use your preferred approach. Just be aware that this guide assumes the use of `setuptools` and adjust accordingly.
109
106
@@ -146,7 +143,7 @@ Save the file and run the NetBox development server (if not already running):
146
143
$ python netbox/manage.py runserver
147
144
```
148
145
149
-
You should see the development start successfully. Open NetBox in a new browser window, log in as a superuser, and navigate to the admin UI. Under **System > Installed Plugins** you should see our plugin listed.
146
+
You should see the development server start successfully. Open NetBox in a new browser window, log in as a superuser, and navigate to the admin UI. Under **System > Installed Plugins** you should see our plugin listed.
Copy file name to clipboardExpand all lines: tutorial/step02-models.md
+36-25
Original file line number
Diff line number
Diff line change
@@ -24,7 +24,7 @@ from netbox.models import NetBoxModel
24
24
We'll create two models:
25
25
26
26
*`AccessList`: This will represent an access list, with a name and one or more rules assigned to it.
27
-
*`AccessListRule`: This will be an individual rule with source/destination IP addresses and port numbers, etc. assigned to an access list.
27
+
*`AccessListRule`: This will be an individual rule with source/destination IP addresses, port numbers, etc. assigned to an access list.
28
28
29
29
### AccessList
30
30
@@ -43,14 +43,14 @@ class AccessList(NetBoxModel):
43
43
)
44
44
```
45
45
46
-
By default, model instances are ordered by their primary keys, but it would make more sense to order access lists by name. We can do that by creating a `Meta`subclass and defining an `ordering` variable. (Be sure to create the `Meta` class *inside*`AccessList`, not under it.)
46
+
By default, model instances are ordered by their primary keys, but it would make more sense to order access lists by name. We can do that by creating a `Meta`child class and defining an `ordering` variable. (Be sure to create the `Meta` class *inside*`AccessList`, not after it.)
47
47
48
48
```python
49
49
classMeta:
50
50
ordering = ('name',)
51
51
```
52
52
53
-
Finally, we'll add a `__str__()` method to control how an instance is rendered in a string. We'll have this return the value of the instance's `name` field. (Again, be sure to create this *inside*`AccessList`.)
53
+
Finally, we'll add a `__str__()` method to control how an instance is coerced to a string. We'll have this return the value of the instance's `name` field. (Again, be sure to create this method *inside*the `AccessList` class.)
54
54
55
55
```python
56
56
def__str__(self):
@@ -59,9 +59,9 @@ Finally, we'll add a `__str__()` method to control how an instance is rendered i
59
59
60
60
### AccessListRule
61
61
62
-
Our second model will hold the individual rules assigned to each access list. This model will be a bit more complex. We'll need to define fields for:
62
+
Our second model will hold the individual rules assigned to each access list. This model will be a bit more complex. We'll need to define fields for all of the following:
63
63
64
-
* Parent access list (pointing to and`AccessList` instance)
64
+
* Parent access list (a foreign key to an`AccessList` instance)
65
65
* Index (the rule's order in the list)
66
66
* Protocol
67
67
* Source prefix
@@ -84,17 +84,17 @@ class AccessListRule(NetBoxModel):
84
84
85
85
We're passing three keyword arguments to the field:
86
86
87
-
*`to` references the related model class (this can alternatively be the _name_ of the class)
88
-
*`on_delete` tells Django what action to take if the related object is deleted. `CASCADE` will automatically delete any rules assigned to it as well.
89
-
*`related_name` defines the attribute of the reverse relationship being added to the related class. The rule assigned to an `AccessList` instance can be referenced as `accesslist.rules.all()`.
87
+
*`to` references the related model class
88
+
*`on_delete` tells Django what action to take if the related object is deleted. `CASCADE` will automatically delete any rules assigned to a deleted access list.
89
+
*`related_name` defines the attribute of the reverse relationship being added to the related class. The rules assigned to an `AccessList` instance can be referenced as `accesslist.rules.all()`.
90
90
91
91
Next we'll add an `index` field to store the rule's number (position) within the access list. We'll use `PositiveIntegerField` because only positive numbers are supported.
92
92
93
93
```python
94
94
index = models.PositiveIntegerField()
95
95
```
96
96
97
-
The protocol field is next. This will store the name of a protocol such as `'tcp'` or `'udp'`. Notice that we're setting `blank=True` because it should not be required to specify a particular protocol when creating a rule.
97
+
The protocol field is next. This will store the name of a protocol such as TCP or UDP. Notice that we're setting `blank=True` because it should not be required to specify a particular protocol when creating a rule.
98
98
99
99
```python
100
100
protocol = models.CharField(
@@ -103,9 +103,7 @@ The protocol field is next. This will store the name of a protocol such as `'tcp
103
103
)
104
104
```
105
105
106
-
:green_circle:**Tip:** Why didn't we set `null=True` like we did for the previous optional fields? Because this is a `CharField`, it's recommended to store empty values as empty strings rather than `null`. For other data types, like integers or booleans, `null` must be explicitly allowed at the database level for optional fields.
107
-
108
-
Next we need to define a source prefix. We're going to use a foreign key field to reference an instance of NetBox's `Prefix` model within its `ipam` app. Instead of importing the model class, we can just reference it by its name. And because we want this to be an _optional_ field, we'll also set `blank=True` and `null=True`.
106
+
Next we need to define a source prefix. We're going to use a foreign key field to reference an instance of NetBox's `Prefix` model within its `ipam` app. Instead of importing the model class, we can instead reference it by name. And because we want this to be an _optional_ field, we'll also set `blank=True` and `null=True`.
109
107
110
108
```python
111
109
source_prefix = models.ForeignKey(
@@ -117,6 +115,8 @@ Next we need to define a source prefix. We're going to use a foreign key field t
117
115
)
118
116
```
119
117
118
+
:green_circle:**Tip:** Whereas `CASCADE` automatically deletes child objects, `PROTECT` prevents the deletion of the parent option if any child objects exist.
119
+
120
120
Notice above that we've defined `related_name='+'`. This tells Django not to create a reverse relationship from the `Prefix` model to the `AccessListRule` model, because it wouldn't be very useful.
121
121
122
122
We also need to add a field for the source port number(s). We could use an integer field for this, however that would limit us to defining a single source port per rule. Instead, we can add an `ArrayField` to store a list of `PositiveIntegerField` values. Like `source_prefix`, this will also be an optional field, so we add `blank=True` and `null=True` as well.
@@ -166,7 +166,7 @@ With our fields out of the way, this model will also need a `Meta` class to defi
166
166
unique_together = ('access_list', 'index')
167
167
```
168
168
169
-
Finally, we'll add a `__str__()` method to display the parent access list and index number when rendering an instance as a string:
169
+
Finally, we'll add a `__str__()` method to display the parent access list and index number when rendering an `AccessListRule`instance as a string:
170
170
171
171
```python
172
172
def__str__(self):
@@ -181,7 +181,7 @@ Looking back at our models, we see a few fields that would benefit from having p
181
181
* Deny
182
182
* Reject
183
183
184
-
We can define a `ChoiceSet` to store these pre-defined values for the user, to avoid the hassle of manually typing the name of the desired value each time. Back at the top of `models.py`, import NetBox's `ChoiceSet` class:
184
+
We can define a `ChoiceSet` to store these pre-defined values for the user, to avoid the hassle of manually typing the name of the desired action each time. Back at the top of `models.py`, import NetBox's `ChoiceSet` class:
185
185
186
186
```python
187
187
from utilities.choices import ChoiceSet
@@ -200,15 +200,15 @@ class ActionChoices(ChoiceSet):
200
200
)
201
201
```
202
202
203
-
The `CHOICES` attribute is a tuple of tuples, each of which holds three values:
203
+
The `CHOICES` attribute must be an iterable of two- or three-value tuples, each of which defines the following:
204
204
205
205
* The raw value to be stored in the database
206
206
* A human-friendly string for display
207
207
* A color for display in the UI (optional)
208
208
209
-
Additionally, we've added a `key` attribute: This will allow the NetBox administrator to replace or extend the plugin's default choices here with his or her own values.
209
+
Additionally, we've added a `key` attribute: This will allow the NetBox administrator to replace or extend the plugin's default choices via NetBox's [`FIELD_CHOICES`](https://netbox.readthedocs.io/en/feature/configuration/optional-settings/#field_choices) configuration parameter.
210
210
211
-
Now, we can reference this as the set of valid choices on the `default_action` and `action` model fields by referencing it with the `choices` keyword argument.
211
+
Now, we can reference this as the set of valid choices on the `default_action` and `action` model fields by passing it as the `choices` keyword argument.
212
212
213
213
```python
214
214
# AccessList
@@ -224,7 +224,7 @@ Now, we can reference this as the set of valid choices on the `default_action` a
224
224
)
225
225
```
226
226
227
-
Let's create a set of choices for the rule model's `protocol` field as well. Add this below the `ActionChoices` class:
227
+
Let's create a set of choices for a rule's `protocol` field as well. Add this below the `ActionChoices` class:
228
228
229
229
```python
230
230
classProtocolChoices(ChoiceSet):
@@ -249,13 +249,13 @@ Then, add the `choices` keyword argument to the `protocol` field:
249
249
250
250
## Create Schema Migrations
251
251
252
-
Now that we have our models defined, we need to generate a schema for the PostgreSQL database. While it's possible to create the tables and constraints by hand, it's _much_ easier to employ Django's [migrations feature](https://docs.djangoproject.com/en/4.0/topics/migrations/), which will introspect our model classes and generate the necessary migration files automatically. This is a two-step process: First we generate the migration file with the `makemigrations` management command, then we run `migrate` to apply it to the live database.
252
+
Now that we have our models defined, we need to generate a schema for the PostgreSQL database. While it's possible to create the tables and constraints by hand, it's _much_ easier to employ Django's [migrations feature](https://docs.djangoproject.com/en/4.0/topics/migrations/). This will inspect our model classes and generate the necessary migration files automatically. This is a two-step process: First we generate the migration file with the `makemigrations` management command, then we run `migrate` to apply it to the live database.
253
253
254
254
:warning:**Warning:** Before continuing, check that you've set `DEVELOPER=True` in NetBox's `configuration.py` file. This is necessary to disable a safeguard intended to prevent people from creating new migrations mistakenly.
255
255
256
256
### Generate Migration Files
257
257
258
-
Change into the NetBox installation root to run `manage.py`. First, we'll run `makemigrations` with the `--dry-run` argument as a sanity-check: This will report what changes have been detected, but won't actually generate any migration files.
258
+
Change into the NetBox installation root to run `manage.py`. First, we'll run `makemigrations` with the `--dry-run` argument as a sanity-check. This will report what changes have been detected, but won't actually generate any migration files.
Now that we have our models installed, let's try creating some objects. First, enter the NetBox shell. This is an interactive Python command line which allows us to interact directly with NetBox objects and other resources.
331
+
Now that we have our models installed, let's try creating some objects. First, enter the NetBox shell. This is an interactive Python command line interface which allows us to interact directly with NetBox objects and other resources.
330
332
331
333
```bash
332
-
$ ./manage.py nbshell
334
+
$ python netbox/manage.py nbshell
333
335
from netbox### NetBox interactive shell
334
336
### Python 3.8.12 | Django 4.0.3 | NetBox 3.2.0
335
337
### lsmodels() will show available models. Use help(<model>) for more info.
@@ -346,7 +348,16 @@ Let's create and save an access list:
346
348
>>> acl.save()
347
349
```
348
350
349
-
And a few rules to go with it:
351
+
Next we'll create some prefixes to reference in rules:
352
+
353
+
```python
354
+
>>> prefix1 = Prefix(prefix='192.168.1.0/24')
355
+
>>> prefix1.save()
356
+
>>> prefix2 = Prefix(prefix='192.168.2.0/24')
357
+
>>> prefix2.save()
358
+
```
359
+
360
+
And finally we'll create a couple rules for our access list:
350
361
351
362
```python
352
363
>>> AccessListRule(
@@ -361,7 +372,7 @@ And a few rules to go with it:
361
372
>>> AccessListRule(
362
373
...access_list=acl,
363
374
...index=20,
364
-
...protocol='dns',
375
+
...protocol='udp',
365
376
...destination_prefix=prefix2,
366
377
...destination_ports=[53],
367
378
...action='permit',
@@ -371,7 +382,7 @@ And a few rules to go with it:
Excellent! We can now create access lists and rules in the database. The next few steps will work on expsoing this functionality in the NetBox user interface.
385
+
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.
0 commit comments