Skip to content

Commit 987e210

Browse files
authored
Closes #93: Add support for NetBox 4.2 (#94)
1 parent edecabc commit 987e210

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+328
-507
lines changed

.github/workflows/commit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
strategy:
3232
max-parallel: 10
3333
matrix:
34-
netbox_version: ["v3.7.8", "v4.0.7", "v4.1.1"]
34+
netbox_version: ["v4.0.7", "v4.1.11", "v4.2.2"]
3535
steps:
3636
- name: Checkout
3737
uses: actions/checkout@v3

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![NetBox version](https://img.shields.io/badge/NetBox-3.7|4.0|4.1-blue.svg)](https://github.com/netbox-community/netbox)
1+
[![NetBox version](https://img.shields.io/badge/NetBox-4.0|4.1|4.2-blue.svg)](https://github.com/netbox-community/netbox)
22
[![Supported Versions](https://img.shields.io/pypi/pyversions/netbox-config-diff.svg)](https://pypi.org/project/netbox-config-diff/)
33
[![PyPI version](https://badge.fury.io/py/netbox-config-diff.svg)](https://badge.fury.io/py/netbox-config-diff)
44
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
@@ -48,9 +48,10 @@ This is possible thanks to the scrapli_cfg. Read [Scrapli](https://github.com/sc
4848
|----------------|------------------|
4949
| 3.5 | =>0.1.0, <=2.5.0 |
5050
| 3.6 | =>0.1.0, <=2.6.0 |
51-
| 3.7 | =>0.1.0 |
51+
| 3.7 | =>0.1.0, <=2.8.0 |
5252
| 4.0 | =>2.6.0 |
5353
| 4.1 | =>2.7.0 |
54+
| 4.2 | =>2.9.0 |
5455

5556
<!--install-start-->
5657
## Installing
89.9 KB
Loading
54.5 KB
Loading
27.6 KB
Loading
Loading
26.1 KB
Loading
18.1 KB
Loading
1.42 KB
Loading
18.8 KB
Loading
Loading

docs/media/screenshots/cr-created.png

51.1 KB
Loading
66.6 KB
Loading

docs/media/screenshots/cr-job-log.png

69.8 KB
Loading
1.42 KB
Loading
27 KB
Loading
1.5 KB
Loading
1.58 KB
Loading

docs/media/screenshots/navbar.png

25.5 KB
Loading
35.6 KB
Loading
19.6 KB
Loading

docs/media/screenshots/script.png

79.6 KB
Loading

docs/media/screenshots/substitute.png

20.4 KB
Loading

netbox_config_diff/__init__.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
from netbox.settings import VERSION
2-
3-
if VERSION.startswith("3."):
4-
from extras.plugins import PluginConfig
5-
else:
6-
from netbox.plugins import PluginConfig
7-
1+
from netbox.plugins import PluginConfig
82

93
__author__ = "Artem Kotik"
104
__email__ = "[email protected]"
11-
__version__ = "2.8.0"
5+
__version__ = "2.9.0"
126

137

148
class ConfigDiffConfig(PluginConfig):
@@ -20,7 +14,7 @@ class ConfigDiffConfig(PluginConfig):
2014
version = __version__
2115
base_url = "config-diff"
2216
required_settings = ["USERNAME", "PASSWORD"]
23-
min_version = "3.7.0"
17+
min_version = "4.0.0"
2418
default_settings = {
2519
"USER_SECRET_ROLE": "Username",
2620
"PASSWORD_SECRET_ROLE": "Password",

netbox_config_diff/api/serializers.py

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,20 @@
1-
from dcim.api.nested_serializers import NestedDeviceSerializer, NestedPlatformSerializer
1+
from dcim.api.serializers import DeviceSerializer, PlatformSerializer
22
from dcim.models import Device
33
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
44
from netbox.api.serializers import NetBoxModelSerializer
5-
from netbox.settings import VERSION
65
from rest_framework import serializers
76
from rest_framework.serializers import ValidationError
7+
from users.api.serializers import UserSerializer
8+
from utilities.datetime import local_now
89

910
from netbox_config_diff.choices import ConfigComplianceStatusChoices, ConfigurationRequestStatusChoices
1011
from netbox_config_diff.constants import ACCEPTABLE_DRIVERS
1112
from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute
1213

13-
if VERSION.startswith("3."):
14-
from users.api.nested_serializers import NestedUserSerializer
15-
from utilities.utils import local_now
16-
elif VERSION.startswith("4.0"):
17-
from users.api.nested_serializers import NestedUserSerializer
18-
from utilities.datetime import local_now
19-
else:
20-
from users.api.serializers_.nested import NestedUserSerializer
21-
from utilities.datetime import local_now
2214

23-
24-
# TODO: after droping support for NetBox 3.x, delete nested serializers and add brief_fields
2515
class ConfigComplianceSerializer(NetBoxModelSerializer):
2616
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_config_diff-api:configcompliance-detail")
27-
device = NestedDeviceSerializer()
17+
device = DeviceSerializer(nested=True)
2818
status = ChoiceField(choices=ConfigComplianceStatusChoices)
2919

3020
class Meta:
@@ -49,7 +39,7 @@ class Meta:
4939

5040
class PlatformSettingSerializer(NetBoxModelSerializer):
5141
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_config_diff-api:platformsetting-detail")
52-
platform = NestedPlatformSerializer()
42+
platform = PlatformSerializer(nested=True)
5343

5444
class Meta:
5545
model = PlatformSetting
@@ -67,14 +57,7 @@ class Meta:
6757
"created",
6858
"last_updated",
6959
)
70-
71-
72-
class NestedPlatformSettingSerializer(NetBoxModelSerializer):
73-
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_config_diff-api:platformsetting-detail")
74-
75-
class Meta:
76-
model = PlatformSetting
77-
fields = ("id", "url", "display", "driver")
60+
brief_fields = ("id", "url", "display", "driver")
7861

7962

8063
class ConfigurationRequestSerializer(NetBoxModelSerializer):
@@ -83,13 +66,14 @@ class ConfigurationRequestSerializer(NetBoxModelSerializer):
8366
)
8467
devices = SerializedPKRelatedField(
8568
queryset=Device.objects.all(),
86-
serializer=NestedDeviceSerializer,
69+
serializer=DeviceSerializer,
70+
nested=True,
8771
many=True,
8872
)
8973
status = ChoiceField(choices=ConfigurationRequestStatusChoices, read_only=True)
90-
created_by = NestedUserSerializer(read_only=True)
91-
approved_by = NestedUserSerializer(read_only=True)
92-
scheduled_by = NestedUserSerializer(read_only=True)
74+
created_by = UserSerializer(read_only=True, nested=True)
75+
approved_by = UserSerializer(read_only=True, nested=True)
76+
scheduled_by = UserSerializer(read_only=True, nested=True)
9377

9478
class Meta:
9579
model = ConfigurationRequest
@@ -113,6 +97,7 @@ class Meta:
11397
"last_updated",
11498
)
11599
read_only_fields = ["started", "scheduled", "completed"]
100+
brief_fields = ("id", "url", "display", "status")
116101

117102
def validate(self, data):
118103
if data.get("devices"):
@@ -136,18 +121,7 @@ def validate(self, data):
136121

137122

138123
class ConfigurationRequestRWSerializer(ConfigurationRequestSerializer):
139-
created_by = NestedUserSerializer()
140-
141-
142-
class NestedConfigurationRequestSerializer(NetBoxModelSerializer):
143-
url = serializers.HyperlinkedIdentityField(
144-
view_name="plugins-api:netbox_config_diff-api:configurationrequest-detail"
145-
)
146-
status = ChoiceField(choices=ConfigurationRequestStatusChoices)
147-
148-
class Meta:
149-
model = ConfigurationRequest
150-
fields = ("id", "url", "display", "status")
124+
created_by = UserSerializer(nested=True)
151125

152126

153127
class ConfigurationRequestScheduleSerializer(serializers.Serializer):
@@ -161,7 +135,7 @@ def validate_schedule_at(self, value):
161135

162136
class SubstituteSerializer(NetBoxModelSerializer):
163137
url = serializers.HyperlinkedIdentityField(view_name="plugins-api:netbox_config_diff-api:substitute-detail")
164-
platform_setting = NestedPlatformSettingSerializer()
138+
platform_setting = PlatformSettingSerializer(nested=True)
165139

166140
class Meta:
167141
model = Substitute

netbox_config_diff/compliance/base.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,18 @@
77
from core.models import DataFile, DataSource
88
from dcim.choices import DeviceStatusChoices
99
from dcim.models import Device, DeviceRole, Site
10-
from django.conf import settings
1110
from django.db.models import Q
1211
from extras.scripts import MultiObjectVar, ObjectVar, TextVar
1312
from jinja2.exceptions import TemplateError
14-
from netbox.settings import VERSION
1513
from netutils.config.compliance import diff_network_config
1614
from utilities.exceptions import AbortScript
15+
from utilities.jinja2 import render_jinja2
1716

1817
from netbox_config_diff.models import ConplianceDeviceDataClass
1918

2019
from .secrets import SecretsMixin
2120
from .utils import PLATFORM_MAPPING, CustomChoiceVar, exclude_lines, get_remediation_commands, get_unified_diff
2221

23-
if VERSION.startswith("3."):
24-
from utilities.utils import render_jinja2
25-
else:
26-
from utilities.jinja2 import render_jinja2
27-
2822

2923
class ConfigDiffBase(SecretsMixin):
3024
site = ObjectVar(
@@ -97,10 +91,7 @@ def validate_data(self, data: dict) -> Iterable[Device]:
9791
if data["site"]:
9892
filters["site"] = data["site"]
9993
elif data["role"]:
100-
if settings.VERSION.split(".", 1)[1].startswith("5"):
101-
filters["device_role"] = data["role"]
102-
else:
103-
filters["role"] = data["role"]
94+
filters["role"] = data["role"]
10495
devices = Device.objects.filter(**filters).exclude(
10596
Q(primary_ip4__isnull=True) & Q(primary_ip6__isnull=True),
10697
)

netbox_config_diff/compliance/secrets.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@
22
from typing import TYPE_CHECKING
33

44
from dcim.models import Device
5-
from netbox.settings import VERSION
65

76
if TYPE_CHECKING:
87
from netbox_secrets.models import Secret
98

10-
if VERSION.startswith("3."):
11-
from extras.plugins import get_installed_plugins, get_plugin_config
12-
else:
13-
from netbox.plugins import get_installed_plugins, get_plugin_config
9+
from netbox.plugins import get_installed_plugins, get_plugin_config
1410

1511

1612
class SecretsMixin:

netbox_config_diff/configurator/base.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
from asgiref.sync import sync_to_async
88
from dcim.models import Device
99
from jinja2.exceptions import TemplateError
10-
from netbox.settings import VERSION
1110
from netutils.config.compliance import diff_network_config
1211
from scrapli import AsyncScrapli
1312
from scrapli_cfg.platform.base.async_platform import AsyncScrapliCfgPlatform
1413
from scrapli_cfg.response import ScrapliCfgResponse
14+
from utilities.request import NetBoxFakeRequest
1515

1616
from netbox_config_diff.compliance.secrets import SecretsMixin
1717
from netbox_config_diff.compliance.utils import PLATFORM_MAPPING, get_remediation_commands, get_unified_diff
@@ -22,11 +22,6 @@
2222

2323
from .factory import AsyncScrapliCfg
2424

25-
if VERSION.startswith("3."):
26-
from utilities.utils import NetBoxFakeRequest
27-
else:
28-
from utilities.request import NetBoxFakeRequest
29-
3025

3126
class Configurator(SecretsMixin):
3227
def __init__(self, devices: Iterable[Device], request: NetBoxFakeRequest) -> None:

netbox_config_diff/forms/base.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

netbox_config_diff/forms/general.py

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,23 @@
33
from django import forms
44
from django.contrib.auth import get_user_model
55
from netbox.forms import NetBoxModelBulkEditForm, NetBoxModelFilterSetForm, NetBoxModelForm
6-
from netbox.settings import VERSION
6+
from utilities.datetime import local_now
77
from utilities.forms.fields import (
88
DynamicModelChoiceField,
99
DynamicModelMultipleChoiceField,
1010
TagFilterField,
1111
)
12+
from utilities.forms.rendering import FieldSet
1213
from utilities.forms.widgets import DateTimePicker
1314

1415
from netbox_config_diff.choices import ConfigComplianceStatusChoices, ConfigurationRequestStatusChoices
1516
from netbox_config_diff.constants import ACCEPTABLE_DRIVERS
1617
from netbox_config_diff.models import ConfigCompliance, ConfigurationRequest, PlatformSetting, Substitute
1718

18-
from .base import CustomForm
19-
20-
if VERSION.startswith("3."):
21-
from utilities.utils import local_now
22-
else:
23-
from utilities.datetime import local_now
24-
from utilities.forms.rendering import FieldSet
25-
2619

2720
class ConfigComplianceFilterForm(NetBoxModelFilterSetForm):
2821
model = ConfigCompliance
29-
if VERSION.startswith("3."):
30-
fieldsets = ((None, ("q", "device_id", "status")),)
31-
else:
32-
fieldsets = (FieldSet("q", "device_id", "status"),)
22+
fieldsets = (FieldSet("q", "device_id", "status"),)
3323
device_id = DynamicModelMultipleChoiceField(
3424
queryset=Device.objects.all(),
3525
required=False,
@@ -60,10 +50,7 @@ class Meta:
6050

6151
class PlatformSettingFilterForm(NetBoxModelFilterSetForm):
6252
model = PlatformSetting
63-
if VERSION.startswith("3."):
64-
fieldsets = ((None, ("q", "platform_id", "tag")),)
65-
else:
66-
fieldsets = (FieldSet("q", "filter_id", "tag"),)
53+
fieldsets = (FieldSet("q", "filter_id", "tag"),)
6754
platform_id = DynamicModelMultipleChoiceField(
6855
queryset=Platform.objects.all(),
6956
required=False,
@@ -91,10 +78,7 @@ class PlatformSettingBulkEditForm(NetBoxModelBulkEditForm):
9178
)
9279

9380
model = PlatformSetting
94-
if VERSION.startswith("3."):
95-
fieldsets = ((None, ("driver", "command", "description", "exclude_regex")),)
96-
else:
97-
fieldsets = (FieldSet("driver", "command", "description", "exclude_regex"),)
81+
fieldsets = (FieldSet("driver", "command", "description", "exclude_regex"),)
9882
nullable_fields = ("description", "exclude_regex")
9983

10084

@@ -145,10 +129,7 @@ def clean(self):
145129

146130
class ConfigurationRequestFilterForm(NetBoxModelFilterSetForm):
147131
model = ConfigurationRequest
148-
if VERSION.startswith("3."):
149-
fieldsets = ((None, ("q", "created_by_id", "approved_by_id", "scheduled_by_id", "device_id", "status", "tag")),)
150-
else:
151-
fieldsets = (FieldSet("q", "created_by_id", "approved_by_id", "scheduled_by_id", "device_id", "status", "tag"),)
132+
fieldsets = (FieldSet("q", "created_by_id", "approved_by_id", "scheduled_by_id", "device_id", "status", "tag"),)
152133
created_by_id = DynamicModelMultipleChoiceField(
153134
queryset=get_user_model().objects.all(),
154135
required=False,
@@ -176,7 +157,7 @@ class ConfigurationRequestFilterForm(NetBoxModelFilterSetForm):
176157
tag = TagFilterField(model)
177158

178159

179-
class ConfigurationRequestScheduleForm(CustomForm):
160+
class ConfigurationRequestScheduleForm(forms.ModelForm):
180161
scheduled = forms.DateTimeField(
181162
widget=DateTimePicker(),
182163
label="Schedule at",
@@ -214,10 +195,7 @@ class Meta:
214195
class SubstituteFilterForm(NetBoxModelFilterSetForm):
215196
model = Substitute
216197
fieldsets = ((None, ("q", "platform_setting_id", "tag")),)
217-
if VERSION.startswith("3."):
218-
fieldsets = ((None, ("q", "platform_setting_id", "tag")),)
219-
else:
220-
fieldsets = (FieldSet("q", "platform_setting_id", "tag"),)
198+
fieldsets = (FieldSet("q", "platform_setting_id", "tag"),)
221199
platform_setting_id = DynamicModelMultipleChoiceField(
222200
queryset=PlatformSetting.objects.all(),
223201
required=False,
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
1-
from netbox.settings import VERSION
2-
3-
if VERSION.startswith("3."):
4-
from .old.schema import schema # noqa
5-
else:
6-
from .new.schema import schema # noqa
1+
from .schema import schema # noqa

netbox_config_diff/graphql/new/__init__.py

Whitespace-only changes.

netbox_config_diff/graphql/old/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)