mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 12:12:53 -06:00
Add bridge to InterfaceTemplate
This commit is contained in:
parent
c44eb65993
commit
a74ae46f86
@ -475,6 +475,7 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
|||||||
default=None
|
default=None
|
||||||
)
|
)
|
||||||
type = ChoiceField(choices=InterfaceTypeChoices)
|
type = ChoiceField(choices=InterfaceTypeChoices)
|
||||||
|
bridge = NestedInterfaceTemplateSerializer(required=False, allow_null=True)
|
||||||
poe_mode = ChoiceField(
|
poe_mode = ChoiceField(
|
||||||
choices=InterfacePoEModeChoices,
|
choices=InterfacePoEModeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -489,7 +490,7 @@ class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceTemplate
|
model = InterfaceTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description',
|
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'bridge', 'enabled', 'mgmt_only', 'description',
|
||||||
'poe_mode', 'poe_type', 'created', 'last_updated',
|
'poe_mode', 'poe_type', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1020,15 +1020,24 @@ class PowerOutletTemplateForm(ModularComponentTemplateForm):
|
|||||||
|
|
||||||
|
|
||||||
class InterfaceTemplateForm(ModularComponentTemplateForm):
|
class InterfaceTemplateForm(ModularComponentTemplateForm):
|
||||||
|
bridge = DynamicModelChoiceField(
|
||||||
|
queryset=InterfaceTemplate.objects.all(),
|
||||||
|
required=False,
|
||||||
|
query_params={
|
||||||
|
'devicetype_id': '$device_type',
|
||||||
|
'moduletype_id': '$module_type',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description')),
|
(None, ('device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only', 'description', 'bridge')),
|
||||||
('PoE', ('poe_mode', 'poe_type'))
|
('PoE', ('poe_mode', 'poe_type'))
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceTemplate
|
model = InterfaceTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', 'poe_type',
|
'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'enabled', 'description', 'poe_mode', 'poe_type', 'bridge',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
19
netbox/dcim/migrations/0171_devicetype_add_bridge.py
Normal file
19
netbox/dcim/migrations/0171_devicetype_add_bridge.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.1.6 on 2023-03-01 13:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0170_configtemplate'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interfacetemplate',
|
||||||
|
name='bridge',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bridge_interfaces', to='dcim.interfacetemplate'),
|
||||||
|
),
|
||||||
|
]
|
@ -350,6 +350,14 @@ class InterfaceTemplate(ModularComponentTemplateModel):
|
|||||||
default=False,
|
default=False,
|
||||||
verbose_name='Management only'
|
verbose_name='Management only'
|
||||||
)
|
)
|
||||||
|
bridge = models.ForeignKey(
|
||||||
|
to='self',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='bridge_interfaces',
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name='Bridge interface'
|
||||||
|
)
|
||||||
poe_mode = models.CharField(
|
poe_mode = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfacePoEModeChoices,
|
choices=InterfacePoEModeChoices,
|
||||||
@ -365,6 +373,19 @@ class InterfaceTemplate(ModularComponentTemplateModel):
|
|||||||
|
|
||||||
component_model = Interface
|
component_model = Interface
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
|
||||||
|
if self.bridge:
|
||||||
|
if self.device_type and self.device_type != self.bridge.device_type:
|
||||||
|
raise ValidationError({
|
||||||
|
'bridge': f"Bridge interface ({self.bridge}) must belong to the same device type"
|
||||||
|
})
|
||||||
|
if self.module_type and self.module_type != self.bridge.module_type:
|
||||||
|
raise ValidationError({
|
||||||
|
'bridge': f"Bridge interface ({self.bridge}) must belong to the same module type"
|
||||||
|
})
|
||||||
|
|
||||||
def instantiate(self, **kwargs):
|
def instantiate(self, **kwargs):
|
||||||
return self.component_model(
|
return self.component_model(
|
||||||
name=self.resolve_name(kwargs.get('module')),
|
name=self.resolve_name(kwargs.get('module')),
|
||||||
@ -385,6 +406,7 @@ class InterfaceTemplate(ModularComponentTemplateModel):
|
|||||||
'mgmt_only': self.mgmt_only,
|
'mgmt_only': self.mgmt_only,
|
||||||
'label': self.label,
|
'label': self.label,
|
||||||
'description': self.description,
|
'description': self.description,
|
||||||
|
'bridge': self.bridge.name if self.bridge else None,
|
||||||
'poe_mode': self.poe_mode,
|
'poe_mode': self.poe_mode,
|
||||||
'poe_type': self.poe_type,
|
'poe_type': self.poe_type,
|
||||||
}
|
}
|
||||||
|
@ -802,6 +802,15 @@ class Device(PrimaryModel, ConfigContextModel):
|
|||||||
'vc_position': "A device assigned to a virtual chassis must have its position defined."
|
'vc_position': "A device assigned to a virtual chassis must have its position defined."
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def _update_interface_bridges(self, interface_templates, module=None):
|
||||||
|
for interface_template in interface_templates.exclude(bridge=None):
|
||||||
|
interface = Interface.objects.get(device=self, name=interface_template.resolve_name(module=module))
|
||||||
|
|
||||||
|
if interface_template.bridge:
|
||||||
|
interface.bridge = Interface.objects.get(device=self, name=interface_template.bridge.resolve_name(module=module))
|
||||||
|
interface.full_clean()
|
||||||
|
interface.save()
|
||||||
|
|
||||||
def _instantiate_components(self, queryset, bulk_create=True):
|
def _instantiate_components(self, queryset, bulk_create=True):
|
||||||
"""
|
"""
|
||||||
Instantiate components for the device from the specified component templates.
|
Instantiate components for the device from the specified component templates.
|
||||||
@ -854,6 +863,8 @@ class Device(PrimaryModel, ConfigContextModel):
|
|||||||
self._instantiate_components(self.device_type.devicebaytemplates.all())
|
self._instantiate_components(self.device_type.devicebaytemplates.all())
|
||||||
# Disable bulk_create to accommodate MPTT
|
# Disable bulk_create to accommodate MPTT
|
||||||
self._instantiate_components(self.device_type.inventoryitemtemplates.all(), bulk_create=False)
|
self._instantiate_components(self.device_type.inventoryitemtemplates.all(), bulk_create=False)
|
||||||
|
# Interface bridges have to be set after interface instantiation
|
||||||
|
self._update_interface_bridges(self.device_type.interfacetemplates.all())
|
||||||
|
|
||||||
# Update Site and Rack assignment for any child Devices
|
# Update Site and Rack assignment for any child Devices
|
||||||
devices = Device.objects.filter(parent_bay__device=self)
|
devices = Device.objects.filter(parent_bay__device=self)
|
||||||
@ -1015,6 +1026,15 @@ class Module(PrimaryModel, ConfigContextModel):
|
|||||||
f"Module must be installed within a module bay belonging to the assigned device ({self.device})."
|
f"Module must be installed within a module bay belonging to the assigned device ({self.device})."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _update_interface_bridges(self, interface_templates, module=None):
|
||||||
|
for interface_template in interface_templates.exclude(bridge=None):
|
||||||
|
interface = Interface.objects.get(device=self.device, name=interface_template.resolve_name(module=module))
|
||||||
|
|
||||||
|
if interface_template.bridge:
|
||||||
|
interface.bridge = Interface.objects.get(device=self.device, name=interface_template.bridge.resolve_name(module=module))
|
||||||
|
interface.full_clean()
|
||||||
|
interface.save()
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
is_new = self.pk is None
|
is_new = self.pk is None
|
||||||
|
|
||||||
@ -1090,6 +1110,9 @@ class Module(PrimaryModel, ConfigContextModel):
|
|||||||
update_fields=update_fields
|
update_fields=update_fields
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Interface bridges have to be set after interface instantiation
|
||||||
|
self._update_interface_bridges(self.module_type.interfacetemplates, self)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
|
@ -187,7 +187,7 @@ class InterfaceTemplateTable(ComponentTemplateTable):
|
|||||||
|
|
||||||
class Meta(ComponentTemplateTable.Meta):
|
class Meta(ComponentTemplateTable.Meta):
|
||||||
model = models.InterfaceTemplate
|
model = models.InterfaceTemplate
|
||||||
fields = ('pk', 'name', 'label', 'enabled', 'mgmt_only', 'type', 'description', 'poe_mode', 'poe_type', 'actions')
|
fields = ('pk', 'name', 'label', 'enabled', 'mgmt_only', 'type', 'description', 'bridge', 'poe_mode', 'poe_type', 'actions')
|
||||||
empty_text = "None"
|
empty_text = "None"
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user