From 68874f249d144c453de1d76c7faa163c9bfdd0f1 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 4 May 2023 14:38:37 -0700 Subject: [PATCH 01/14] 12489 use htmx for site view locations and non-racked-devices --- netbox/dcim/views.py | 16 ------ .../templates/dcim/inc/nonracked_devices.html | 50 ++-------------- netbox/templates/dcim/site.html | 57 +++++-------------- 3 files changed, 19 insertions(+), 104 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index bcbbf17395b..cadaa2d13f3 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -377,21 +377,6 @@ def get_extra_context(self, request, instance): (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'), ) - locations = Location.objects.add_related_count( - Location.objects.all(), - Rack, - 'location', - 'rack_count', - cumulative=True - ) - locations = Location.objects.add_related_count( - locations, - Device, - 'location', - 'device_count', - cumulative=True - ).restrict(request.user, 'view').filter(site=instance) - nonracked_devices = Device.objects.filter( site=instance, rack__isnull=True, @@ -400,7 +385,6 @@ def get_extra_context(self, request, instance): return { 'related_models': related_models, - 'locations': locations, 'nonracked_devices': nonracked_devices.order_by('-pk')[:10], 'total_nonracked_devices_count': nonracked_devices.count(), } diff --git a/netbox/templates/dcim/inc/nonracked_devices.html b/netbox/templates/dcim/inc/nonracked_devices.html index d4cd588393d..8331357f391 100644 --- a/netbox/templates/dcim/inc/nonracked_devices.html +++ b/netbox/templates/dcim/inc/nonracked_devices.html @@ -4,50 +4,10 @@
Non-Racked Devices
-
- {% if nonracked_devices %} - - - - - - - - {% for device in nonracked_devices %} - - - - - {% if device.parent_bay %} - - - {% else %} - - {% endif %} - - {% endfor %} -
NameRoleTypeParent Device
- {{ device }} - {{ device.device_role }}{{ device.device_type }}{{ device.parent_bay.device|linkify }}{{ device.parent_bay }}
- - {% if total_nonracked_devices_count > nonracked_devices.count %} - {% if object|meta:'verbose_name' == 'site' %} -
- Displaying {{ nonracked_devices.count }} of {{ total_nonracked_devices_count }} devices (View full list) -
- {% elif object|meta:'verbose_name' == 'location' %} -
- Displaying {{ nonracked_devices.count }} of {{ total_nonracked_devices_count }} devices (View full list) -
- {% endif %} - {% endif %} - - {% else %} -
- None -
- {% endif %} -
+
{% if perms.dcim.add_device %} {% if object|meta:'verbose_name' == 'rack' %} @@ -73,4 +33,4 @@
{% endif %} {% endif %} - \ No newline at end of file + diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index 91fdba7be2b..4e5d627c781 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -132,55 +132,26 @@
Site
{% include 'inc/panels/related_objects.html' with filter_name='site_id' %} {% include 'inc/panels/contacts.html' %} -
-
Locations
-
- {% if locations %} - - - - - - - - {% for location in locations %} - - - - - - - {% endfor %} -
LocationRacksDevices
- {% for i in location.level|as_range %}{% endfor %} - {{ location|linkify }} - - {{ location.rack_count }} - - {{ location.device_count }} - - - - -
- {% else %} - None - {% endif %} -
- {% if perms.dcim.add_location %} - - {% endif %} -
{% include 'inc/panels/image_attachments.html' %} {% plugin_right_page object %}
+
+
Locations
+
+ {% if perms.dcim.add_location %} + + {% endif %} +
{% include 'dcim/inc/nonracked_devices.html' %} {% plugin_full_width_page object %}
From 054e3c119bb242c43c35c44c01cc789a4f158665 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 4 May 2023 14:41:17 -0700 Subject: [PATCH 02/14] 12489 remove now unused queries in context --- netbox/dcim/views.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index cadaa2d13f3..20cf085353e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -377,16 +377,8 @@ def get_extra_context(self, request, instance): (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'), ) - nonracked_devices = Device.objects.filter( - site=instance, - rack__isnull=True, - parent_bay__isnull=True - ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role') - return { 'related_models': related_models, - 'nonracked_devices': nonracked_devices.order_by('-pk')[:10], - 'total_nonracked_devices_count': nonracked_devices.count(), } From 20ad5526c6558a9206f604d79e3ebdaac24dacad Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Fri, 5 May 2023 20:10:41 +0530 Subject: [PATCH 03/14] adds device type and role to device component filter #12015 --- netbox/dcim/filtersets.py | 22 ++++++++++++++++ netbox/dcim/forms/filtersets.py | 45 ++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index fccaa72f089..e784be8e85b 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1219,6 +1219,28 @@ class DeviceComponentFilterSet(django_filters.FilterSet): to_field_name='name', label=_('Device (name)'), ) + device_type_id = django_filters.ModelMultipleChoiceFilter( + field_name='device__device_type', + queryset=DeviceType.objects.all(), + label=_('Device type (ID)'), + ) + device_type = django_filters.ModelMultipleChoiceFilter( + field_name='device__device_type__model', + queryset=DeviceType.objects.all(), + to_field_name='model', + label=_('Device type (model)'), + ) + device_role_id = django_filters.ModelMultipleChoiceFilter( + field_name='device__device_role', + queryset=DeviceRole.objects.all(), + label=_('Device role (ID)'), + ) + device_role = django_filters.ModelMultipleChoiceFilter( + field_name='device__device_role__slug', + queryset=DeviceRole.objects.all(), + to_field_name='slug', + label=_('Device role (slug)'), + ) virtual_chassis_id = django_filters.ModelMultipleChoiceFilter( field_name='device__virtual_chassis', queryset=VirtualChassis.objects.all(), diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index d31bba03053..b671bbdc2fa 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -102,13 +102,25 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm): required=False, label=_('Virtual Chassis') ) + device_type_id = DynamicModelMultipleChoiceField( + queryset=DeviceType.objects.all(), + required=False, + label=_('Device type') + ) + device_role_id = DynamicModelMultipleChoiceField( + queryset=DeviceRole.objects.all(), + required=False, + label=_('Device role') + ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), required=False, query_params={ 'site_id': '$site_id', 'location_id': '$location_id', - 'virtual_chassis_id': '$virtual_chassis_id' + 'virtual_chassis_id': '$virtual_chassis_id', + 'device_type_id': '$device_type_id', + 'role_id': '$device_role_id' }, label=_('Device') ) @@ -1070,7 +1082,8 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'speed')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id','device_role_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1089,7 +1102,8 @@ class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterF fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'speed')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1108,7 +1122,8 @@ class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1123,7 +1138,8 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1141,8 +1157,8 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): ('Addressing', ('vrf_id', 'l2vpn_id', 'mac_address', 'wwn')), ('PoE', ('poe_mode', 'poe_type')), ('Wireless', ('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', - 'device_id', 'vdc_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id', 'vdc_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) vdc_id = DynamicModelMultipleChoiceField( @@ -1242,7 +1258,8 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'color')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ('Cable', ('cabled', 'occupied')), ) model = FrontPort @@ -1261,7 +1278,8 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'color')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ('Cable', ('cabled', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1279,7 +1297,8 @@ class ModuleBayFilterForm(DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'position')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ) tag = TagFilterField(model) position = forms.CharField( @@ -1292,7 +1311,8 @@ class DeviceBayFilterForm(DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ) tag = TagFilterField(model) @@ -1302,7 +1322,8 @@ class InventoryItemFilterForm(DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'role_id', 'manufacturer_id', 'serial', 'asset_tag', 'discovered')), - ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ) role_id = DynamicModelMultipleChoiceField( queryset=InventoryItemRole.objects.all(), From 0a6c630d74da2ed0d7c7ace6520928f2e5a59162 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Fri, 5 May 2023 20:16:04 +0530 Subject: [PATCH 04/14] Revert "Fixes #12463: Fix the association of completed jobs with reports & scripts in the REST API" This reverts commit a29a07ed26b2fe15ea21b000edbede1c20faef93. --- docs/release-notes/version-3.5.md | 1 - netbox/extras/api/views.py | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 8bdfb1578f4..4823549fdd5 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -43,7 +43,6 @@ * [#12415](https://github.com/netbox-community/netbox/issues/12415) - Fix `ImportError` exception when running RQ worker * [#12433](https://github.com/netbox-community/netbox/issues/12433) - Correct the application of URL query parameters for object list dashboard widgets * [#12436](https://github.com/netbox-community/netbox/issues/12436) - Remove extraneous "add" button from contact assignments list -* [#12463](https://github.com/netbox-community/netbox/issues/12463) - Fix the association of completed jobs with reports & scripts in the REST API * [#12464](https://github.com/netbox-community/netbox/issues/12464) - Apply credentials for git data source only when connecting via HTTP/S * [#12476](https://github.com/netbox-community/netbox/issues/12476) - Fix `TypeError` exception when running the `runscript` management command * [#12483](https://github.com/netbox-community/netbox/issues/12483) - Fix git remote data syncing when with HTTP proxies defined diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 3f796d7f88d..f302024b05f 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -187,10 +187,11 @@ def list(self, request): """ Compile all reports and their related results (if any). Result data is deferred in the list view. """ + report_content_type = ContentType.objects.get(app_label='extras', model='report') results = { - job.name: job - for job in Job.objects.filter( - object_type=ContentType.objects.get(app_label='extras', model='reportmodule'), + r.name: r + for r in Job.objects.filter( + object_type=report_content_type, status__in=JobStatusChoices.TERMINAL_STATE_CHOICES ).order_by('name', '-created').distinct('name').defer('data') } @@ -201,7 +202,7 @@ def list(self, request): # Attach Job objects to each report (if any) for report in report_list: - report.result = results.get(report.name, None) + report.result = results.get(report.full_name, None) serializer = serializers.ReportSerializer(report_list, many=True, context={ 'request': request, @@ -289,10 +290,12 @@ def _get_script(self, pk): return module, script def list(self, request): + + script_content_type = ContentType.objects.get(app_label='extras', model='script') results = { - job.name: job - for job in Job.objects.filter( - object_type=ContentType.objects.get(app_label='extras', model='scriptmodule'), + r.name: r + for r in Job.objects.filter( + object_type=script_content_type, status__in=JobStatusChoices.TERMINAL_STATE_CHOICES ).order_by('name', '-created').distinct('name').defer('data') } @@ -303,7 +306,7 @@ def list(self, request): # Attach Job objects to each script (if any) for script in script_list: - script.result = results.get(script.name, None) + script.result = results.get(script.full_name, None) serializer = serializers.ScriptSerializer(script_list, many=True, context={'request': request}) From 73e20ef11b664326ffd702b6a965df86c36b6cce Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 5 May 2023 08:08:26 -0700 Subject: [PATCH 05/14] 12489 update nonracked_devices on rack and location templates --- netbox/templates/dcim/location.html | 2 +- netbox/templates/dcim/rack.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html index 193d93f9a85..b6cae1d2a9b 100644 --- a/netbox/templates/dcim/location.html +++ b/netbox/templates/dcim/location.html @@ -66,13 +66,13 @@
{% include 'inc/panels/related_objects.html' %} {% include 'inc/panels/contacts.html' %} - {% include 'dcim/inc/nonracked_devices.html' %} {% include 'inc/panels/image_attachments.html' %} {% plugin_right_page object %}
+ {% include 'dcim/inc/nonracked_devices.html' %}
Child Locations
Rear
{% include 'inc/panels/related_objects.html' %} - {% include 'dcim/inc/nonracked_devices.html' %} {% include 'inc/panels/contacts.html' %} {% plugin_right_page object %}
+ {% include 'dcim/inc/nonracked_devices.html' %} {% plugin_full_width_page object %}
From e1b342e35675a47e0e7278c66a1dbe848a4d1d62 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 5 May 2023 08:20:41 -0700 Subject: [PATCH 06/14] 12489 fix whitespace issue --- netbox/dcim/forms/filtersets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index b671bbdc2fa..f10f08910a6 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -1083,7 +1083,7 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'speed')), ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id','device_role_id', 'device_id')), + ('Device', ('device_type_id', 'device_role_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( From cb3fa5f41716eb2558ec8acaecb5a8f74e8bd0b4 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 9 May 2023 12:12:55 -0400 Subject: [PATCH 07/14] Undo errant commits --- docs/release-notes/version-3.5.md | 1 + netbox/dcim/filtersets.py | 22 --------------- netbox/dcim/forms/filtersets.py | 45 +++++++++---------------------- netbox/extras/api/views.py | 19 ++++++------- 4 files changed, 21 insertions(+), 66 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 4823549fdd5..8bdfb1578f4 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -43,6 +43,7 @@ * [#12415](https://github.com/netbox-community/netbox/issues/12415) - Fix `ImportError` exception when running RQ worker * [#12433](https://github.com/netbox-community/netbox/issues/12433) - Correct the application of URL query parameters for object list dashboard widgets * [#12436](https://github.com/netbox-community/netbox/issues/12436) - Remove extraneous "add" button from contact assignments list +* [#12463](https://github.com/netbox-community/netbox/issues/12463) - Fix the association of completed jobs with reports & scripts in the REST API * [#12464](https://github.com/netbox-community/netbox/issues/12464) - Apply credentials for git data source only when connecting via HTTP/S * [#12476](https://github.com/netbox-community/netbox/issues/12476) - Fix `TypeError` exception when running the `runscript` management command * [#12483](https://github.com/netbox-community/netbox/issues/12483) - Fix git remote data syncing when with HTTP proxies defined diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index e784be8e85b..fccaa72f089 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1219,28 +1219,6 @@ class DeviceComponentFilterSet(django_filters.FilterSet): to_field_name='name', label=_('Device (name)'), ) - device_type_id = django_filters.ModelMultipleChoiceFilter( - field_name='device__device_type', - queryset=DeviceType.objects.all(), - label=_('Device type (ID)'), - ) - device_type = django_filters.ModelMultipleChoiceFilter( - field_name='device__device_type__model', - queryset=DeviceType.objects.all(), - to_field_name='model', - label=_('Device type (model)'), - ) - device_role_id = django_filters.ModelMultipleChoiceFilter( - field_name='device__device_role', - queryset=DeviceRole.objects.all(), - label=_('Device role (ID)'), - ) - device_role = django_filters.ModelMultipleChoiceFilter( - field_name='device__device_role__slug', - queryset=DeviceRole.objects.all(), - to_field_name='slug', - label=_('Device role (slug)'), - ) virtual_chassis_id = django_filters.ModelMultipleChoiceFilter( field_name='device__virtual_chassis', queryset=VirtualChassis.objects.all(), diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index f10f08910a6..d31bba03053 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -102,25 +102,13 @@ class DeviceComponentFilterForm(NetBoxModelFilterSetForm): required=False, label=_('Virtual Chassis') ) - device_type_id = DynamicModelMultipleChoiceField( - queryset=DeviceType.objects.all(), - required=False, - label=_('Device type') - ) - device_role_id = DynamicModelMultipleChoiceField( - queryset=DeviceRole.objects.all(), - required=False, - label=_('Device role') - ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), required=False, query_params={ 'site_id': '$site_id', 'location_id': '$location_id', - 'virtual_chassis_id': '$virtual_chassis_id', - 'device_type_id': '$device_type_id', - 'role_id': '$device_role_id' + 'virtual_chassis_id': '$virtual_chassis_id' }, label=_('Device') ) @@ -1082,8 +1070,7 @@ class ConsolePortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'speed')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1102,8 +1089,7 @@ class ConsoleServerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterF fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'speed')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1122,8 +1108,7 @@ class PowerPortFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1138,8 +1123,7 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1157,8 +1141,8 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): ('Addressing', ('vrf_id', 'l2vpn_id', 'mac_address', 'wwn')), ('PoE', ('poe_mode', 'poe_type')), ('Wireless', ('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id', 'vdc_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', + 'device_id', 'vdc_id')), ('Connection', ('cabled', 'connected', 'occupied')), ) vdc_id = DynamicModelMultipleChoiceField( @@ -1258,8 +1242,7 @@ class FrontPortFilterForm(CabledFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'color')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ('Cable', ('cabled', 'occupied')), ) model = FrontPort @@ -1278,8 +1261,7 @@ class RearPortFilterForm(CabledFilterForm, DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'type', 'color')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ('Cable', ('cabled', 'occupied')), ) type = forms.MultipleChoiceField( @@ -1297,8 +1279,7 @@ class ModuleBayFilterForm(DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'position')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ) tag = TagFilterField(model) position = forms.CharField( @@ -1311,8 +1292,7 @@ class DeviceBayFilterForm(DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ) tag = TagFilterField(model) @@ -1322,8 +1302,7 @@ class InventoryItemFilterForm(DeviceComponentFilterForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), ('Attributes', ('name', 'label', 'role_id', 'manufacturer_id', 'serial', 'asset_tag', 'discovered')), - ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id')), - ('Device', ('device_type_id', 'device_role_id', 'device_id')), + ('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', 'virtual_chassis_id', 'device_id')), ) role_id = DynamicModelMultipleChoiceField( queryset=InventoryItemRole.objects.all(), diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index f302024b05f..3f796d7f88d 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -187,11 +187,10 @@ def list(self, request): """ Compile all reports and their related results (if any). Result data is deferred in the list view. """ - report_content_type = ContentType.objects.get(app_label='extras', model='report') results = { - r.name: r - for r in Job.objects.filter( - object_type=report_content_type, + job.name: job + for job in Job.objects.filter( + object_type=ContentType.objects.get(app_label='extras', model='reportmodule'), status__in=JobStatusChoices.TERMINAL_STATE_CHOICES ).order_by('name', '-created').distinct('name').defer('data') } @@ -202,7 +201,7 @@ def list(self, request): # Attach Job objects to each report (if any) for report in report_list: - report.result = results.get(report.full_name, None) + report.result = results.get(report.name, None) serializer = serializers.ReportSerializer(report_list, many=True, context={ 'request': request, @@ -290,12 +289,10 @@ def _get_script(self, pk): return module, script def list(self, request): - - script_content_type = ContentType.objects.get(app_label='extras', model='script') results = { - r.name: r - for r in Job.objects.filter( - object_type=script_content_type, + job.name: job + for job in Job.objects.filter( + object_type=ContentType.objects.get(app_label='extras', model='scriptmodule'), status__in=JobStatusChoices.TERMINAL_STATE_CHOICES ).order_by('name', '-created').distinct('name').defer('data') } @@ -306,7 +303,7 @@ def list(self, request): # Attach Job objects to each script (if any) for script in script_list: - script.result = results.get(script.full_name, None) + script.result = results.get(script.name, None) serializer = serializers.ScriptSerializer(script_list, many=True, context={'request': request}) From 77737b595520513553ad946f0da90a4570690b62 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 9 May 2023 09:40:11 -0700 Subject: [PATCH 08/14] 12489 update site id in templates --- netbox/templates/dcim/inc/nonracked_devices.html | 8 ++++---- netbox/templates/dcim/location.html | 2 +- netbox/templates/dcim/rack.html | 2 +- netbox/templates/dcim/site.html | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/netbox/templates/dcim/inc/nonracked_devices.html b/netbox/templates/dcim/inc/nonracked_devices.html index 8331357f391..c0df20bc85e 100644 --- a/netbox/templates/dcim/inc/nonracked_devices.html +++ b/netbox/templates/dcim/inc/nonracked_devices.html @@ -5,28 +5,28 @@
Non-Racked Devices
{% if perms.dcim.add_device %} {% if object|meta:'verbose_name' == 'rack' %} {% elif object|meta:'verbose_name' == 'site' %} {% elif object|meta:'verbose_name' == 'location' %}
- {% include 'dcim/inc/nonracked_devices.html' %} + {% include 'dcim/inc/nonracked_devices.html' with site_id=object.site.pk %}
Child Locations
Rear
- {% include 'dcim/inc/nonracked_devices.html' %} + {% include 'dcim/inc/nonracked_devices.html' with site_id=object.site.pk %} {% plugin_full_width_page object %}
diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index 4e5d627c781..c2df5509155 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -152,7 +152,7 @@
Locations
{% endif %}
- {% include 'dcim/inc/nonracked_devices.html' %} + {% include 'dcim/inc/nonracked_devices.html' with site_id=object.pk %} {% plugin_full_width_page object %}
From 816cee53841d723421cde275d4b487a82aeb3726 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 May 2023 08:45:39 -0700 Subject: [PATCH 09/14] 12489 remove nonracked_devices include --- netbox/dcim/views.py | 16 --------- .../templates/dcim/inc/nonracked_devices.html | 36 ------------------- netbox/templates/dcim/location.html | 19 +++++++++- netbox/templates/dcim/rack.html | 21 +++++++++-- netbox/templates/dcim/site.html | 19 +++++++++- 5 files changed, 55 insertions(+), 56 deletions(-) delete mode 100644 netbox/templates/dcim/inc/nonracked_devices.html diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 20cf085353e..558b9704bd9 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -445,16 +445,8 @@ def get_extra_context(self, request, instance): (Device.objects.restrict(request.user, 'view').filter(location__in=locations), 'location_id'), ) - nonracked_devices = Device.objects.filter( - location=instance, - rack__isnull=True, - parent_bay__isnull=True - ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role') - return { 'related_models': related_models, - 'nonracked_devices': nonracked_devices.order_by('-pk')[:10], - 'total_nonracked_devices_count': nonracked_devices.count(), } @@ -636,13 +628,6 @@ def get_extra_context(self, request, instance): (PowerFeed.objects.restrict(request.user).filter(rack=instance), 'rack_id'), ) - # Get 0U devices located within the rack - nonracked_devices = Device.objects.filter( - rack=instance, - position__isnull=True, - parent_bay__isnull=True - ).prefetch_related('device_type__manufacturer', 'parent_bay', 'device_role') - peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site) if instance.location: @@ -659,7 +644,6 @@ def get_extra_context(self, request, instance): return { 'related_models': related_models, - 'nonracked_devices': nonracked_devices, 'next_rack': next_rack, 'prev_rack': prev_rack, 'svg_extra': svg_extra, diff --git a/netbox/templates/dcim/inc/nonracked_devices.html b/netbox/templates/dcim/inc/nonracked_devices.html deleted file mode 100644 index c0df20bc85e..00000000000 --- a/netbox/templates/dcim/inc/nonracked_devices.html +++ /dev/null @@ -1,36 +0,0 @@ -{% load helpers %} - -
-
- Non-Racked Devices -
-
- - {% if perms.dcim.add_device %} - {% if object|meta:'verbose_name' == 'rack' %} - - {% elif object|meta:'verbose_name' == 'site' %} - - {% elif object|meta:'verbose_name' == 'location' %} - - {% endif %} - {% endif %} -
diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html index 955a6b14089..743622ee615 100644 --- a/netbox/templates/dcim/location.html +++ b/netbox/templates/dcim/location.html @@ -72,7 +72,24 @@
- {% include 'dcim/inc/nonracked_devices.html' with site_id=object.site.pk %} +
+
+ Non-Racked Devices +
+
+ + {% if perms.dcim.add_device %} + + {% endif %} +
Child Locations
Rear
- {% include 'dcim/inc/nonracked_devices.html' with site_id=object.site.pk %} - {% plugin_full_width_page object %} +
+
+ Non-Racked Devices +
+
+ + {% if perms.dcim.add_device %} + + {% endif %} +
+ {% plugin_full_width_page object %}
{% endblock %} diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index c2df5509155..0cb5ce8265d 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -152,7 +152,24 @@
Locations
{% endif %} - {% include 'dcim/inc/nonracked_devices.html' with site_id=object.pk %} +
+
+ Non-Racked Devices +
+
+ + {% if perms.dcim.add_device %} + + {% endif %} +
{% plugin_full_width_page object %} From 19b7a20e8016889f2299b19bea0bc3d819f7ffb2 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 15 May 2023 13:11:21 -0700 Subject: [PATCH 10/14] 12489 add has_position filter --- netbox/dcim/filtersets.py | 7 +++++++ netbox/templates/dcim/rack.html | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index fccaa72f089..1fc7dc832f5 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -909,6 +909,10 @@ class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilter queryset=Rack.objects.all(), label=_('Rack (ID)'), ) + has_position = django_filters.BooleanFilter( + label=_('Has a position'), + method='_has_position' + ) cluster_id = django_filters.ModelMultipleChoiceFilter( queryset=Cluster.objects.all(), label=_('VM cluster (ID)'), @@ -1050,6 +1054,9 @@ def _module_bays(self, queryset, name, value): def _device_bays(self, queryset, name, value): return queryset.exclude(devicebays__isnull=value) + def _has_position(self, queryset, name, value): + return queryset.exclude(position__isnull=value) + class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet): device_id = django_filters.ModelMultipleChoiceFilter( diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 083bf77dc55..2aed3da33f7 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -201,7 +201,7 @@
Non-Racked Devices
From 207ff89c96c506cb02d23fcf207526cb8dab5743 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 28 Aug 2023 15:33:39 -0400 Subject: [PATCH 11/14] Use empty lookup for position field --- netbox/dcim/filtersets.py | 7 ------- netbox/templates/dcim/rack.html | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 26e527d3182..5ddaf9a9a0e 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -909,10 +909,6 @@ class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilter queryset=Rack.objects.all(), label=_('Rack (ID)'), ) - has_position = django_filters.BooleanFilter( - label=_('Has a position'), - method='_has_position' - ) cluster_id = django_filters.ModelMultipleChoiceFilter( queryset=Cluster.objects.all(), label=_('VM cluster (ID)'), @@ -1054,9 +1050,6 @@ def _module_bays(self, queryset, name, value): def _device_bays(self, queryset, name, value): return queryset.exclude(devicebays__isnull=value) - def _has_position(self, queryset, name, value): - return queryset.exclude(position__isnull=value) - class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet): device_id = django_filters.ModelMultipleChoiceFilter( diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 2b7f905097c..0d99e74ef1a 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -200,7 +200,7 @@
Non-Racked Devices
From 211eed4845f572bbfa00989337193e4cfa0b4ac0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 28 Aug 2023 15:37:41 -0400 Subject: [PATCH 12/14] Remove non-racked devices list from rack view (was moved to a tab) --- netbox/templates/dcim/rack.html | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 0d99e74ef1a..d17dfc98889 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -193,27 +193,4 @@

Rear

{% plugin_right_page object %} -
-
-
-
- Non-Racked Devices -
-
- - {% if perms.dcim.add_device %} - - {% endif %} -
- {% plugin_full_width_page object %} -
-
{% endblock %} From 1402d267271104946cc54fc9e3dd20749da29b67 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 28 Aug 2023 15:47:04 -0400 Subject: [PATCH 13/14] Clean up location and device tables --- netbox/templates/dcim/location.html | 39 ++++++++------- netbox/templates/dcim/site.html | 73 +++++------------------------ 2 files changed, 34 insertions(+), 78 deletions(-) diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html index dae6d78ff8e..48700588a91 100644 --- a/netbox/templates/dcim/location.html +++ b/netbox/templates/dcim/location.html @@ -71,30 +71,33 @@
-
-
- Non-Racked Devices -
-
- - {% if perms.dcim.add_device %} - - {% endif %} -
Child Locations
+ {% if perms.dcim.add_location %} + + {% endif %} +
+
+
Non-Racked Devices
+
+ {% if perms.dcim.add_device %} + + {% endif %}
{% plugin_full_width_page object %}
diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index 1d57de19b7f..e0f44261fe0 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -131,49 +131,6 @@
Site
{% include 'inc/panels/related_objects.html' with filter_name='site_id' %} -
-
Locations
-
- {% if locations %} - - - - - - - - {% for location in locations %} - - - - - - - {% endfor %} -
LocationRacksDevices
- {% for i in location.level|as_range %}{% endfor %} - {{ location|linkify }} - - {{ location.rack_count }} - - {{ location.device_count }} - - - - -
- {% else %} - None - {% endif %} -
- {% if perms.dcim.add_location %} - - {% endif %} -
{% include 'inc/panels/image_attachments.html' %} {% plugin_right_page object %}
@@ -189,28 +146,24 @@
Locations
{% if perms.dcim.add_location %} {% endif %}
-
- Non-Racked Devices -
-
- - {% if perms.dcim.add_device %} - - {% endif %} +
Non-Racked Devices
+
+ {% if perms.dcim.add_device %} + + {% endif %}
{% plugin_full_width_page object %} From 4f5dba543fd148f3db37d4709f21450d68a1a260 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 28 Aug 2023 15:50:50 -0400 Subject: [PATCH 14/14] Restore plugins block on rack template --- netbox/templates/dcim/rack.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index d17dfc98889..e513b178db1 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -193,4 +193,9 @@

Rear

{% plugin_right_page object %} +
+
+ {% plugin_full_width_page object %} +
+
{% endblock %}