Misc cleanup

This commit is contained in:
Jeremy Stretch 2024-11-15 13:40:16 -05:00
parent 3183f376bd
commit 0575dfff85
9 changed files with 33 additions and 18 deletions

View File

@ -1,8 +1,8 @@
# MAC Addresses
A MAC address object in NetBox comprises a single physical (hardware) address, and represents a MAC address as reported by or assigned to a network interface. MAC addresses can be assigned to [device](../dcim/device.md) and [virtual machine](../virtualization/virtualmachine.md) interfaces. A MAC address can be specified as the "primary" MAC address for a given interface or VM interface.
A MAC address object in NetBox comprises a single Ethernet link layer address, and represents a MAC address as reported by or assigned to a network interface. MAC addresses can be assigned to [device](../dcim/device.md) and [virtual machine](../virtualization/virtualmachine.md) interfaces. A MAC address can be specified as the "primary" MAC address for a given interface or VM interface.
Most interfaces only have a single MAC address, hard-coded at the factory. However, on some devices (particularly virtual interfaces) it is possible to assign additional MAC addresses or change existing ones. For this reason NetBox allows multiple MACAddress objects to be assigned to a single interface. However, for convenience and backward compatibiility reasons, the value of the `mac_address` field of the primary (or single) MAC address on an interface is reflected as a simple property in the interface detail page.
Most interfaces have only a single MAC address, hard-coded at the factory. However, on some devices (particularly virtual interfaces) it is possible to assign additional MAC addresses or change existing ones. For this reason NetBox allows multiple MACAddress objects to be assigned to a single interface. However, for convenience and backward compatibiility reasons, the value of the `mac_address` field of the primary (or single) MAC address on an interface is reflected as a simple property in the interface detail page.
## Fields

View File

@ -10,7 +10,6 @@ from dcim.models import (
)
from ipam.api.serializers_.vlans import VLANSerializer, VLANTranslationPolicySerializer
from ipam.api.serializers_.vrfs import VRFSerializer
from ipam.api.serializers_.ip import IPAddressSerializer
from ipam.models import VLAN
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
@ -216,7 +215,6 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
read_only=True
)
mac_addresses = MACAddressSerializer(many=True, nested=True, read_only=True, allow_null=True)
ip_addresses = IPAddressSerializer(many=True, nested=True, read_only=True, allow_null=True)
wwn = serializers.CharField(required=False, default=None, allow_blank=True, allow_null=True)
class Meta:
@ -229,7 +227,7 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect
'cable', 'cable_end', 'wireless_link', 'link_peers', 'link_peers_type', 'wireless_lans', 'vrf',
'l2vpn_termination', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
'tags', 'custom_fields', 'created', 'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
'mac_addresses', 'ip_addresses',
'mac_addresses',
]
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')

View File

@ -20,7 +20,7 @@ from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
NumericArrayFilter, TreeNodeMultipleChoiceFilter,
)
from virtualization.models import Cluster, ClusterGroup, VMInterface
from virtualization.models import Cluster, ClusterGroup, VMInterface, VirtualMachine
from vpn.models import L2VPN
from wireless.choices import WirelessRoleChoices, WirelessChannelChoices
from wireless.models import WirelessLAN, WirelessLink
@ -1659,7 +1659,7 @@ class MACAddressFilterSet(NetBoxModelFilterSet):
return queryset.filter(qs_filter)
def filter_device(self, queryset, name, value):
devices = Device.objects.filter(**{'{}__in'.format(name): value})
devices = Device.objects.filter(**{f'{name}__in': value})
if not devices.exists():
return queryset.none()
interface_ids = []
@ -1670,7 +1670,7 @@ class MACAddressFilterSet(NetBoxModelFilterSet):
)
def filter_virtual_machine(self, queryset, name, value):
virtual_machines = VirtualMachine.objects.filter(**{'{}__in'.format(name): value})
virtual_machines = VirtualMachine.objects.filter(**{f'{name}__in': value})
if not virtual_machines.exists():
return queryset.none()
interface_ids = []

View File

@ -1233,10 +1233,9 @@ class MACAddressImportForm(NetBoxModelImportForm):
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:
if interface and not device and not virtual_machine:
raise forms.ValidationError({
"is_primary": _("No device or virtual machine specified; cannot set as primary")
"interface": _("Must specify the parent device or VM when assigning an interface")
})
if is_primary and not interface:
raise forms.ValidationError({

View File

@ -1583,7 +1583,7 @@ class MACAddressFilterForm(NetBoxModelFilterSetForm):
model = MACAddress
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('mac_address', name=_('Addressing')),
FieldSet('mac_address', 'is_primary', name=_('Addressing')),
FieldSet('device_id', 'virtual_machine_id', name=_('Device/VM')),
)
selector_fields = ('filter_id', 'q', 'device_id', 'virtual_machine_id')
@ -1591,6 +1591,13 @@ class MACAddressFilterForm(NetBoxModelFilterSetForm):
required=False,
label=_('MAC address')
)
is_primary = forms.NullBooleanField(
required=False,
label=_('Is primary'),
widget=forms.Select(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,

View File

@ -369,12 +369,19 @@ class FrontPortTemplateType(ModularComponentTemplateType):
@strawberry_django.type(
models.MACAddress,
fields='__all__',
exclude=('assigned_object_type', 'assigned_object_id'),
filters=MACAddressFilter
)
class MACAddressType(NetBoxObjectType):
mac_address: str
@strawberry_django.field
def assigned_object(self) -> Annotated[Union[
Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
], strawberry.union("MACAddressAssignmentType")] | None:
return self.assigned_object
@strawberry_django.type(
models.Interface,

View File

@ -606,7 +606,7 @@ class BaseInterface(models.Model):
@cached_property
def mac_address(self):
if macaddress := self.mac_addresses.order_by('-is_primary').first():
if macaddress := self.mac_addresses.order_by('-is_primary', 'mac_address').first():
return macaddress.mac_address
return None

View File

@ -1516,7 +1516,12 @@ class MACAddress(PrimaryModel):
super().clean()
if self.is_primary and self.assigned_object:
if self.assigned_object.mac_addresses.filter(is_primary=True).exclude(pk=self.pk).exists():
peer_macs = MACAddress.objects.exclude(pk=self.pk).filter(
assigned_object_type=self.assigned_object_type,
assigned_object_id=self.assigned_object_id,
is_primary=True
)
if peer_macs.exists():
raise ValidationError({
'is_primary': _("There is already a primary MAC address for this interface.")
'is_primary': _("A primary MAC address is already designated for this interface.")
})

View File

@ -101,7 +101,6 @@ class VMInterfaceSerializer(NetBoxModelSerializer):
read_only=True
)
mac_addresses = MACAddressSerializer(many=True, nested=True, read_only=True, allow_null=True)
ip_addresses = IPAddressSerializer(many=True, nested=True, read_only=True, allow_null=True)
class Meta:
model = VMInterface
@ -109,7 +108,7 @@ class VMInterfaceSerializer(NetBoxModelSerializer):
'id', 'url', 'display_url', 'display', 'virtual_machine', 'name', 'enabled', 'parent', 'bridge', 'mtu',
'mac_address', 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan',
'vlan_translation_policy', 'vrf', 'l2vpn_termination', 'tags', 'custom_fields', 'created', 'last_updated',
'count_ipaddresses', 'count_fhrp_groups', 'mac_addresses', 'ip_addresses',
'count_ipaddresses', 'count_fhrp_groups', 'mac_addresses',
]
brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description')