From 29a5fb041ff6b1b82d23f110b5008f84b3f954d2 Mon Sep 17 00:00:00 2001 From: Craig Pund Date: Mon, 13 Jun 2022 17:04:25 -0400 Subject: [PATCH 01/15] add fields for virtual chassis to device_edit form --- netbox/templates/dcim/device_edit.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/netbox/templates/dcim/device_edit.html b/netbox/templates/dcim/device_edit.html index 7cbb224c9..38125e83c 100644 --- a/netbox/templates/dcim/device_edit.html +++ b/netbox/templates/dcim/device_edit.html @@ -86,6 +86,15 @@ {% render_field form.tenant %} +
+
+
Virtual Chassis
+
+ {% render_field form.virtual_chassis %} + {% render_field form.vc_position %} + {% render_field form.vc_priority %} +
+ {% if form.custom_fields %}
From 6876c9878e1e0965b61a341fd63083fc514c4540 Mon Sep 17 00:00:00 2001 From: Craig Pund Date: Mon, 13 Jun 2022 17:06:08 -0400 Subject: [PATCH 02/15] added field definitions for device form --- netbox/dcim/forms/models.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 179893219..2c905cc5c 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -521,13 +521,26 @@ class DeviceForm(TenancyForm, NetBoxModelForm): required=False, label='' ) + virtual_chassis = DynamicModelChoiceField( + queryset=VirtualChassis.objects.all(), + required=False + ) + vc_position = forms.IntegerField( + required=False, + help_text="The position in the virtual chassis this device is identified by" + ) + vc_priority = forms.IntegerField( + required=False, + help_text="The priority of the device in the virtual chassis" + ) class Meta: model = Device fields = [ 'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack', 'location', 'position', 'face', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6', - 'cluster_group', 'cluster', 'tenant_group', 'tenant', 'comments', 'tags', 'local_context_data' + 'cluster_group', 'cluster', 'tenant_group', 'tenant', 'virtual_chassis', 'vc_position', 'vc_priority', + 'comments', 'tags', 'local_context_data' ] help_texts = { 'device_role': "The function this device serves", From 52178f78d1b22478aeee567dddeab511c8cc2e3a Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Tue, 21 Jun 2022 12:58:41 -0400 Subject: [PATCH 03/15] Closes #7702: Add Powerfeed Defaults to User Configurations --- netbox/dcim/urls.py | 2 +- netbox/dcim/views.py | 12 ++++++++++++ netbox/extras/admin.py | 3 +++ netbox/netbox/config/parameters.py | 25 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index c5cd0fa65..82ea3fec0 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -489,7 +489,7 @@ urlpatterns = [ # Power feeds path('power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'), - path('power-feeds/add/', views.PowerFeedEditView.as_view(), name='powerfeed_add'), + path('power-feeds/add/', views.PowerFeedCreateView.as_view(), name='powerfeed_add'), path('power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'), path('power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'), path('power-feeds/disconnect/', views.PowerFeedBulkDisconnectView.as_view(), name='powerfeed_bulk_disconnect'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 35a1056b2..0716f595e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -16,6 +16,7 @@ from circuits.models import Circuit from extras.views import ObjectConfigContextView from ipam.models import ASN, IPAddress, Prefix, Service, VLAN, VLANGroup from ipam.tables import AssignedIPAddressesTable, InterfaceVLANTable +from netbox.config import ConfigItem from netbox.views import generic from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator, get_paginate_count @@ -3253,6 +3254,17 @@ class PowerFeedView(generic.ObjectView): queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack') +class PowerFeedCreateView(generic.ObjectEditView): + queryset = PowerFeed.objects.all() + form = forms.PowerFeedForm + + def alter_object(self, obj, request, args, kwargs): + obj.voltage = ConfigItem('POWERFEED_DEFAULT_VOLTAGE') + obj.amperage = ConfigItem('POWERFEED_DEFAULT_AMPERAGE') + obj.max_utilization = ConfigItem('POWERFEED_DEFAULT_MAX_UTILIZATION') + return obj + + class PowerFeedEditView(generic.ObjectEditView): queryset = PowerFeed.objects.all() form = forms.PowerFeedForm diff --git a/netbox/extras/admin.py b/netbox/extras/admin.py index 28902c323..01011b276 100644 --- a/netbox/extras/admin.py +++ b/netbox/extras/admin.py @@ -15,6 +15,9 @@ class ConfigRevisionAdmin(admin.ModelAdmin): ('Rack Elevations', { 'fields': ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH'), }), + ('Power', { + 'fields': ('POWERFEED_DEFAULT_VOLTAGE', 'POWERFEED_DEFAULT_AMPERAGE', 'POWERFEED_DEFAULT_MAX_UTILIZATION') + }), ('IPAM', { 'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'), }), diff --git a/netbox/netbox/config/parameters.py b/netbox/netbox/config/parameters.py index 68c96b38a..e2295888f 100644 --- a/netbox/netbox/config/parameters.py +++ b/netbox/netbox/config/parameters.py @@ -82,6 +82,31 @@ PARAMS = ( field=forms.IntegerField ), + # Power + ConfigParam( + name='POWERFEED_DEFAULT_VOLTAGE', + label='Powerfeed voltage', + default=120, + description="Default voltage for powerfeeds", + field=forms.IntegerField + ), + + ConfigParam( + name='POWERFEED_DEFAULT_AMPERAGE', + label='Powerfeed amperage', + default=15, + description="Default amperage for powerfeeds", + field=forms.IntegerField + ), + + ConfigParam( + name='POWERFEED_DEFAULT_MAX_UTILIZATION', + label='Powerfeed max utilization', + default=80, + description="Default max utilization for powerfeeds", + field=forms.IntegerField + ), + # Security ConfigParam( name='ALLOWED_URL_SCHEMES', From 6cb8b9110ebf2d6ff5a41485ad7a1c29cb89008c Mon Sep 17 00:00:00 2001 From: Hunter Johnston <64506580+huntabyte@users.noreply.github.com> Date: Thu, 23 Jun 2022 13:28:36 -0400 Subject: [PATCH 04/15] Closes #9396: Query modules by module bay & display installed_modules for module_bay REST API endpoint (#9574) * Closes #9396 - Added ability to query modules by module bay & installed_modules for module bay REST API endpoint * Closes #9396 - Added ability to query modules by module bay & installed_modules for module bay REST API endpoint * Closes #9396 - Added ability to query modules by module bay & installed_modules for module bay REST API endpoint --- netbox/dcim/api/nested_serializers.py | 9 +++++++++ netbox/dcim/api/serializers.py | 4 ++-- netbox/dcim/api/views.py | 2 +- netbox/dcim/filtersets.py | 6 ++++++ netbox/dcim/tests/test_filtersets.py | 5 +++++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py index 0ec0e07e0..1be8bb9dc 100644 --- a/netbox/dcim/api/nested_serializers.py +++ b/netbox/dcim/api/nested_serializers.py @@ -5,6 +5,7 @@ from netbox.api.serializers import BaseModelSerializer, WritableNestedSerializer __all__ = [ 'ComponentNestedModuleSerializer', + 'ModuleBayNestedModuleSerializer', 'NestedCableSerializer', 'NestedConsolePortSerializer', 'NestedConsolePortTemplateSerializer', @@ -281,6 +282,14 @@ class ModuleNestedModuleBaySerializer(WritableNestedSerializer): fields = ['id', 'url', 'display', 'name'] +class ModuleBayNestedModuleSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail') + + class Meta: + model = models.Module + fields = ['id', 'url', 'display', 'serial'] + + class ComponentNestedModuleSerializer(WritableNestedSerializer): """ Used by device component serializers. diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 7fcab6ba3..c2cb846a9 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -886,12 +886,12 @@ class FrontPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer): class ModuleBaySerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail') device = NestedDeviceSerializer() - # installed_module = NestedModuleSerializer(required=False, allow_null=True) + installed_module = ModuleBayNestedModuleSerializer(required=False, allow_null=True) class Meta: model = ModuleBay fields = [ - 'id', 'url', 'display', 'device', 'name', 'label', 'position', 'description', 'tags', 'custom_fields', + 'id', 'url', 'display', 'device', 'name', 'installed_module', 'label', 'position', 'description', 'tags', 'custom_fields', 'created', 'last_updated', ] diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index c4c25f654..3fa652a9b 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -611,7 +611,7 @@ class RearPortViewSet(PassThroughPortMixin, NetBoxModelViewSet): class ModuleBayViewSet(NetBoxModelViewSet): - queryset = ModuleBay.objects.prefetch_related('tags') + queryset = ModuleBay.objects.prefetch_related('tags', 'installed_module') serializer_class = serializers.ModuleBaySerializer filterset_class = filtersets.ModuleBayFilterSet brief_prefetch_fields = ['device'] diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index f052a8be9..4b4201578 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -992,6 +992,12 @@ class ModuleFilterSet(NetBoxModelFilterSet): to_field_name='model', label='Module type (model)', ) + module_bay_id = django_filters.ModelMultipleChoiceFilter( + field_name='module_bay', + queryset=ModuleBay.objects.all(), + to_field_name='id', + label='Module Bay (ID)' + ) device_id = django_filters.ModelMultipleChoiceFilter( queryset=Device.objects.all(), label='Device (ID)', diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 273ee6570..47aa9368c 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -1849,6 +1849,11 @@ class ModuleTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'module_type': [module_types[0].model, module_types[1].model]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) + def test_module_bay(self): + module_bays = ModuleBay.objects.all()[:2] + params = {'module_bay_id': [module_bays[0].pk, module_bays[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_device(self): device_types = Device.objects.all()[:2] params = {'device_id': [device_types[0].pk, device_types[1].pk]} From afec53cea3873dac9ea970bf70431bde0a399a66 Mon Sep 17 00:00:00 2001 From: Hunter Johnston <64506580+huntabyte@users.noreply.github.com> Date: Thu, 23 Jun 2022 14:11:59 -0400 Subject: [PATCH 05/15] Fixes #9575: Add exception handling to services (#9586) --- netbox/ipam/views.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index a01f2d052..6bcdc4c64 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -680,13 +680,16 @@ class IPAddressView(generic.ObjectView): service_filter = Q(ipaddresses=instance) # Find services listening on all IPs on the assigned device/vm - if instance.assigned_object and instance.assigned_object.parent_object: - parent_object = instance.assigned_object.parent_object + try: + if instance.assigned_object and instance.assigned_object.parent_object: + parent_object = instance.assigned_object.parent_object - if isinstance(parent_object, VirtualMachine): - service_filter |= (Q(virtual_machine=parent_object) & Q(ipaddresses=None)) - elif isinstance(parent_object, Device): - service_filter |= (Q(device=parent_object) & Q(ipaddresses=None)) + if isinstance(parent_object, VirtualMachine): + service_filter |= (Q(virtual_machine=parent_object) & Q(ipaddresses=None)) + elif isinstance(parent_object, Device): + service_filter |= (Q(device=parent_object) & Q(ipaddresses=None)) + except AttributeError: + pass services = Service.objects.restrict(request.user, 'view').filter(service_filter) From d55e3c352a3e8c3ac7d2d3639b85a509272f9d0e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 23 Jun 2022 14:14:02 -0400 Subject: [PATCH 06/15] Changelog for #9396, #9575, #9597 --- docs/release-notes/version-3.2.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 059fc8924..c87200147 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -2,6 +2,15 @@ ## v3.2.6 (FUTURE) +### Enhancements + +* [#9396](https://github.com/netbox-community/netbox/issues/9396) - Allow filtering modules by bay ID + +### Bug Fixes + +* [#9575](https://github.com/netbox-community/netbox/issues/9575) - Fix AttributeError exception for FHRP group with an IP address assigned +* [#9597](https://github.com/netbox-community/netbox/issues/9597) - Include `installed_module` in module bay REST API serializer + --- ## v3.2.5 (2022-06-20) From db807ab4a6f5d0ff9ff84ae39b5f18688bb19a1a Mon Sep 17 00:00:00 2001 From: Hunter Johnston <64506580+huntabyte@users.noreply.github.com> Date: Thu, 23 Jun 2022 14:16:09 -0400 Subject: [PATCH 07/15] Closes #7702: Add power feed defaults to user configurations --- netbox/dcim/models/power.py | 7 ++++--- netbox/dcim/urls.py | 2 +- netbox/dcim/views.py | 12 ------------ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index 08f89e3b0..5316951c6 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -6,6 +6,7 @@ from django.urls import reverse from dcim.choices import * from dcim.constants import * +from netbox.config import ConfigItem from netbox.models import NetBoxModel from utilities.validators import ExclusionValidator from .device_components import LinkTermination, PathEndpoint @@ -105,16 +106,16 @@ class PowerFeed(NetBoxModel, PathEndpoint, LinkTermination): default=PowerFeedPhaseChoices.PHASE_SINGLE ) voltage = models.SmallIntegerField( - default=POWERFEED_VOLTAGE_DEFAULT, + default=ConfigItem('POWERFEED_DEFAULT_VOLTAGE') validators=[ExclusionValidator([0])] ) amperage = models.PositiveSmallIntegerField( validators=[MinValueValidator(1)], - default=POWERFEED_AMPERAGE_DEFAULT + default=ConfigItem('POWERFEED_DEFAULT_AMPERAGE') ) max_utilization = models.PositiveSmallIntegerField( validators=[MinValueValidator(1), MaxValueValidator(100)], - default=POWERFEED_MAX_UTILIZATION_DEFAULT, + default=ConfigItem('POWERFEED_DEFAULT_MAX_UTILIZATION') help_text="Maximum permissible draw (percentage)" ) available_power = models.PositiveIntegerField( diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 82ea3fec0..c5cd0fa65 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -489,7 +489,7 @@ urlpatterns = [ # Power feeds path('power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'), - path('power-feeds/add/', views.PowerFeedCreateView.as_view(), name='powerfeed_add'), + path('power-feeds/add/', views.PowerFeedEditView.as_view(), name='powerfeed_add'), path('power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'), path('power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'), path('power-feeds/disconnect/', views.PowerFeedBulkDisconnectView.as_view(), name='powerfeed_bulk_disconnect'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 0716f595e..35a1056b2 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -16,7 +16,6 @@ from circuits.models import Circuit from extras.views import ObjectConfigContextView from ipam.models import ASN, IPAddress, Prefix, Service, VLAN, VLANGroup from ipam.tables import AssignedIPAddressesTable, InterfaceVLANTable -from netbox.config import ConfigItem from netbox.views import generic from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator, get_paginate_count @@ -3254,17 +3253,6 @@ class PowerFeedView(generic.ObjectView): queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack') -class PowerFeedCreateView(generic.ObjectEditView): - queryset = PowerFeed.objects.all() - form = forms.PowerFeedForm - - def alter_object(self, obj, request, args, kwargs): - obj.voltage = ConfigItem('POWERFEED_DEFAULT_VOLTAGE') - obj.amperage = ConfigItem('POWERFEED_DEFAULT_AMPERAGE') - obj.max_utilization = ConfigItem('POWERFEED_DEFAULT_MAX_UTILIZATION') - return obj - - class PowerFeedEditView(generic.ObjectEditView): queryset = PowerFeed.objects.all() form = forms.PowerFeedForm From c330282919f7903315abc0263c9d3f5d22321fa2 Mon Sep 17 00:00:00 2001 From: Hunter Johnston <64506580+huntabyte@users.noreply.github.com> Date: Thu, 23 Jun 2022 14:56:24 -0400 Subject: [PATCH 08/15] Fix syntax error --- netbox/dcim/models/power.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index 5316951c6..5978d86bd 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -106,7 +106,7 @@ class PowerFeed(NetBoxModel, PathEndpoint, LinkTermination): default=PowerFeedPhaseChoices.PHASE_SINGLE ) voltage = models.SmallIntegerField( - default=ConfigItem('POWERFEED_DEFAULT_VOLTAGE') + default=ConfigItem('POWERFEED_DEFAULT_VOLTAGE'), validators=[ExclusionValidator([0])] ) amperage = models.PositiveSmallIntegerField( @@ -115,7 +115,7 @@ class PowerFeed(NetBoxModel, PathEndpoint, LinkTermination): ) max_utilization = models.PositiveSmallIntegerField( validators=[MinValueValidator(1), MaxValueValidator(100)], - default=ConfigItem('POWERFEED_DEFAULT_MAX_UTILIZATION') + default=ConfigItem('POWERFEED_DEFAULT_MAX_UTILIZATION'), help_text="Maximum permissible draw (percentage)" ) available_power = models.PositiveIntegerField( From b77013c859d91f5bbd4da64eb28094886280d70f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 23 Jun 2022 17:26:20 -0400 Subject: [PATCH 09/15] Docs & cleanup for #7702 --- docs/configuration/dynamic-settings.md | 48 +++++++++++++++++++------- docs/release-notes/version-3.2.md | 1 + netbox/dcim/constants.py | 9 ----- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/docs/configuration/dynamic-settings.md b/docs/configuration/dynamic-settings.md index 2fa046fcf..d376dc5c4 100644 --- a/docs/configuration/dynamic-settings.md +++ b/docs/configuration/dynamic-settings.md @@ -43,18 +43,6 @@ changes in the database indefinitely. --- -## JOBRESULT_RETENTION - -Default: 90 - -The number of days to retain job results (scripts and reports). Set this to `0` to retain -job results in the database indefinitely. - -!!! warning - If enabling indefinite job results retention, it is recommended to periodically delete old entries. Otherwise, the database may eventually exceed capacity. - ---- - ## CUSTOM_VALIDATORS This is a mapping of models to [custom validators](../customization/custom-validation.md) that have been defined locally to enforce custom validation logic. An example is provided below: @@ -110,6 +98,18 @@ Setting this to False will disable the GraphQL API. --- +## JOBRESULT_RETENTION + +Default: 90 + +The number of days to retain job results (scripts and reports). Set this to `0` to retain +job results in the database indefinitely. + +!!! warning + If enabling indefinite job results retention, it is recommended to periodically delete old entries. Otherwise, the database may eventually exceed capacity. + +--- + ## MAINTENANCE_MODE Default: False @@ -185,6 +185,30 @@ The default maximum number of objects to display per page within each list of ob --- +## POWERFEED_DEFAULT_AMPERAGE + +Default: 15 + +The default value for the `amperage` field when creating new power feeds. + +--- + +## POWERFEED_DEFAULT_MAX_UTILIZATION + +Default: 80 + +The default value (percentage) for the `max_utilization` field when creating new power feeds. + +--- + +## POWERFEED_DEFAULT_VOLTAGE + +Default: 120 + +The default value for the `voltage` field when creating new power feeds. + +--- + ## PREFER_IPV4 Default: False diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index c87200147..719ceb7e1 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -4,6 +4,7 @@ ### Enhancements +* [#7702](https://github.com/netbox-community/netbox/issues/7702) - Enable dynamic configuration for default powerfeed attributes * [#9396](https://github.com/netbox-community/netbox/issues/9396) - Allow filtering modules by bay ID ### Bug Fixes diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index 38bf16f0b..155f19c88 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -49,15 +49,6 @@ WIRELESS_IFACE_TYPES = [ NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES -# -# Power feeds -# - -POWERFEED_VOLTAGE_DEFAULT = 120 -POWERFEED_AMPERAGE_DEFAULT = 20 -POWERFEED_MAX_UTILIZATION_DEFAULT = 80 # Percentage - - # # Device components # From 4315c4697cc3ade4901c0a9bb9bef6720027252e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 23 Jun 2022 17:44:19 -0400 Subject: [PATCH 10/15] Ignore default field values which reference ConfigItems when calculating migrations --- netbox/dcim/migrations/0001_squashed.py | 6 +++--- netbox/utilities/management/commands/__init__.py | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/migrations/0001_squashed.py b/netbox/dcim/migrations/0001_squashed.py index bb99d199f..374d3bf45 100644 --- a/netbox/dcim/migrations/0001_squashed.py +++ b/netbox/dcim/migrations/0001_squashed.py @@ -386,9 +386,9 @@ class Migration(migrations.Migration): ('type', models.CharField(default='primary', max_length=50)), ('supply', models.CharField(default='ac', max_length=50)), ('phase', models.CharField(default='single-phase', max_length=50)), - ('voltage', models.SmallIntegerField(default=120, validators=[utilities.validators.ExclusionValidator([0])])), - ('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])), - ('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])), + ('voltage', models.SmallIntegerField(validators=[utilities.validators.ExclusionValidator([0])])), + ('amperage', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1)])), + ('max_utilization', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])), ('available_power', models.PositiveIntegerField(default=0, editable=False)), ('comments', models.TextField(blank=True)), ], diff --git a/netbox/utilities/management/commands/__init__.py b/netbox/utilities/management/commands/__init__.py index bdd4face6..2c261b0d3 100644 --- a/netbox/utilities/management/commands/__init__.py +++ b/netbox/utilities/management/commands/__init__.py @@ -1,6 +1,8 @@ from django.db import models from timezone_field import TimeZoneField +from netbox.config import ConfigItem + SKIP_FIELDS = ( TimeZoneField, @@ -26,4 +28,9 @@ def custom_deconstruct(field): for attr in EXEMPT_ATTRS: kwargs.pop(attr, None) + # Ignore any field defaults which reference a ConfigItem + kwargs = { + k: v for k, v in kwargs.items() if not isinstance(v, ConfigItem) + } + return name, path, args, kwargs From d8b40056b52d6c12c1e37160f0e86f0730658d89 Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Thu, 23 Jun 2022 20:54:26 -0400 Subject: [PATCH 11/15] Fixes #8854: Remote auth default groups added to new remote auth users --- netbox/netbox/authentication.py | 25 +++++++++++++++++++++++++ netbox/netbox/settings.py | 13 +++++++++++++ 2 files changed, 38 insertions(+) diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index a13e8d192..208378872 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -1,5 +1,7 @@ import logging from collections import defaultdict +import requests +from rich import print from django.conf import settings from django.contrib.auth import get_user_model @@ -348,3 +350,26 @@ class LDAPBackend: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) return obj + + +# Custom Social Auth Pipeline Handlers +def user_default_groups_handler(backend, user, response, *args, **kwargs): + """ + Custom pipeline handler which adds remote auth users to the default group specified in the + configuration file. + """ + logger = logging.getLogger('netbox.auth.user_default_groups_handler') + if settings.REMOTE_AUTH_DEFAULT_GROUPS: + # Assign default groups to the user + group_list = [] + for name in settings.REMOTE_AUTH_DEFAULT_GROUPS: + try: + group_list.append(Group.objects.get(name=name)) + except Group.DoesNotExist: + logging.error( + f"Could not assign group {name} to remotely-authenticated user {user}: Group not found") + if group_list: + user.groups.add(*group_list) + else: + user.groups.clear() + logger.debug(f"Stripping user {user} from Groups") diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index b2e1eca6c..c0df42a2b 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -483,6 +483,19 @@ for param in dir(configuration): SOCIAL_AUTH_JSONFIELD_ENABLED = True +SOCIAL_AUTH_PIPELINE = ( + 'social_core.pipeline.social_auth.social_details', + 'social_core.pipeline.social_auth.social_uid', + 'social_core.pipeline.social_auth.social_user', + 'social_core.pipeline.user.get_username', + 'social_core.pipeline.social_auth.associate_by_email', + 'social_core.pipeline.user.create_user', + 'social_core.pipeline.social_auth.associate_user', + 'netbox.authentication.user_default_groups_handler', + 'social_core.pipeline.social_auth.load_extra_data', + 'social_core.pipeline.user.user_details', +) + # # Django Prometheus From 9b91c2a88665f3621c05ae81f52ab2a8fed60c7f Mon Sep 17 00:00:00 2001 From: Hunter Johnston <64506580+huntabyte@users.noreply.github.com> Date: Thu, 23 Jun 2022 23:29:14 -0400 Subject: [PATCH 12/15] syntax: Removed dev imports --- netbox/netbox/authentication.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index 208378872..00fb3ee66 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -1,7 +1,5 @@ import logging from collections import defaultdict -import requests -from rich import print from django.conf import settings from django.contrib.auth import get_user_model From 2077378ae174013f43cfca75bf4fc082bbfd3ed1 Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Sat, 25 Jun 2022 15:41:31 -0400 Subject: [PATCH 13/15] Closes #9540: Filter IP addresses by assigned Device/VM --- netbox/ipam/forms/filtersets.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index bbd6bb97b..3d67d4d37 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -1,7 +1,8 @@ from django import forms from django.utils.translation import gettext as _ -from dcim.models import Location, Rack, Region, Site, SiteGroup +from dcim.models import Location, Rack, Region, Site, SiteGroup, Device +from virtualization.models import VirtualMachine from ipam.choices import * from ipam.constants import * from ipam.models import * @@ -265,6 +266,7 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): ('Attributes', ('parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface')), ('VRF', ('vrf_id', 'present_in_vrf_id')), ('Tenant', ('tenant_group_id', 'tenant_id')), + ('Device/VM', ('device_id', 'virtual_machine_id')), ) parent = forms.CharField( required=False, @@ -298,6 +300,16 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): required=False, label=_('Present in VRF') ) + device_id = DynamicModelMultipleChoiceField( + queryset=Device.objects.all(), + required=False, + label=_('Assigned Device'), + ) + virtual_machine_id = DynamicModelMultipleChoiceField( + queryset=VirtualMachine.objects.all(), + required=False, + label=_('Assigned VM'), + ) status = MultipleChoiceField( choices=IPAddressStatusChoices, required=False From ccb7e96d8a294c45bf8413c4bba167e5835d8a64 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 28 Jun 2022 16:22:38 -0400 Subject: [PATCH 14/15] Changelog for #8854, #9403, #9540 --- docs/release-notes/version-3.2.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 719ceb7e1..57d965538 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -6,9 +6,12 @@ * [#7702](https://github.com/netbox-community/netbox/issues/7702) - Enable dynamic configuration for default powerfeed attributes * [#9396](https://github.com/netbox-community/netbox/issues/9396) - Allow filtering modules by bay ID +* [#9403](https://github.com/netbox-community/netbox/issues/9403) - Enable modifying virtual chassis properties when creating/editing a device +* [#9540](https://github.com/netbox-community/netbox/issues/9540) - Add filters for assigned device & VM to IP addresses list ### Bug Fixes +* [#8854](https://github.com/netbox-community/netbox/issues/8854) - Fix `REMOTE_AUTH_DEFAULT_GROUPS` for social-auth backends * [#9575](https://github.com/netbox-community/netbox/issues/9575) - Fix AttributeError exception for FHRP group with an IP address assigned * [#9597](https://github.com/netbox-community/netbox/issues/9597) - Include `installed_module` in module bay REST API serializer From 8e200a9cb485df698fe8ac30f13025fcab0f3281 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 28 Jun 2022 16:24:56 -0400 Subject: [PATCH 15/15] #9403: Add labels to device VC fields --- netbox/dcim/forms/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 2c905cc5c..043af751d 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -527,10 +527,12 @@ class DeviceForm(TenancyForm, NetBoxModelForm): ) vc_position = forms.IntegerField( required=False, + label='Position', help_text="The position in the virtual chassis this device is identified by" ) vc_priority = forms.IntegerField( required=False, + label='Priority', help_text="The priority of the device in the virtual chassis" )