From f74b47ca1660b1829823605859f15ae916b18ac5 Mon Sep 17 00:00:00 2001 From: bluikko <14869000+bluikko@users.noreply.github.com> Date: Sun, 9 May 2021 12:33:09 +0700 Subject: [PATCH 01/25] Typo in powerfeed.md pot -> port --- docs/models/dcim/powerfeed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/models/dcim/powerfeed.md b/docs/models/dcim/powerfeed.md index 48ad2a5dc..bac7214f1 100644 --- a/docs/models/dcim/powerfeed.md +++ b/docs/models/dcim/powerfeed.md @@ -1,6 +1,6 @@ # Power Feed -A power feed represents the distribution of power from a power panel to a particular device, typically a power distribution unit (PDU). The power pot (inlet) on a device can be connected via a cable to a power feed. A power feed may optionally be assigned to a rack to allow more easily tracking the distribution of power among racks. +A power feed represents the distribution of power from a power panel to a particular device, typically a power distribution unit (PDU). The power port (inlet) on a device can be connected via a cable to a power feed. A power feed may optionally be assigned to a rack to allow more easily tracking the distribution of power among racks. Each power feed is assigned an operational type (primary or redundant) and one of the following statuses: From 0fec03ad3f4f926cf923a5c5467113b4030bdb30 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 12 May 2021 13:38:52 -0400 Subject: [PATCH 02/25] Closes #6393: Add description filter for IP addresses --- docs/release-notes/version-2.11.md | 8 ++++++++ netbox/ipam/filtersets.py | 2 +- netbox/ipam/tests/test_filtersets.py | 8 ++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 0827d5434..5b5f686ca 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -1,5 +1,13 @@ # NetBox v2.11 +## v2.11.4 (FUTURE) + +### Enhancements + +* [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses + +--- + ## v2.11.3 (2021-05-07) ### Enhancements diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 5ab4994ea..71df90bb7 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -468,7 +468,7 @@ class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet): class Meta: model = IPAddress - fields = ['id', 'dns_name'] + fields = ['id', 'dns_name', 'description'] def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index f43a44c62..282a19b66 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -571,12 +571,12 @@ class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests): Tenant.objects.bulk_create(tenants) ipaddresses = ( - IPAddress(address='10.0.0.1/24', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'), + IPAddress(address='10.0.0.1/24', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a', description='foobar1'), IPAddress(address='10.0.0.2/24', tenant=tenants[0], vrf=vrfs[0], assigned_object=interfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'), IPAddress(address='10.0.0.3/24', tenant=tenants[1], vrf=vrfs[1], assigned_object=interfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'), IPAddress(address='10.0.0.4/24', tenant=tenants[2], vrf=vrfs[2], assigned_object=interfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'), IPAddress(address='10.0.0.1/25', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE), - IPAddress(address='2001:db8::1/64', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a'), + IPAddress(address='2001:db8::1/64', tenant=None, vrf=None, assigned_object=None, status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-a', description='foobar2'), IPAddress(address='2001:db8::2/64', tenant=tenants[0], vrf=vrfs[0], assigned_object=vminterfaces[0], status=IPAddressStatusChoices.STATUS_ACTIVE, dns_name='ipaddress-b'), IPAddress(address='2001:db8::3/64', tenant=tenants[1], vrf=vrfs[1], assigned_object=vminterfaces[1], status=IPAddressStatusChoices.STATUS_RESERVED, role=IPAddressRoleChoices.ROLE_VIP, dns_name='ipaddress-c'), IPAddress(address='2001:db8::4/64', tenant=tenants[2], vrf=vrfs[2], assigned_object=vminterfaces[2], status=IPAddressStatusChoices.STATUS_DEPRECATED, role=IPAddressRoleChoices.ROLE_SECONDARY, dns_name='ipaddress-d'), @@ -592,6 +592,10 @@ class IPAddressTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'dns_name': ['ipaddress-a', 'ipaddress-b']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + def test_description(self): + params = {'description': ['foobar1', 'foobar2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_parent(self): params = {'parent': '10.0.0.0/24'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 5) From 72d41eac859887f50dd5c18ef078523c29931999 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 12 May 2021 13:47:42 -0400 Subject: [PATCH 03/25] Fixes #6376: Fix assignment of VLAN groups to clusters, cluster groups via REST API --- docs/release-notes/version-2.11.md | 4 ++++ netbox/ipam/api/serializers.py | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 5b5f686ca..9bf803d13 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -6,6 +6,10 @@ * [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses +### Bug Fixes + +* [#6376](https://github.com/netbox-community/netbox/issues/6376) - Fix assignment of VLAN groups to clusters, cluster groups via REST API + --- ## v2.11.3 (2021-05-07) diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 931e2cc47..3270162a5 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -7,7 +7,7 @@ from rest_framework.validators import UniqueTogetherValidator from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerializer from ipam.choices import * -from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS +from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField from netbox.api.serializers import OrganizationalModelSerializer @@ -116,8 +116,7 @@ class VLANGroupSerializer(OrganizationalModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail') scope_type = ContentTypeField( queryset=ContentType.objects.filter( - app_label='dcim', - model__in=['region', 'sitegroup', 'site', 'location', 'rack'] + model__in=VLANGROUP_SCOPE_TYPES ), required=False ) From 834b233c305ca3b268a7549b741d0936ef57cadb Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 14 May 2021 09:06:00 -0400 Subject: [PATCH 04/25] Fixes #6398: Avoid exception when deleting device connected to self via circuit --- docs/release-notes/version-2.11.md | 1 + netbox/dcim/signals.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 9bf803d13..4997f665d 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -9,6 +9,7 @@ ### Bug Fixes * [#6376](https://github.com/netbox-community/netbox/issues/6376) - Fix assignment of VLAN groups to clusters, cluster groups via REST API +* [#6398](https://github.com/netbox-community/netbox/issues/6398) - Avoid exception when deleting device connected to self via circuit --- diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 1dbdca140..8675ee7ce 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -31,9 +31,10 @@ def rebuild_paths(obj): with transaction.atomic(): for cp in cable_paths: - invalidate_obj(cp.origin) cp.delete() - create_cablepath(cp.origin) + if cp.origin: + invalidate_obj(cp.origin) + create_cablepath(cp.origin) # From 138231059bd3d690e4bc259d4f7b0d50c02ad4fe Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 14 May 2021 09:13:36 -0400 Subject: [PATCH 05/25] Closes #6400: Add cyan color choice for plugin buttons --- docs/release-notes/version-2.11.md | 1 + netbox/utilities/choices.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 4997f665d..ea2f7d0ac 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -5,6 +5,7 @@ ### Enhancements * [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses +* [#6400](https://github.com/netbox-community/netbox/issues/6400) - Add cyan color choice for plugin buttons ### Bug Fixes diff --git a/netbox/utilities/choices.py b/netbox/utilities/choices.py index 3d1002105..2d8e27acf 100644 --- a/netbox/utilities/choices.py +++ b/netbox/utilities/choices.py @@ -130,22 +130,24 @@ class ColorChoices(ChoiceSet): class ButtonColorChoices(ChoiceSet): """ - Map standard button color choices to Bootstrap color classes + Map standard button color choices to Bootstrap 3 button classes """ DEFAULT = 'default' BLUE = 'primary' - GREY = 'secondary' + CYAN = 'info' GREEN = 'success' RED = 'danger' YELLOW = 'warning' + GREY = 'secondary' BLACK = 'dark' CHOICES = ( (DEFAULT, 'Default'), (BLUE, 'Blue'), - (GREY, 'Grey'), + (CYAN, 'Cyan'), (GREEN, 'Green'), (RED, 'Red'), (YELLOW, 'Yellow'), + (GREY, 'Grey'), (BLACK, 'Black') ) From dc573329880f35e349ff98d2749caa824e4a51f3 Mon Sep 17 00:00:00 2001 From: Thomas <3999809+tehtbl@users.noreply.github.com> Date: Fri, 14 May 2021 18:22:01 +0200 Subject: [PATCH 06/25] Update views.py Fixing typo in 'version' --- netbox/extras/plugins/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/plugins/views.py b/netbox/extras/plugins/views.py index 7484b9632..65b33f0c4 100644 --- a/netbox/extras/plugins/views.py +++ b/netbox/extras/plugins/views.py @@ -42,7 +42,7 @@ class InstalledPluginsAPIView(APIView): 'author': plugin_app_config.author, 'author_email': plugin_app_config.author_email, 'description': plugin_app_config.description, - 'verison': plugin_app_config.version + 'version': plugin_app_config.version } def get(self, request, format=None): From 8823aeb9d7d5dc9cfbcf563459569722860155db Mon Sep 17 00:00:00 2001 From: Shin Sterneck Date: Mon, 17 May 2021 09:27:44 +0200 Subject: [PATCH 07/25] Typo fix Fix a small typo --- docs/additional-features/caching.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/additional-features/caching.md b/docs/additional-features/caching.md index ebce63578..18c9dca68 100644 --- a/docs/additional-features/caching.md +++ b/docs/additional-features/caching.md @@ -6,7 +6,7 @@ If a change is made to any of the objects returned by the query within that time ## Invalidating Cached Data -Although caching is performed automatically and rarely requires administrative intervention, NetBox provides the `invalidate` management command to force invalidation of cached results. This command can reference a specific object my its type and numeric ID: +Although caching is performed automatically and rarely requires administrative intervention, NetBox provides the `invalidate` management command to force invalidation of cached results. This command can reference a specific object by its type and numeric ID: ```no-highlight $ python netbox/manage.py invalidate dcim.Device.34 From 568148a3492ab9a758aaee558c415ba68fac88a8 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 17 May 2021 13:10:49 -0400 Subject: [PATCH 08/25] Warn against relying on demo instance for bug reports --- .github/ISSUE_TEMPLATE/bug_report.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 3c52c973c..ea9d7aa79 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -39,8 +39,9 @@ body: reproduce this bug using the current stable release of NetBox. Begin with the creation of any necessary database objects and call out every operation being performed explicitly. If reporting a bug in the REST API, be sure to reconstruct - the raw HTTP request(s) being made: Don't rely on a client library such as - pynetbox." + the raw HTTP request(s) being made: Don't rely on a client library such as + pynetbox. Additionally, **do not rely on the demo instance** for reproducing + suspected bugs, as its data is prone to modification or deletion at any time." placeholder: | 1. Click on "create widget" 2. Set foo to 12 and bar to G From 5ca7f375d3bce1826381f91c694f990e825a4e70 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 17 May 2021 13:18:02 -0400 Subject: [PATCH 09/25] Clean up stray quote --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index ea9d7aa79..adbfba419 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -41,7 +41,7 @@ body: performed explicitly. If reporting a bug in the REST API, be sure to reconstruct the raw HTTP request(s) being made: Don't rely on a client library such as pynetbox. Additionally, **do not rely on the demo instance** for reproducing - suspected bugs, as its data is prone to modification or deletion at any time." + suspected bugs, as its data is prone to modification or deletion at any time. placeholder: | 1. Click on "create widget" 2. Set foo to 12 and bar to G From 546bbe54184814eee4c91af0614aa12e172b4c0e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 18 May 2021 16:42:21 -0400 Subject: [PATCH 10/25] Fixes #6426: Allow assigning virtual chassis member interfaces to LAG on VC master --- docs/release-notes/version-2.11.md | 1 + netbox/dcim/forms.py | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index ea2f7d0ac..3436c9c34 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -11,6 +11,7 @@ * [#6376](https://github.com/netbox-community/netbox/issues/6376) - Fix assignment of VLAN groups to clusters, cluster groups via REST API * [#6398](https://github.com/netbox-community/netbox/issues/6398) - Avoid exception when deleting device connected to self via circuit +* [#6426](https://github.com/netbox-community/netbox/issues/6426) - Allow assigning virtual chassis member interfaces to LAG on VC master --- diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index acaa3f4ec..5b81c80a9 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -3126,9 +3126,13 @@ class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm): device = Device.objects.get(pk=self.data['device']) if self.is_bound else self.instance.device - # Restrict parent/LAG interface assignment by device + # Restrict parent/LAG interface assignment by device/VC self.fields['parent'].widget.add_query_param('device_id', device.pk) - self.fields['lag'].widget.add_query_param('device_id', device.pk) + if device.virtual_chassis and device.virtual_chassis.master: + # Get available LAG interfaces by VirtualChassis master + self.fields['lag'].widget.add_query_param('device_id', device.virtual_chassis.master.pk) + else: + self.fields['lag'].widget.add_query_param('device_id', device.pk) # Limit VLAN choices by device self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device.pk) From ea6c8a1a65f2db8a6ff6937a83fb90726954ec5e Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Thu, 20 May 2021 06:30:44 +0000 Subject: [PATCH 11/25] Fixed #6438 Device Type Import does not import/export description/label fields for many components --- docs/release-notes/version-2.11.md | 1 + netbox/dcim/forms.py | 16 ++++++++-------- netbox/dcim/models/devices.py | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 3436c9c34..4d17dfbc1 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -12,6 +12,7 @@ * [#6376](https://github.com/netbox-community/netbox/issues/6376) - Fix assignment of VLAN groups to clusters, cluster groups via REST API * [#6398](https://github.com/netbox-community/netbox/issues/6398) - Avoid exception when deleting device connected to self via circuit * [#6426](https://github.com/netbox-community/netbox/issues/6426) - Allow assigning virtual chassis member interfaces to LAG on VC master +* [#6438](https://github.com/netbox-community/netbox/issues/6438) - Fix missing descriptions and label for device type imports and exports --- diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 5b81c80a9..efb712963 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1825,7 +1825,7 @@ class ConsolePortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = ConsolePortTemplate fields = [ - 'device_type', 'name', 'label', 'type', + 'device_type', 'name', 'label', 'type', 'description', ] @@ -1834,7 +1834,7 @@ class ConsoleServerPortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = ConsoleServerPortTemplate fields = [ - 'device_type', 'name', 'label', 'type', + 'device_type', 'name', 'label', 'type', 'description', ] @@ -1843,7 +1843,7 @@ class PowerPortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = PowerPortTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', + 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', ] @@ -1857,7 +1857,7 @@ class PowerOutletTemplateImportForm(ComponentTemplateImportForm): class Meta: model = PowerOutletTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', + 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', ] @@ -1869,7 +1869,7 @@ class InterfaceTemplateImportForm(ComponentTemplateImportForm): class Meta: model = InterfaceTemplate fields = [ - 'device_type', 'name', 'label', 'type', 'mgmt_only', + 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', ] @@ -1886,7 +1886,7 @@ class FrontPortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = FrontPortTemplate fields = [ - 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', + 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', 'label', 'description', ] @@ -1898,7 +1898,7 @@ class RearPortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = RearPortTemplate fields = [ - 'device_type', 'name', 'type', 'positions', + 'device_type', 'name', 'type', 'positions', 'label', 'description', ] @@ -1907,7 +1907,7 @@ class DeviceBayTemplateImportForm(ComponentTemplateImportForm): class Meta: model = DeviceBayTemplate fields = [ - 'device_type', 'name', + 'device_type', 'name', 'label', 'description', ] diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index e8c7d5b51..f5a95f15d 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -183,6 +183,8 @@ class DeviceType(PrimaryModel): { 'name': c.name, 'type': c.type, + 'label': c.label, + 'description': c.description, } for c in self.consoleporttemplates.all() ] @@ -191,6 +193,8 @@ class DeviceType(PrimaryModel): { 'name': c.name, 'type': c.type, + 'label': c.label, + 'description': c.description, } for c in self.consoleserverporttemplates.all() ] @@ -201,6 +205,8 @@ class DeviceType(PrimaryModel): 'type': c.type, 'maximum_draw': c.maximum_draw, 'allocated_draw': c.allocated_draw, + 'label': c.label, + 'description': c.description, } for c in self.powerporttemplates.all() ] @@ -211,6 +217,8 @@ class DeviceType(PrimaryModel): 'type': c.type, 'power_port': c.power_port.name if c.power_port else None, 'feed_leg': c.feed_leg, + 'label': c.label, + 'description': c.description, } for c in self.poweroutlettemplates.all() ] @@ -220,6 +228,8 @@ class DeviceType(PrimaryModel): 'name': c.name, 'type': c.type, 'mgmt_only': c.mgmt_only, + 'label': c.label, + 'description': c.description, } for c in self.interfacetemplates.all() ] @@ -230,6 +240,8 @@ class DeviceType(PrimaryModel): 'type': c.type, 'rear_port': c.rear_port.name, 'rear_port_position': c.rear_port_position, + 'label': c.label, + 'description': c.description, } for c in self.frontporttemplates.all() ] @@ -239,6 +251,8 @@ class DeviceType(PrimaryModel): 'name': c.name, 'type': c.type, 'positions': c.positions, + 'label': c.label, + 'description': c.description, } for c in self.rearporttemplates.all() ] @@ -246,6 +260,8 @@ class DeviceType(PrimaryModel): data['device-bays'] = [ { 'name': c.name, + 'label': c.label, + 'description': c.description, } for c in self.devicebaytemplates.all() ] From a39522a25eb10accfb2dda2097bde969ce9005ca Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 20 May 2021 10:51:41 -0400 Subject: [PATCH 12/25] Closes #6434: Add deprecation warning for stock secrets functionality --- netbox/secrets/views.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py index 091e16224..88f647225 100644 --- a/netbox/secrets/views.py +++ b/netbox/secrets/views.py @@ -86,6 +86,18 @@ class SecretRoleBulkDeleteView(generic.BulkDeleteView): # Secrets # +def inject_deprecation_warning(request): + """ + Inject a warning message notifying the user of the pending removal of secrets functionality. + """ + messages.warning( + request, + mark_safe(' The secrets functionality will be moved to a plugin in NetBox v2.12. ' + 'Please see issue #5278 for ' + 'more information.') + ) + + class SecretListView(generic.ObjectListView): queryset = Secret.objects.all() filterset = filtersets.SecretFilterSet @@ -93,10 +105,18 @@ class SecretListView(generic.ObjectListView): table = tables.SecretTable action_buttons = ('import', 'export') + def get(self, request): + inject_deprecation_warning(request) + return super().get(request) + class SecretView(generic.ObjectView): queryset = Secret.objects.all() + def get(self, request, *args, **kwargs): + inject_deprecation_warning(request) + return super().get(request, *args, **kwargs) + class SecretEditView(generic.ObjectEditView): queryset = Secret.objects.all() From 22927bfc7662976a342855bf874176f406964058 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 20 May 2021 12:00:31 -0400 Subject: [PATCH 13/25] Closes #6441: Improve UI paginator to optimize page object count --- docs/release-notes/version-2.11.md | 1 + netbox/utilities/paginator.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 3436c9c34..112609b0e 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -6,6 +6,7 @@ * [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses * [#6400](https://github.com/netbox-community/netbox/issues/6400) - Add cyan color choice for plugin buttons +* [#6441](https://github.com/netbox-community/netbox/issues/6441) - Improve UI paginator to optimize page object count ### Bug Fixes diff --git a/netbox/utilities/paginator.py b/netbox/utilities/paginator.py index cdad1f230..3b9e1cb37 100644 --- a/netbox/utilities/paginator.py +++ b/netbox/utilities/paginator.py @@ -4,7 +4,9 @@ from django.core.paginator import Paginator, Page class EnhancedPaginator(Paginator): - def __init__(self, object_list, per_page, **kwargs): + def __init__(self, object_list, per_page, orphans=None, **kwargs): + + # Determine the page size try: per_page = int(per_page) if per_page < 1: @@ -12,7 +14,13 @@ class EnhancedPaginator(Paginator): except ValueError: per_page = settings.PAGINATE_COUNT - super().__init__(object_list, per_page, **kwargs) + # Set orphans count based on page size + if orphans is None and per_page <= 50: + orphans = 5 + elif orphans is None: + orphans = 10 + + super().__init__(object_list, per_page, orphans=orphans, **kwargs) def _get_page(self, *args, **kwargs): return EnhancedPage(*args, **kwargs) From 44b24de5d0031f4c878962f115d0f8a4544d19b0 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 20 May 2021 12:41:23 -0400 Subject: [PATCH 14/25] Add DigitalOcean as sponsor --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ad0595782..2ab04db02 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ The complete documentation for NetBox can be found at [Read the Docs](https://ne

Thank you to our sponsors!

+ [![DigitalOcean](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/digitalocean.png)](https://try.digitalocean.com/developer-cloud) +            [![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/)            [![Stellar Technologies](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/stellar.png)](https://stellar.tech/) From b27f9bf74c6c11b67f423369a29b85358996cd98 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 May 2021 11:17:58 -0400 Subject: [PATCH 15/25] Changelog for #6465 --- docs/release-notes/version-2.11.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 21488350e..3354ede94 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -14,6 +14,7 @@ * [#6398](https://github.com/netbox-community/netbox/issues/6398) - Avoid exception when deleting device connected to self via circuit * [#6426](https://github.com/netbox-community/netbox/issues/6426) - Allow assigning virtual chassis member interfaces to LAG on VC master * [#6438](https://github.com/netbox-community/netbox/issues/6438) - Fix missing descriptions and label for device type imports and exports +* [#6465](https://github.com/netbox-community/netbox/issues/6465) - Fix typo in installed plugins REST API endpoint --- From 239fddcac2db9e552554afe61e1cdc46a202281e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 May 2021 15:43:18 -0400 Subject: [PATCH 16/25] Fixes #6468: Disable ordering VLAN groups list by scope object --- docs/release-notes/version-2.11.md | 1 + netbox/ipam/tables.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 3354ede94..6a8af102f 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -15,6 +15,7 @@ * [#6426](https://github.com/netbox-community/netbox/issues/6426) - Allow assigning virtual chassis member interfaces to LAG on VC master * [#6438](https://github.com/netbox-community/netbox/issues/6438) - Fix missing descriptions and label for device type imports and exports * [#6465](https://github.com/netbox-community/netbox/issues/6465) - Fix typo in installed plugins REST API endpoint +* [#6468](https://github.com/netbox-community/netbox/issues/6468) - Disable ordering VLAN groups list by scope object --- diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index ff28f2fc7..82e8751a9 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -430,7 +430,8 @@ class VLANGroupTable(BaseTable): name = tables.Column(linkify=True) scope_type = ContentTypeColumn() scope = tables.Column( - linkify=True + linkify=True, + orderable=False ) vlan_count = LinkedCountColumn( viewname='ipam:vlan_list', From a6eeed406148fb6231696ad079077b5ebc2e88c2 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 May 2021 15:56:22 -0400 Subject: [PATCH 17/25] Fixes #6467: Fix access to metrics on custom BASE_PATH when login is required --- docs/release-notes/version-2.11.md | 1 + netbox/netbox/middleware.py | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 6a8af102f..451f07a99 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -15,6 +15,7 @@ * [#6426](https://github.com/netbox-community/netbox/issues/6426) - Allow assigning virtual chassis member interfaces to LAG on VC master * [#6438](https://github.com/netbox-community/netbox/issues/6438) - Fix missing descriptions and label for device type imports and exports * [#6465](https://github.com/netbox-community/netbox/issues/6465) - Fix typo in installed plugins REST API endpoint +* [#6467](https://github.com/netbox-community/netbox/issues/6467) - Fix access to metrics on custom `BASE_PATH` when login is required * [#6468](https://github.com/netbox-community/netbox/issues/6468) - Disable ordering VLAN groups list by scope object --- diff --git a/netbox/netbox/middleware.py b/netbox/netbox/middleware.py index 1395cbd1f..d3b3dae40 100644 --- a/netbox/netbox/middleware.py +++ b/netbox/netbox/middleware.py @@ -20,17 +20,20 @@ class LoginRequiredMiddleware(object): self.get_response = get_response def __call__(self, request): + # Redirect unauthenticated requests (except those exempted) to the login page if LOGIN_REQUIRED is true if settings.LOGIN_REQUIRED and not request.user.is_authenticated: - # Redirect unauthenticated requests to the login page. API requests are exempt from redirection as the API - # performs its own authentication. Also metrics can be read without login. - api_path = reverse('api-root') - if not request.path_info.startswith((api_path, '/metrics')) and request.path_info != settings.LOGIN_URL: - return HttpResponseRedirect( - '{}?next={}'.format( - settings.LOGIN_URL, - parse.quote(request.get_full_path_info()) - ) - ) + # Determine exempt paths + exempt_paths = [ + reverse('api-root') + ] + if settings.METRICS_ENABLED: + exempt_paths.append(reverse('prometheus-django-metrics')) + + # Redirect unauthenticated requests + if not request.path_info.startswith(tuple(exempt_paths)) and request.path_info != settings.LOGIN_URL: + login_url = f'{settings.LOGIN_URL}?next={parse.quote(request.get_full_path_info())}' + return HttpResponseRedirect(login_url) + return self.get_response(request) From b6660c72e1f024fc491e663cb2c2fd0a32d49c7c Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 May 2021 16:54:33 -0400 Subject: [PATCH 18/25] Add tags as a feature query --- netbox/circuits/models.py | 6 +++--- netbox/dcim/models/cables.py | 2 +- netbox/dcim/models/device_components.py | 18 +++++++++--------- netbox/dcim/models/devices.py | 6 +++--- netbox/dcim/models/power.py | 4 ++-- netbox/dcim/models/racks.py | 4 ++-- netbox/dcim/models/sites.py | 2 +- netbox/extras/constants.py | 1 + netbox/ipam/models/ip.py | 6 +++--- netbox/ipam/models/services.py | 2 +- netbox/ipam/models/vlans.py | 2 +- netbox/ipam/models/vrfs.py | 4 ++-- netbox/secrets/models.py | 2 +- netbox/tenancy/models.py | 2 +- netbox/virtualization/models.py | 6 +++--- 15 files changed, 34 insertions(+), 33 deletions(-) diff --git a/netbox/circuits/models.py b/netbox/circuits/models.py index 31d08537e..699ded7b0 100644 --- a/netbox/circuits/models.py +++ b/netbox/circuits/models.py @@ -20,7 +20,7 @@ __all__ = ( ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Provider(PrimaryModel): """ Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model @@ -96,7 +96,7 @@ class Provider(PrimaryModel): # Provider networks # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class ProviderNetwork(PrimaryModel): """ This represents a provider network which exists outside of NetBox, the details of which are unknown or @@ -189,7 +189,7 @@ class CircuitType(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Circuit(PrimaryModel): """ A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 28d21ff68..e7040376c 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -30,7 +30,7 @@ __all__ = ( # Cables # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Cable(PrimaryModel): """ A physical connection between two endpoints. diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index f2b13ed6f..bd7f4ac55 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -211,7 +211,7 @@ class PathEndpoint(models.Model): # Console ports # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class ConsolePort(ComponentModel, CableTermination, PathEndpoint): """ A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts. @@ -254,7 +254,7 @@ class ConsolePort(ComponentModel, CableTermination, PathEndpoint): # Console server ports # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class ConsoleServerPort(ComponentModel, CableTermination, PathEndpoint): """ A physical port within a Device (typically a designated console server) which provides access to ConsolePorts. @@ -297,7 +297,7 @@ class ConsoleServerPort(ComponentModel, CableTermination, PathEndpoint): # Power ports # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class PowerPort(ComponentModel, CableTermination, PathEndpoint): """ A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets. @@ -408,7 +408,7 @@ class PowerPort(ComponentModel, CableTermination, PathEndpoint): # Power outlets # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class PowerOutlet(ComponentModel, CableTermination, PathEndpoint): """ A physical power outlet (output) within a Device which provides power to a PowerPort. @@ -512,7 +512,7 @@ class BaseInterface(models.Model): return self.ip_addresses.count() -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint): """ A network interface within a Device. A physical Interface can connect to exactly one other Interface. @@ -683,7 +683,7 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint): # Pass-through ports # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class FrontPort(ComponentModel, CableTermination): """ A pass-through port on the front of a Device. @@ -748,7 +748,7 @@ class FrontPort(ComponentModel, CableTermination): }) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class RearPort(ComponentModel, CableTermination): """ A pass-through port on the rear of a Device. @@ -801,7 +801,7 @@ class RearPort(ComponentModel, CableTermination): # Device bays # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class DeviceBay(ComponentModel): """ An empty space within a Device which can house a child device @@ -860,7 +860,7 @@ class DeviceBay(ComponentModel): # Inventory items # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class InventoryItem(MPTTModel, ComponentModel): """ An InventoryItem represents a serialized piece of hardware within a Device, such as a line card or power supply. diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index f5a95f15d..95c3c50db 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -75,7 +75,7 @@ class Manufacturer(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class DeviceType(PrimaryModel): """ A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as @@ -468,7 +468,7 @@ class Platform(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Device(PrimaryModel, ConfigContextModel): """ A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType, @@ -922,7 +922,7 @@ class Device(PrimaryModel, ConfigContextModel): # Virtual chassis # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class VirtualChassis(PrimaryModel): """ A collection of Devices which operate with a shared control plane (e.g. a switch stack). diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index a5e3149f8..03e77eea9 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -21,7 +21,7 @@ __all__ = ( # Power # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class PowerPanel(PrimaryModel): """ A distribution point for electrical power; e.g. a data center RPP. @@ -71,7 +71,7 @@ class PowerPanel(PrimaryModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class PowerFeed(PrimaryModel, PathEndpoint, CableTermination): """ An electrical circuit delivered from a PowerPanel. diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 0eb799dd4..c4416ca28 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -78,7 +78,7 @@ class RackRole(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Rack(PrimaryModel): """ Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face. @@ -467,7 +467,7 @@ class Rack(PrimaryModel): return int(allocated_draw_total / available_power_total * 100) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class RackReservation(PrimaryModel): """ One or more reserved units within a Rack. diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 1e5165088..7ab37567a 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -130,7 +130,7 @@ class SiteGroup(NestedGroupModel): # Sites # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Site(PrimaryModel): """ A Site represents a geographic location within a network; typically a building or campus. The optional facility diff --git a/netbox/extras/constants.py b/netbox/extras/constants.py index 190e68c36..64cc82f63 100644 --- a/netbox/extras/constants.py +++ b/netbox/extras/constants.py @@ -7,5 +7,6 @@ EXTRAS_FEATURES = [ 'custom_links', 'export_templates', 'job_results', + 'tags', 'webhooks' ] diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index 2490a0c5a..7df84c98b 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -77,7 +77,7 @@ class RIR(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Aggregate(PrimaryModel): """ An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize @@ -228,7 +228,7 @@ class Role(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Prefix(PrimaryModel): """ A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and @@ -477,7 +477,7 @@ class Prefix(PrimaryModel): return int(float(child_count) / prefix_size * 100) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class IPAddress(PrimaryModel): """ An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index 336f3a269..3fc9c8ec6 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -17,7 +17,7 @@ __all__ = ( ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Service(PrimaryModel): """ A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index 0801504fe..aa1e6b68c 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -100,7 +100,7 @@ class VLANGroup(OrganizationalModel): return None -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class VLAN(PrimaryModel): """ A VLAN is a distinct layer two forwarding domain identified by a 12-bit integer (1-4094). Each VLAN must be assigned diff --git a/netbox/ipam/models/vrfs.py b/netbox/ipam/models/vrfs.py index 9eb2c6ab6..c6c895697 100644 --- a/netbox/ipam/models/vrfs.py +++ b/netbox/ipam/models/vrfs.py @@ -13,7 +13,7 @@ __all__ = ( ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class VRF(PrimaryModel): """ A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing @@ -92,7 +92,7 @@ class VRF(PrimaryModel): return self.name -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class RouteTarget(PrimaryModel): """ A BGP extended community used to control the redistribution of routes among VRFs, as defined in RFC 4364. diff --git a/netbox/secrets/models.py b/netbox/secrets/models.py index 04a7ed58c..77c1e34e6 100644 --- a/netbox/secrets/models.py +++ b/netbox/secrets/models.py @@ -273,7 +273,7 @@ class SecretRole(OrganizationalModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Secret(PrimaryModel): """ A Secret stores an AES256-encrypted copy of sensitive data, such as passwords or secret keys. An irreversible diff --git a/netbox/tenancy/models.py b/netbox/tenancy/models.py index c9f55ec84..63f960b0e 100644 --- a/netbox/tenancy/models.py +++ b/netbox/tenancy/models.py @@ -57,7 +57,7 @@ class TenantGroup(NestedGroupModel): ) -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Tenant(PrimaryModel): """ A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 5aa43a869..0b679bac0 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -116,7 +116,7 @@ class ClusterGroup(OrganizationalModel): # Clusters # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class Cluster(PrimaryModel): """ A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices. @@ -199,7 +199,7 @@ class Cluster(PrimaryModel): # Virtual machines # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class VirtualMachine(PrimaryModel, ConfigContextModel): """ A virtual machine which runs inside a Cluster. @@ -380,7 +380,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): # Interfaces # -@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks') class VMInterface(PrimaryModel, BaseInterface): virtual_machine = models.ForeignKey( to='virtualization.VirtualMachine', From 5b4793a2d592638d91d7e82b9c3ecfed0d81382b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 May 2021 17:05:32 -0400 Subject: [PATCH 19/25] Closes #5121: Add content_type filters for tags --- netbox/extras/filtersets.py | 34 +++++++++++++++++++++++++- netbox/extras/forms.py | 10 ++++++-- netbox/extras/tests/test_filtersets.py | 16 ++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 92c0dc9a6..2a6ae088c 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -6,7 +6,7 @@ from django.db.models import Q from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet from tenancy.models import Tenant, TenantGroup -from utilities.filters import ContentTypeFilter +from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter from virtualization.models import Cluster, ClusterGroup from .choices import * from .models import * @@ -114,6 +114,12 @@ class TagFilterSet(ChangeLoggedModelFilterSet): method='search', label='Search', ) + content_type = MultiValueCharFilter( + method='_content_type' + ) + content_type_id = MultiValueNumberFilter( + method='_content_type_id' + ) class Meta: model = Tag @@ -127,6 +133,32 @@ class TagFilterSet(ChangeLoggedModelFilterSet): Q(slug__icontains=value) ) + def _content_type(self, queryset, name, values): + ct_filter = Q() + + # Compile list of app_label & model pairings + for value in values: + try: + app_label, model = value.lower().split('.') + ct_filter |= Q( + app_label=app_label, + model=model + ) + except ValueError: + pass + + # Get ContentType instances + content_types = ContentType.objects.filter(ct_filter) + + return queryset.filter(extras_taggeditem_items__content_type__in=content_types).distinct() + + def _content_type_id(self, queryset, name, values): + + # Get ContentType instances + content_types = ContentType.objects.filter(pk__in=values) + + return queryset.filter(extras_taggeditem_items__content_type__in=content_types).distinct() + class ConfigContextFilterSet(ChangeLoggedModelFilterSet): q = django_filters.CharFilter( diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 977ad9d68..ab1c5aded 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -8,12 +8,13 @@ from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGrou from tenancy.models import Tenant, TenantGroup from utilities.forms import ( add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, - CommentField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2, - BOOLEAN_WITH_BLANK_CHOICES, + CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, + JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES, ) from virtualization.models import Cluster, ClusterGroup from .choices import * from .models import ConfigContext, CustomField, ImageAttachment, JournalEntry, ObjectChange, Tag +from .utils import FeatureQuery # @@ -180,6 +181,11 @@ class TagFilterForm(BootstrapMixin, forms.Form): required=False, label=_('Search') ) + content_type_id = ContentTypeMultipleChoiceField( + queryset=ContentType.objects.filter(FeatureQuery('tags').get_query()), + required=False, + label=_('Tagged object type') + ) class TagBulkEditForm(BootstrapMixin, BulkEditForm): diff --git a/netbox/extras/tests/test_filtersets.py b/netbox/extras/tests/test_filtersets.py index eb08f5930..656c3efdc 100644 --- a/netbox/extras/tests/test_filtersets.py +++ b/netbox/extras/tests/test_filtersets.py @@ -5,6 +5,7 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.test import TestCase +from circuits.models import Provider from dcim.models import DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup from extras.choices import JournalEntryKindChoices, ObjectChangeActionChoices from extras.filtersets import * @@ -537,6 +538,13 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): ) Tag.objects.bulk_create(tags) + # Apply some tags so we can filter by content type + site = Site.objects.create(name='Site 1', slug='site-1') + provider = Provider.objects.create(name='Provider 1', slug='provider-1') + + site.tags.set(tags[0]) + provider.tags.set(tags[1]) + def test_name(self): params = {'name': ['Tag 1', 'Tag 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -549,6 +557,14 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'color': ['ff0000', '00ff00']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_content_type(self): + params = {'content_type': ['dcim.site', 'circuits.provider']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + site_ct = ContentType.objects.get_for_model(Site).pk + provider_ct = ContentType.objects.get_for_model(Provider).pk + params = {'content_type_id': [site_ct, provider_ct]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class ObjectChangeTestCase(TestCase, BaseFilterSetTests): queryset = ObjectChange.objects.all() From a7371c048b4614a9a7e0185f41f3e850507d4da1 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 May 2021 17:25:37 -0400 Subject: [PATCH 20/25] Changelog for #5121 --- docs/release-notes/version-2.11.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 451f07a99..48f675957 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -4,6 +4,7 @@ ### Enhancements +* [#5121](https://github.com/netbox-community/netbox/issues/5121) - Add content type filters for tags * [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses * [#6400](https://github.com/netbox-community/netbox/issues/6400) - Add cyan color choice for plugin buttons * [#6441](https://github.com/netbox-community/netbox/issues/6441) - Improve UI paginator to optimize page object count From 78b0e507421845637c8b37f9598f9b4748705221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20R=C3=B8dvand?= Date: Sat, 22 May 2021 14:27:18 +0200 Subject: [PATCH 21/25] Closes #6358: Add search to VLAN group overview. --- netbox/ipam/filtersets.py | 13 +++++++++++++ netbox/ipam/forms.py | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 71df90bb7..d618c8eab 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -536,6 +536,10 @@ class IPAddressFilterSet(PrimaryModelFilterSet, TenancyFilterSet): class VLANGroupFilterSet(OrganizationalModelFilterSet): + q = django_filters.CharFilter( + method='search', + label='Search', + ) scope_type = ContentTypeFilter() region = django_filters.NumberFilter( method='filter_scope' @@ -563,6 +567,15 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet): model = VLANGroup fields = ['id', 'name', 'slug', 'description', 'scope_id'] + def search(self, queryset, name, value): + if not value.strip(): + return queryset + qs_filter = ( + Q(name__icontains=value) | + Q(description__icontains=value) + ) + return queryset.filter(qs_filter) + def filter_scope(self, queryset, name, value): return queryset.filter( scope_type=ContentType.objects.get(model=name), diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 6a3753859..1b38b63f4 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -1270,6 +1270,10 @@ class VLANGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): class VLANGroupFilterForm(BootstrapMixin, forms.Form): + q = forms.CharField( + required=False, + label=_('Search') + ) region = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, From 44c62f8f440da232c871f8a9ae9666e5ffd3674b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 25 May 2021 11:16:06 -0400 Subject: [PATCH 22/25] Release notes for #6358 --- docs/release-notes/version-2.11.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index 48f675957..dab1d6227 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -5,6 +5,7 @@ ### Enhancements * [#5121](https://github.com/netbox-community/netbox/issues/5121) - Add content type filters for tags +* [#6358](https://github.com/netbox-community/netbox/issues/6358) - Add search field for VLAN groups * [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses * [#6400](https://github.com/netbox-community/netbox/issues/6400) - Add cyan color choice for plugin buttons * [#6441](https://github.com/netbox-community/netbox/issues/6441) - Improve UI paginator to optimize page object count From bfb91fcf10f163cd9e58b881572c2e9e3f86dbb5 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 25 May 2021 11:26:18 -0400 Subject: [PATCH 23/25] Closes #6422: Enable filtering users by group under admin UI --- docs/release-notes/version-2.11.md | 1 + netbox/users/admin.py | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index dab1d6227..e7c0ab304 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -8,6 +8,7 @@ * [#6358](https://github.com/netbox-community/netbox/issues/6358) - Add search field for VLAN groups * [#6393](https://github.com/netbox-community/netbox/issues/6393) - Add `description` filter for IP addresses * [#6400](https://github.com/netbox-community/netbox/issues/6400) - Add cyan color choice for plugin buttons +* [#6422](https://github.com/netbox-community/netbox/issues/6422) - Enable filtering users by group under admin UI * [#6441](https://github.com/netbox-community/netbox/issues/6441) - Improve UI paginator to optimize page object count ### Bug Fixes diff --git a/netbox/users/admin.py b/netbox/users/admin.py index 5eff6ec22..8926203df 100644 --- a/netbox/users/admin.py +++ b/netbox/users/admin.py @@ -89,6 +89,7 @@ class UserAdmin(UserAdmin_): ('Important dates', {'fields': ('last_login', 'date_joined')}), ) filter_horizontal = ('groups',) + list_filter = ('is_active', 'is_staff', 'is_superuser', 'groups__name') def get_inlines(self, request, obj): if obj is not None: From b3423e1722deee0d68abd35949e0ef4023dba81e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 25 May 2021 11:38:43 -0400 Subject: [PATCH 24/25] Release v2.11.4 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/version-2.11.md | 2 +- netbox/netbox/settings.py | 2 +- netbox/utilities/tables.py | 14 -------------- requirements.txt | 6 +++--- 6 files changed, 7 insertions(+), 21 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index adbfba419..9af9647f0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -17,7 +17,7 @@ body: What version of NetBox are you currently running? (If you don't have access to the most recent NetBox release, consider testing on our [demo instance](https://demo.netbox.dev/) before opening a bug report to see if your issue has already been addressed.) - placeholder: v2.11.3 + placeholder: v2.11.4 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 9181f7ce4..b6e53491e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.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: v2.11.3 + placeholder: v2.11.4 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md index e7c0ab304..4a0fcef8f 100644 --- a/docs/release-notes/version-2.11.md +++ b/docs/release-notes/version-2.11.md @@ -1,6 +1,6 @@ # NetBox v2.11 -## v2.11.4 (FUTURE) +## v2.11.4 (2021-05-25) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index f2e6f6e72..4f694c388 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -16,7 +16,7 @@ from django.core.validators import URLValidator # Environment setup # -VERSION = '2.11.4-dev' +VERSION = '2.11.4' # Hostname HOSTNAME = platform.node() diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index 78e1f6d53..86660c2e5 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -5,7 +5,6 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist from django.db.models.fields.related import RelatedField from django.urls import reverse -from django.utils.html import strip_tags from django.utils.safestring import mark_safe from django_tables2 import RequestConfig from django_tables2.data import TableQuerysetData @@ -15,19 +14,6 @@ from extras.models import CustomField from .paginator import EnhancedPaginator, get_paginate_count -def stripped_value(self, **kwargs): - """ - Replaces TemplateColumn's value() method to both strip HTML tags and remove any leading/trailing whitespace. - """ - html = super(tables.TemplateColumn, self).value(**kwargs) - return strip_tags(html).strip() if isinstance(html, str) else html - - -# TODO: We're monkey-patching TemplateColumn here to strip leading/trailing whitespace. This will no longer -# be necessary under django-tables2 v2.3.5+. (See #5926) -tables.TemplateColumn.value = stripped_value - - class BaseTable(tables.Table): """ Default table for object lists diff --git a/requirements.txt b/requirements.txt index f7ae8178d..904563d05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==3.2.2 +Django==3.2.3 django-cacheops==6.0 django-cors-headers==3.7.0 django-debug-toolbar==3.2.1 @@ -7,13 +7,13 @@ django-mptt==0.12.0 django-pglocks==1.0.4 django-prometheus==2.1.0 django-rq==2.4.1 -django-tables2==2.3.4 +django-tables2==2.4.0 django-taggit==1.4.0 django-timezone-field==4.1.2 djangorestframework==3.12.4 drf-yasg[validation]==1.20.0 gunicorn==20.1.0 -Jinja2==2.11.3 +Jinja2==3.0.1 Markdown==3.3.4 netaddr==0.8.0 Pillow==8.2.0 From 9046f59b9feacc3971ee5698c028a59329f59798 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 25 May 2021 12:12:08 -0400 Subject: [PATCH 25/25] PRVB --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 4f694c388..2d3f8570d 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -16,7 +16,7 @@ from django.core.validators import URLValidator # Environment setup # -VERSION = '2.11.4' +VERSION = '2.11.5-dev' # Hostname HOSTNAME = platform.node()