From 383918d83ba039e0e1217afa353312d972c181c3 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 20 Jul 2022 11:15:02 -0400 Subject: [PATCH 01/33] PRVB --- docs/release-notes/version-3.2.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 35e9b9a22..c36344912 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -1,5 +1,9 @@ # NetBox v3.2 +## v3.2.8 (FUTURE) + +--- + ## v3.2.7 (2022-07-20) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 094771581..c7e49c1be 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -29,7 +29,7 @@ django.utils.encoding.force_text = force_str # Environment setup # -VERSION = '3.2.7' +VERSION = '3.2.8-dev' # Hostname HOSTNAME = platform.node() From 451a0067c70f13741cb52e5b9ee3dad5cb1d9d58 Mon Sep 17 00:00:00 2001 From: Gabor SOMOGYVARI Date: Fri, 22 Jul 2022 10:42:20 +0200 Subject: [PATCH 02/33] Closes #9825: Add Contacts to VM table view --- netbox/virtualization/tables/virtualmachines.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py index 8cff96227..d2be07876 100644 --- a/netbox/virtualization/tables/virtualmachines.py +++ b/netbox/virtualization/tables/virtualmachines.py @@ -48,6 +48,9 @@ class VirtualMachineTable(TenancyColumnsMixin, NetBoxTable): order_by=('primary_ip4', 'primary_ip6'), verbose_name='IP Address' ) + contacts = columns.ManyToManyColumn( + linkify_item=True + ) tags = columns.TagColumn( url_name='virtualization:virtualmachine_list' ) @@ -56,7 +59,7 @@ class VirtualMachineTable(TenancyColumnsMixin, NetBoxTable): model = VirtualMachine fields = ( 'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'tenant_group', 'platform', 'vcpus', 'memory', 'disk', - 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'tags', 'created', 'last_updated', + 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'contacts', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', From bbf4b906e4083dbeadbaa802af5aac18e1896ba3 Mon Sep 17 00:00:00 2001 From: sleepinggenius2 Date: Tue, 26 Jul 2022 17:16:03 -0400 Subject: [PATCH 03/33] Adds patterned_fields support for bulk components --- netbox/dcim/views.py | 1 + netbox/netbox/views/generic/bulk_views.py | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index ec3e9152e..3bec02f5c 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -2707,6 +2707,7 @@ class DeviceBulkAddModuleBayView(generic.BulkComponentCreateView): filterset = filtersets.DeviceFilterSet table = tables.DeviceTable default_return_url = 'dcim:device_list' + patterned_fields = ('name', 'label', 'position') class DeviceBulkAddDeviceBayView(generic.BulkComponentCreateView): diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 5bdf5cbc9..7e07c57d0 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -795,6 +795,7 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): model_form = None filterset = None table = None + patterned_fields = ('name', 'label') def get_required_permission(self): return f'dcim.add_{self.queryset.model._meta.model_name}' @@ -830,16 +831,16 @@ class BulkComponentCreateView(GetReturnURLMixin, BaseMultiObjectView): for obj in data['pk']: - names = data['name_pattern'] - labels = data['label_pattern'] if 'label_pattern' in data else None - for i, name in enumerate(names): - label = labels[i] if labels else None - + pattern_count = len(data[f'{self.patterned_fields[0]}_pattern']) + for i in range(pattern_count): component_data = { - self.parent_field: obj.pk, - 'name': name, - 'label': label + self.parent_field: obj.pk } + + for field_name in self.patterned_fields: + if data.get(f'{field_name}_pattern'): + component_data[field_name] = data[f'{field_name}_pattern'][i] + component_data.update(data) component_form = self.model_form(component_data) if component_form.is_valid(): From 6cee12b1530597bf308ba39c661e456bd1802835 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 27 Jul 2022 15:40:25 -0400 Subject: [PATCH 04/33] Fix formatting of webhook conditions form field --- netbox/extras/forms/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/extras/forms/models.py b/netbox/extras/forms/models.py index 112911f42..82575de21 100644 --- a/netbox/extras/forms/models.py +++ b/netbox/extras/forms/models.py @@ -133,6 +133,7 @@ class WebhookForm(BootstrapMixin, forms.ModelForm): 'http_method': StaticSelect(), 'additional_headers': forms.Textarea(attrs={'class': 'font-monospace'}), 'body_template': forms.Textarea(attrs={'class': 'font-monospace'}), + 'conditions': forms.Textarea(attrs={'class': 'font-monospace'}), } From 498b655cb726b5bb1be442739923ae3746a8da41 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 27 Jul 2022 16:50:31 -0400 Subject: [PATCH 05/33] Changelog and cleanup for #9825 --- docs/release-notes/version-3.2.md | 4 ++++ netbox/virtualization/tables/virtualmachines.py | 11 ++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index c36344912..14d73d1eb 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -2,6 +2,10 @@ ## v3.2.8 (FUTURE) +### Enhancements + +* [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table + --- ## v3.2.7 (2022-07-20) diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py index d2be07876..cace51ccc 100644 --- a/netbox/virtualization/tables/virtualmachines.py +++ b/netbox/virtualization/tables/virtualmachines.py @@ -58,8 +58,9 @@ class VirtualMachineTable(TenancyColumnsMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = VirtualMachine fields = ( - 'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'tenant_group', 'platform', 'vcpus', 'memory', 'disk', - 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'contacts', 'tags', 'created', 'last_updated', + 'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'tenant_group', 'platform', 'vcpus', 'memory', + 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'contacts', 'tags', 'created', + 'last_updated', ) default_columns = ( 'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', @@ -80,9 +81,6 @@ class VMInterfaceTable(BaseInterfaceTable): vrf = tables.Column( linkify=True ) - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='virtualization:vminterface_list' ) @@ -91,8 +89,7 @@ class VMInterfaceTable(BaseInterfaceTable): model = VMInterface fields = ( 'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'tags', - 'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'contacts', 'created', - 'last_updated', + 'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'created', 'last_updated', ) default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description') From 62d1510c55f4afb59b8fd0a09c38405b71a29899 Mon Sep 17 00:00:00 2001 From: atownson <52260120+atownson@users.noreply.github.com> Date: Thu, 28 Jul 2022 10:23:04 -0500 Subject: [PATCH 06/33] Closes netbox-community#9762 Added nat_outside to IPAddressTable class --- netbox/ipam/tables/ip.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index bec05eeff..087d0de73 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -369,6 +369,11 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable): orderable=False, verbose_name='NAT (Inside)' ) + nat_outside = tables.Column( + linkify=True, + orderable=False, + verbose_name='NAT (Outside)' + ) assigned = columns.BooleanColumn( accessor='assigned_object_id', linkify=True, @@ -381,7 +386,7 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = IPAddress fields = ( - 'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'assigned', 'dns_name', 'description', + 'pk', 'id', 'address', 'vrf', 'status', 'role', 'tenant', 'tenant_group', 'nat_inside', 'nat_outside', 'assigned', 'dns_name', 'description', 'tags', 'created', 'last_updated', ) default_columns = ( From 07620db027823fe55e49155f6adb4d2c80cace76 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 28 Jul 2022 12:45:27 -0400 Subject: [PATCH 07/33] Changelog for #9762 --- docs/release-notes/version-3.2.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 14d73d1eb..c0c99bb73 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -4,6 +4,7 @@ ### Enhancements +* [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table --- From 890efa5400b0325fc32ccbd4946a554a78566447 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Fri, 29 Jul 2022 11:55:26 -0500 Subject: [PATCH 08/33] Fixes #9062 - Add/edit {module} substitution to help text for component template name --- docs/release-notes/version-3.2.md | 1 + netbox/dcim/forms/object_create.py | 8 ++++++++ netbox/dcim/models/device_component_templates.py | 5 ++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index c0c99bb73..ce8552b0b 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -4,6 +4,7 @@ ### Enhancements +* [#9062](https://github.com/netbox-community/netbox/issues/9062) - Add/edit {module} substitution to help text for component template name * [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index 8c9ddab19..d2c941b34 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -64,6 +64,14 @@ class ModularComponentTemplateCreateForm(ComponentCreateForm): """ Creation form for component templates that can be assigned to either a DeviceType *or* a ModuleType. """ + name_pattern = ExpandableNameField( + label='Name', + help_text=""" + Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range + are not supported. Example: [ge,xe]-0/0/[0-9]. {module} is accepted as a substitution for + the module bay position. + """ + ) device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), required=False diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 92658d310..ac0738b3f 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -39,7 +39,10 @@ class ComponentTemplateModel(WebhooksMixin, ChangeLoggedModel): related_name='%(class)ss' ) name = models.CharField( - max_length=64 + max_length=64, + help_text=""" + {module} is accepted as a substitution for the module bay position when attached to a module type. + """ ) _name = NaturalOrderingField( target_field='name', From 5ab03b7e926506a7ae949982bb252d62133fb7eb Mon Sep 17 00:00:00 2001 From: atownson <52260120+atownson@users.noreply.github.com> Date: Mon, 1 Aug 2022 08:01:18 -0500 Subject: [PATCH 09/33] Closes #9637: Add existing fields to the Rack Reservation user interface pages (#9870) * Closes netbox-community#9637 Added site_group to RackReservationFrom class fieldsets Added location to RackReservationTable class --- netbox/dcim/forms/filtersets.py | 2 +- netbox/dcim/forms/models.py | 2 +- netbox/dcim/tables/racks.py | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 38221b371..9a9f91ffb 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -287,7 +287,7 @@ class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): fieldsets = ( (None, ('q', 'tag')), ('User', ('user_id',)), - ('Rack', ('region_id', 'site_group_id', 'site_id', 'location_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id')), ('Tenant', ('tenant_group_id', 'tenant_id')), ) region_id = DynamicModelMultipleChoiceField( diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 043af751d..fb09b9871 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -321,7 +321,7 @@ class RackReservationForm(TenancyForm, NetBoxModelForm): ) fieldsets = ( - ('Reservation', ('region', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')), + ('Reservation', ('region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')), ('Tenancy', ('tenant_group', 'tenant')), ) diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index 5412e2297..d83f25a5f 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -109,6 +109,10 @@ class RackReservationTable(TenancyColumnsMixin, NetBoxTable): accessor=Accessor('rack__site'), linkify=True ) + location = tables.Column( + accessor=Accessor('rack__location'), + linkify=True + ) rack = tables.Column( linkify=True ) @@ -123,7 +127,7 @@ class RackReservationTable(TenancyColumnsMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = RackReservation fields = ( - 'pk', 'id', 'reservation', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'tenant_group', 'description', 'tags', + 'pk', 'id', 'reservation', 'site', 'location', 'rack', 'unit_list', 'user', 'created', 'tenant', 'tenant_group', 'description', 'tags', 'actions', 'created', 'last_updated', ) default_columns = ('pk', 'reservation', 'site', 'rack', 'unit_list', 'user', 'description') From 728ad5162466fa805a428553a621bd78134ba61b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 09:12:15 -0400 Subject: [PATCH 10/33] Changelog & cleanup for #9637 --- docs/release-notes/version-3.2.md | 1 + netbox/dcim/forms/filtersets.py | 35 +++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index ce8552b0b..57a6dffe1 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -5,6 +5,7 @@ ### Enhancements * [#9062](https://github.com/netbox-community/netbox/issues/9062) - Add/edit {module} substitution to help text for component template name +* [#9637](https://github.com/netbox-community/netbox/issues/9637) - Add site group field to rack reservation form * [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 9a9f91ffb..12905aec9 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -287,7 +287,7 @@ class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): fieldsets = ( (None, ('q', 'tag')), ('User', ('user_id',)), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id')), + ('Rack', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')), ('Tenant', ('tenant_group_id', 'tenant_id')), ) region_id = DynamicModelMultipleChoiceField( @@ -295,25 +295,38 @@ class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): required=False, label=_('Region') ) - site_id = DynamicModelMultipleChoiceField( - queryset=Site.objects.all(), - required=False, - query_params={ - 'region_id': '$region_id' - }, - label=_('Site') - ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, label=_('Site group') ) - location_id = DynamicModelMultipleChoiceField( - queryset=Location.objects.prefetch_related('site'), + site_id = DynamicModelMultipleChoiceField( + queryset=Site.objects.all(), required=False, + query_params={ + 'region_id': '$region_id', + 'group_id': '$site_group_id', + }, + label=_('Site') + ) + location_id = DynamicModelMultipleChoiceField( + queryset=Location.objects.all(), + required=False, + query_params={ + 'site_id': '$site_id', + }, label=_('Location'), null_option='None' ) + rack_id = DynamicModelMultipleChoiceField( + queryset=Rack.objects.all(), + required=False, + query_params={ + 'site_id': '$site_id', + 'location_id': '$location_id', + }, + label=_('Rack') + ) user_id = DynamicModelMultipleChoiceField( queryset=User.objects.all(), required=False, From 84f2225f42f16ca64777172135eba2689db958f3 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 09:16:58 -0400 Subject: [PATCH 11/33] PEP8 cleanup --- netbox/dcim/tests/test_models.py | 4 ++-- netbox/extras/models/customfields.py | 2 +- netbox/extras/tests/test_customfields.py | 2 +- netbox/extras/tests/test_registry.py | 2 +- netbox/ipam/forms/models.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index 8566f969b..03438a441 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -194,14 +194,14 @@ class RackTestCase(TestCase): # Validate inventory (front face) rack1_inventory_front = self.rack.get_rack_units(face=DeviceFaceChoices.FACE_FRONT) self.assertEqual(rack1_inventory_front[-10]['device'], device1) - del(rack1_inventory_front[-10]) + del rack1_inventory_front[-10] for u in rack1_inventory_front: self.assertIsNone(u['device']) # Validate inventory (rear face) rack1_inventory_rear = self.rack.get_rack_units(face=DeviceFaceChoices.FACE_REAR) self.assertEqual(rack1_inventory_rear[-10]['device'], device1) - del(rack1_inventory_rear[-10]) + del rack1_inventory_rear[-10] for u in rack1_inventory_rear: self.assertIsNone(u['device']) diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 6a8c1dacf..b7d77e550 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -169,7 +169,7 @@ class CustomField(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel): model = ct.model_class() instances = model.objects.filter(**{f'custom_field_data__{self.name}__isnull': False}) for instance in instances: - del(instance.custom_field_data[self.name]) + del instance.custom_field_data[self.name] model.objects.bulk_update(instances, ['custom_field_data'], batch_size=100) def rename_object_data(self, old_name, new_name): diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 8dcb53b09..946999bc2 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -992,7 +992,7 @@ class CustomFieldModelTest(TestCase): with self.assertRaises(ValidationError): site.clean() - del(site.cf['bar']) + del site.cf['bar'] site.clean() def test_missing_required_field(self): diff --git a/netbox/extras/tests/test_registry.py b/netbox/extras/tests/test_registry.py index 53ba6584a..38a6b9f83 100644 --- a/netbox/extras/tests/test_registry.py +++ b/netbox/extras/tests/test_registry.py @@ -30,4 +30,4 @@ class RegistryTest(TestCase): reg['foo'] = 123 with self.assertRaises(TypeError): - del(reg['foo']) + del reg['foo'] diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index e86abc672..d3421f22b 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -848,7 +848,7 @@ class ServiceCreateForm(ServiceForm): # Fields which may be populated from a ServiceTemplate are not required for field in ('name', 'protocol', 'ports'): self.fields[field].required = False - del(self.fields[field].widget.attrs['required']) + del self.fields[field].widget.attrs['required'] def clean(self): if self.cleaned_data['service_template']: From 9fe5f09742b8d9ed0a0ddc8ea57a7ad21884726b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 09:32:52 -0400 Subject: [PATCH 12/33] Fixes #9891: Ensure consistent ordering for tags during object serialization --- docs/release-notes/version-3.2.md | 6 +++++- netbox/utilities/utils.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 57a6dffe1..ed08ad891 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -4,11 +4,15 @@ ### Enhancements -* [#9062](https://github.com/netbox-community/netbox/issues/9062) - Add/edit {module} substitution to help text for component template name +* [#9062](https://github.com/netbox-community/netbox/issues/9062) - Add/edit {module} substitution to help text for component template name * [#9637](https://github.com/netbox-community/netbox/issues/9637) - Add site group field to rack reservation form * [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table +### Bug Fixes + +* [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization + --- ## v3.2.7 (2022-07-20) diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index 2b939471c..da7cdde94 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -148,7 +148,7 @@ def serialize_object(obj, extra=None): # Include any tags. Check for tags cached on the instance; fall back to using the manager. if is_taggable(obj): tags = getattr(obj, '_tags', None) or obj.tags.all() - data['tags'] = [tag.name for tag in tags] + data['tags'] = sorted([tag.name for tag in tags]) # Append any extra data if extra is not None: From 8a075bcff9b3a75ae86cd21eeb3921108594802f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 09:47:18 -0400 Subject: [PATCH 13/33] Fixes #9884: Prevent querying assigned VRF on prefix object init --- docs/release-notes/version-3.2.md | 1 + netbox/ipam/models/ip.py | 2 +- netbox/ipam/signals.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index ed08ad891..caaf079b2 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -11,6 +11,7 @@ ### Bug Fixes +* [#9884](https://github.com/netbox-community/netbox/issues/9884) - Prevent querying assigned VRF on prefix object init * [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization --- diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index a3b8fb2c1..d1538953a 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -373,7 +373,7 @@ class Prefix(GetAvailablePrefixesMixin, NetBoxModel): # Cache the original prefix and VRF so we can check if they have changed on post_save self._prefix = self.prefix - self._vrf = self.vrf + self._vrf_id = self.vrf_id def __str__(self): return str(self.prefix) diff --git a/netbox/ipam/signals.py b/netbox/ipam/signals.py index 3e8b86050..8555f5e67 100644 --- a/netbox/ipam/signals.py +++ b/netbox/ipam/signals.py @@ -30,14 +30,14 @@ def update_children_depth(prefix): def handle_prefix_saved(instance, created, **kwargs): # Prefix has changed (or new instance has been created) - if created or instance.vrf != instance._vrf or instance.prefix != instance._prefix: + if created or instance.vrf_id != instance._vrf_id or instance.prefix != instance._prefix: update_parents_children(instance) update_children_depth(instance) # If this is not a new prefix, clean up parent/children of previous prefix if not created: - old_prefix = Prefix(vrf=instance._vrf, prefix=instance._prefix) + old_prefix = Prefix(vrf_id=instance._vrf_id, prefix=instance._prefix) update_parents_children(old_prefix) update_children_depth(old_prefix) From 1bbf5d214b3c58d97c0c600ca2cf4c929f588e5e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 10:23:18 -0400 Subject: [PATCH 14/33] Closes #9881: Increase granularity in utilization graph values --- docs/release-notes/version-3.2.md | 1 + netbox/utilities/templates/helpers/utilization_graph.html | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index caaf079b2..b366d7c8f 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -8,6 +8,7 @@ * [#9637](https://github.com/netbox-community/netbox/issues/9637) - Add site group field to rack reservation form * [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table +* [#9881](https://github.com/netbox-community/netbox/issues/9881) - Increase granularity in utilization graph values ### Bug Fixes diff --git a/netbox/utilities/templates/helpers/utilization_graph.html b/netbox/utilities/templates/helpers/utilization_graph.html index e6829befc..08e4e6f8a 100644 --- a/netbox/utilities/templates/helpers/utilization_graph.html +++ b/netbox/utilities/templates/helpers/utilization_graph.html @@ -12,10 +12,10 @@ class="progress-bar {{ bar_class }}" style="width: {{ utilization }}%;" > - {% if utilization >= 25 %}{{ utilization|floatformat:0 }}%{% endif %} + {% if utilization >= 25 %}{{ utilization|floatformat }}%{% endif %} {% if utilization < 25 %} - {{ utilization|floatformat:0 }}% + {{ utilization|floatformat }}% {% endif %} {% endif %} From 9646f88384360855fdc000156920eb3365d68d81 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 10:28:21 -0400 Subject: [PATCH 15/33] Fixes #9885: Fix child prefix counts when editing/deleting aggregates in bulk --- docs/release-notes/version-3.2.md | 1 + netbox/ipam/views.py | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index b366d7c8f..8a57f4644 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -13,6 +13,7 @@ ### Bug Fixes * [#9884](https://github.com/netbox-community/netbox/issues/9884) - Prevent querying assigned VRF on prefix object init +* [#9885](https://github.com/netbox-community/netbox/issues/9885) - Fix child prefix counts when editing/deleting aggregates in bulk * [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization --- diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 706670cad..9ae7cd4d7 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -333,14 +333,18 @@ class AggregateBulkImportView(generic.BulkImportView): class AggregateBulkEditView(generic.BulkEditView): - queryset = Aggregate.objects.prefetch_related('rir') + queryset = Aggregate.objects.annotate( + child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) + ) filterset = filtersets.AggregateFilterSet table = tables.AggregateTable form = forms.AggregateBulkEditForm class AggregateBulkDeleteView(generic.BulkDeleteView): - queryset = Aggregate.objects.prefetch_related('rir') + queryset = Aggregate.objects.annotate( + child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) + ) filterset = filtersets.AggregateFilterSet table = tables.AggregateTable From 3af989763e41fefdfbee1806c7e4c163e148db9b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 10:32:05 -0400 Subject: [PATCH 16/33] Closes #9883: Linkify location column in power panels table --- docs/release-notes/version-3.2.md | 1 + netbox/dcim/tables/power.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 8a57f4644..e4c7d9268 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -9,6 +9,7 @@ * [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table * [#9881](https://github.com/netbox-community/netbox/issues/9881) - Increase granularity in utilization graph values +* [#9883](https://github.com/netbox-community/netbox/issues/9883) - Linkify location column in power panels table ### Bug Fixes diff --git a/netbox/dcim/tables/power.py b/netbox/dcim/tables/power.py index 92c4bb0aa..6696d516a 100644 --- a/netbox/dcim/tables/power.py +++ b/netbox/dcim/tables/power.py @@ -21,6 +21,9 @@ class PowerPanelTable(NetBoxTable): site = tables.Column( linkify=True ) + location = tables.Column( + linkify=True + ) powerfeed_count = columns.LinkedCountColumn( viewname='dcim:powerfeed_list', url_params={'power_panel_id': 'pk'}, @@ -35,7 +38,9 @@ class PowerPanelTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = PowerPanel - fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'tags', 'created', 'last_updated',) + fields = ( + 'pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'tags', 'created', 'last_updated', + ) default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count') From efa449faff99205a351553770e7e5186b487b754 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 10:36:53 -0400 Subject: [PATCH 17/33] Closes #9882: Add manufacturer column to modules table --- docs/release-notes/version-3.2.md | 1 + netbox/dcim/tables/modules.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index e4c7d9268..64e229129 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -9,6 +9,7 @@ * [#9762](https://github.com/netbox-community/netbox/issues/9762) - Add `nat_outside` column to the IPAddress table * [#9825](https://github.com/netbox-community/netbox/issues/9825) - Add contacts column to virtual machines table * [#9881](https://github.com/netbox-community/netbox/issues/9881) - Increase granularity in utilization graph values +* [#9882](https://github.com/netbox-community/netbox/issues/9882) - Add manufacturer column to modules table * [#9883](https://github.com/netbox-community/netbox/issues/9883) - Linkify location column in power panels table ### Bug Fixes diff --git a/netbox/dcim/tables/modules.py b/netbox/dcim/tables/modules.py index 5b009e42e..e40d7bd80 100644 --- a/netbox/dcim/tables/modules.py +++ b/netbox/dcim/tables/modules.py @@ -14,6 +14,9 @@ class ModuleTypeTable(NetBoxTable): linkify=True, verbose_name='Module Type' ) + manufacturer = tables.Column( + linkify=True + ) instance_count = columns.LinkedCountColumn( viewname='dcim:module_list', url_params={'module_type_id': 'pk'}, @@ -41,6 +44,10 @@ class ModuleTable(NetBoxTable): module_bay = tables.Column( linkify=True ) + manufacturer = tables.Column( + accessor=tables.A('module_type__manufacturer'), + linkify=True + ) module_type = tables.Column( linkify=True ) @@ -52,8 +59,9 @@ class ModuleTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Module fields = ( - 'pk', 'id', 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'comments', 'tags', + 'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', 'comments', + 'tags', ) default_columns = ( - 'pk', 'id', 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', + 'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'serial', 'asset_tag', ) From 984d15d7fb637f7e5a73fa2138637d11a76f524b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 11:39:07 -0400 Subject: [PATCH 18/33] Closes #9893: Move YAML serialization logic for component templates under the individual models --- .../dcim/models/device_component_templates.py | 79 ++++++++ netbox/dcim/models/devices.py | 179 ++++-------------- 2 files changed, 112 insertions(+), 146 deletions(-) diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index ac0738b3f..f596cf28b 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -160,6 +160,14 @@ class ConsolePortTemplate(ModularComponentTemplateModel): **kwargs ) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'label': self.label, + 'description': self.description, + } + class ConsoleServerPortTemplate(ModularComponentTemplateModel): """ @@ -188,6 +196,14 @@ class ConsoleServerPortTemplate(ModularComponentTemplateModel): **kwargs ) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'label': self.label, + 'description': self.description, + } + class PowerPortTemplate(ModularComponentTemplateModel): """ @@ -239,6 +255,16 @@ class PowerPortTemplate(ModularComponentTemplateModel): 'allocated_draw': f"Allocated draw cannot exceed the maximum draw ({self.maximum_draw}W)." }) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'maximum_draw': self.maximum_draw, + 'allocated_draw': self.allocated_draw, + 'label': self.label, + 'description': self.description, + } + class PowerOutletTemplate(ModularComponentTemplateModel): """ @@ -301,6 +327,16 @@ class PowerOutletTemplate(ModularComponentTemplateModel): **kwargs ) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'power_port': self.power_port.name if self.power_port else None, + 'feed_leg': self.feed_leg, + 'label': self.label, + 'description': self.description, + } + class InterfaceTemplate(ModularComponentTemplateModel): """ @@ -340,6 +376,15 @@ class InterfaceTemplate(ModularComponentTemplateModel): **kwargs ) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'mgmt_only': self.mgmt_only, + 'label': self.label, + 'description': self.description, + } + class FrontPortTemplate(ModularComponentTemplateModel): """ @@ -413,6 +458,16 @@ class FrontPortTemplate(ModularComponentTemplateModel): **kwargs ) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'rear_port': self.rear_port.name, + 'rear_port_position': self.rear_port_position, + 'label': self.label, + 'description': self.description, + } + class RearPortTemplate(ModularComponentTemplateModel): """ @@ -452,6 +507,15 @@ class RearPortTemplate(ModularComponentTemplateModel): **kwargs ) + def to_yaml(self): + return { + 'name': self.name, + 'type': self.type, + 'positions': self.positions, + 'label': self.label, + 'description': self.description, + } + class ModuleBayTemplate(ComponentTemplateModel): """ @@ -477,6 +541,14 @@ class ModuleBayTemplate(ComponentTemplateModel): position=self.position ) + def to_yaml(self): + return { + 'name': self.name, + 'label': self.label, + 'position': self.position, + 'description': self.description, + } + class DeviceBayTemplate(ComponentTemplateModel): """ @@ -501,6 +573,13 @@ class DeviceBayTemplate(ComponentTemplateModel): f"Subdevice role of device type ({self.device_type}) must be set to \"parent\" to allow device bays." ) + def to_yaml(self): + return { + 'name': self.name, + 'label': self.label, + 'description': self.description, + } + class InventoryItemTemplate(MPTTModel, ComponentTemplateModel): """ diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index e88af2d05..91227f1cf 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - import yaml from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError @@ -161,115 +159,54 @@ class DeviceType(NetBoxModel): return reverse('dcim:devicetype', args=[self.pk]) def to_yaml(self): - data = OrderedDict(( - ('manufacturer', self.manufacturer.name), - ('model', self.model), - ('slug', self.slug), - ('part_number', self.part_number), - ('u_height', self.u_height), - ('is_full_depth', self.is_full_depth), - ('subdevice_role', self.subdevice_role), - ('airflow', self.airflow), - ('comments', self.comments), - )) + data = { + 'manufacturer': self.manufacturer.name, + 'model': self.model, + 'slug': self.slug, + 'part_number': self.part_number, + 'u_height': self.u_height, + 'is_full_depth': self.is_full_depth, + 'subdevice_role': self.subdevice_role, + 'airflow': self.airflow, + 'comments': self.comments, + } # Component templates if self.consoleporttemplates.exists(): data['console-ports'] = [ - { - 'name': c.name, - 'type': c.type, - 'label': c.label, - 'description': c.description, - } - for c in self.consoleporttemplates.all() + c.to_yaml() for c in self.consoleporttemplates.all() ] if self.consoleserverporttemplates.exists(): data['console-server-ports'] = [ - { - 'name': c.name, - 'type': c.type, - 'label': c.label, - 'description': c.description, - } - for c in self.consoleserverporttemplates.all() + c.to_yaml() for c in self.consoleserverporttemplates.all() ] if self.powerporttemplates.exists(): data['power-ports'] = [ - { - 'name': c.name, - '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() + c.to_yaml() for c in self.powerporttemplates.all() ] if self.poweroutlettemplates.exists(): data['power-outlets'] = [ - { - 'name': c.name, - '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() + c.to_yaml() for c in self.poweroutlettemplates.all() ] if self.interfacetemplates.exists(): data['interfaces'] = [ - { - 'name': c.name, - 'type': c.type, - 'mgmt_only': c.mgmt_only, - 'label': c.label, - 'description': c.description, - } - for c in self.interfacetemplates.all() + c.to_yaml() for c in self.interfacetemplates.all() ] if self.frontporttemplates.exists(): data['front-ports'] = [ - { - 'name': c.name, - '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() + c.to_yaml() for c in self.frontporttemplates.all() ] if self.rearporttemplates.exists(): data['rear-ports'] = [ - { - 'name': c.name, - 'type': c.type, - 'positions': c.positions, - 'label': c.label, - 'description': c.description, - } - for c in self.rearporttemplates.all() + c.to_yaml() for c in self.rearporttemplates.all() ] if self.modulebaytemplates.exists(): data['module-bays'] = [ - { - 'name': c.name, - 'label': c.label, - 'position': c.position, - 'description': c.description, - } - for c in self.modulebaytemplates.all() + c.to_yaml() for c in self.modulebaytemplates.all() ] if self.devicebaytemplates.exists(): data['device-bays'] = [ - { - 'name': c.name, - 'label': c.label, - 'description': c.description, - } - for c in self.devicebaytemplates.all() + c.to_yaml() for c in self.devicebaytemplates.all() ] return yaml.dump(dict(data), sort_keys=False) @@ -395,91 +332,41 @@ class ModuleType(NetBoxModel): return reverse('dcim:moduletype', args=[self.pk]) def to_yaml(self): - data = OrderedDict(( - ('manufacturer', self.manufacturer.name), - ('model', self.model), - ('part_number', self.part_number), - ('comments', self.comments), - )) + data = { + 'manufacturer': self.manufacturer.name, + 'model': self.model, + 'part_number': self.part_number, + 'comments': self.comments, + } # Component templates if self.consoleporttemplates.exists(): data['console-ports'] = [ - { - 'name': c.name, - 'type': c.type, - 'label': c.label, - 'description': c.description, - } - for c in self.consoleporttemplates.all() + c.to_yaml() for c in self.consoleporttemplates.all() ] if self.consoleserverporttemplates.exists(): data['console-server-ports'] = [ - { - 'name': c.name, - 'type': c.type, - 'label': c.label, - 'description': c.description, - } - for c in self.consoleserverporttemplates.all() + c.to_yaml() for c in self.consoleserverporttemplates.all() ] if self.powerporttemplates.exists(): data['power-ports'] = [ - { - 'name': c.name, - '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() + c.to_yaml() for c in self.powerporttemplates.all() ] if self.poweroutlettemplates.exists(): data['power-outlets'] = [ - { - 'name': c.name, - '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() + c.to_yaml() for c in self.poweroutlettemplates.all() ] if self.interfacetemplates.exists(): data['interfaces'] = [ - { - 'name': c.name, - 'type': c.type, - 'mgmt_only': c.mgmt_only, - 'label': c.label, - 'description': c.description, - } - for c in self.interfacetemplates.all() + c.to_yaml() for c in self.interfacetemplates.all() ] if self.frontporttemplates.exists(): data['front-ports'] = [ - { - 'name': c.name, - '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() + c.to_yaml() for c in self.frontporttemplates.all() ] if self.rearporttemplates.exists(): data['rear-ports'] = [ - { - 'name': c.name, - 'type': c.type, - 'positions': c.positions, - 'label': c.label, - 'description': c.description, - } - for c in self.rearporttemplates.all() + c.to_yaml() for c in self.rearporttemplates.all() ] return yaml.dump(dict(data), sort_keys=False) From d4d73674fced54570db111574dae0a080f89ce77 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 11:54:39 -0400 Subject: [PATCH 19/33] Fixes #9871: Fix utilization graph value alignments --- docs/release-notes/version-3.2.md | 1 + .../templates/helpers/utilization_graph.html | 34 ++++++++----------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 64e229129..5e538890c 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -14,6 +14,7 @@ ### Bug Fixes +* [#9871](https://github.com/netbox-community/netbox/issues/9871) - Fix utilization graph value alignments * [#9884](https://github.com/netbox-community/netbox/issues/9884) - Prevent querying assigned VRF on prefix object init * [#9885](https://github.com/netbox-community/netbox/issues/9885) - Fix child prefix counts when editing/deleting aggregates in bulk * [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization diff --git a/netbox/utilities/templates/helpers/utilization_graph.html b/netbox/utilities/templates/helpers/utilization_graph.html index 08e4e6f8a..2a1856abf 100644 --- a/netbox/utilities/templates/helpers/utilization_graph.html +++ b/netbox/utilities/templates/helpers/utilization_graph.html @@ -1,21 +1,15 @@ -{% if utilization == 0 %} -
- {{ utilization }}% +
+
+ {% if utilization >= 25 %}{{ utilization|floatformat:1 }}%{% endif %}
-{% else %} -
-
- {% if utilization >= 25 %}{{ utilization|floatformat }}%{% endif %} -
- {% if utilization < 25 %} - {{ utilization|floatformat }}% - {% endif %} -
-{% endif %} + {% if utilization < 25 %} + {{ utilization|floatformat:1 }}% + {% endif %} +
From ff3fcb8134c229033453afadcc1e315c52f3fd20 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 Aug 2022 12:38:12 -0400 Subject: [PATCH 20/33] #9871: Tweak display of utilization graph value --- netbox/utilities/templates/helpers/utilization_graph.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/utilities/templates/helpers/utilization_graph.html b/netbox/utilities/templates/helpers/utilization_graph.html index 2a1856abf..967ac8a87 100644 --- a/netbox/utilities/templates/helpers/utilization_graph.html +++ b/netbox/utilities/templates/helpers/utilization_graph.html @@ -7,9 +7,9 @@ class="progress-bar {{ bar_class }}" style="width: {{ utilization }}%;" > - {% if utilization >= 25 %}{{ utilization|floatformat:1 }}%{% endif %} + {% if utilization >= 35 %}{{ utilization|floatformat:1 }}%{% endif %}
- {% if utilization < 25 %} + {% if utilization < 35 %} {{ utilization|floatformat:1 }}% {% endif %} From c6e25f068d7aeb77f18add68ccab9ced6ff68d9a Mon Sep 17 00:00:00 2001 From: Jason Lavoie Date: Tue, 2 Aug 2022 20:39:56 -0400 Subject: [PATCH 21/33] import/export color field on front- and rear-ports for module-types and device-types Closes: #9906 - Adds `color` field to front and rearport template import forms - Adds `color` field to `to_yaml` export for front and rearport templates --- netbox/dcim/forms/object_import.py | 4 ++-- netbox/dcim/models/device_component_templates.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/forms/object_import.py b/netbox/dcim/forms/object_import.py index afbcd6543..606333e83 100644 --- a/netbox/dcim/forms/object_import.py +++ b/netbox/dcim/forms/object_import.py @@ -146,7 +146,7 @@ class FrontPortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = FrontPortTemplate fields = [ - 'device_type', 'module_type', 'name', 'type', 'rear_port', 'rear_port_position', 'label', 'description', + 'device_type', 'module_type', 'name', 'type', 'color', 'rear_port', 'rear_port_position', 'label', 'description', ] @@ -158,7 +158,7 @@ class RearPortTemplateImportForm(ComponentTemplateImportForm): class Meta: model = RearPortTemplate fields = [ - 'device_type', 'module_type', 'name', 'type', 'positions', 'label', 'description', + 'device_type', 'module_type', 'name', 'type', 'color', 'positions', 'label', 'description', ] diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index f596cf28b..74252e480 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -462,6 +462,7 @@ class FrontPortTemplate(ModularComponentTemplateModel): return { 'name': self.name, 'type': self.type, + 'color': self.color, 'rear_port': self.rear_port.name, 'rear_port_position': self.rear_port_position, 'label': self.label, @@ -511,6 +512,7 @@ class RearPortTemplate(ModularComponentTemplateModel): return { 'name': self.name, 'type': self.type, + 'color': self.color, 'positions': self.positions, 'label': self.label, 'description': self.description, From a2e84dd279549c7284184ff98da4a1ce23c646c9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 3 Aug 2022 15:22:51 -0400 Subject: [PATCH 22/33] Changelog for #9827, #9906 --- docs/release-notes/version-3.2.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 5e538890c..1c7eb79e8 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -11,9 +11,11 @@ * [#9881](https://github.com/netbox-community/netbox/issues/9881) - Increase granularity in utilization graph values * [#9882](https://github.com/netbox-community/netbox/issues/9882) - Add manufacturer column to modules table * [#9883](https://github.com/netbox-community/netbox/issues/9883) - Linkify location column in power panels table +* [#9906](https://github.com/netbox-community/netbox/issues/9906) - Include `color` attribute in front & rear port YAML import/export ### Bug Fixes +* [#9827](https://github.com/netbox-community/netbox/issues/9827) - Fix assignment of module bay position during bulk creation * [#9871](https://github.com/netbox-community/netbox/issues/9871) - Fix utilization graph value alignments * [#9884](https://github.com/netbox-community/netbox/issues/9884) - Prevent querying assigned VRF on prefix object init * [#9885](https://github.com/netbox-community/netbox/issues/9885) - Fix child prefix counts when editing/deleting aggregates in bulk From f874e9932d06653e330da65d93c2fe0fd7720968 Mon Sep 17 00:00:00 2001 From: Osamu-kj Date: Thu, 4 Aug 2022 18:52:25 +0200 Subject: [PATCH 23/33] Added HTML Sanitization to the custom fields --- netbox/netbox/tables/columns.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index 7da241566..7774a495f 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from glob import escape from typing import Optional import django_tables2 as tables @@ -433,21 +434,21 @@ class CustomFieldColumn(tables.Column): def render(self, value): if self.customfield.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value is True: - return mark_safe('') + return escape('') if self.customfield.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value is False: - return mark_safe('') + return escape('') if self.customfield.type == CustomFieldTypeChoices.TYPE_URL: - return mark_safe(f'{value}') + return escape(f'{value}') if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTISELECT: return ', '.join(v for v in value) if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: - return mark_safe(', '.join([ + return escape(', '.join([ self._likify_item(obj) for obj in self.customfield.deserialize(value) ])) if value is not None: obj = self.customfield.deserialize(value) - return mark_safe(self._likify_item(obj)) - return self.default + return escape(self._likify_item(obj)) + return escape(self.default) def value(self, value): if isinstance(value, list): From db38ed4f19e5943bf60434f4cfc08d232dbb5e6e Mon Sep 17 00:00:00 2001 From: Osamu-kj Date: Sat, 6 Aug 2022 15:10:31 +0200 Subject: [PATCH 24/33] Fixed the XSS protection code inside custom fields --- netbox/netbox/tables/columns.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index 7774a495f..277482512 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from glob import escape from typing import Optional import django_tables2 as tables @@ -8,6 +7,7 @@ from django.contrib.auth.models import AnonymousUser from django.db.models import DateField, DateTimeField from django.template import Context, Template from django.urls import reverse +from django.utils.html import escape from django.utils.formats import date_format from django.utils.safestring import mark_safe from django_tables2.columns import library @@ -430,25 +430,28 @@ class CustomFieldColumn(tables.Column): def _likify_item(item): if hasattr(item, 'get_absolute_url'): return f'{item}' - return item + return escape(item) def render(self, value): if self.customfield.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value is True: - return escape('') + return mark_safe('') if self.customfield.type == CustomFieldTypeChoices.TYPE_BOOLEAN and value is False: - return escape('') + return mark_safe('') if self.customfield.type == CustomFieldTypeChoices.TYPE_URL: - return escape(f'{value}') + return mark_safe(f'{escape(value)}') if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTISELECT: return ', '.join(v for v in value) if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: - return escape(', '.join([ + print (mark_safe(', '.join([ + self._likify_item(obj) for obj in self.customfield.deserialize(value) + ]))) + return mark_safe(', '.join([ self._likify_item(obj) for obj in self.customfield.deserialize(value) ])) if value is not None: obj = self.customfield.deserialize(value) - return escape(self._likify_item(obj)) - return escape(self.default) + return mark_safe(self._likify_item(obj)) + return self.default def value(self, value): if isinstance(value, list): From 7141fc8eb03eeba0d751e7af374f7d6a92ea60cb Mon Sep 17 00:00:00 2001 From: Osamu-kj Date: Sat, 6 Aug 2022 17:17:43 +0200 Subject: [PATCH 25/33] Custom fields - removed the debug lines --- netbox/netbox/tables/columns.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index 277482512..573ac058c 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -429,7 +429,7 @@ class CustomFieldColumn(tables.Column): @staticmethod def _likify_item(item): if hasattr(item, 'get_absolute_url'): - return f'{item}' + return f'{escape(item)}' return escape(item) def render(self, value): @@ -442,9 +442,6 @@ class CustomFieldColumn(tables.Column): if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTISELECT: return ', '.join(v for v in value) if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: - print (mark_safe(', '.join([ - self._likify_item(obj) for obj in self.customfield.deserialize(value) - ]))) return mark_safe(', '.join([ self._likify_item(obj) for obj in self.customfield.deserialize(value) ])) From 0e1947bc4bceaf01d519bc7cc2e9fc09768b0409 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 09:58:58 -0400 Subject: [PATCH 26/33] PEP8 fix --- netbox/netbox/tables/columns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index 573ac058c..e176b9af7 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -7,7 +7,7 @@ from django.contrib.auth.models import AnonymousUser from django.db.models import DateField, DateTimeField from django.template import Context, Template from django.urls import reverse -from django.utils.html import escape +from django.utils.html import escape from django.utils.formats import date_format from django.utils.safestring import mark_safe from django_tables2.columns import library From 135543683db21d2856f5b12311b348e7061ff743 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 10:24:49 -0400 Subject: [PATCH 27/33] Changelog for #9919 --- docs/release-notes/version-3.2.md | 1 + netbox/netbox/tables/columns.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 1c7eb79e8..d13e8db75 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -20,6 +20,7 @@ * [#9884](https://github.com/netbox-community/netbox/issues/9884) - Prevent querying assigned VRF on prefix object init * [#9885](https://github.com/netbox-community/netbox/issues/9885) - Fix child prefix counts when editing/deleting aggregates in bulk * [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization +* [#9919](https://github.com/netbox-community/netbox/issues/9919) - Fix potential XSS avenue via linked objects in tables --- diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index e176b9af7..f78b9f37c 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -442,9 +442,9 @@ class CustomFieldColumn(tables.Column): if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTISELECT: return ', '.join(v for v in value) if self.customfield.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: - return mark_safe(', '.join([ + return mark_safe(', '.join( self._likify_item(obj) for obj in self.customfield.deserialize(value) - ])) + )) if value is not None: obj = self.customfield.deserialize(value) return mark_safe(self._likify_item(obj)) From 90317adae77c7aeab79d798c1e6f3a983d18fd8a Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 10:47:07 -0400 Subject: [PATCH 28/33] Clean up usages of mark_safe() --- netbox/dcim/views.py | 5 ++--- netbox/netbox/views/generic/object_views.py | 6 +++--- netbox/utilities/templatetags/builtins/filters.py | 4 ++-- netbox/utilities/templatetags/helpers.py | 4 +--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 3bec02f5c..0bdca686d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -3083,7 +3083,7 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix if membership_form.is_valid(): membership_form.save() - msg = 'Added member {}'.format(device.get_absolute_url(), escape(device)) + msg = f'Added member {escape(device)}' messages.success(request, mark_safe(msg)) if '_addanother' in request.POST: @@ -3128,8 +3128,7 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL # Protect master device from being removed virtual_chassis = VirtualChassis.objects.filter(master=device).first() if virtual_chassis is not None: - msg = 'Unable to remove master device {} from the virtual chassis.'.format(escape(device)) - messages.error(request, mark_safe(msg)) + messages.error(request, f'Unable to remove master device {device} from the virtual chassis.') return redirect(device.get_absolute_url()) if form.is_valid(): diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 4ebfe71cc..88e078ae3 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -386,10 +386,10 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): ) logger.info(f"{msg} {obj} (PK: {obj.pk})") if hasattr(obj, 'get_absolute_url'): - msg = '{} {}'.format(msg, obj.get_absolute_url(), escape(obj)) + msg = mark_safe(f'{msg} {escape(obj)}') else: - msg = '{} {}'.format(msg, escape(obj)) - messages.success(request, mark_safe(msg)) + msg = f'{msg} {obj}' + messages.success(request, msg) if '_addanother' in request.POST: redirect_url = request.path diff --git a/netbox/utilities/templatetags/builtins/filters.py b/netbox/utilities/templatetags/builtins/filters.py index 5a6841286..bc395e438 100644 --- a/netbox/utilities/templatetags/builtins/filters.py +++ b/netbox/utilities/templatetags/builtins/filters.py @@ -86,8 +86,8 @@ def placeholder(value): """ if value not in ('', None): return value - placeholder = '' - return mark_safe(placeholder) + + return mark_safe('') @register.filter() diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py index db4d14c24..67ed553b2 100644 --- a/netbox/utilities/templatetags/helpers.py +++ b/netbox/utilities/templatetags/helpers.py @@ -109,9 +109,7 @@ def annotated_date(date_value): long_ts = date(date_value, 'DATETIME_FORMAT') short_ts = date(date_value, 'SHORT_DATETIME_FORMAT') - span = f'{short_ts}' - - return mark_safe(span) + return mark_safe(f'{short_ts}') @register.simple_tag From 36ac83a319f1dbd1df3752c9ab8f129eefb23f9b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 11:43:27 -0400 Subject: [PATCH 29/33] Fixes #9949: Fix KeyError exception resulting from invalid API token provisioning request --- docs/release-notes/version-3.2.md | 1 + netbox/users/api/views.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index d13e8db75..0ee235a82 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -21,6 +21,7 @@ * [#9885](https://github.com/netbox-community/netbox/issues/9885) - Fix child prefix counts when editing/deleting aggregates in bulk * [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization * [#9919](https://github.com/netbox-community/netbox/issues/9919) - Fix potential XSS avenue via linked objects in tables +* [#9949](https://github.com/netbox-community/netbox/issues/9949) - Fix KeyError exception resulting from invalid API token provisioning request --- diff --git a/netbox/users/api/views.py b/netbox/users/api/views.py index c3495afdf..e5c2bc8ab 100644 --- a/netbox/users/api/views.py +++ b/netbox/users/api/views.py @@ -74,11 +74,11 @@ class TokenProvisionView(APIView): serializer.is_valid() # Authenticate the user account based on the provided credentials - user = authenticate( - request=request, - username=serializer.data['username'], - password=serializer.data['password'] - ) + username = serializer.data.get('username') + password = serializer.data.get('password') + if not username or not password: + raise AuthenticationFailed("Username and password must be provided to provision a token.") + user = authenticate(request=request, username=username, password=password) if user is None: raise AuthenticationFailed("Invalid username/password") From 876251c1cf6da80894780d3856412a2918da9d0e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 11:48:43 -0400 Subject: [PATCH 30/33] Fixes #9948: Fix TypeError exception when requesting API tokens list as non-authenticated user --- docs/release-notes/version-3.2.md | 1 + netbox/users/api/views.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 0ee235a82..745ace110 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -21,6 +21,7 @@ * [#9885](https://github.com/netbox-community/netbox/issues/9885) - Fix child prefix counts when editing/deleting aggregates in bulk * [#9891](https://github.com/netbox-community/netbox/issues/9891) - Ensure consistent ordering for tags during object serialization * [#9919](https://github.com/netbox-community/netbox/issues/9919) - Fix potential XSS avenue via linked objects in tables +* [#9948](https://github.com/netbox-community/netbox/issues/9948) - Fix TypeError exception when requesting API tokens list as non-authenticated user * [#9949](https://github.com/netbox-community/netbox/issues/9949) - Fix KeyError exception resulting from invalid API token provisioning request --- diff --git a/netbox/users/api/views.py b/netbox/users/api/views.py index e5c2bc8ab..66ef92ab7 100644 --- a/netbox/users/api/views.py +++ b/netbox/users/api/views.py @@ -58,6 +58,8 @@ class TokenViewSet(NetBoxModelViewSet): # Workaround for schema generation (drf_yasg) if getattr(self, 'swagger_fake_view', False): return queryset.none() + if not self.request.user.is_authenticated: + return queryset.none() if self.request.user.is_superuser: return queryset return queryset.filter(user=self.request.user) From 8721ad987cf178cc5421f64c28b1772bdabaa967 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 12:22:22 -0400 Subject: [PATCH 31/33] Fixes #9952: Prevent InvalidMove when attempting to assign a nested child object as parent --- docs/release-notes/version-3.2.md | 1 + netbox/netbox/models/__init__.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 745ace110..f231b6e58 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -23,6 +23,7 @@ * [#9919](https://github.com/netbox-community/netbox/issues/9919) - Fix potential XSS avenue via linked objects in tables * [#9948](https://github.com/netbox-community/netbox/issues/9948) - Fix TypeError exception when requesting API tokens list as non-authenticated user * [#9949](https://github.com/netbox-community/netbox/issues/9949) - Fix KeyError exception resulting from invalid API token provisioning request +* [#9952](https://github.com/netbox-community/netbox/issues/9952) - Prevent InvalidMove when attempting to assign a nested child object as parent --- diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index b3bfe06c0..ea2feb8de 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -89,9 +89,9 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel): super().clean() # An MPTT model cannot be its own parent - if self.pk and self.parent_id == self.pk: + if self.pk and self.parent and self.parent in self.get_descendants(include_self=True): raise ValidationError({ - "parent": "Cannot assign self as parent." + "parent": f"Cannot assign self or child {self._meta.verbose_name} as parent." }) From caca074161977fcbd7d3d0cd0e02b42efb3e8184 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 14:21:42 -0400 Subject: [PATCH 32/33] Fixes #9950: Prevent redirection to arbitrary URLs via 'next' parameter on login URL --- docs/release-notes/version-3.2.md | 1 + netbox/users/views.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index f231b6e58..46718837e 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -23,6 +23,7 @@ * [#9919](https://github.com/netbox-community/netbox/issues/9919) - Fix potential XSS avenue via linked objects in tables * [#9948](https://github.com/netbox-community/netbox/issues/9948) - Fix TypeError exception when requesting API tokens list as non-authenticated user * [#9949](https://github.com/netbox-community/netbox/issues/9949) - Fix KeyError exception resulting from invalid API token provisioning request +* [#9950](https://github.com/netbox-community/netbox/issues/9950) - Prevent redirection to arbitrary URLs via `next` parameter on login URL * [#9952](https://github.com/netbox-community/netbox/issues/9952) - Prevent InvalidMove when attempting to assign a nested child object as parent --- diff --git a/netbox/users/views.py b/netbox/users/views.py index 344f375fc..f08cac844 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -10,6 +10,7 @@ from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.decorators import method_decorator +from django.utils.http import url_has_allowed_host_and_scheme from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import View from social_core.backends.utils import load_backends @@ -91,7 +92,7 @@ class LoginView(View): data = request.POST if request.method == "POST" else request.GET redirect_url = data.get('next', settings.LOGIN_REDIRECT_URL) - if redirect_url and redirect_url.startswith('/'): + if redirect_url and url_has_allowed_host_and_scheme(redirect_url, allowed_hosts=None): logger.debug(f"Redirecting user to {redirect_url}") else: if redirect_url: From ce7fb8ab17406a8eba02055c22ec0b6cbe8ef425 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 8 Aug 2022 15:17:36 -0400 Subject: [PATCH 33/33] Release v3.2.8 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- base_requirements.txt | 2 +- docs/release-notes/version-3.2.md | 2 +- netbox/netbox/settings.py | 2 +- requirements.txt | 12 ++++++------ 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 332a0ad75..c26584f32 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.2.7 + placeholder: v3.2.8 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index ff9b5e358..e6be95e49 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: v3.2.7 + placeholder: v3.2.8 validations: required: true - type: dropdown diff --git a/base_requirements.txt b/base_requirements.txt index 9dc85231b..672ce402c 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 +Django<4.1 # Django middleware which permits cross-domain API requests # https://github.com/OttoYiu/django-cors-headers diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 46718837e..bf6f2f848 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -1,6 +1,6 @@ # NetBox v3.2 -## v3.2.8 (FUTURE) +## v3.2.8 (2022-08-08) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index c7e49c1be..12ab44399 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -29,7 +29,7 @@ django.utils.encoding.force_text = force_str # Environment setup # -VERSION = '3.2.8-dev' +VERSION = '3.2.8' # Hostname HOSTNAME = platform.node() diff --git a/requirements.txt b/requirements.txt index f987ad7ba..59bd1e8cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ bleach==5.0.1 -Django==4.0.6 +Django==4.0.7 django-cors-headers==3.13.0 django-debug-toolbar==3.5.0 django-filter==22.1 @@ -13,22 +13,22 @@ django-tables2==2.4.1 django-taggit==2.1.0 django-timezone-field==5.0 djangorestframework==3.13.1 -drf-yasg[validation]==1.20.0 +drf-yasg[validation]==1.21.3 graphene-django==2.15.0 gunicorn==20.1.0 Jinja2==3.1.2 -Markdown==3.3.7 -markdown-include==0.6.0 +Markdown==3.4.1 +markdown-include==0.7.0 mkdocs-material==8.3.9 mkdocstrings[python-legacy]==0.19.0 netaddr==0.8.0 Pillow==9.2.0 psycopg2-binary==2.9.3 PyYAML==6.0 -sentry-sdk==1.7.0 +sentry-sdk==1.9.2 social-auth-app-django==5.0.0 social-auth-core==4.3.0 -svgwrite==1.4.2 +svgwrite==1.4.3 tablib==3.2.1 tzdata==2022.1