mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Refactor REST API serializers to avoid circular imports
This commit is contained in:
366
netbox/dcim/api/serializers_/device_components.py
Normal file
366
netbox/dcim/api/serializers_/device_components.py
Normal file
@@ -0,0 +1,366 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.constants import *
|
||||
from dcim.models import (
|
||||
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
|
||||
RearPort, VirtualDeviceContext,
|
||||
)
|
||||
from ipam.api.serializers_.vlans import VLANSerializer
|
||||
from ipam.api.serializers_.vrfs import VRFSerializer
|
||||
from ipam.api.nested_serializers import NestedVLANSerializer
|
||||
from ipam.models import VLAN
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
||||
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
||||
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||
from utilities.api import get_serializer_for_model
|
||||
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
|
||||
from wireless.api.nested_serializers import NestedWirelessLANSerializer, NestedWirelessLinkSerializer
|
||||
from wireless.choices import *
|
||||
from wireless.models import WirelessLAN
|
||||
from .base import ConnectedEndpointsSerializer
|
||||
from .cables import CabledObjectSerializer
|
||||
from .devices import DeviceSerializer, ModuleSerializer
|
||||
from .manufacturers import ManufacturerSerializer
|
||||
from .roles import InventoryItemRoleSerializer
|
||||
from ..nested_serializers import *
|
||||
|
||||
__all__ = (
|
||||
'ConsolePortSerializer',
|
||||
'ConsoleServerPortSerializer',
|
||||
'DeviceBaySerializer',
|
||||
'FrontPortSerializer',
|
||||
'InterfaceSerializer',
|
||||
'InventoryItemSerializer',
|
||||
'ModuleBaySerializer',
|
||||
'PowerOutletSerializer',
|
||||
'PowerPortSerializer',
|
||||
'RearPortSerializer',
|
||||
)
|
||||
|
||||
|
||||
class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(
|
||||
choices=ConsolePortTypeChoices,
|
||||
allow_blank=True,
|
||||
required=False
|
||||
)
|
||||
speed = ChoiceField(
|
||||
choices=ConsolePortSpeedChoices,
|
||||
allow_null=True,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsoleServerPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', '_occupied',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
|
||||
class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(
|
||||
choices=ConsolePortTypeChoices,
|
||||
allow_blank=True,
|
||||
required=False
|
||||
)
|
||||
speed = ChoiceField(
|
||||
choices=ConsolePortSpeedChoices,
|
||||
allow_null=True,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ConsolePort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', '_occupied',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
|
||||
class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(
|
||||
choices=PowerPortTypeChoices,
|
||||
allow_blank=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
|
||||
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
||||
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||
'created', 'last_updated', '_occupied',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
|
||||
class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(
|
||||
choices=PowerOutletTypeChoices,
|
||||
allow_blank=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
power_port = PowerPortSerializer(
|
||||
nested=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
feed_leg = ChoiceField(
|
||||
choices=PowerOutletFeedLegChoices,
|
||||
allow_blank=True,
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerOutlet
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg',
|
||||
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
||||
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||
'created', 'last_updated', '_occupied',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
|
||||
class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
vdcs = SerializedPKRelatedField(
|
||||
queryset=VirtualDeviceContext.objects.all(),
|
||||
serializer=NestedVirtualDeviceContextSerializer,
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(choices=InterfaceTypeChoices)
|
||||
parent = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
bridge = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_blank=True)
|
||||
duplex = ChoiceField(choices=InterfaceDuplexChoices, required=False, allow_blank=True, allow_null=True)
|
||||
rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_blank=True)
|
||||
rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False, allow_blank=True)
|
||||
poe_mode = ChoiceField(choices=InterfacePoEModeChoices, required=False, allow_blank=True)
|
||||
poe_type = ChoiceField(choices=InterfacePoETypeChoices, required=False, allow_blank=True)
|
||||
untagged_vlan = VLANSerializer(nested=True, required=False, allow_null=True)
|
||||
tagged_vlans = SerializedPKRelatedField(
|
||||
queryset=VLAN.objects.all(),
|
||||
serializer=NestedVLANSerializer,
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||
l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
|
||||
wireless_link = NestedWirelessLinkSerializer(read_only=True, allow_null=True)
|
||||
wireless_lans = SerializedPKRelatedField(
|
||||
queryset=WirelessLAN.objects.all(),
|
||||
serializer=NestedWirelessLANSerializer,
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
count_ipaddresses = serializers.IntegerField(read_only=True)
|
||||
count_fhrp_groups = serializers.IntegerField(read_only=True)
|
||||
mac_address = serializers.CharField(
|
||||
required=False,
|
||||
default=None,
|
||||
allow_blank=True,
|
||||
allow_null=True
|
||||
)
|
||||
wwn = serializers.CharField(required=False, default=None, allow_blank=True, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge',
|
||||
'lag', 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role',
|
||||
'rf_channel', 'poe_mode', 'poe_type', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
|
||||
'untagged_vlan', 'tagged_vlans', 'mark_connected', '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',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
# Validate many-to-many VLAN assignments
|
||||
if not self.nested:
|
||||
device = self.instance.device if self.instance else data.get('device')
|
||||
for vlan in data.get('tagged_vlans', []):
|
||||
if vlan.site not in [device.site, None]:
|
||||
raise serializers.ValidationError({
|
||||
'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent device, "
|
||||
f"or it must be global."
|
||||
})
|
||||
|
||||
return super().validate(data)
|
||||
|
||||
|
||||
class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(choices=PortTypeChoices)
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'description',
|
||||
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created',
|
||||
'last_updated', '_occupied',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
|
||||
class FrontPortRearPortSerializer(WritableNestedSerializer):
|
||||
"""
|
||||
NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
|
||||
"""
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||
|
||||
class Meta:
|
||||
model = RearPort
|
||||
fields = ['id', 'url', 'display', 'name', 'label', 'description']
|
||||
|
||||
|
||||
class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
type = ChoiceField(choices=PortTypeChoices)
|
||||
rear_port = FrontPortRearPortSerializer()
|
||||
|
||||
class Meta:
|
||||
model = FrontPort
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
|
||||
'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
||||
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||
|
||||
|
||||
class ModuleBaySerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
installed_module = ModuleSerializer(
|
||||
nested=True,
|
||||
requested_fields=('id', 'url', 'display', 'serial', 'description'),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ModuleBay
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'name', 'installed_module', 'label', 'position', 'description', 'tags',
|
||||
'custom_fields', 'created', 'last_updated',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
|
||||
|
||||
|
||||
class DeviceBaySerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = DeviceBay
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'name', 'label', 'description', 'installed_device', 'tags',
|
||||
'custom_fields', 'created', 'last_updated',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
|
||||
|
||||
|
||||
class InventoryItemSerializer(NetBoxModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
|
||||
device = DeviceSerializer(nested=True)
|
||||
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
||||
role = InventoryItemRoleSerializer(nested=True, required=False, allow_null=True)
|
||||
manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||
component_type = ContentTypeField(
|
||||
queryset=ContentType.objects.filter(MODULAR_COMPONENT_MODELS),
|
||||
required=False,
|
||||
allow_null=True
|
||||
)
|
||||
component = serializers.SerializerMethodField(read_only=True)
|
||||
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = InventoryItem
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial',
|
||||
'asset_tag', 'discovered', 'description', 'component_type', 'component_id', 'component', 'tags',
|
||||
'custom_fields', 'created', 'last_updated', '_depth',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
|
||||
|
||||
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||
def get_component(self, obj):
|
||||
if obj.component is None:
|
||||
return None
|
||||
serializer = get_serializer_for_model(obj.component, prefix=NESTED_SERIALIZER_PREFIX)
|
||||
context = {'request': self.context['request']}
|
||||
return serializer(obj.component, context=context).data
|
||||
Reference in New Issue
Block a user