Răsfoiți Sursa

Merge pull request #15423 from netbox-community/develop

Release v3.7.4
Jeremy Stretch 1 an în urmă
părinte
comite
c2cabe0273

+ 3 - 2
.github/ISSUE_TEMPLATE/bug_report.yaml

@@ -17,15 +17,16 @@ body:
         How are you running NetBox? (For issues with the Docker image, please go to the
         [netbox-docker](https://github.com/netbox-community/netbox-docker) repo.)
       options:
-        - Self-hosted
         - NetBox Cloud
+        - NetBox Enterprise
+        - Self-hosted
     validations:
       required: true
   - type: input
     attributes:
       label: NetBox Version
       description: What version of NetBox are you currently running?
-      placeholder: v3.7.3
+      placeholder: v3.7.4
     validations:
       required: true
   - type: dropdown

+ 1 - 1
.github/ISSUE_TEMPLATE/feature_request.yaml

@@ -14,7 +14,7 @@ body:
     attributes:
       label: NetBox version
       description: What version of NetBox are you currently running?
-      placeholder: v3.7.3
+      placeholder: v3.7.4
     validations:
       required: true
   - type: dropdown

+ 1 - 1
.github/workflows/ci.yml

@@ -84,4 +84,4 @@ jobs:
       run: coverage run --source="netbox/" netbox/manage.py test netbox/ --parallel
 
     - name: Show coverage report
-      run: coverage report --skip-covered --omit *migrations*
+      run: coverage report --skip-covered --omit '*/migrations/*,*/tests/*'

+ 1 - 1
base_requirements.txt

@@ -101,7 +101,7 @@ markdown-include
 mkdocs-material
 
 # Introspection for embedded code
-# https://github.com/mkdocstrings/mkdocstrings/blob/master/CHANGELOG.md
+# https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md
 mkdocstrings[python-legacy]
 
 # Library for manipulating IP prefixes and addresses

+ 3 - 0
contrib/generated_schema.json

@@ -384,7 +384,10 @@
                         "8gfc-sfpp",
                         "16gfc-sfpp",
                         "32gfc-sfp28",
+                        "32gfc-sfpp",
                         "64gfc-qsfpp",
+                        "64gfc-sfpdd",
+                        "64gfc-sfpp",
                         "128gfc-qsfp28",
                         "infiniband-sdr",
                         "infiniband-ddr",

+ 1 - 2
docs/installation/1-postgresql.md

@@ -31,8 +31,7 @@ This section entails the installation and configuration of a local PostgreSQL da
     Once PostgreSQL has been installed, start the service and enable it to run at boot:
 
     ```no-highlight
-    sudo systemctl start postgresql
-    sudo systemctl enable postgresql
+    sudo systemctl enable --now postgresql
     ```
 
 Before continuing, verify that you have installed PostgreSQL 12 or later:

+ 1 - 2
docs/installation/2-redis.md

@@ -14,8 +14,7 @@
 
     ```no-highlight
     sudo yum install -y redis
-    sudo systemctl start redis
-    sudo systemctl enable redis
+    sudo systemctl enable --now redis
     ```
 
 Before continuing, verify that your installed version of Redis is at least v4.0:

+ 1 - 2
docs/installation/4-gunicorn.md

@@ -27,8 +27,7 @@ sudo systemctl daemon-reload
 Then, start the `netbox` and `netbox-rq` services and enable them to initiate at boot time:
 
 ```no-highlight
-sudo systemctl start netbox netbox-rq
-sudo systemctl enable netbox netbox-rq
+sudo systemctl enable --now netbox netbox-rq
 ```
 
 You can use the command `systemctl status netbox` to verify that the WSGI service is running:

+ 25 - 0
docs/release-notes/version-3.7.md

@@ -1,5 +1,30 @@
 # NetBox v3.7
 
+## v3.7.4 (2024-03-13)
+
+### Enhancements
+
+* [#14206](https://github.com/netbox-community/netbox/issues/14206) - Add additional FibreChannel SFP+ interface types
+* [#14366](https://github.com/netbox-community/netbox/issues/14366) - Enable custom links for config contexts & templates
+* [#15291](https://github.com/netbox-community/netbox/issues/15291) - Add tunnel termination buttons to VM interfaces table
+* [#15297](https://github.com/netbox-community/netbox/issues/15297) - Linkify platform column in device & virtual machine tables
+
+### Bug Fixes
+
+* [#13722](https://github.com/netbox-community/netbox/issues/13722) - Fix range expansion for comma-separated numerical values
+* [#14832](https://github.com/netbox-community/netbox/issues/14832) - Enable querying IP addresses for an FHRP group via GraphQL
+* [#15220](https://github.com/netbox-community/netbox/issues/15220) - Fix validation check when bulk editing the mask length of IP addresses
+* [#15232](https://github.com/netbox-community/netbox/issues/15232) - Permit user with sufficient permissions to assign an inventory item to a device type
+* [#15241](https://github.com/netbox-community/netbox/issues/15241) - Restore missing `display` field on VirtualDisk serialization in REST API
+* [#15243](https://github.com/netbox-community/netbox/issues/15243) - Correct representation of installed module when listing module bays using REST API brief mode
+* [#15316](https://github.com/netbox-community/netbox/issues/15316) - Fix selection of 3DES encryption for IKE & IPSec proposals
+* [#15322](https://github.com/netbox-community/netbox/issues/15322) - Add description field to YAML export for device & module types
+* [#15336](https://github.com/netbox-community/netbox/issues/15336) - Correct label for recurring scheduled jobs
+* [#15347](https://github.com/netbox-community/netbox/issues/15347) - Fix querying virtual machine contacts via GraphQL
+* [#15356](https://github.com/netbox-community/netbox/issues/15356) - Fix assignment of front & rear images to device types via REST API
+
+---
+
 ## v3.7.3 (2024-02-21)
 
 ### Enhancements

+ 2 - 2
netbox/dcim/api/nested_serializers.py

@@ -414,11 +414,11 @@ class NestedFrontPortSerializer(WritableNestedSerializer):
 
 class NestedModuleBaySerializer(WritableNestedSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
-    module = NestedModuleSerializer(required=False, read_only=True, allow_null=True)
+    installed_module = ModuleBayNestedModuleSerializer(required=False, allow_null=True)
 
     class Meta:
         model = models.ModuleBay
-        fields = ['id', 'url', 'display', 'module', 'name']
+        fields = ['id', 'url', 'display', 'installed_module', 'name']
 
 
 class NestedDeviceBaySerializer(WritableNestedSerializer):

+ 3 - 4
netbox/dcim/api/serializers.py

@@ -326,8 +326,8 @@ class DeviceTypeSerializer(NetBoxModelSerializer):
     airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False, allow_null=True)
     weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
     device_count = serializers.IntegerField(read_only=True)
-    front_image = serializers.URLField(allow_null=True, required=False)
-    rear_image = serializers.URLField(allow_null=True, required=False)
+    front_image = serializers.ImageField(required=False, allow_null=True)
+    rear_image = serializers.ImageField(required=False, allow_null=True)
 
     # Counter fields
     console_port_template_count = serializers.IntegerField(read_only=True)
@@ -1039,8 +1039,7 @@ class ModuleBaySerializer(NetBoxModelSerializer):
         model = ModuleBay
         fields = [
             'id', 'url', 'display', 'device', 'name', 'installed_module', 'label', 'position', 'description', 'tags',
-            'custom_fields',
-            'created', 'last_updated',
+            'custom_fields', 'created', 'last_updated',
         ]
 
 

+ 6 - 0
netbox/dcim/choices.py

@@ -889,7 +889,10 @@ class InterfaceTypeChoices(ChoiceSet):
     TYPE_8GFC_SFP_PLUS = '8gfc-sfpp'
     TYPE_16GFC_SFP_PLUS = '16gfc-sfpp'
     TYPE_32GFC_SFP28 = '32gfc-sfp28'
+    TYPE_32GFC_SFP_PLUS = '32gfc-sfpp'
     TYPE_64GFC_QSFP_PLUS = '64gfc-qsfpp'
+    TYPE_64GFC_SFP_DD = '64gfc-sfpdd'
+    TYPE_64GFC_SFP_PLUS = '64gfc-sfpp'
     TYPE_128GFC_QSFP28 = '128gfc-qsfp28'
 
     # InfiniBand
@@ -1058,7 +1061,10 @@ class InterfaceTypeChoices(ChoiceSet):
                 (TYPE_8GFC_SFP_PLUS, 'SFP+ (8GFC)'),
                 (TYPE_16GFC_SFP_PLUS, 'SFP+ (16GFC)'),
                 (TYPE_32GFC_SFP28, 'SFP28 (32GFC)'),
+                (TYPE_32GFC_SFP_PLUS, 'SFP+ (32GFC)'),
                 (TYPE_64GFC_QSFP_PLUS, 'QSFP+ (64GFC)'),
+                (TYPE_64GFC_SFP_DD, 'SFP-DD (64GFC)'),
+                (TYPE_64GFC_SFP_PLUS, 'SFP+ (64GFC)'),
                 (TYPE_128GFC_QSFP28, 'QSFP28 (128GFC)'),
             )
         ),

+ 4 - 2
netbox/dcim/models/devices.py

@@ -229,15 +229,16 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
             'manufacturer': self.manufacturer.name,
             'model': self.model,
             'slug': self.slug,
+            'description': self.description,
             'default_platform': self.default_platform.name if self.default_platform else None,
             'part_number': self.part_number,
             'u_height': float(self.u_height),
             'is_full_depth': self.is_full_depth,
             'subdevice_role': self.subdevice_role,
             'airflow': self.airflow,
-            'comments': self.comments,
             'weight': float(self.weight) if self.weight is not None else None,
             'weight_unit': self.weight_unit,
+            'comments': self.comments,
         }
 
         # Component templates
@@ -415,9 +416,10 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin):
             'manufacturer': self.manufacturer.name,
             'model': self.model,
             'part_number': self.part_number,
-            'comments': self.comments,
+            'description': self.description,
             'weight': float(self.weight) if self.weight is not None else None,
             'weight_unit': self.weight_unit,
+            'comments': self.comments,
         }
 
         # Component templates

+ 5 - 1
netbox/dcim/tables/devices.py

@@ -210,6 +210,10 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
         linkify=True,
         verbose_name=_('Type')
     )
+    platform = tables.Column(
+        linkify=True,
+        verbose_name=_('Platform')
+    )
     primary_ip = tables.Column(
         linkify=True,
         order_by=('primary_ip4', 'primary_ip6'),
@@ -294,7 +298,7 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
         model = models.Device
         fields = (
             'pk', 'id', 'name', 'status', 'tenant', 'tenant_group', 'role', 'manufacturer', 'device_type',
-            'platform', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'location', 'rack', 'parent_device',
+            'serial', 'asset_tag', 'region', 'site_group', 'site', 'location', 'rack', 'parent_device',
             'device_bay_position', 'position', 'face', 'latitude', 'longitude', 'airflow', 'primary_ip', 'primary_ip4',
             'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'description',
             'config_template', 'comments', 'contacts', 'tags', 'created', 'last_updated',

+ 1 - 1
netbox/dcim/tests/test_api.py

@@ -1755,7 +1755,7 @@ class RearPortTest(APIViewTestCases.APIViewTestCase):
 
 class ModuleBayTest(APIViewTestCases.APIViewTestCase):
     model = ModuleBay
-    brief_fields = ['display', 'id', 'module', 'name', 'url']
+    brief_fields = ['display', 'id', 'installed_module', 'name', 'url']
     bulk_update_data = {
         'description': 'New description',
     }

+ 1 - 1
netbox/dcim/views.py

@@ -1079,7 +1079,7 @@ class DeviceTypeInventoryItemsView(DeviceTypeComponentsView):
     tab = ViewTab(
         label=_('Inventory Items'),
         badge=lambda obj: obj.inventory_item_template_count,
-        permission='dcim.view_invenotryitemtemplate',
+        permission='dcim.view_inventoryitemtemplate',
         weight=590,
         hide_if_empty=True
     )

+ 1 - 0
netbox/extras/graphql/mixins.py

@@ -7,6 +7,7 @@ from extras.models import ObjectChange
 __all__ = (
     'ChangelogMixin',
     'ConfigContextMixin',
+    'ContactsMixin',
     'CustomFieldsMixin',
     'ImageAttachmentsMixin',
     'JournalEntriesMixin',

+ 3 - 3
netbox/extras/models/configs.py

@@ -11,7 +11,7 @@ from extras.querysets import ConfigContextQuerySet
 from netbox.config import get_config
 from netbox.registry import registry
 from netbox.models import ChangeLoggedModel
-from netbox.models.features import CloningMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin
+from netbox.models.features import CloningMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin
 from utilities.jinja2 import ConfigTemplateLoader
 from utilities.utils import deepmerge
 
@@ -26,7 +26,7 @@ __all__ = (
 # Config contexts
 #
 
-class ConfigContext(SyncedDataMixin, CloningMixin, ChangeLoggedModel):
+class ConfigContext(SyncedDataMixin, CloningMixin, CustomLinksMixin, ChangeLoggedModel):
     """
     A ConfigContext represents a set of arbitrary data available to any Device or VirtualMachine matching its assigned
     qualifiers (region, site, etc.). For example, the data stored in a ConfigContext assigned to site A and tenant B
@@ -210,7 +210,7 @@ class ConfigContextModel(models.Model):
 # Config templates
 #
 
-class ConfigTemplate(SyncedDataMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
+class ConfigTemplate(SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
     name = models.CharField(
         verbose_name=_('name'),
         max_length=100

+ 0 - 14
netbox/ipam/forms/model_forms.py

@@ -367,20 +367,6 @@ class IPAddressForm(TenancyForm, NetBoxModelForm):
                 'primary_for_parent', _("Only IP addresses assigned to an interface can be designated as primary IPs.")
             )
 
-        # Do not allow assigning a network ID or broadcast address to an interface.
-        if interface and (address := self.cleaned_data.get('address')):
-            if address.ip == address.network:
-                msg = _("{ip} is a network ID, which may not be assigned to an interface.").format(ip=address.ip)
-                if address.version == 4 and address.prefixlen not in (31, 32):
-                    raise ValidationError(msg)
-                if address.version == 6 and address.prefixlen not in (127, 128):
-                    raise ValidationError(msg)
-            if address.version == 4 and address.ip == address.broadcast and address.prefixlen not in (31, 32):
-                msg = _("{ip} is a broadcast address, which may not be assigned to an interface.").format(
-                    ip=address.ip
-                )
-                raise ValidationError(msg)
-
     def save(self, *args, **kwargs):
         ipaddress = super().save(*args, **kwargs)
 

+ 2 - 1
netbox/ipam/graphql/types.py

@@ -1,6 +1,7 @@
 import graphene
 
 from ipam import filtersets, models
+from .mixins import IPAddressesMixin
 from netbox.graphql.scalars import BigInt
 from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType
 
@@ -71,7 +72,7 @@ class AggregateType(NetBoxObjectType, BaseIPAddressFamilyType):
         filterset_class = filtersets.AggregateFilterSet
 
 
-class FHRPGroupType(NetBoxObjectType):
+class FHRPGroupType(NetBoxObjectType, IPAddressesMixin):
 
     class Meta:
         model = models.FHRPGroup

+ 19 - 0
netbox/ipam/models/ip.py

@@ -844,6 +844,25 @@ class IPAddress(PrimaryModel):
                     'address': _("Cannot create IP address with /0 mask.")
                 })
 
+            # Do not allow assigning a network ID or broadcast address to an interface.
+            if self.assigned_object:
+                if self.address.ip == self.address.network:
+                    msg = _("{ip} is a network ID, which may not be assigned to an interface.").format(
+                        ip=self.address.ip
+                    )
+                    if self.address.version == 4 and self.address.prefixlen not in (31, 32):
+                        raise ValidationError(msg)
+                    if self.address.version == 6 and self.address.prefixlen not in (127, 128):
+                        raise ValidationError(msg)
+                if (
+                        self.address.version == 4 and self.address.ip == self.address.broadcast and
+                        self.address.prefixlen not in (31, 32)
+                ):
+                    msg = _("{ip} is a broadcast address, which may not be assigned to an interface.").format(
+                        ip=self.address.ip
+                    )
+                    raise ValidationError(msg)
+
             # Enforce unique IP space (if applicable)
             if (self.vrf is None and get_config().ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique):
                 duplicate_ips = self.get_duplicates()

+ 1 - 1
netbox/netbox/settings.py

@@ -28,7 +28,7 @@ from netbox.plugins import PluginConfig
 # Environment setup
 #
 
-VERSION = '3.7.3'
+VERSION = '3.7.4'
 
 # Hostname
 HOSTNAME = platform.node()

+ 1 - 1
netbox/templates/core/job.html

@@ -63,7 +63,7 @@
               <td>
                 {{ object.scheduled|annotated_date|placeholder }}
                 {% if object.interval %}
-                  ({% blocktrans with interval=object.interval %}every {{ interval }} seconds{% endblocktrans %})
+                  ({% blocktrans with interval=object.interval %}every {{ interval }} minutes{% endblocktrans %})
                 {% endif %}
               </td>
             </tr>

+ 26 - 19
netbox/utilities/forms/utils.py

@@ -51,36 +51,43 @@ def parse_alphanumeric_range(string):
     '0-3,a-d' => [0, 1, 2, 3, a, b, c, d]
     """
     values = []
-    for dash_range in string.split(','):
+    for value in string.split(','):
+        if '-' not in value:
+            # Item is not a range
+            values.append(value)
+            continue
+
+        # Find the range's beginning & end values
         try:
-            begin, end = dash_range.split('-')
+            begin, end = value.split('-')
             vals = begin + end
             # Break out of loop if there's an invalid pattern to return an error
             if (not (vals.isdigit() or vals.isalpha())) or (vals.isalpha() and not (vals.isupper() or vals.islower())):
                 return []
         except ValueError:
-            begin, end = dash_range, dash_range
+            raise forms.ValidationError(_('Range "{value}" is invalid.').format(value=value))
+
+        # Numeric range
         if begin.isdigit() and end.isdigit():
             if int(begin) >= int(end):
-                raise forms.ValidationError(_('Range "{value}" is invalid.').format(value=dash_range))
-
+                raise forms.ValidationError(
+                    _('Invalid range: Ending value ({end}) must be greater than beginning value ({begin}).').format(
+                        begin=begin, end=end
+                    )
+                )
             for n in list(range(int(begin), int(end) + 1)):
                 values.append(n)
+
+        # Alphanumeric range
         else:
-            # Value-based
-            if begin == end:
-                values.append(begin)
-            # Range-based
-            else:
-                # Not a valid range (more than a single character)
-                if not len(begin) == len(end) == 1:
-                    raise forms.ValidationError(_('Range "{value}" is invalid.').format(value=dash_range))
-
-                if ord(begin) >= ord(end):
-                    raise forms.ValidationError(_('Range "{value}" is invalid.').format(value=dash_range))
-
-                for n in list(range(ord(begin), ord(end) + 1)):
-                    values.append(chr(n))
+            # Not a valid range (more than a single character)
+            if not len(begin) == len(end) == 1:
+                raise forms.ValidationError(_('Range "{value}" is invalid.').format(value=value))
+            if ord(begin) >= ord(end):
+                raise forms.ValidationError(_('Range "{value}" is invalid.').format(value=value))
+            for n in list(range(ord(begin), ord(end) + 1)):
+                values.append(chr(n))
+
     return values
 
 

+ 10 - 1
netbox/utilities/tests/test_forms.py

@@ -191,7 +191,16 @@ class ExpandAlphanumeric(TestCase):
 
         self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output)
 
-    def test_set(self):
+    def test_set_numeric(self):
+        input = 'r[1,2]a'
+        output = sorted([
+            'r1a',
+            'r2a',
+        ])
+
+        self.assertEqual(sorted(expand_alphanumeric_pattern(input)), output)
+
+    def test_set_alpha(self):
         input = '[r,t]1a'
         output = sorted([
             'r1a',

+ 2 - 2
netbox/virtualization/api/serializers.py

@@ -172,6 +172,6 @@ class VirtualDiskSerializer(NetBoxModelSerializer):
     class Meta:
         model = VirtualDisk
         fields = [
-            'id', 'url', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields', 'created',
-            'last_updated',
+            'id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields',
+            'created', 'last_updated',
         ]

+ 2 - 2
netbox/virtualization/graphql/types.py

@@ -1,5 +1,5 @@
 from dcim.graphql.types import ComponentObjectType
-from extras.graphql.mixins import ConfigContextMixin
+from extras.graphql.mixins import ConfigContextMixin, ContactsMixin
 from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
 from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType
 from virtualization import filtersets, models
@@ -38,7 +38,7 @@ class ClusterTypeType(OrganizationalObjectType):
         filterset_class = filtersets.ClusterTypeFilterSet
 
 
-class VirtualMachineType(ConfigContextMixin, NetBoxObjectType):
+class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType):
 
     class Meta:
         model = models.VirtualMachine

+ 16 - 3
netbox/virtualization/tables/virtualmachines.py

@@ -33,6 +33,15 @@ VMINTERFACE_BUTTONS = """
     </ul>
   </span>
 {% endif %}
+{% if perms.vpn.add_tunnel and not record.tunnel_termination %}
+  <a href="{% url 'vpn:tunnel_add' %}?termination1_type=virtualization.virtualmachine&termination1_parent={{ record.virtual_machine.pk }}&termination1_termination={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" title="Create a tunnel" class="btn btn-success btn-sm">
+    <i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
+  </a>
+{% elif perms.vpn.delete_tunneltermination and record.tunnel_termination %}
+  <a href="{% url 'vpn:tunneltermination_delete' pk=record.tunnel_termination.pk %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" title="Remove tunnel" class="btn btn-danger btn-sm">
+    <i class="mdi mdi-tunnel-outline" aria-hidden="true"></i>
+  </a>
+{% endif %}
 """
 
 
@@ -64,6 +73,10 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
     role = columns.ColoredLabelColumn(
         verbose_name=_('Role'),
     )
+    platform = tables.Column(
+        linkify=True,
+        verbose_name=_('Platform')
+    )
     comments = columns.MarkdownColumn(
         verbose_name=_('Comments'),
     )
@@ -97,9 +110,9 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
     class Meta(NetBoxTable.Meta):
         model = VirtualMachine
         fields = (
-            'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'platform',
-            'vcpus', 'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'description', 'comments',
-            'config_template', 'contacts', 'tags', 'created', 'last_updated',
+            'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'vcpus',
+            'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'description', 'comments', 'config_template',
+            'contacts', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',

+ 0 - 3
netbox/vpn/admin.py

@@ -1,3 +0,0 @@
-from django.contrib import admin
-
-# Register your models here.

+ 1 - 1
netbox/vpn/choices.py

@@ -124,7 +124,7 @@ class EncryptionAlgorithmChoices(ChoiceSet):
         (ENCRYPTION_AES256_CBC, '256-bit AES (CBC)'),
         (ENCRYPTION_AES256_GCM, '256-bit AES (GCM)'),
         (ENCRYPTION_3DES, '3DES'),
-        (ENCRYPTION_3DES, 'DES'),
+        (ENCRYPTION_DES, 'DES'),
     )
 
 

+ 5 - 5
requirements.txt

@@ -1,8 +1,8 @@
 bleach==6.1.0
-Django==4.2.10
+Django==4.2.11
 django-cors-headers==4.3.1
 django-debug-toolbar==4.3.0
-django-filter==23.5
+django-filter==24.1
 django-graphiql-debug-toolbar==0.2.0
 django-mptt==0.14.0
 django-pglocks==1.0.4
@@ -15,14 +15,14 @@ django-tables2==2.7.0
 django-timezone-field==6.1.0
 djangorestframework==3.14.0
 drf-spectacular==0.27.1
-drf-spectacular-sidecar==2024.2.1
+drf-spectacular-sidecar==2024.3.4
 feedparser==6.0.11
 graphene-django==3.0.0
 gunicorn==21.2.0
 Jinja2==3.1.3
 Markdown==3.5.2
-mkdocs-material==9.5.10
-mkdocstrings[python-legacy]==0.24.0
+mkdocs-material==9.5.13
+mkdocstrings[python-legacy]==0.24.1
 netaddr==1.2.1
 Pillow==10.2.0
 psycopg[binary,pool]==3.1.18