mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 04:02:52 -06:00
Merge branch 'develop' into feature
This commit is contained in:
commit
cd3111ca8d
@ -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
|
## 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:
|
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
|
## MAINTENANCE_MODE
|
||||||
|
|
||||||
Default: False
|
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
|
## PREFER_IPV4
|
||||||
|
|
||||||
Default: False
|
Default: False
|
||||||
|
@ -2,6 +2,19 @@
|
|||||||
|
|
||||||
## v3.2.6 (FUTURE)
|
## v3.2.6 (FUTURE)
|
||||||
|
|
||||||
|
### 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
|
||||||
|
* [#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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v3.2.5 (2022-06-20)
|
## v3.2.5 (2022-06-20)
|
||||||
|
@ -5,6 +5,7 @@ from netbox.api.serializers import BaseModelSerializer, WritableNestedSerializer
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ComponentNestedModuleSerializer',
|
'ComponentNestedModuleSerializer',
|
||||||
|
'ModuleBayNestedModuleSerializer',
|
||||||
'NestedCableSerializer',
|
'NestedCableSerializer',
|
||||||
'NestedConsolePortSerializer',
|
'NestedConsolePortSerializer',
|
||||||
'NestedConsolePortTemplateSerializer',
|
'NestedConsolePortTemplateSerializer',
|
||||||
@ -281,6 +282,14 @@ class ModuleNestedModuleBaySerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'display', 'name']
|
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):
|
class ComponentNestedModuleSerializer(WritableNestedSerializer):
|
||||||
"""
|
"""
|
||||||
Used by device component serializers.
|
Used by device component serializers.
|
||||||
|
@ -912,12 +912,12 @@ class FrontPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
|
|||||||
class ModuleBaySerializer(NetBoxModelSerializer):
|
class ModuleBaySerializer(NetBoxModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
# installed_module = NestedModuleSerializer(required=False, allow_null=True)
|
installed_module = ModuleBayNestedModuleSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ModuleBay
|
model = ModuleBay
|
||||||
fields = [
|
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',
|
'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -620,7 +620,7 @@ class RearPortViewSet(PassThroughPortMixin, NetBoxModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class ModuleBayViewSet(NetBoxModelViewSet):
|
class ModuleBayViewSet(NetBoxModelViewSet):
|
||||||
queryset = ModuleBay.objects.prefetch_related('tags')
|
queryset = ModuleBay.objects.prefetch_related('tags', 'installed_module')
|
||||||
serializer_class = serializers.ModuleBaySerializer
|
serializer_class = serializers.ModuleBaySerializer
|
||||||
filterset_class = filtersets.ModuleBayFilterSet
|
filterset_class = filtersets.ModuleBayFilterSet
|
||||||
brief_prefetch_fields = ['device']
|
brief_prefetch_fields = ['device']
|
||||||
|
@ -50,15 +50,6 @@ WIRELESS_IFACE_TYPES = [
|
|||||||
NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + 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
|
# Device components
|
||||||
#
|
#
|
||||||
|
@ -996,6 +996,12 @@ class ModuleFilterSet(NetBoxModelFilterSet):
|
|||||||
to_field_name='model',
|
to_field_name='model',
|
||||||
label='Module type (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(
|
device_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
label='Device (ID)',
|
label='Device (ID)',
|
||||||
|
@ -525,13 +525,28 @@ class DeviceForm(TenancyForm, NetBoxModelForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=''
|
label=''
|
||||||
)
|
)
|
||||||
|
virtual_chassis = DynamicModelChoiceField(
|
||||||
|
queryset=VirtualChassis.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack',
|
'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'region', 'site_group', 'site', 'rack',
|
||||||
'location', 'position', 'face', 'status', 'airflow', 'platform', 'primary_ip4', 'primary_ip6',
|
'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 = {
|
help_texts = {
|
||||||
'device_role': "The function this device serves",
|
'device_role': "The function this device serves",
|
||||||
|
@ -386,9 +386,9 @@ class Migration(migrations.Migration):
|
|||||||
('type', models.CharField(default='primary', max_length=50)),
|
('type', models.CharField(default='primary', max_length=50)),
|
||||||
('supply', models.CharField(default='ac', max_length=50)),
|
('supply', models.CharField(default='ac', max_length=50)),
|
||||||
('phase', models.CharField(default='single-phase', max_length=50)),
|
('phase', models.CharField(default='single-phase', max_length=50)),
|
||||||
('voltage', models.SmallIntegerField(default=120, validators=[utilities.validators.ExclusionValidator([0])])),
|
('voltage', models.SmallIntegerField(validators=[utilities.validators.ExclusionValidator([0])])),
|
||||||
('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])),
|
('amperage', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1)])),
|
||||||
('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
|
('max_utilization', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
|
||||||
('available_power', models.PositiveIntegerField(default=0, editable=False)),
|
('available_power', models.PositiveIntegerField(default=0, editable=False)),
|
||||||
('comments', models.TextField(blank=True)),
|
('comments', models.TextField(blank=True)),
|
||||||
],
|
],
|
||||||
|
@ -6,6 +6,7 @@ from django.urls import reverse
|
|||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
|
from netbox.config import ConfigItem
|
||||||
from netbox.models import NetBoxModel
|
from netbox.models import NetBoxModel
|
||||||
from utilities.validators import ExclusionValidator
|
from utilities.validators import ExclusionValidator
|
||||||
from .device_components import LinkTermination, PathEndpoint
|
from .device_components import LinkTermination, PathEndpoint
|
||||||
@ -105,16 +106,16 @@ class PowerFeed(NetBoxModel, PathEndpoint, LinkTermination):
|
|||||||
default=PowerFeedPhaseChoices.PHASE_SINGLE
|
default=PowerFeedPhaseChoices.PHASE_SINGLE
|
||||||
)
|
)
|
||||||
voltage = models.SmallIntegerField(
|
voltage = models.SmallIntegerField(
|
||||||
default=POWERFEED_VOLTAGE_DEFAULT,
|
default=ConfigItem('POWERFEED_DEFAULT_VOLTAGE'),
|
||||||
validators=[ExclusionValidator([0])]
|
validators=[ExclusionValidator([0])]
|
||||||
)
|
)
|
||||||
amperage = models.PositiveSmallIntegerField(
|
amperage = models.PositiveSmallIntegerField(
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
default=POWERFEED_AMPERAGE_DEFAULT
|
default=ConfigItem('POWERFEED_DEFAULT_AMPERAGE')
|
||||||
)
|
)
|
||||||
max_utilization = models.PositiveSmallIntegerField(
|
max_utilization = models.PositiveSmallIntegerField(
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||||
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
|
default=ConfigItem('POWERFEED_DEFAULT_MAX_UTILIZATION'),
|
||||||
help_text="Maximum permissible draw (percentage)"
|
help_text="Maximum permissible draw (percentage)"
|
||||||
)
|
)
|
||||||
available_power = models.PositiveIntegerField(
|
available_power = models.PositiveIntegerField(
|
||||||
|
@ -1853,6 +1853,11 @@ class ModuleTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'module_type': [module_types[0].model, module_types[1].model]}
|
params = {'module_type': [module_types[0].model, module_types[1].model]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
|
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):
|
def test_device(self):
|
||||||
device_types = Device.objects.all()[:2]
|
device_types = Device.objects.all()[:2]
|
||||||
params = {'device_id': [device_types[0].pk, device_types[1].pk]}
|
params = {'device_id': [device_types[0].pk, device_types[1].pk]}
|
||||||
|
@ -15,6 +15,9 @@ class ConfigRevisionAdmin(admin.ModelAdmin):
|
|||||||
('Rack Elevations', {
|
('Rack Elevations', {
|
||||||
'fields': ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH'),
|
'fields': ('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH'),
|
||||||
}),
|
}),
|
||||||
|
('Power', {
|
||||||
|
'fields': ('POWERFEED_DEFAULT_VOLTAGE', 'POWERFEED_DEFAULT_AMPERAGE', 'POWERFEED_DEFAULT_MAX_UTILIZATION')
|
||||||
|
}),
|
||||||
('IPAM', {
|
('IPAM', {
|
||||||
'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'),
|
'fields': ('ENFORCE_GLOBAL_UNIQUE', 'PREFER_IPV4'),
|
||||||
}),
|
}),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext as _
|
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.choices import *
|
||||||
from ipam.constants import *
|
from ipam.constants import *
|
||||||
from ipam.models import *
|
from ipam.models import *
|
||||||
@ -265,6 +266,7 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
('Attributes', ('parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface')),
|
('Attributes', ('parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface')),
|
||||||
('VRF', ('vrf_id', 'present_in_vrf_id')),
|
('VRF', ('vrf_id', 'present_in_vrf_id')),
|
||||||
('Tenant', ('tenant_group_id', 'tenant_id')),
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Device/VM', ('device_id', 'virtual_machine_id')),
|
||||||
)
|
)
|
||||||
parent = forms.CharField(
|
parent = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -298,6 +300,16 @@ class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Present in VRF')
|
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(
|
status = MultipleChoiceField(
|
||||||
choices=IPAddressStatusChoices,
|
choices=IPAddressStatusChoices,
|
||||||
required=False
|
required=False
|
||||||
|
@ -680,13 +680,16 @@ class IPAddressView(generic.ObjectView):
|
|||||||
service_filter = Q(ipaddresses=instance)
|
service_filter = Q(ipaddresses=instance)
|
||||||
|
|
||||||
# Find services listening on all IPs on the assigned device/vm
|
# Find services listening on all IPs on the assigned device/vm
|
||||||
if instance.assigned_object and instance.assigned_object.parent_object:
|
try:
|
||||||
parent_object = instance.assigned_object.parent_object
|
if instance.assigned_object and instance.assigned_object.parent_object:
|
||||||
|
parent_object = instance.assigned_object.parent_object
|
||||||
|
|
||||||
if isinstance(parent_object, VirtualMachine):
|
if isinstance(parent_object, VirtualMachine):
|
||||||
service_filter |= (Q(virtual_machine=parent_object) & Q(ipaddresses=None))
|
service_filter |= (Q(virtual_machine=parent_object) & Q(ipaddresses=None))
|
||||||
elif isinstance(parent_object, Device):
|
elif isinstance(parent_object, Device):
|
||||||
service_filter |= (Q(device=parent_object) & Q(ipaddresses=None))
|
service_filter |= (Q(device=parent_object) & Q(ipaddresses=None))
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
services = Service.objects.restrict(request.user, 'view').filter(service_filter)
|
services = Service.objects.restrict(request.user, 'view').filter(service_filter)
|
||||||
|
|
||||||
|
@ -348,3 +348,26 @@ class LDAPBackend:
|
|||||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||||
|
|
||||||
return obj
|
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")
|
||||||
|
@ -82,6 +82,31 @@ PARAMS = (
|
|||||||
field=forms.IntegerField
|
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
|
# Security
|
||||||
ConfigParam(
|
ConfigParam(
|
||||||
name='ALLOWED_URL_SCHEMES',
|
name='ALLOWED_URL_SCHEMES',
|
||||||
|
@ -485,6 +485,19 @@ for param in dir(configuration):
|
|||||||
|
|
||||||
SOCIAL_AUTH_JSONFIELD_ENABLED = True
|
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
|
# Django Prometheus
|
||||||
|
@ -86,6 +86,15 @@
|
|||||||
{% render_field form.tenant %}
|
{% render_field form.tenant %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group my-5">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<h5 class="offset-sm-3">Virtual Chassis</h5>
|
||||||
|
</div>
|
||||||
|
{% render_field form.virtual_chassis %}
|
||||||
|
{% render_field form.vc_position %}
|
||||||
|
{% render_field form.vc_priority %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if form.custom_fields %}
|
{% if form.custom_fields %}
|
||||||
<div class="field-group my-5">
|
<div class="field-group my-5">
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from timezone_field import TimeZoneField
|
from timezone_field import TimeZoneField
|
||||||
|
|
||||||
|
from netbox.config import ConfigItem
|
||||||
|
|
||||||
|
|
||||||
SKIP_FIELDS = (
|
SKIP_FIELDS = (
|
||||||
TimeZoneField,
|
TimeZoneField,
|
||||||
@ -26,4 +28,9 @@ def custom_deconstruct(field):
|
|||||||
for attr in EXEMPT_ATTRS:
|
for attr in EXEMPT_ATTRS:
|
||||||
kwargs.pop(attr, None)
|
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
|
return name, path, args, kwargs
|
||||||
|
Loading…
Reference in New Issue
Block a user