mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-11 21:10:29 +01:00
459 lines
18 KiB
Python
459 lines
18 KiB
Python
from django.utils.translation import gettext as _
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from rest_framework import serializers
|
|
|
|
from dcim.choices import *
|
|
from dcim.constants import *
|
|
from dcim.models import (
|
|
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PortMapping,
|
|
PowerOutlet, PowerPort, RearPort, VirtualDeviceContext,
|
|
)
|
|
from ipam.api.serializers_.vlans import VLANSerializer, VLANTranslationPolicySerializer
|
|
from ipam.api.serializers_.vrfs import VRFSerializer
|
|
from ipam.models import VLAN
|
|
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
|
from netbox.api.gfk_fields import GFKSerializerField
|
|
from netbox.api.serializers import NetBoxModelSerializer
|
|
from users.api.serializers_.mixins import OwnerMixin
|
|
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
|
|
from wireless.api.serializers_.nested import NestedWirelessLinkSerializer
|
|
from wireless.api.serializers_.wirelesslans import WirelessLANSerializer
|
|
from wireless.choices import *
|
|
from wireless.models import WirelessLAN
|
|
from .base import ConnectedEndpointsSerializer, PortSerializer
|
|
from .cables import CabledObjectSerializer
|
|
from .devices import DeviceSerializer, MACAddressSerializer, ModuleSerializer, VirtualDeviceContextSerializer
|
|
from .manufacturers import ManufacturerSerializer
|
|
from .nested import NestedInterfaceSerializer
|
|
from .roles import InventoryItemRoleSerializer
|
|
|
|
__all__ = (
|
|
'ConsolePortSerializer',
|
|
'ConsoleServerPortSerializer',
|
|
'DeviceBaySerializer',
|
|
'FrontPortSerializer',
|
|
'InterfaceSerializer',
|
|
'InventoryItemSerializer',
|
|
'ModuleBaySerializer',
|
|
'PowerOutletSerializer',
|
|
'PowerPortSerializer',
|
|
'RearPortSerializer',
|
|
)
|
|
|
|
|
|
class ConsoleServerPortSerializer(
|
|
OwnerMixin,
|
|
NetBoxModelSerializer,
|
|
CabledObjectSerializer,
|
|
ConnectedEndpointsSerializer
|
|
):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
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_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', 'owner', 'tags', 'custom_fields', 'created',
|
|
'last_updated', '_occupied',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
|
|
|
|
|
class ConsolePortSerializer(
|
|
OwnerMixin,
|
|
NetBoxModelSerializer,
|
|
CabledObjectSerializer,
|
|
ConnectedEndpointsSerializer
|
|
):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
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_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', 'owner', 'tags', 'custom_fields', 'created',
|
|
'last_updated', '_occupied',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
|
|
|
|
|
class PowerPortSerializer(
|
|
OwnerMixin,
|
|
NetBoxModelSerializer,
|
|
CabledObjectSerializer,
|
|
ConnectedEndpointsSerializer
|
|
):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
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_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', 'owner', 'tags',
|
|
'custom_fields', 'created', 'last_updated', '_occupied',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
|
|
|
|
|
class PowerOutletSerializer(
|
|
OwnerMixin,
|
|
NetBoxModelSerializer,
|
|
CabledObjectSerializer,
|
|
ConnectedEndpointsSerializer
|
|
):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
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
|
|
)
|
|
status = ChoiceField(choices=PowerOutletStatusChoices, required=False)
|
|
|
|
class Meta:
|
|
model = PowerOutlet
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'status', 'color',
|
|
'power_port', 'feed_leg', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
|
'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
|
|
'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
|
|
|
|
|
class InterfaceSerializer(
|
|
OwnerMixin,
|
|
NetBoxModelSerializer,
|
|
CabledObjectSerializer,
|
|
ConnectedEndpointsSerializer
|
|
):
|
|
device = DeviceSerializer(nested=True)
|
|
vdcs = SerializedPKRelatedField(
|
|
queryset=VirtualDeviceContext.objects.all(),
|
|
serializer=VirtualDeviceContextSerializer,
|
|
nested=True,
|
|
required=False,
|
|
many=True
|
|
)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
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=VLANSerializer,
|
|
nested=True,
|
|
required=False,
|
|
many=True
|
|
)
|
|
qinq_svlan = VLANSerializer(nested=True, required=False, allow_null=True)
|
|
vlan_translation_policy = VLANTranslationPolicySerializer(nested=True, required=False, allow_null=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=WirelessLANSerializer,
|
|
nested=True,
|
|
required=False,
|
|
many=True
|
|
)
|
|
count_ipaddresses = serializers.IntegerField(read_only=True)
|
|
count_fhrp_groups = serializers.IntegerField(read_only=True)
|
|
# Maintains backward compatibility with NetBox <v4.2
|
|
mac_address = serializers.CharField(allow_null=True, read_only=True)
|
|
primary_mac_address = MACAddressSerializer(nested=True, required=False, allow_null=True)
|
|
mac_addresses = MACAddressSerializer(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:
|
|
model = Interface
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled',
|
|
'parent', 'bridge', 'lag', 'mtu', 'mac_address', 'primary_mac_address', 'mac_addresses', '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', 'qinq_svlan',
|
|
'vlan_translation_policy', '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', 'owner', '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):
|
|
|
|
if not self.nested:
|
|
|
|
# Validate 802.1q mode and vlan(s)
|
|
mode = None
|
|
tagged_vlans = []
|
|
|
|
# Gather Information
|
|
if self.instance:
|
|
mode = data.get('mode') if 'mode' in data.keys() else self.instance.mode
|
|
untagged_vlan = data.get('untagged_vlan') if 'untagged_vlan' in data.keys() else \
|
|
self.instance.untagged_vlan
|
|
qinq_svlan = data.get('qinq_svlan') if 'qinq_svlan' in data.keys() else \
|
|
self.instance.qinq_svlan
|
|
tagged_vlans = data.get('tagged_vlans') if 'tagged_vlans' in data.keys() else \
|
|
self.instance.tagged_vlans.all()
|
|
else:
|
|
mode = data.get('mode', None)
|
|
untagged_vlan = data.get('untagged_vlan') if 'untagged_vlan' in data.keys() else None
|
|
qinq_svlan = data.get('qinq_svlan') if 'qinq_svlan' in data.keys() else None
|
|
tagged_vlans = data.get('tagged_vlans') if 'tagged_vlans' in data.keys() else None
|
|
|
|
errors = {}
|
|
|
|
# Non Q-in-Q mode with service vlan set
|
|
if mode != InterfaceModeChoices.MODE_Q_IN_Q and qinq_svlan:
|
|
errors.update({
|
|
'qinq_svlan': _("Interface mode does not support q-in-q service vlan")
|
|
})
|
|
# Routed mode
|
|
if not mode:
|
|
# Untagged vlan
|
|
if untagged_vlan:
|
|
errors.update({
|
|
'untagged_vlan': _("Interface mode does not support untagged vlan")
|
|
})
|
|
# Tagged vlan
|
|
if tagged_vlans:
|
|
errors.update({
|
|
'tagged_vlans': _("Interface mode does not support tagged vlans")
|
|
})
|
|
# Non-tagged mode
|
|
elif mode in (InterfaceModeChoices.MODE_TAGGED_ALL, InterfaceModeChoices.MODE_ACCESS) and tagged_vlans:
|
|
errors.update({
|
|
'tagged_vlans': _("Interface mode does not support tagged vlans")
|
|
})
|
|
|
|
if errors:
|
|
raise serializers.ValidationError(errors)
|
|
|
|
# Validate many-to-many VLAN assignments
|
|
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 RearPortMappingSerializer(serializers.ModelSerializer):
|
|
position = serializers.IntegerField(
|
|
source='rear_port_position'
|
|
)
|
|
front_port = serializers.PrimaryKeyRelatedField(
|
|
queryset=FrontPort.objects.all(),
|
|
)
|
|
|
|
class Meta:
|
|
model = PortMapping
|
|
fields = ('position', 'front_port', 'front_port_position')
|
|
|
|
|
|
class RearPortSerializer(OwnerMixin, NetBoxModelSerializer, CabledObjectSerializer, PortSerializer):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
fields=('id', 'url', 'display', 'device', 'module_bay'),
|
|
required=False,
|
|
allow_null=True
|
|
)
|
|
type = ChoiceField(choices=PortTypeChoices)
|
|
front_ports = RearPortMappingSerializer(
|
|
source='mappings',
|
|
many=True,
|
|
required=False,
|
|
)
|
|
|
|
class Meta:
|
|
model = RearPort
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions',
|
|
'front_ports', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
|
'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
|
|
|
|
|
class FrontPortMappingSerializer(serializers.ModelSerializer):
|
|
position = serializers.IntegerField(
|
|
source='front_port_position'
|
|
)
|
|
rear_port = serializers.PrimaryKeyRelatedField(
|
|
queryset=RearPort.objects.all(),
|
|
)
|
|
|
|
class Meta:
|
|
model = PortMapping
|
|
fields = ('position', 'rear_port', 'rear_port_position')
|
|
|
|
|
|
class FrontPortSerializer(OwnerMixin, NetBoxModelSerializer, CabledObjectSerializer, PortSerializer):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
fields=('id', 'url', 'display', 'device', 'module_bay'),
|
|
required=False,
|
|
allow_null=True
|
|
)
|
|
type = ChoiceField(choices=PortTypeChoices)
|
|
rear_ports = FrontPortMappingSerializer(
|
|
source='mappings',
|
|
many=True,
|
|
required=False,
|
|
)
|
|
|
|
class Meta:
|
|
model = FrontPort
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions',
|
|
'rear_ports', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
|
'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
|
|
|
|
|
class ModuleBaySerializer(OwnerMixin, NetBoxModelSerializer):
|
|
device = DeviceSerializer(nested=True)
|
|
module = ModuleSerializer(
|
|
nested=True,
|
|
fields=('id', 'url', 'display'),
|
|
required=False,
|
|
allow_null=True,
|
|
default=None
|
|
)
|
|
installed_module = ModuleSerializer(
|
|
nested=True,
|
|
fields=('id', 'url', 'display', 'serial', 'description'),
|
|
required=False,
|
|
allow_null=True
|
|
)
|
|
|
|
class Meta:
|
|
model = ModuleBay
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'installed_module', 'label', 'position',
|
|
'description', 'owner', 'tags', 'custom_fields', 'created', 'last_updated',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
|
|
|
|
|
|
class DeviceBaySerializer(OwnerMixin, NetBoxModelSerializer):
|
|
device = DeviceSerializer(nested=True)
|
|
installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
|
|
|
class Meta:
|
|
model = DeviceBay
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'description', 'installed_device',
|
|
'owner', 'tags', 'custom_fields', 'created', 'last_updated',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
|
|
|
|
|
|
class InventoryItemSerializer(OwnerMixin, NetBoxModelSerializer):
|
|
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 = GFKSerializerField(read_only=True)
|
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
|
status = ChoiceField(choices=InventoryItemStatusChoices, required=False)
|
|
|
|
class Meta:
|
|
model = InventoryItem
|
|
fields = [
|
|
'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'status', 'role',
|
|
'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type',
|
|
'component_id', 'component', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
|
|
]
|
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
|