mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-29 11:56:25 -06:00
Reorganize MACAddress classes out of association with DeviceComponents
This commit is contained in:
parent
eff2225464
commit
016a5335ae
@ -1274,6 +1274,28 @@ class InventoryItemTemplateBulkEditForm(BulkEditForm):
|
||||
nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description')
|
||||
|
||||
|
||||
#
|
||||
# Addressing
|
||||
#
|
||||
|
||||
class MACAddressBulkEditForm(NetBoxModelBulkEditForm):
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = MACAddress
|
||||
fieldsets = (
|
||||
FieldSet('description'),
|
||||
# FieldSet('vrf', 'mask_length', 'dns_name', name=_('Addressing')),
|
||||
)
|
||||
nullable_fields = (
|
||||
'description', 'comments',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Device components
|
||||
#
|
||||
@ -1390,24 +1412,6 @@ class PowerOutletBulkEditForm(
|
||||
self.fields['power_port'].widget.attrs['disabled'] = True
|
||||
|
||||
|
||||
class MACAddressBulkEditForm(NetBoxModelBulkEditForm):
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
comments = CommentField()
|
||||
|
||||
model = MACAddress
|
||||
fieldsets = (
|
||||
FieldSet('description'),
|
||||
# FieldSet('vrf', 'mask_length', 'dns_name', name=_('Addressing')),
|
||||
)
|
||||
nullable_fields = (
|
||||
'description', 'comments',
|
||||
)
|
||||
|
||||
|
||||
class InterfaceBulkEditForm(
|
||||
ComponentBulkEditForm,
|
||||
form_from_model(Interface, [
|
||||
|
@ -696,6 +696,102 @@ class ModuleImportForm(ModuleCommonForm, NetBoxModelImportForm):
|
||||
return self.cleaned_data['replicate_components']
|
||||
|
||||
|
||||
#
|
||||
# Addressing
|
||||
#
|
||||
|
||||
class MACAddressImportForm(NetBoxModelImportForm):
|
||||
device = CSVModelChoiceField(
|
||||
label=_('Device'),
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Parent device of assigned interface (if any)')
|
||||
)
|
||||
virtual_machine = CSVModelChoiceField(
|
||||
label=_('Virtual machine'),
|
||||
queryset=VirtualMachine.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Parent VM of assigned interface (if any)')
|
||||
)
|
||||
interface = CSVModelChoiceField(
|
||||
label=_('Interface'),
|
||||
queryset=Interface.objects.none(), # Can also refer to VMInterface
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Assigned interface')
|
||||
)
|
||||
is_primary = forms.BooleanField(
|
||||
label=_('Is primary'),
|
||||
help_text=_('Make this the primary MAC for the assigned interface'),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MACAddress
|
||||
fields = [
|
||||
'mac_address', 'device', 'virtual_machine', 'interface', 'is_primary',
|
||||
'description', 'comments', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(data, *args, **kwargs)
|
||||
|
||||
if data:
|
||||
|
||||
# Limit interface queryset by assigned device
|
||||
if data.get('device'):
|
||||
self.fields['interface'].queryset = Interface.objects.filter(
|
||||
**{f"device__{self.fields['device'].to_field_name}": data['device']}
|
||||
)
|
||||
|
||||
# Limit interface queryset by assigned device
|
||||
elif data.get('virtual_machine'):
|
||||
self.fields['interface'].queryset = VMInterface.objects.filter(
|
||||
**{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']}
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
device = self.cleaned_data.get('device')
|
||||
virtual_machine = self.cleaned_data.get('virtual_machine')
|
||||
interface = self.cleaned_data.get('interface')
|
||||
is_primary = self.cleaned_data.get('is_primary')
|
||||
|
||||
# Validate is_primary
|
||||
# TODO: scope to interface rather than device/VM
|
||||
if is_primary and not device and not virtual_machine:
|
||||
raise forms.ValidationError({
|
||||
"is_primary": _("No device or virtual machine specified; cannot set as primary MAC")
|
||||
})
|
||||
if is_primary and not interface:
|
||||
raise forms.ValidationError({
|
||||
"is_primary": _("No interface specified; cannot set as primary MAC")
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Set interface assignment
|
||||
if self.cleaned_data.get('interface'):
|
||||
self.instance.assigned_object = self.cleaned_data['interface']
|
||||
|
||||
mac_address = super().save(*args, **kwargs)
|
||||
|
||||
# Set as primary for device/VM
|
||||
# TODO: set as primary for interface
|
||||
# if self.cleaned_data.get('is_primary'):
|
||||
# parent = self.cleaned_data.get('device') or self.cleaned_data.get('virtual_machine')
|
||||
# if self.instance.address.version == 4:
|
||||
# parent.primary_ip4 = ipaddress
|
||||
# elif self.instance.address.version == 6:
|
||||
# parent.primary_ip6 = ipaddress
|
||||
# parent.save()
|
||||
|
||||
return mac_address
|
||||
|
||||
|
||||
#
|
||||
# Device components
|
||||
#
|
||||
@ -825,98 +921,6 @@ class PowerOutletImportForm(NetBoxModelImportForm):
|
||||
self.fields['power_port'].queryset = PowerPort.objects.none()
|
||||
|
||||
|
||||
class MACAddressImportForm(NetBoxModelImportForm):
|
||||
device = CSVModelChoiceField(
|
||||
label=_('Device'),
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Parent device of assigned interface (if any)')
|
||||
)
|
||||
virtual_machine = CSVModelChoiceField(
|
||||
label=_('Virtual machine'),
|
||||
queryset=VirtualMachine.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Parent VM of assigned interface (if any)')
|
||||
)
|
||||
interface = CSVModelChoiceField(
|
||||
label=_('Interface'),
|
||||
queryset=Interface.objects.none(), # Can also refer to VMInterface
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Assigned interface')
|
||||
)
|
||||
is_primary = forms.BooleanField(
|
||||
label=_('Is primary'),
|
||||
help_text=_('Make this the primary MAC for the assigned interface'),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MACAddress
|
||||
fields = [
|
||||
'mac_address', 'device', 'virtual_machine', 'interface', 'is_primary',
|
||||
'description', 'comments', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, data=None, *args, **kwargs):
|
||||
super().__init__(data, *args, **kwargs)
|
||||
|
||||
if data:
|
||||
|
||||
# Limit interface queryset by assigned device
|
||||
if data.get('device'):
|
||||
self.fields['interface'].queryset = Interface.objects.filter(
|
||||
**{f"device__{self.fields['device'].to_field_name}": data['device']}
|
||||
)
|
||||
|
||||
# Limit interface queryset by assigned device
|
||||
elif data.get('virtual_machine'):
|
||||
self.fields['interface'].queryset = VMInterface.objects.filter(
|
||||
**{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']}
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
device = self.cleaned_data.get('device')
|
||||
virtual_machine = self.cleaned_data.get('virtual_machine')
|
||||
interface = self.cleaned_data.get('interface')
|
||||
is_primary = self.cleaned_data.get('is_primary')
|
||||
|
||||
# Validate is_primary
|
||||
# TODO: scope to interface rather than device/VM
|
||||
if is_primary and not device and not virtual_machine:
|
||||
raise forms.ValidationError({
|
||||
"is_primary": _("No device or virtual machine specified; cannot set as primary MAC")
|
||||
})
|
||||
if is_primary and not interface:
|
||||
raise forms.ValidationError({
|
||||
"is_primary": _("No interface specified; cannot set as primary MAC")
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Set interface assignment
|
||||
if self.cleaned_data.get('interface'):
|
||||
self.instance.assigned_object = self.cleaned_data['interface']
|
||||
|
||||
mac_address = super().save(*args, **kwargs)
|
||||
|
||||
# Set as primary for device/VM
|
||||
# TODO: set as primary for interface
|
||||
# if self.cleaned_data.get('is_primary'):
|
||||
# parent = self.cleaned_data.get('device') or self.cleaned_data.get('virtual_machine')
|
||||
# if self.instance.address.version == 4:
|
||||
# parent.primary_ip4 = ipaddress
|
||||
# elif self.instance.address.version == 6:
|
||||
# parent.primary_ip6 = ipaddress
|
||||
# parent.save()
|
||||
|
||||
return mac_address
|
||||
|
||||
|
||||
class InterfaceImportForm(NetBoxModelImportForm):
|
||||
device = CSVModelChoiceField(
|
||||
label=_('Device'),
|
||||
|
@ -1202,6 +1202,24 @@ class PowerFeedFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
#
|
||||
# Addressing
|
||||
#
|
||||
|
||||
class MACAddressFilterForm(NetBoxModelFilterSetForm):
|
||||
model = MACAddress
|
||||
fieldsets = (
|
||||
FieldSet('q', 'filter_id', 'tag'),
|
||||
FieldSet('mac_address', name=_('Addressing')),
|
||||
)
|
||||
selector_fields = ('filter_id', 'q', 'device_id')
|
||||
mac_address = forms.CharField(
|
||||
required=False,
|
||||
label=_('MAC address')
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
#
|
||||
# Device components
|
||||
#
|
||||
@ -1325,20 +1343,6 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
|
||||
)
|
||||
|
||||
|
||||
class MACAddressFilterForm(NetBoxModelFilterSetForm):
|
||||
model = MACAddress
|
||||
fieldsets = (
|
||||
FieldSet('q', 'filter_id', 'tag'),
|
||||
FieldSet('mac_address', name=_('Addressing')),
|
||||
)
|
||||
selector_fields = ('filter_id', 'q', 'device_id')
|
||||
mac_address = forms.CharField(
|
||||
required=False,
|
||||
label=_('MAC address')
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
|
||||
model = Interface
|
||||
fieldsets = (
|
||||
|
@ -866,6 +866,93 @@ class VCMemberSelectForm(forms.Form):
|
||||
return device
|
||||
|
||||
|
||||
#
|
||||
# Addressing
|
||||
#
|
||||
|
||||
class MACAddressForm(NetBoxModelForm):
|
||||
mac_address = forms.CharField(
|
||||
required=True,
|
||||
label=_('MAC address')
|
||||
)
|
||||
interface = DynamicModelChoiceField(
|
||||
label=_('Interface'),
|
||||
queryset=Interface.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
vminterface = DynamicModelChoiceField(
|
||||
label=_('VM Interface'),
|
||||
queryset=VMInterface.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
is_primary = forms.BooleanField(
|
||||
required=False,
|
||||
label=_('Primary for interface'),
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
FieldSet(
|
||||
'mac_address', 'description', 'tags',
|
||||
),
|
||||
FieldSet(
|
||||
TabbedGroups(
|
||||
FieldSet('interface', name=_('Device')),
|
||||
FieldSet('vminterface', name=_('Virtual Machine')),
|
||||
),
|
||||
'is_primary', name=_('Assignment')
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MACAddress
|
||||
fields = [
|
||||
'mac_address', 'interface', 'vminterface', 'is_primary', 'description', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
# Initialize helper selectors
|
||||
instance = kwargs.get('instance')
|
||||
initial = kwargs.get('initial', {}).copy()
|
||||
if instance:
|
||||
if type(instance.assigned_object) is Interface:
|
||||
initial['interface'] = instance.assigned_object
|
||||
elif type(instance.assigned_object) is VMInterface:
|
||||
initial['vminterface'] = instance.assigned_object
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Handle object assignment
|
||||
selected_objects = [
|
||||
field for field in ('interface', 'vminterface') if self.cleaned_data[field]
|
||||
]
|
||||
if len(selected_objects) > 1:
|
||||
raise forms.ValidationError({
|
||||
selected_objects[1]: _("A MAC address can only be assigned to a single object.")
|
||||
})
|
||||
elif selected_objects:
|
||||
assigned_object = self.cleaned_data[selected_objects[0]]
|
||||
if self.instance.pk and self.instance.assigned_object and self.cleaned_data['is_primary'] and assigned_object != self.instance.assigned_object:
|
||||
raise ValidationError(
|
||||
_("Cannot reassign MAC address while it is designated as the primary MAC for the interface")
|
||||
)
|
||||
self.instance.assigned_object = assigned_object
|
||||
else:
|
||||
self.instance.assigned_object = None
|
||||
|
||||
# Primary MAC assignment is only available if an interface has been assigned.
|
||||
interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
|
||||
if self.cleaned_data.get('is_primary') and not interface:
|
||||
self.add_error(
|
||||
'is_primary', _("Only IP addresses assigned to an interface can be designated as primary IPs.")
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Device component templates
|
||||
#
|
||||
@ -1301,89 +1388,6 @@ class PowerOutletForm(ModularDeviceComponentForm):
|
||||
]
|
||||
|
||||
|
||||
class MACAddressForm(NetBoxModelForm):
|
||||
mac_address = forms.CharField(
|
||||
required=True,
|
||||
label=_('MAC address')
|
||||
)
|
||||
interface = DynamicModelChoiceField(
|
||||
label=_('Interface'),
|
||||
queryset=Interface.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
vminterface = DynamicModelChoiceField(
|
||||
label=_('VM Interface'),
|
||||
queryset=VMInterface.objects.all(),
|
||||
required=False,
|
||||
)
|
||||
is_primary = forms.BooleanField(
|
||||
required=False,
|
||||
label=_('Primary for interface'),
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
FieldSet(
|
||||
'mac_address', 'description', 'tags',
|
||||
),
|
||||
FieldSet(
|
||||
TabbedGroups(
|
||||
FieldSet('interface', name=_('Device')),
|
||||
FieldSet('vminterface', name=_('Virtual Machine')),
|
||||
),
|
||||
'is_primary', name=_('Assignment')
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MACAddress
|
||||
fields = [
|
||||
'mac_address', 'interface', 'vminterface', 'is_primary', 'description', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
# Initialize helper selectors
|
||||
instance = kwargs.get('instance')
|
||||
initial = kwargs.get('initial', {}).copy()
|
||||
if instance:
|
||||
if type(instance.assigned_object) is Interface:
|
||||
initial['interface'] = instance.assigned_object
|
||||
elif type(instance.assigned_object) is VMInterface:
|
||||
initial['vminterface'] = instance.assigned_object
|
||||
kwargs['initial'] = initial
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Handle object assignment
|
||||
selected_objects = [
|
||||
field for field in ('interface', 'vminterface') if self.cleaned_data[field]
|
||||
]
|
||||
if len(selected_objects) > 1:
|
||||
raise forms.ValidationError({
|
||||
selected_objects[1]: _("A MAC address can only be assigned to a single object.")
|
||||
})
|
||||
elif selected_objects:
|
||||
assigned_object = self.cleaned_data[selected_objects[0]]
|
||||
if self.instance.pk and self.instance.assigned_object and self.cleaned_data['is_primary'] and assigned_object != self.instance.assigned_object:
|
||||
raise ValidationError(
|
||||
_("Cannot reassign MAC address while it is designated as the primary MAC for the interface")
|
||||
)
|
||||
self.instance.assigned_object = assigned_object
|
||||
else:
|
||||
self.instance.assigned_object = None
|
||||
|
||||
# Primary MAC assignment is only available if an interface has been assigned.
|
||||
interface = self.cleaned_data.get('interface') or self.cleaned_data.get('vminterface')
|
||||
if self.cleaned_data.get('is_primary') and not interface:
|
||||
self.add_error(
|
||||
'is_primary', _("Only IP addresses assigned to an interface can be designated as primary IPs.")
|
||||
)
|
||||
|
||||
|
||||
class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
|
||||
vdcs = DynamicModelMultipleChoiceField(
|
||||
queryset=VirtualDeviceContext.objects.all(),
|
||||
|
@ -23,7 +23,6 @@ __all__ = (
|
||||
'InventoryItemCreateForm',
|
||||
'InventoryItemTemplateCreateForm',
|
||||
'ModuleBayCreateForm',
|
||||
# 'MACAddressCreateForm',
|
||||
'ModuleBayTemplateCreateForm',
|
||||
'PowerOutletCreateForm',
|
||||
'PowerOutletTemplateCreateForm',
|
||||
@ -239,12 +238,6 @@ class PowerOutletCreateForm(ComponentCreateForm, model_forms.PowerOutletForm):
|
||||
exclude = ('name', 'label')
|
||||
|
||||
|
||||
# class MACAddressCreateForm(ComponentCreateForm, model_forms.MACAddressForm):
|
||||
#
|
||||
# class Meta(model_forms.MACAddressForm.Meta):
|
||||
# exclude = ('name', 'label')
|
||||
|
||||
|
||||
class InterfaceCreateForm(ComponentCreateForm, model_forms.InterfaceForm):
|
||||
|
||||
class Meta(model_forms.InterfaceForm.Meta):
|
||||
|
@ -10,9 +10,9 @@ from mptt.models import MPTTModel, TreeForeignKey
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.fields import MACAddressField, WWNField
|
||||
from dcim.fields import WWNField
|
||||
from netbox.choices import ColorChoices
|
||||
from netbox.models import OrganizationalModel, NetBoxModel, PrimaryModel
|
||||
from netbox.models import OrganizationalModel, NetBoxModel
|
||||
from utilities.fields import ColorField, NaturalOrderingField
|
||||
from utilities.mptt import TreeManager
|
||||
from utilities.ordering import naturalize_interface
|
||||
@ -31,7 +31,6 @@ __all__ = (
|
||||
'Interface',
|
||||
'InventoryItem',
|
||||
'InventoryItemRole',
|
||||
'MACAddress',
|
||||
'ModuleBay',
|
||||
'PathEndpoint',
|
||||
'PowerOutlet',
|
||||
@ -1355,39 +1354,3 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
||||
|
||||
def get_status_color(self):
|
||||
return InventoryItemStatusChoices.colors.get(self.status)
|
||||
|
||||
|
||||
class MACAddress(PrimaryModel):
|
||||
mac_address = MACAddressField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_('MAC address')
|
||||
)
|
||||
assigned_object_type = models.ForeignKey(
|
||||
to='contenttypes.ContentType',
|
||||
limit_choices_to=MACADDRESS_ASSIGNMENT_MODELS,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='+',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
assigned_object_id = models.PositiveBigIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
assigned_object = GenericForeignKey(
|
||||
ct_field='assigned_object_type',
|
||||
fk_field='assigned_object_id'
|
||||
)
|
||||
is_primary = models.BooleanField(
|
||||
verbose_name=_('is primary for interface'),
|
||||
default=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('mac_address',)
|
||||
verbose_name = _('MAC address')
|
||||
verbose_name_plural = _('MAC addresses')
|
||||
|
||||
def __str__(self):
|
||||
return f'{str(self.mac_address)} {self.assigned_object}'
|
||||
|
@ -3,6 +3,7 @@ import yaml
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
@ -16,6 +17,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.fields import MACAddressField
|
||||
from extras.models import ConfigContextModel, CustomField
|
||||
from extras.querysets import ConfigContextModelQuerySet
|
||||
from netbox.choices import ColorChoices
|
||||
@ -33,6 +35,7 @@ __all__ = (
|
||||
'Device',
|
||||
'DeviceRole',
|
||||
'DeviceType',
|
||||
'MACAddress',
|
||||
'Manufacturer',
|
||||
'Module',
|
||||
'ModuleType',
|
||||
@ -1473,3 +1476,43 @@ class VirtualDeviceContext(PrimaryModel):
|
||||
raise ValidationError({
|
||||
f'primary_ip{family}': _('Primary IP address must belong to an interface on the assigned device.')
|
||||
})
|
||||
|
||||
|
||||
#
|
||||
# Addressing
|
||||
#
|
||||
|
||||
class MACAddress(PrimaryModel):
|
||||
mac_address = MACAddressField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_('MAC address')
|
||||
)
|
||||
assigned_object_type = models.ForeignKey(
|
||||
to='contenttypes.ContentType',
|
||||
limit_choices_to=MACADDRESS_ASSIGNMENT_MODELS,
|
||||
on_delete=models.PROTECT,
|
||||
related_name='+',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
assigned_object_id = models.PositiveBigIntegerField(
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
assigned_object = GenericForeignKey(
|
||||
ct_field='assigned_object_type',
|
||||
fk_field='assigned_object_id'
|
||||
)
|
||||
is_primary = models.BooleanField(
|
||||
verbose_name=_('is primary for interface'),
|
||||
default=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('mac_address',)
|
||||
verbose_name = _('MAC address')
|
||||
verbose_name_plural = _('MAC addresses')
|
||||
|
||||
def __str__(self):
|
||||
return f'{str(self.mac_address)} {self.assigned_object}'
|
||||
|
@ -89,9 +89,14 @@ DEVICES_MENU = Menu(
|
||||
),
|
||||
),
|
||||
MenuGroup(
|
||||
label=_('Device Components'),
|
||||
label=_('Addressing'),
|
||||
items=(
|
||||
get_model_item('dcim', 'macaddress', _('MAC Addresses')),
|
||||
),
|
||||
),
|
||||
MenuGroup(
|
||||
label=_('Device Components'),
|
||||
items=(
|
||||
get_model_item('dcim', 'interface', _('Interfaces')),
|
||||
get_model_item('dcim', 'frontport', _('Front Ports')),
|
||||
get_model_item('dcim', 'rearport', _('Rear Ports')),
|
||||
|
Loading…
Reference in New Issue
Block a user