From 27d72746ca4aec377001cfc69fa416bf4bd652dc Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 8 Sep 2022 13:20:27 -0700 Subject: [PATCH 01/19] #10172 upgrade Django to 4.1.1 --- base_requirements.txt | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base_requirements.txt b/base_requirements.txt index 363f97b31..22106587d 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -4,7 +4,7 @@ bleach # The Python web framework on which NetBox is built # https://github.com/django/django -Django<4.1 +Django<4.2 # Django middleware which permits cross-domain API requests # https://github.com/OttoYiu/django-cors-headers diff --git a/requirements.txt b/requirements.txt index ddbf07b9b..37659c523 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ bleach==5.0.1 -Django==4.0.7 +Django==4.1.1 django-cors-headers==3.13.0 django-debug-toolbar==3.6.0 django-filter==22.1 From ce6bf9e5c1bc08edc80f6ea1e55cf1318ae6e14b Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 12 Sep 2022 09:59:37 -0700 Subject: [PATCH 02/19] #10172 fixes for Django 4.1 --- netbox/dcim/models/device_components.py | 13 +++++++------ netbox/dcim/models/devices.py | 2 +- netbox/virtualization/models.py | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 838336e21..e22913a8b 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -953,12 +953,13 @@ class RearPort(ModularComponentModel, CabledObjectModel): super().clean() # Check that positions count is greater than or equal to the number of associated FrontPorts - frontport_count = self.frontports.count() - if self.positions < frontport_count: - raise ValidationError({ - "positions": f"The number of positions cannot be less than the number of mapped front ports " - f"({frontport_count})" - }) + if self.pk: + frontport_count = self.frontports.count() + if self.positions < frontport_count: + raise ValidationError({ + "positions": f"The number of positions cannot be less than the number of mapped front ports " + f"({frontport_count})" + }) # diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index ccf4613bf..7858960a1 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -268,7 +268,7 @@ class DeviceType(NetBoxModel): if ( self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT - ) and self.devicebaytemplates.count(): + ) and self.pk and self.devicebaytemplates.count(): raise ValidationError({ 'subdevice_role': "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index abad57f88..4acbe6daf 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -367,7 +367,7 @@ class VirtualMachine(NetBoxModel, ConfigContextModel): }) # Validate primary IP addresses - interfaces = self.interfaces.all() + interfaces = self.interfaces.all() if self.pk else None for family in (4, 6): field = f'primary_ip{family}' ip = getattr(self, field) From 6a9274a95f87ad8c3c5dcdb1da47d6781d70a4da Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 13 Sep 2022 14:36:37 -0400 Subject: [PATCH 03/19] Closes #10314: Move clone() method from NetBoxModel to CloningMixin --- docs/plugins/development/models.md | 20 ++------------------ docs/release-notes/version-3.4.md | 5 +++++ mkdocs.yml | 1 + netbox/netbox/models/features.py | 13 +++++++++++-- 4 files changed, 19 insertions(+), 20 deletions(-) create mode 100644 docs/release-notes/version-3.4.md diff --git a/docs/plugins/development/models.md b/docs/plugins/development/models.md index c58621b81..16f5dd0df 100644 --- a/docs/plugins/development/models.md +++ b/docs/plugins/development/models.md @@ -49,24 +49,6 @@ class MyModel(NetBoxModel): ... ``` -### The `clone()` Method - -!!! info - This method was introduced in NetBox v3.3. - -The `NetBoxModel` class includes a `clone()` method to be used for gathering attributes which can be used to create a "cloned" instance. This is used primarily for form initialization, e.g. when using the "clone" button in the NetBox UI. By default, this method will replicate any fields listed in the model's `clone_fields` list, if defined. - -Plugin models can leverage this method by defining `clone_fields` as a list of field names to be replicated, or override this method to replace or extend its content: - -```python -class MyModel(NetBoxModel): - - def clone(self): - attrs = super().clone() - attrs['extra-value'] = 123 - return attrs -``` - ### Enabling Features Individually If you prefer instead to enable only a subset of these features for a plugin model, NetBox provides a discrete "mix-in" class for each feature. You can subclass each of these individually when defining your model. (Your model will also need to inherit from Django's built-in `Model` class.) @@ -116,6 +98,8 @@ For more information about database migrations, see the [Django documentation](h ::: netbox.models.features.ChangeLoggingMixin +::: netbox.models.features.CloningMixin + ::: netbox.models.features.CustomLinksMixin ::: netbox.models.features.CustomFieldsMixin diff --git a/docs/release-notes/version-3.4.md b/docs/release-notes/version-3.4.md new file mode 100644 index 000000000..88bc7810e --- /dev/null +++ b/docs/release-notes/version-3.4.md @@ -0,0 +1,5 @@ +# NetBox v3.4 + +### Plugins API + +* [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin diff --git a/mkdocs.yml b/mkdocs.yml index 530c6d52e..8f6e2930a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -252,6 +252,7 @@ nav: - git Cheat Sheet: 'development/git-cheat-sheet.md' - Release Notes: - Summary: 'release-notes/index.md' + - Version 3.4: 'release-notes/version-3.4.md' - Version 3.3: 'release-notes/version-3.3.md' - Version 3.2: 'release-notes/version-3.2.md' - Version 3.1: 'release-notes/version-3.1.md' diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index 7f30248b4..9fa1c5cef 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -92,8 +92,17 @@ class CloningMixin(models.Model): def clone(self): """ - Return a dictionary of attributes suitable for creating a copy of the current instance. This is used for pre- - populating an object creation form in the UI. + Returns a dictionary of attributes suitable for creating a copy of the current instance. This is used for pre- + populating an object creation form in the UI. By default, this method will replicate any fields listed in the + model's `clone_fields` list (if defined), but it can be overridden to apply custom logic. + + ```python + class MyModel(NetBoxModel): + def clone(self): + attrs = super().clone() + attrs['extra-value'] = 123 + return attrs + ``` """ attrs = {} From 4208dbd514feb2a68bf89b969246cb03d2fdf3b5 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 14 Sep 2022 17:10:14 -0400 Subject: [PATCH 04/19] Closes #10358: Raise minimum required PostgreSQL version from 10 to 11 --- docs/configuration/required-parameters.md | 2 +- docs/installation/1-postgresql.md | 6 +++--- docs/installation/index.md | 2 +- docs/installation/upgrading.md | 9 ++++----- docs/introduction.md | 2 +- docs/release-notes/version-3.4.md | 7 +++++++ 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/configuration/required-parameters.md b/docs/configuration/required-parameters.md index a62d14fef..15f743754 100644 --- a/docs/configuration/required-parameters.md +++ b/docs/configuration/required-parameters.md @@ -25,7 +25,7 @@ ALLOWED_HOSTS = ['*'] ## DATABASE -NetBox requires access to a PostgreSQL 10 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary: +NetBox requires access to a PostgreSQL 11 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary: * `NAME` - Database name * `USER` - PostgreSQL username diff --git a/docs/installation/1-postgresql.md b/docs/installation/1-postgresql.md index a6aa27b1b..583a4f3e9 100644 --- a/docs/installation/1-postgresql.md +++ b/docs/installation/1-postgresql.md @@ -2,8 +2,8 @@ This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md). -!!! warning "PostgreSQL 10 or later required" - NetBox requires PostgreSQL 10 or later. Please note that MySQL and other relational databases are **not** supported. +!!! warning "PostgreSQL 11 or later required" + NetBox requires PostgreSQL 11 or later. Please note that MySQL and other relational databases are **not** supported. ## Installation @@ -35,7 +35,7 @@ sudo systemctl start postgresql sudo systemctl enable postgresql ``` -Before continuing, verify that you have installed PostgreSQL 10 or later: +Before continuing, verify that you have installed PostgreSQL 11 or later: ```no-highlight psql -V diff --git a/docs/installation/index.md b/docs/installation/index.md index 8b588fccd..49163550d 100644 --- a/docs/installation/index.md +++ b/docs/installation/index.md @@ -18,7 +18,7 @@ The following sections detail how to set up a new instance of NetBox: | Dependency | Minimum Version | |------------|-----------------| | Python | 3.8 | -| PostgreSQL | 10 | +| PostgreSQL | 11 | | Redis | 4.0 | Below is a simplified overview of the NetBox application stack for reference: diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 802c13e49..cc49cd30e 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -20,7 +20,7 @@ NetBox v3.0 and later require the following: | Dependency | Minimum Version | |------------|-----------------| | Python | 3.8 | -| PostgreSQL | 10 | +| PostgreSQL | 11 | | Redis | 4.0 | ## 3. Install the Latest Release @@ -28,16 +28,15 @@ NetBox v3.0 and later require the following: As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by cloning the `master` branch of the git repository. !!! warning - Use the same method as you used to install Netbox originally + Use the same method as you used to install NetBox originally -If you are not sure how Netbox was installed originally, check with this -command: +If you are not sure how NetBox was installed originally, check with this command: ``` ls -ld /opt/netbox /opt/netbox/.git ``` -If Netbox was installed from a release package, then `/opt/netbox` will be a +If NetBox was installed from a release package, then `/opt/netbox` will be a symlink pointing to the current version, and `/opt/netbox/.git` will not exist. If it was installed from git, then `/opt/netbox` and `/opt/netbox/.git` will both exist as normal directories. diff --git a/docs/introduction.md b/docs/introduction.md index cffcb37dd..fe82e68aa 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -74,6 +74,6 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and | HTTP service | nginx or Apache | | WSGI service | gunicorn or uWSGI | | Application | Django/Python | -| Database | PostgreSQL 10+ | +| Database | PostgreSQL 11+ | | Task queuing | Redis/django-rq | | Live device access | NAPALM (optional) | diff --git a/docs/release-notes/version-3.4.md b/docs/release-notes/version-3.4.md index 88bc7810e..39c44f38e 100644 --- a/docs/release-notes/version-3.4.md +++ b/docs/release-notes/version-3.4.md @@ -1,5 +1,12 @@ # NetBox v3.4 +!!! warning "PostgreSQL 11 Required" + NetBox v3.4 requires PostgreSQL 11 or later. + ### Plugins API * [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin + +### Other Changes + +* [#10358](https://github.com/netbox-community/netbox/issues/10358) - Raise minimum required PostgreSQL version from 10 to 11 From 695ad47fe9e4f5fa3193fe4f08afd985d6eac4d6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 19 Sep 2022 10:46:16 -0700 Subject: [PATCH 05/19] 10408 add error message if already exists --- netbox/netbox/views/generic/object_views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index a56a832b6..85604cd8f 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -3,7 +3,7 @@ from copy import deepcopy from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist -from django.db import transaction +from django.db import transaction, IntegrityError from django.db.models import ProtectedError from django.forms.widgets import HiddenInput from django.shortcuts import redirect, render @@ -421,7 +421,11 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): return redirect(return_url) - except (AbortRequest, PermissionsViolation) as e: + except IntegrityError: + form.add_error(None, f"{obj} already exists") + clear_webhooks.send(sender=self) + + except (IntegrityError, AbortRequest, PermissionsViolation) as e: logger.debug(e.message) form.add_error(None, e.message) clear_webhooks.send(sender=self) From 5d07f2c837a140b316598206fa76ead1792a3fde Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 20 Sep 2022 16:03:55 -0700 Subject: [PATCH 06/19] 9892 add FHRP group name --- netbox/ipam/migrations/0061_fhrpgroup_name.py | 18 ++++++++++++++++++ netbox/ipam/models/fhrp.py | 4 ++++ 2 files changed, 22 insertions(+) create mode 100644 netbox/ipam/migrations/0061_fhrpgroup_name.py diff --git a/netbox/ipam/migrations/0061_fhrpgroup_name.py b/netbox/ipam/migrations/0061_fhrpgroup_name.py new file mode 100644 index 000000000..7e232c18f --- /dev/null +++ b/netbox/ipam/migrations/0061_fhrpgroup_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.7 on 2022-09-20 23:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0060_alter_l2vpn_slug'), + ] + + operations = [ + migrations.AddField( + model_name='fhrpgroup', + name='name', + field=models.CharField(blank=True, max_length=100), + ), + ] diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index 286251444..b022386c8 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -22,6 +22,10 @@ class FHRPGroup(NetBoxModel): group_id = models.PositiveSmallIntegerField( verbose_name='Group ID' ) + name = models.CharField( + max_length=100, + blank=True + ) protocol = models.CharField( max_length=50, choices=FHRPGroupProtocolChoices From a5421ae1707142c0ceb74399757df765a2a9f8d4 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 20 Sep 2022 16:13:01 -0700 Subject: [PATCH 07/19] 9892 add FHRP group name --- netbox/ipam/filtersets.py | 2 +- netbox/ipam/tables/fhrp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 3c0ab1ac8..1eb1ee863 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -653,7 +653,7 @@ class FHRPGroupFilterSet(NetBoxModelFilterSet): class Meta: model = FHRPGroup - fields = ['id', 'group_id', 'auth_key'] + fields = ['id', 'group_id', 'name', 'auth_key'] def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/ipam/tables/fhrp.py b/netbox/ipam/tables/fhrp.py index f709bfeb2..dc4982e26 100644 --- a/netbox/ipam/tables/fhrp.py +++ b/netbox/ipam/tables/fhrp.py @@ -36,7 +36,7 @@ class FHRPGroupTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = FHRPGroup fields = ( - 'pk', 'group_id', 'protocol', 'auth_type', 'auth_key', 'description', 'ip_addresses', 'member_count', + 'pk', 'name', 'group_id', 'protocol', 'auth_type', 'auth_key', 'description', 'ip_addresses', 'member_count', 'tags', 'created', 'last_updated', ) default_columns = ('pk', 'group_id', 'protocol', 'auth_type', 'description', 'ip_addresses', 'member_count') From 7735634649933e1c64d410a4fbc69f6bc8c975e6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 22 Sep 2022 10:34:37 -0700 Subject: [PATCH 08/19] 10435 check if vm.cluster in qs --- netbox/ipam/querysets.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index 7edac2eff..b64ae04b8 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -81,30 +81,31 @@ class VLANQuerySet(RestrictedQuerySet): # Find all relevant VLANGroups q = Q() - if vm.cluster.site: - if vm.cluster.site.region: + if vm.cluster: + if vm.cluster.site: + if vm.cluster.site.region: + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), + scope_id__in=vm.cluster.site.region.get_ancestors(include_self=True) + ) + if vm.cluster.site.group: + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), + scope_id__in=vm.cluster.site.group.get_ancestors(include_self=True) + ) q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), - scope_id__in=vm.cluster.site.region.get_ancestors(include_self=True) + scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'), + scope_id=vm.cluster.site_id ) - if vm.cluster.site.group: + if vm.cluster.group: q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), - scope_id__in=vm.cluster.site.group.get_ancestors(include_self=True) + scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'), + scope_id=vm.cluster.group_id ) q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'), - scope_id=vm.cluster.site_id + scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), + scope_id=vm.cluster_id ) - if vm.cluster.group: - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'), - scope_id=vm.cluster.group_id - ) - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), - scope_id=vm.cluster_id - ) vlan_groups = VLANGroup.objects.filter(q) # Return all applicable VLANs @@ -113,7 +114,7 @@ class VLANQuerySet(RestrictedQuerySet): Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs Q(group__isnull=True, site__isnull=True) # Global VLANs ) - if vm.cluster.site: + if vm.cluster and vm.cluster.site: q |= Q(site=vm.cluster.site) return self.filter(q) From c97d2d4fe9c9fdc4f348fc03facdae79c7f2894b Mon Sep 17 00:00:00 2001 From: "Artem I. Kotik" Date: Sat, 24 Sep 2022 15:49:23 +0400 Subject: [PATCH 09/19] Add widget for Airflow field in DeviceTypeForm --- netbox/dcim/forms/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 5728e7f2d..b33023ece 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -373,6 +373,7 @@ class DeviceTypeForm(NetBoxModelForm): 'front_image', 'rear_image', 'comments', 'tags', ] widgets = { + 'airflow': StaticSelect(), 'subdevice_role': StaticSelect(), 'front_image': ClearableFileInput(attrs={ 'accept': DEVICETYPE_IMAGE_FORMATS From 39129ecedfb5755ac1c1c827abf60f5904670d80 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 26 Sep 2022 06:17:02 -0700 Subject: [PATCH 10/19] 10407 fix documentation link to requests (#10409) * 10407 fix documentation link to requests * Append page heading to URL Co-authored-by: jeremystretch --- docs/configuration/system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/system.md b/docs/configuration/system.md index 21607e566..93f8fa902 100644 --- a/docs/configuration/system.md +++ b/docs/configuration/system.md @@ -58,7 +58,7 @@ Email is sent from NetBox only for critical events or if configured for [logging Default: None -A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://2.python-requests.org/en/master/user/advanced/). For example: +A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://requests.readthedocs.io/en/latest/user/advanced/#proxies). For example: ```python HTTP_PROXIES = { From 3ad337dd15b569a644e7f0a1a22fed4970ccf295 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 26 Sep 2022 10:08:54 -0400 Subject: [PATCH 11/19] Filter VLANs and VLANGroups by site or cluster site for VM --- netbox/ipam/querysets.py | 41 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index b64ae04b8..9f4463f61 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -81,31 +81,34 @@ class VLANQuerySet(RestrictedQuerySet): # Find all relevant VLANGroups q = Q() + site = vm.site or vm.cluster.site if vm.cluster: - if vm.cluster.site: - if vm.cluster.site.region: - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), - scope_id__in=vm.cluster.site.region.get_ancestors(include_self=True) - ) - if vm.cluster.site.group: - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), - scope_id__in=vm.cluster.site.group.get_ancestors(include_self=True) - ) - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'), - scope_id=vm.cluster.site_id - ) + # Add VLANGroups scoped to the assigned cluster (or its group) + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), + scope_id=vm.cluster_id + ) if vm.cluster.group: q |= Q( scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'), scope_id=vm.cluster.group_id ) + if site: + # Add VLANGroups scoped to the assigned site (or its group or region) q |= Q( - scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), - scope_id=vm.cluster_id + scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'), + scope_id=site.pk ) + if site.region: + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), + scope_id__in=site.region.get_ancestors(include_self=True) + ) + if site.group: + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), + scope_id__in=site.group.get_ancestors(include_self=True) + ) vlan_groups = VLANGroup.objects.filter(q) # Return all applicable VLANs @@ -114,7 +117,7 @@ class VLANQuerySet(RestrictedQuerySet): Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs Q(group__isnull=True, site__isnull=True) # Global VLANs ) - if vm.cluster and vm.cluster.site: - q |= Q(site=vm.cluster.site) + if site: + q |= Q(site=site) return self.filter(q) From fd89ef04b6eac975bf7acf2cbada4810442c0bf3 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 26 Sep 2022 10:24:40 -0400 Subject: [PATCH 12/19] Revert "10408 add error message if already exists" --- netbox/netbox/views/generic/object_views.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 85604cd8f..a56a832b6 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -3,7 +3,7 @@ from copy import deepcopy from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist -from django.db import transaction, IntegrityError +from django.db import transaction from django.db.models import ProtectedError from django.forms.widgets import HiddenInput from django.shortcuts import redirect, render @@ -421,11 +421,7 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): return redirect(return_url) - except IntegrityError: - form.add_error(None, f"{obj} already exists") - clear_webhooks.send(sender=self) - - except (IntegrityError, AbortRequest, PermissionsViolation) as e: + except (AbortRequest, PermissionsViolation) as e: logger.debug(e.message) form.add_error(None, e.message) clear_webhooks.send(sender=self) From 96784640e371d12c0dcd97d15ceae4a45663f8c0 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 26 Sep 2022 10:27:35 -0400 Subject: [PATCH 13/19] Changelog for #10435, #10439 --- docs/release-notes/version-3.3.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 71f5605f9..2955e17d5 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -2,13 +2,17 @@ ## v3.3.5 (FUTURE) +### Bug Fixes + +* [#9497](https://github.com/netbox-community/netbox/issues/9497) - Adjust non-racked device filter on site and location detailed view +* [#10435](https://github.com/netbox-community/netbox/issues/10435) - Fix exception when filtering VLANs by virtual machine with no cluster assigned +* [#10439](https://github.com/netbox-community/netbox/issues/10439) - Fix form widget styling for DeviceType airflow field + --- ## v3.3.4 (2022-09-16) ### Bug Fixes - -* [#9497](https://github.com/netbox-community/netbox/issues/9497) - Adjust non-racked device filter on site and location detailed view * [#10383](https://github.com/netbox-community/netbox/issues/10383) - Fix assignment of component templates to module types via web UI * [#10387](https://github.com/netbox-community/netbox/issues/10387) - Fix `MultiValueDictKeyError` exception when editing a device interface From e11d5941222312cde96429b411d04c88db8f300c Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 26 Sep 2022 11:39:43 -0700 Subject: [PATCH 14/19] 9892 add supporting tests, form fields --- docs/models/ipam/fhrpgroup.md | 8 ++++++++ netbox/ipam/api/serializers.py | 2 +- netbox/ipam/filtersets.py | 3 ++- netbox/ipam/forms/bulk_edit.py | 4 ++-- netbox/ipam/forms/bulk_import.py | 2 +- netbox/ipam/forms/filtersets.py | 5 ++++- netbox/ipam/forms/models.py | 4 ++-- netbox/ipam/tables/fhrp.py | 2 +- netbox/ipam/tests/test_api.py | 1 + netbox/ipam/tests/test_filtersets.py | 6 +++++- netbox/ipam/tests/test_views.py | 1 + netbox/templates/ipam/fhrpgroup.html | 4 ++++ netbox/templates/ipam/fhrpgroup_edit.html | 1 + 13 files changed, 33 insertions(+), 10 deletions(-) diff --git a/docs/models/ipam/fhrpgroup.md b/docs/models/ipam/fhrpgroup.md index 4da390310..ddb3010e2 100644 --- a/docs/models/ipam/fhrpgroup.md +++ b/docs/models/ipam/fhrpgroup.md @@ -19,6 +19,14 @@ The wire protocol employed by cooperating servers to maintain the virtual [IP ad The group's numeric identifier. +### Name + +An optional name for the FHRP group. + +### Description + +A brief description of the FHRP group. + ### Authentication Type The type of authentication employed by group nodes, if any. diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index fa8b563e9..8a8fc03d6 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -123,7 +123,7 @@ class FHRPGroupSerializer(NetBoxModelSerializer): class Meta: model = FHRPGroup fields = [ - 'id', 'url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'ip_addresses', + 'id', 'name', 'url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'ip_addresses', 'tags', 'custom_fields', 'created', 'last_updated', ] diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 1eb1ee863..360cf2a56 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -659,7 +659,8 @@ class FHRPGroupFilterSet(NetBoxModelFilterSet): if not value.strip(): return queryset return queryset.filter( - Q(description__icontains=value) + Q(description__icontains=value) | + Q(name__icontains=value) ) def filter_related_ip(self, queryset, name, value): diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index 5f579b07f..7746b6625 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -328,10 +328,10 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm): model = FHRPGroup fieldsets = ( - (None, ('protocol', 'group_id', 'description')), + (None, ('protocol', 'group_id', 'name', 'description')), ('Authentication', ('auth_type', 'auth_key')), ) - nullable_fields = ('auth_type', 'auth_key', 'description') + nullable_fields = ('auth_type', 'auth_key', 'name', 'description') class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py index 880d2722f..6a9dd91ac 100644 --- a/netbox/ipam/forms/bulk_import.py +++ b/netbox/ipam/forms/bulk_import.py @@ -326,7 +326,7 @@ class FHRPGroupCSVForm(NetBoxModelCSVForm): class Meta: model = FHRPGroup - fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'description') + fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description') class VLANGroupCSVForm(NetBoxModelCSVForm): diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index ecf63b49f..a2ff7085b 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -335,9 +335,12 @@ class FHRPGroupFilterForm(NetBoxModelFilterSetForm): model = FHRPGroup fieldsets = ( (None, ('q', 'tag')), - ('Attributes', ('protocol', 'group_id')), + ('Attributes', ('name', 'protocol', 'group_id')), ('Authentication', ('auth_type', 'auth_key')), ) + name = forms.CharField( + required=False + ) protocol = MultipleChoiceField( choices=FHRPGroupProtocolChoices, required=False diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index 724812585..dea42065c 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -527,7 +527,7 @@ class FHRPGroupForm(NetBoxModelForm): ) fieldsets = ( - ('FHRP Group', ('protocol', 'group_id', 'description', 'tags')), + ('FHRP Group', ('protocol', 'group_id', 'name', 'description', 'tags')), ('Authentication', ('auth_type', 'auth_key')), ('Virtual IP Address', ('ip_vrf', 'ip_address', 'ip_status')) ) @@ -535,7 +535,7 @@ class FHRPGroupForm(NetBoxModelForm): class Meta: model = FHRPGroup fields = ( - 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'ip_vrf', 'ip_address', 'ip_status', 'tags', + 'protocol', 'group_id', 'auth_type', 'auth_key', 'name', 'description', 'ip_vrf', 'ip_address', 'ip_status', 'tags', ) def save(self, *args, **kwargs): diff --git a/netbox/ipam/tables/fhrp.py b/netbox/ipam/tables/fhrp.py index dc4982e26..cfbfb036b 100644 --- a/netbox/ipam/tables/fhrp.py +++ b/netbox/ipam/tables/fhrp.py @@ -36,7 +36,7 @@ class FHRPGroupTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = FHRPGroup fields = ( - 'pk', 'name', 'group_id', 'protocol', 'auth_type', 'auth_key', 'description', 'ip_addresses', 'member_count', + 'pk', 'group_id', 'protocol', 'auth_type', 'auth_key', 'name', 'description', 'ip_addresses', 'member_count', 'tags', 'created', 'last_updated', ) default_columns = ('pk', 'group_id', 'protocol', 'auth_type', 'description', 'ip_addresses', 'member_count') diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 0fefb0162..1bbeb0487 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -552,6 +552,7 @@ class FHRPGroupTest(APIViewTestCases.APIViewTestCase): 'group_id': 200, 'auth_type': FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, 'auth_key': 'foobarbaz999', + 'name': 'foobar-999' 'description': 'New description', } diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index 5c4113786..abb5a3cc3 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -932,7 +932,7 @@ class FHRPGroupTestCase(TestCase, ChangeLoggedFilterSetTests): fhrp_groups = ( FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP2, group_id=10, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_PLAINTEXT, auth_key='foo123'), - FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='bar456'), + FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_VRRP3, group_id=20, auth_type=FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, auth_key='bar456', name='bar123'), FHRPGroup(protocol=FHRPGroupProtocolChoices.PROTOCOL_HSRP, group_id=30), ) FHRPGroup.objects.bulk_create(fhrp_groups) @@ -956,6 +956,10 @@ class FHRPGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'auth_key': ['foo123', 'bar456']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_name(self): + params = {'name': ['bar123', ]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_related_ip(self): # Create some regular IPs to query for related IPs ipaddresses = ( diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index 27520229a..dc9b89141 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -524,6 +524,7 @@ class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'auth_type': FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, 'auth_key': 'abc123def456', 'description': 'Blah blah blah', + 'name': 'test123 name' 'tags': [t.pk for t in tags], } diff --git a/netbox/templates/ipam/fhrpgroup.html b/netbox/templates/ipam/fhrpgroup.html index b4911ce44..89fc7083c 100644 --- a/netbox/templates/ipam/fhrpgroup.html +++ b/netbox/templates/ipam/fhrpgroup.html @@ -26,6 +26,10 @@ Group ID {{ object.group_id }} + + Name + {{ object.name|placeholder }} + Description {{ object.description|placeholder }} diff --git a/netbox/templates/ipam/fhrpgroup_edit.html b/netbox/templates/ipam/fhrpgroup_edit.html index 858d265ab..02816b440 100644 --- a/netbox/templates/ipam/fhrpgroup_edit.html +++ b/netbox/templates/ipam/fhrpgroup_edit.html @@ -8,6 +8,7 @@ {% render_field form.protocol %} {% render_field form.group_id %} + {% render_field form.name %} {% render_field form.description %} {% render_field form.tags %} From 3eeb31d5777a434a5e0250cd128a4fdb83039515 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 26 Sep 2022 11:49:51 -0700 Subject: [PATCH 15/19] 9892 doh - fix tests --- netbox/ipam/tests/test_api.py | 2 +- netbox/ipam/tests/test_views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 1bbeb0487..ea6441650 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -552,7 +552,7 @@ class FHRPGroupTest(APIViewTestCases.APIViewTestCase): 'group_id': 200, 'auth_type': FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, 'auth_key': 'foobarbaz999', - 'name': 'foobar-999' + 'name': 'foobar-999', 'description': 'New description', } diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index dc9b89141..5cc8fad24 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -524,7 +524,7 @@ class FHRPGroupTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'auth_type': FHRPGroupAuthTypeChoices.AUTHENTICATION_MD5, 'auth_key': 'abc123def456', 'description': 'Blah blah blah', - 'name': 'test123 name' + 'name': 'test123 name', 'tags': [t.pk for t in tags], } From 5ce805db2e2051b96e174014bbf6d258f65a8433 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 26 Sep 2022 12:56:16 -0700 Subject: [PATCH 16/19] 9892 doh - fix tests --- netbox/ipam/forms/bulk_edit.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index 7746b6625..67bcf83fb 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -321,6 +321,10 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm): required=False, label='Authentication key' ) + name = forms.CharField( + max_length=100, + required=False + ) description = forms.CharField( max_length=200, required=False From 10cb3c2c3d3550baea4d22ec7c998e1497856571 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 26 Sep 2022 13:54:49 -0700 Subject: [PATCH 17/19] 9892 add name to str --- netbox/ipam/models/fhrp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index b022386c8..fe61a7fec 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -59,7 +59,7 @@ class FHRPGroup(NetBoxModel): verbose_name = 'FHRP group' def __str__(self): - name = f'{self.get_protocol_display()}: {self.group_id}' + name = f'{self.name} {self.get_protocol_display()}: {self.group_id}' # Append the first assigned IP addresses (if any) to serve as an additional identifier if self.pk: From 8103ad3b9e441bedf09f22278604b9d966caddc3 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 26 Sep 2022 13:57:09 -0700 Subject: [PATCH 18/19] 9892 add name to str --- netbox/ipam/models/fhrp.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index fe61a7fec..88e6e19d9 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -59,7 +59,11 @@ class FHRPGroup(NetBoxModel): verbose_name = 'FHRP group' def __str__(self): - name = f'{self.name} {self.get_protocol_display()}: {self.group_id}' + name = '' + if self.name: + name = f'{self.name} ' + + name += f'{self.get_protocol_display()}: {self.group_id}' # Append the first assigned IP addresses (if any) to serve as an additional identifier if self.pk: From 6016e1b15daade776be0d407c51ae53e707f7d6e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 27 Sep 2022 09:55:35 -0400 Subject: [PATCH 19/19] Changelog & cleanup for #9892 --- docs/models/ipam/fhrpgroup.md | 4 ---- docs/release-notes/version-3.4.md | 9 +++++++++ netbox/ipam/tables/fhrp.py | 8 +++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/models/ipam/fhrpgroup.md b/docs/models/ipam/fhrpgroup.md index ddb3010e2..de09fee29 100644 --- a/docs/models/ipam/fhrpgroup.md +++ b/docs/models/ipam/fhrpgroup.md @@ -23,10 +23,6 @@ The group's numeric identifier. An optional name for the FHRP group. -### Description - -A brief description of the FHRP group. - ### Authentication Type The type of authentication employed by group nodes, if any. diff --git a/docs/release-notes/version-3.4.md b/docs/release-notes/version-3.4.md index 39c44f38e..257ffd625 100644 --- a/docs/release-notes/version-3.4.md +++ b/docs/release-notes/version-3.4.md @@ -3,6 +3,10 @@ !!! warning "PostgreSQL 11 Required" NetBox v3.4 requires PostgreSQL 11 or later. +### Enhancements + +* [#9892](https://github.com/netbox-community/netbox/issues/9892) - Add optional `name` field for FHRP groups + ### Plugins API * [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin @@ -10,3 +14,8 @@ ### Other Changes * [#10358](https://github.com/netbox-community/netbox/issues/10358) - Raise minimum required PostgreSQL version from 10 to 11 + +### REST API Changes + +* ipam.FHRPGroup + * Added optional `name` field diff --git a/netbox/ipam/tables/fhrp.py b/netbox/ipam/tables/fhrp.py index cfbfb036b..beffdd232 100644 --- a/netbox/ipam/tables/fhrp.py +++ b/netbox/ipam/tables/fhrp.py @@ -36,10 +36,12 @@ class FHRPGroupTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = FHRPGroup fields = ( - 'pk', 'group_id', 'protocol', 'auth_type', 'auth_key', 'name', 'description', 'ip_addresses', 'member_count', - 'tags', 'created', 'last_updated', + 'pk', 'group_id', 'protocol', 'name', 'auth_type', 'auth_key', 'description', 'ip_addresses', + 'member_count', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'group_id', 'protocol', 'name', 'auth_type', 'description', 'ip_addresses', 'member_count', ) - default_columns = ('pk', 'group_id', 'protocol', 'auth_type', 'description', 'ip_addresses', 'member_count') class FHRPGroupAssignmentTable(NetBoxTable):