From a6fd0ab09aa3538977fe605a626769cdf7588776 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 7 Apr 2023 13:58:12 -0400 Subject: [PATCH 1/6] #12007: Move vlan & vlan_id filter methods to CommonInterfaceFilterSet --- netbox/dcim/filtersets.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index fb59659d3..7b6c855d9 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1354,6 +1354,24 @@ class CommonInterfaceFilterSet(django_filters.FilterSet): label=_('L2VPN'), ) + def filter_vlan_id(self, queryset, name, value): + value = value.strip() + if not value: + return queryset + return queryset.filter( + Q(untagged_vlan_id=value) | + Q(tagged_vlans=value) + ) + + def filter_vlan(self, queryset, name, value): + value = value.strip() + if not value: + return queryset + return queryset.filter( + Q(untagged_vlan_id__vid=value) | + Q(tagged_vlans__vid=value) + ) + class InterfaceFilterSet( ModularDeviceComponentFilterSet, @@ -1461,24 +1479,6 @@ class InterfaceFilterSet( except Device.DoesNotExist: return queryset.none() - def filter_vlan_id(self, queryset, name, value): - value = value.strip() - if not value: - return queryset - return queryset.filter( - Q(untagged_vlan_id=value) | - Q(tagged_vlans=value) - ) - - def filter_vlan(self, queryset, name, value): - value = value.strip() - if not value: - return queryset - return queryset.filter( - Q(untagged_vlan_id__vid=value) | - Q(tagged_vlans__vid=value) - ) - def filter_kind(self, queryset, name, value): value = value.strip().lower() return { From 5a4feb70998d986d2ce121c64942a9e6d585b498 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 7 Apr 2023 11:13:58 -0700 Subject: [PATCH 2/6] 10615 filter cable termination_id with cable_end (#12182) * 10615 filter cable termination_id with cable_end * 10615 filter distinct * 10615 filter distinct --- netbox/dcim/filtersets.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 7b6c855d9..d9f378c6b 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1667,12 +1667,14 @@ class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet): field_name='terminations__termination_type' ) termination_a_id = MultiValueNumberFilter( + method='filter_by_cable_end_a', field_name='terminations__termination_id' ) termination_b_type = ContentTypeFilter( field_name='terminations__termination_type' ) termination_b_id = MultiValueNumberFilter( + method='filter_by_cable_end_b', field_name='terminations__termination_id' ) type = django_filters.MultipleChoiceFilter( @@ -1730,6 +1732,18 @@ class CableFilterSet(TenancyFilterSet, NetBoxModelFilterSet): # Supported objects: device, rack, location, site return queryset.filter(**{f'terminations___{name}__in': value}).distinct() + def filter_by_cable_end(self, queryset, name, value, side): + # Filter by termination id and cable_end type + return queryset.filter(**{f'{name}__in': value, 'terminations__cable_end': side}).distinct() + + def filter_by_cable_end_a(self, queryset, name, value): + # Filter by termination id and cable_end type + return self.filter_by_cable_end(queryset, name, value, CableEndChoices.SIDE_A) + + def filter_by_cable_end_b(self, queryset, name, value): + # Filter by termination id and cable_end type + return self.filter_by_cable_end(queryset, name, value, CableEndChoices.SIDE_B) + class CableTerminationFilterSet(BaseFilterSet): termination_type = ContentTypeFilter() From 1146aaff89a0c6bf607c88ba24737c04a06686bc Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 10 Apr 2023 09:12:04 -0400 Subject: [PATCH 3/6] Closes #11453: Display a warning banner when DEBUG is enabled --- docs/configuration/development.md | 2 +- docs/release-notes/version-3.4.md | 2 ++ netbox/templates/base/layout.html | 11 +++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/configuration/development.md b/docs/configuration/development.md index 3af56b0e3..1579f2cdb 100644 --- a/docs/configuration/development.md +++ b/docs/configuration/development.md @@ -18,4 +18,4 @@ interface. Default: False -This parameter serves as a safeguard to prevent some potentially dangerous behavior, such as generating new database schema migrations. Set this to `True` **only** if you are actively developing the NetBox code base. +This parameter serves as a safeguard to prevent some potentially dangerous behavior, such as generating new database schema migrations. Additionally, enabling this setting disables the debug warning banner in the UI. Set this to `True` **only** if you are actively developing the NetBox code base. diff --git a/docs/release-notes/version-3.4.md b/docs/release-notes/version-3.4.md index 5f086bca4..0df40a74d 100644 --- a/docs/release-notes/version-3.4.md +++ b/docs/release-notes/version-3.4.md @@ -4,11 +4,13 @@ ### Enhancements +* [#11453](https://github.com/netbox-community/netbox/issues/11453) - Display a warning banner when `DEBUG` is enabled * [#12007](https://github.com/netbox-community/netbox/issues/12007) - Enable filtering of VM Interfaces by assigned VLAN * [#12095](https://github.com/netbox-community/netbox/issues/12095) - Specify UTF-8 encoding for default export template MIME type ### Bug Fixes +* [#10615](https://github.com/netbox-community/netbox/issues/10615) - Fix filtering of cable terminations by A/B end * [#11746](https://github.com/netbox-community/netbox/issues/11746) - Fix cleanup of object data when deleting a custom field * [#12011](https://github.com/netbox-community/netbox/issues/12011) - Fix KeyError exception when attempting to add module bays in bulk * [#12074](https://github.com/netbox-community/netbox/issues/12074) - Fix the automatic assignment of racks to devices via the REST API diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 2fc6d0d98..6b247d81a 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -70,10 +70,17 @@ Blocks: {% endif %} + {% if settings.DEBUG and not settings.DEVELOPER %} + + {% endif %} + {% if config.MAINTENANCE_MODE %} {% endif %} From 768d6f624ebde75d287d00ec0e37f655bd280676 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 10 Apr 2023 09:17:13 -0400 Subject: [PATCH 4/6] Fixes #12191: Change absolute image path to relative --- docs/customization/reports.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/customization/reports.md b/docs/customization/reports.md index b83c4a177..f7d9109ec 100644 --- a/docs/customization/reports.md +++ b/docs/customization/reports.md @@ -132,7 +132,7 @@ Once you have created a report, it will appear in the reports list. Initially, r !!! note To run a report, a user must be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action in the admin UI as shown below. - ![Adding the run action to a permission](/media/admin_ui_run_permission.png) + ![Adding the run action to a permission](../media/admin_ui_run_permission.png) ### Via the Web UI From 278f2b173af3612e1bd5ccb7de9e78a7e9a40186 Mon Sep 17 00:00:00 2001 From: kkthxbye <400797+kkthxbye-code@users.noreply.github.com> Date: Mon, 10 Apr 2023 16:13:08 +0200 Subject: [PATCH 5/6] Fixes #11431 - Disallow changing customfield type after creation (#11449) * Disallow changing customfield type after creation * Fix test_api.CustomFieldTest --------- Co-authored-by: kkthxbye-code <> --- netbox/extras/api/serializers.py | 6 ++++++ netbox/extras/forms/model_forms.py | 7 +++++++ netbox/extras/tests/test_api.py | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 8b9c6dcb1..01b841a0f 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -97,6 +97,12 @@ class CustomFieldSerializer(ValidatedModelSerializer): 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'created', 'last_updated', ] + def validate_type(self, value): + if self.instance and self.instance.type != value: + raise serializers.ValidationError('Changing the type of custom fields is not supported.') + + return value + def get_data_type(self, obj): types = CustomFieldTypeChoices if obj.type == types.TYPE_INTEGER: diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 040882d27..365d55c72 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -64,6 +64,13 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm): 'ui_visibility': StaticSelect(), } + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Disable changing the type of a CustomField as it almost universally causes errors if custom field data is already present. + if self.instance.pk: + self.fields['type'].disabled = True + class CustomLinkForm(BootstrapMixin, forms.ModelForm): content_types = ContentTypeMultipleChoiceField( diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 4b5efda3c..c915d596a 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -102,6 +102,11 @@ class CustomFieldTest(APIViewTestCases.APIViewTestCase): bulk_update_data = { 'description': 'New description', } + update_data = { + 'content_types': ['dcim.device'], + 'name': 'New_Name', + 'description': 'New description', + } @classmethod def setUpTestData(cls): From 2c07762b7a4f992eac17007d38f45a0dd04b4c1c Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Mon, 10 Apr 2023 22:10:13 +0530 Subject: [PATCH 6/6] Added optional user and group on custom field (#12206) * added group and user model to object_type * Update netbox/utilities/utils.py Co-authored-by: Jeremy Stretch --------- Co-authored-by: Jeremy Stretch --- netbox/extras/forms/model_forms.py | 3 ++- netbox/utilities/utils.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 365d55c72..b7e606f7d 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -1,6 +1,7 @@ import json from django import forms +from django.db.models import Q from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext as _ @@ -37,7 +38,7 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm): object_type = ContentTypeChoiceField( queryset=ContentType.objects.all(), # TODO: Come up with a canonical way to register suitable models - limit_choices_to=FeatureQuery('webhooks'), + limit_choices_to=FeatureQuery('webhooks').get_query() | Q(app_label='auth', model__in=['user', 'group']), required=False, help_text=_("Type of the related object (for object/multi-object fields only)") ) diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index aec0d896c..57092bb7d 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -48,6 +48,10 @@ def get_viewname(model, action=None, rest_api=False): if is_plugin: viewname = f'plugins-api:{app_label}-api:{model_name}' else: + # Alter the app_label for group and user model_name to point to users app + if app_label == 'auth' and model_name in ['group', 'user']: + app_label = 'users' + viewname = f'{app_label}-api:{model_name}' # Append the action, if any if action: