1
0
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:
Jeremy Stretch
2024-02-27 13:26:43 -05:00
parent c146f5e1b5
commit c382ba0ae0
71 changed files with 4096 additions and 3616 deletions

View File

@ -1,144 +1,3 @@
from rest_framework import serializers
from circuits.choices import CircuitStatusChoices
from circuits.models import *
from dcim.api.serializers import CabledObjectSerializer, SiteSerializer
from ipam.api.nested_serializers import NestedASNSerializer
from ipam.models import ASN
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from tenancy.api.serializers import TenantSerializer
from .serializers_.providers import *
from .serializers_.circuits import *
from .nested_serializers import *
#
# Providers
#
class ProviderSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
required=False,
many=True
)
asns = SerializedPKRelatedField(
queryset=ASN.objects.all(),
serializer=NestedASNSerializer,
required=False,
many=True
)
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = Provider
fields = [
'id', 'url', 'display', 'name', 'slug', 'accounts', 'description', 'comments', 'asns', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
#
# Provider Accounts
#
class ProviderAccountSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderAccount
fields = [
'id', 'url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
#
# Provider networks
#
class ProviderNetworkSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderNetwork
fields = [
'id', 'url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Circuits
#
class CircuitTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
site = SiteSerializer(nested=True, allow_null=True)
provider_network = ProviderNetworkSerializer(nested=True, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
'description',
]
class CircuitSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
provider = ProviderSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
type = CircuitTypeSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
class Meta:
model = Circuit
fields = [
'id', 'url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date',
'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'cid', 'description')
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
circuit = CircuitSerializer(nested=True)
site = SiteSerializer(nested=True, required=False, allow_null=True)
provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
brief_fields = ('id', 'url', 'display', 'circuit', 'term_side', 'description', 'cable', '_occupied')

View File

@ -0,0 +1,81 @@
from rest_framework import serializers
from circuits.choices import CircuitStatusChoices
from circuits.models import Circuit, CircuitTermination, CircuitType
from dcim.api.serializers_.cables import CabledObjectSerializer
from dcim.api.serializers_.sites import SiteSerializer
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, ProviderSerializer
__all__ = (
'CircuitSerializer',
'CircuitTerminationSerializer',
'CircuitTypeSerializer',
)
class CircuitTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
site = SiteSerializer(nested=True, allow_null=True)
provider_network = ProviderNetworkSerializer(nested=True, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
'description',
]
class CircuitSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
provider = ProviderSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
type = CircuitTypeSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
class Meta:
model = Circuit
fields = [
'id', 'url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date',
'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'cid', 'description')
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
circuit = CircuitSerializer(nested=True)
site = SiteSerializer(nested=True, required=False, allow_null=True)
provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
brief_fields = ('id', 'url', 'display', 'circuit', 'term_side', 'description', 'cable', '_occupied')

View File

@ -0,0 +1,67 @@
from rest_framework import serializers
from circuits.models import Provider, ProviderAccount, ProviderNetwork
from ipam.api.nested_serializers import NestedASNSerializer
from ipam.models import ASN
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from ..nested_serializers import *
__all__ = (
'ProviderAccountSerializer',
'ProviderNetworkSerializer',
'ProviderSerializer',
)
class ProviderSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
required=False,
many=True
)
asns = SerializedPKRelatedField(
queryset=ASN.objects.all(),
serializer=NestedASNSerializer,
required=False,
many=True
)
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = Provider
fields = [
'id', 'url', 'display', 'name', 'slug', 'accounts', 'description', 'comments', 'asns', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
class ProviderAccountSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderAccount
fields = [
'id', 'url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
class ProviderNetworkSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderNetwork
fields = [
'id', 'url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -1,75 +1,3 @@
from rest_framework import serializers
from core.choices import *
from core.models import *
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer
from netbox.utils import get_data_backend_choices
from users.api.serializers import UserSerializer
__all__ = (
'DataFileSerializer',
'DataSourceSerializer',
'JobSerializer',
)
class DataSourceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datasource-detail'
)
type = ChoiceField(
choices=get_data_backend_choices()
)
status = ChoiceField(
choices=DataSourceStatusChoices,
read_only=True
)
# Related object counts
file_count = RelatedObjectCountField('datafiles')
class Meta:
model = DataSource
fields = [
'id', 'url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'comments',
'parameters', 'ignore_rules', 'custom_fields', 'created', 'last_updated', 'file_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class DataFileSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datafile-detail'
)
source = DataSourceSerializer(
nested=True,
read_only=True
)
class Meta:
model = DataFile
fields = [
'id', 'url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
]
brief_fields = ('id', 'url', 'display', 'path')
class JobSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
user = UserSerializer(
nested=True,
read_only=True
)
status = ChoiceField(choices=JobStatusChoices, read_only=True)
object_type = ContentTypeField(
read_only=True
)
class Meta:
model = Job
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
'started', 'completed', 'user', 'data', 'error', 'job_id',
]
brief_fields = ('url', 'created', 'completed', 'user', 'status')
from .serializers_.data import *
from .serializers_.jobs import *
from .nested_serializers import *

View File

View File

@ -0,0 +1,53 @@
from rest_framework import serializers
from core.choices import *
from core.models import DataFile, DataSource
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.utils import get_data_backend_choices
__all__ = (
'DataFileSerializer',
'DataSourceSerializer',
)
class DataSourceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datasource-detail'
)
type = ChoiceField(
choices=get_data_backend_choices()
)
status = ChoiceField(
choices=DataSourceStatusChoices,
read_only=True
)
# Related object counts
file_count = RelatedObjectCountField('datafiles')
class Meta:
model = DataSource
fields = [
'id', 'url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'comments',
'parameters', 'ignore_rules', 'custom_fields', 'created', 'last_updated', 'file_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class DataFileSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datafile-detail'
)
source = DataSourceSerializer(
nested=True,
read_only=True
)
class Meta:
model = DataFile
fields = [
'id', 'url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
]
brief_fields = ('id', 'url', 'display', 'path')

View File

@ -0,0 +1,31 @@
from rest_framework import serializers
from core.choices import *
from core.models import Job
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer
from users.api.serializers_.users import UserSerializer
__all__ = (
'JobSerializer',
)
class JobSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
user = UserSerializer(
nested=True,
read_only=True
)
status = ChoiceField(choices=JobStatusChoices, read_only=True)
object_type = ContentTypeField(
read_only=True
)
class Meta:
model = Job
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
'started', 'completed', 'user', 'data', 'error', 'job_id',
]
brief_fields = ('url', 'created', 'completed', 'user', 'status')

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,38 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model
__all__ = (
'ConnectedEndpointsSerializer',
)
class ConnectedEndpointsSerializer(serializers.ModelSerializer):
"""
Legacy serializer for pre-v3.3 connections
"""
connected_endpoints_type = serializers.SerializerMethodField(read_only=True)
connected_endpoints = serializers.SerializerMethodField(read_only=True)
connected_endpoints_reachable = serializers.SerializerMethodField(read_only=True)
@extend_schema_field(OpenApiTypes.STR)
def get_connected_endpoints_type(self, obj):
if endpoints := obj.connected_endpoints:
return f'{endpoints[0]._meta.app_label}.{endpoints[0]._meta.model_name}'
@extend_schema_field(serializers.ListField)
def get_connected_endpoints(self, obj):
"""
Return the appropriate serializer for the type of connected object.
"""
if endpoints := obj.connected_endpoints:
serializer = get_serializer_for_model(endpoints[0], prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(endpoints, many=True, context=context).data
@extend_schema_field(serializers.BooleanField)
def get_connected_endpoints_reachable(self, obj):
return obj._path and obj._path.is_complete and obj._path.is_active

View File

@ -0,0 +1,127 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.types import OpenApiTypes
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 Cable, CablePath, CableTermination
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import GenericObjectSerializer, NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
__all__ = (
'CablePathSerializer',
'CableSerializer',
'CableTerminationSerializer',
'CabledObjectSerializer',
'TracedCableSerializer',
)
class CableSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
a_terminations = GenericObjectSerializer(many=True, required=False)
b_terminations = GenericObjectSerializer(many=True, required=False)
status = ChoiceField(choices=LinkStatusChoices, required=False)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False, allow_null=True)
class Meta:
model = Cable
fields = [
'id', 'url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'tenant', 'label', 'color',
'length', 'length_unit', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'label', 'description')
class TracedCableSerializer(serializers.ModelSerializer):
"""
Used only while tracing a cable path.
"""
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
class Meta:
model = Cable
fields = [
'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'description',
]
class CableTerminationSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cabletermination-detail')
termination_type = ContentTypeField(
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
)
termination = serializers.SerializerMethodField(read_only=True)
class Meta:
model = CableTermination
fields = [
'id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id', 'termination',
'created', 'last_updated',
]
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_termination(self, obj):
serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.termination, context=context).data
class CablePathSerializer(serializers.ModelSerializer):
path = serializers.SerializerMethodField(read_only=True)
class Meta:
model = CablePath
fields = ['id', 'path', 'is_active', 'is_complete', 'is_split']
@extend_schema_field(serializers.ListField)
def get_path(self, obj):
ret = []
for nodes in obj.path_objects:
serializer = get_serializer_for_model(nodes[0], prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
ret.append(serializer(nodes, context=context, many=True).data)
return ret
class CabledObjectSerializer(serializers.ModelSerializer):
cable = CableSerializer(nested=True, read_only=True, allow_null=True)
cable_end = serializers.CharField(read_only=True)
link_peers_type = serializers.SerializerMethodField(read_only=True)
link_peers = serializers.SerializerMethodField(read_only=True)
_occupied = serializers.SerializerMethodField(read_only=True)
@extend_schema_field(OpenApiTypes.STR)
def get_link_peers_type(self, obj):
"""
Return the type of the peer link terminations, or None.
"""
if not obj.cable:
return None
if obj.link_peers:
return f'{obj.link_peers[0]._meta.app_label}.{obj.link_peers[0]._meta.model_name}'
return None
@extend_schema_field(serializers.ListField)
def get_link_peers(self, obj):
"""
Return the appropriate serializer for the link termination model.
"""
if not obj.link_peers:
return []
# Return serialized peer termination objects
serializer = get_serializer_for_model(obj.link_peers[0], prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.link_peers, context=context, many=True).data
@extend_schema_field(serializers.BooleanField)
def get__occupied(self, obj):
return obj._occupied

View 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

View File

@ -0,0 +1,165 @@
import decimal
from django.utils.translation import gettext as _
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.choices import *
from dcim.models import Device, DeviceBay, Module, VirtualDeviceContext
from extras.api.serializers_.provisioning import ConfigTemplateSerializer
from ipam.api.serializers_.ip import IPAddressSerializer
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from virtualization.api.serializers_.clusters import ClusterSerializer
from .devicetypes import *
from .platforms import PlatformSerializer
from .racks import RackSerializer
from .roles import DeviceRoleSerializer
from .sites import LocationSerializer, SiteSerializer
from .virtualchassis import VirtualChassisSerializer
from ..nested_serializers import *
__all__ = (
'DeviceSerializer',
'DeviceWithConfigContextSerializer',
'ModuleSerializer',
'VirtualDeviceContextSerializer',
)
class DeviceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
device_type = DeviceTypeSerializer(nested=True)
role = DeviceRoleSerializer(nested=True)
device_role = DeviceRoleSerializer(
nested=True,
read_only=True,
help_text='Deprecated in v3.6 in favor of `role`.'
)
tenant = TenantSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
platform = PlatformSerializer(nested=True, required=False, allow_null=True)
site = SiteSerializer(nested=True)
location = LocationSerializer(nested=True, required=False, allow_null=True, default=None)
rack = RackSerializer(nested=True, required=False, allow_null=True, default=None)
face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, default=lambda: '')
position = serializers.DecimalField(
max_digits=4,
decimal_places=1,
allow_null=True,
label=_('Position (U)'),
min_value=decimal.Decimal(0.5),
default=None
)
status = ChoiceField(choices=DeviceStatusChoices, required=False)
airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
primary_ip = IPAddressSerializer(nested=True, read_only=True)
primary_ip4 = IPAddressSerializer(nested=True, required=False, allow_null=True)
primary_ip6 = IPAddressSerializer(nested=True, required=False, allow_null=True)
oob_ip = IPAddressSerializer(nested=True, required=False, allow_null=True)
parent_device = serializers.SerializerMethodField()
cluster = ClusterSerializer(nested=True, required=False, allow_null=True)
virtual_chassis = VirtualChassisSerializer(nested=True, required=False, allow_null=True, default=None)
vc_position = serializers.IntegerField(allow_null=True, max_value=255, min_value=0, default=None)
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
# Counter fields
console_port_count = serializers.IntegerField(read_only=True)
console_server_port_count = serializers.IntegerField(read_only=True)
power_port_count = serializers.IntegerField(read_only=True)
power_outlet_count = serializers.IntegerField(read_only=True)
interface_count = serializers.IntegerField(read_only=True)
front_port_count = serializers.IntegerField(read_only=True)
rear_port_count = serializers.IntegerField(read_only=True)
device_bay_count = serializers.IntegerField(read_only=True)
module_bay_count = serializers.IntegerField(read_only=True)
inventory_item_count = serializers.IntegerField(read_only=True)
class Meta:
model = Device
fields = [
'id', 'url', 'display', 'name', 'device_type', 'role', 'device_role', 'tenant', 'platform', 'serial',
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device',
'status', 'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis',
'vc_position', 'vc_priority', 'description', 'comments', 'config_template', 'local_context_data', 'tags',
'custom_fields', 'created', 'last_updated', 'console_port_count', 'console_server_port_count',
'power_port_count', 'power_outlet_count', 'interface_count', 'front_port_count', 'rear_port_count',
'device_bay_count', 'module_bay_count', 'inventory_item_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@extend_schema_field(NestedDeviceSerializer)
def get_parent_device(self, obj):
try:
device_bay = obj.parent_bay
except DeviceBay.DoesNotExist:
return None
context = {'request': self.context['request']}
data = NestedDeviceSerializer(instance=device_bay.device, context=context).data
data['device_bay'] = NestedDeviceBaySerializer(instance=device_bay, context=context).data
return data
def get_device_role(self, obj):
return obj.role
class DeviceWithConfigContextSerializer(DeviceSerializer):
config_context = serializers.SerializerMethodField(read_only=True)
class Meta(DeviceSerializer.Meta):
fields = [
'id', 'url', 'display', 'name', 'device_type', 'role', 'device_role', 'tenant', 'platform', 'serial',
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'latitude', 'longitude', 'parent_device',
'status', 'airflow', 'primary_ip', 'primary_ip4', 'primary_ip6', 'oob_ip', 'cluster', 'virtual_chassis',
'vc_position', 'vc_priority', 'description', 'comments', 'config_template', 'config_context',
'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated', 'console_port_count',
'console_server_port_count', 'power_port_count', 'power_outlet_count', 'interface_count',
'front_port_count', 'rear_port_count', 'device_bay_count', 'module_bay_count', 'inventory_item_count',
]
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_config_context(self, obj):
return obj.get_config_context()
class VirtualDeviceContextSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualdevicecontext-detail')
device = DeviceSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True, default=None)
primary_ip = IPAddressSerializer(nested=True, read_only=True, allow_null=True)
primary_ip4 = IPAddressSerializer(nested=True, required=False, allow_null=True)
primary_ip6 = IPAddressSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=VirtualDeviceContextStatusChoices)
# Related object counts
interface_count = RelatedObjectCountField('interfaces')
class Meta:
model = VirtualDeviceContext
fields = [
'id', 'url', 'display', 'name', 'device', 'identifier', 'tenant', 'primary_ip', 'primary_ip4',
'primary_ip6', 'status', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'interface_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'identifier', 'device', 'description')
class ModuleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
device = DeviceSerializer(nested=True)
module_bay = NestedModuleBaySerializer()
module_type = ModuleTypeSerializer(nested=True)
status = ChoiceField(choices=ModuleStatusChoices, required=False)
class Meta:
model = Module
fields = [
'id', 'url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial', 'asset_tag',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description')

View File

@ -0,0 +1,328 @@
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 (
ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate, InterfaceTemplate,
InventoryItemTemplate, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
)
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model
from wireless.choices import *
from .devicetypes import DeviceTypeSerializer, ModuleTypeSerializer
from .manufacturers import ManufacturerSerializer
from .roles import InventoryItemRoleSerializer
from ..nested_serializers import *
__all__ = (
'ConsolePortTemplateSerializer',
'ConsoleServerPortTemplateSerializer',
'DeviceBayTemplateSerializer',
'FrontPortTemplateSerializer',
'InterfaceTemplateSerializer',
'InventoryItemTemplateSerializer',
'ModuleBayTemplateSerializer',
'PowerOutletTemplateSerializer',
'PowerPortTemplateSerializer',
'RearPortTemplateSerializer',
)
class ConsolePortTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleporttemplate-detail')
device_type = DeviceTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(
choices=ConsolePortTypeChoices,
allow_blank=True,
required=False
)
class Meta:
model = ConsolePortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'description', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverporttemplate-detail')
device_type = DeviceTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(
choices=ConsolePortTypeChoices,
allow_blank=True,
required=False
)
class Meta:
model = ConsoleServerPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'description', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class PowerPortTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
device_type = DeviceTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(
choices=PowerPortTypeChoices,
allow_blank=True,
required=False,
allow_null=True
)
class Meta:
model = PowerPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'maximum_draw',
'allocated_draw', 'description', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlettemplate-detail')
device_type = DeviceTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(
choices=PowerOutletTypeChoices,
allow_blank=True,
required=False,
allow_null=True
)
power_port = PowerPortTemplateSerializer(
nested=True,
required=False,
allow_null=True
)
feed_leg = ChoiceField(
choices=PowerOutletFeedLegChoices,
allow_blank=True,
required=False,
allow_null=True
)
class Meta:
model = PowerOutletTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg',
'description', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class InterfaceTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfacetemplate-detail')
device_type = DeviceTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(choices=InterfaceTypeChoices)
bridge = NestedInterfaceTemplateSerializer(
required=False,
allow_null=True
)
poe_mode = ChoiceField(
choices=InterfacePoEModeChoices,
required=False,
allow_blank=True,
allow_null=True
)
poe_type = ChoiceField(
choices=InterfacePoETypeChoices,
required=False,
allow_blank=True,
allow_null=True
)
rf_role = ChoiceField(
choices=WirelessRoleChoices,
required=False,
allow_blank=True,
allow_null=True
)
class Meta:
model = InterfaceTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'enabled', 'mgmt_only',
'description', 'bridge', 'poe_mode', 'poe_type', 'rf_role', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class RearPortTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
device_type = DeviceTypeSerializer(
required=False,
nested=True,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(choices=PortTypeChoices)
class Meta:
model = RearPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions',
'description', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class FrontPortTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
device_type = DeviceTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
module_type = ModuleTypeSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(choices=PortTypeChoices)
rear_port = RearPortTemplateSerializer(nested=True)
class Meta:
model = FrontPortTemplate
fields = [
'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'rear_port',
'rear_port_position', 'description', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ModuleBayTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebaytemplate-detail')
device_type = DeviceTypeSerializer(
nested=True
)
class Meta:
model = ModuleBayTemplate
fields = [
'id', 'url', 'display', 'device_type', 'name', 'label', 'position', 'description', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
device_type = DeviceTypeSerializer(
nested=True
)
class Meta:
model = DeviceBayTemplate
fields = ['id', 'url', 'display', 'device_type', 'name', 'label', 'description', 'created', 'last_updated']
brief_fields = ('id', 'url', 'display', 'name', 'description')
class InventoryItemTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemtemplate-detail')
device_type = DeviceTypeSerializer(
nested=True
)
parent = serializers.PrimaryKeyRelatedField(
queryset=InventoryItemTemplate.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_TEMPLATE_MODELS),
required=False,
allow_null=True
)
component = serializers.SerializerMethodField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = InventoryItemTemplate
fields = [
'id', 'url', 'display', 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id',
'description', 'component_type', 'component_id', 'component', 'created', 'last_updated', '_depth',
]
brief_fields = ('id', 'url', 'display', '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

View File

@ -0,0 +1,74 @@
from django.utils.translation import gettext as _
from rest_framework import serializers
from dcim.choices import *
from dcim.models import DeviceType, ModuleType
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from .manufacturers import ManufacturerSerializer
from .platforms import PlatformSerializer
__all__ = (
'DeviceTypeSerializer',
'ModuleTypeSerializer',
)
class DeviceTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
manufacturer = ManufacturerSerializer(nested=True)
default_platform = PlatformSerializer(nested=True, required=False, allow_null=True)
u_height = serializers.DecimalField(
max_digits=4,
decimal_places=1,
label=_('Position (U)'),
min_value=0,
default=1.0
)
subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False, allow_null=True)
airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False, allow_null=True)
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
front_image = serializers.URLField(allow_null=True, required=False)
rear_image = serializers.URLField(allow_null=True, required=False)
# Counter fields
console_port_template_count = serializers.IntegerField(read_only=True)
console_server_port_template_count = serializers.IntegerField(read_only=True)
power_port_template_count = serializers.IntegerField(read_only=True)
power_outlet_template_count = serializers.IntegerField(read_only=True)
interface_template_count = serializers.IntegerField(read_only=True)
front_port_template_count = serializers.IntegerField(read_only=True)
rear_port_template_count = serializers.IntegerField(read_only=True)
device_bay_template_count = serializers.IntegerField(read_only=True)
module_bay_template_count = serializers.IntegerField(read_only=True)
inventory_item_template_count = serializers.IntegerField(read_only=True)
# Related object counts
device_count = RelatedObjectCountField('instances')
class Meta:
model = DeviceType
fields = [
'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height',
'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit',
'front_image', 'rear_image', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'device_count', 'console_port_template_count', 'console_server_port_template_count',
'power_port_template_count', 'power_outlet_template_count', 'interface_template_count',
'front_port_template_count', 'rear_port_template_count', 'device_bay_template_count',
'module_bay_template_count', 'inventory_item_template_count',
]
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description', 'device_count')
class ModuleTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
manufacturer = ManufacturerSerializer(nested=True)
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
class Meta:
model = ModuleType
fields = [
'id', 'url', 'display', 'manufacturer', 'model', 'part_number', 'weight', 'weight_unit', 'description',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'description')

View File

@ -0,0 +1,26 @@
from rest_framework import serializers
from dcim.models import Manufacturer
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
__all__ = (
'ManufacturerSerializer',
)
class ManufacturerSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
# Related object counts
devicetype_count = RelatedObjectCountField('device_types')
inventoryitem_count = RelatedObjectCountField('inventory_items')
platform_count = RelatedObjectCountField('platforms')
class Meta:
model = Manufacturer
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'devicetype_count', 'inventoryitem_count', 'platform_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'devicetype_count')

View File

@ -0,0 +1,29 @@
from rest_framework import serializers
from dcim.models import Platform
from extras.api.serializers_.provisioning import ConfigTemplateSerializer
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from .manufacturers import ManufacturerSerializer
__all__ = (
'PlatformSerializer',
)
class PlatformSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True)
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
# Related object counts
device_count = RelatedObjectCountField('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
class Meta:
model = Platform
fields = [
'id', 'url', 'display', 'name', 'slug', 'manufacturer', 'config_template', 'description', 'tags',
'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')

View File

@ -0,0 +1,80 @@
from rest_framework import serializers
from dcim.choices import *
from dcim.models import PowerFeed, PowerPanel
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .base import ConnectedEndpointsSerializer
from .cables import CabledObjectSerializer
from .racks import RackSerializer
from .sites import LocationSerializer, SiteSerializer
__all__ = (
'PowerFeedSerializer',
'PowerPanelSerializer',
)
class PowerPanelSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
site = SiteSerializer(nested=True)
location = LocationSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
# Related object counts
powerfeed_count = RelatedObjectCountField('powerfeeds')
class Meta:
model = PowerPanel
fields = [
'id', 'url', 'display', 'site', 'location', 'name', 'description', 'comments', 'tags', 'custom_fields',
'powerfeed_count', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'powerfeed_count')
class PowerFeedSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
power_panel = PowerPanelSerializer(nested=True)
rack = RackSerializer(
nested=True,
required=False,
allow_null=True,
default=None
)
type = ChoiceField(
choices=PowerFeedTypeChoices,
default=lambda: PowerFeedTypeChoices.TYPE_PRIMARY,
)
status = ChoiceField(
choices=PowerFeedStatusChoices,
default=lambda: PowerFeedStatusChoices.STATUS_ACTIVE,
)
supply = ChoiceField(
choices=PowerFeedSupplyChoices,
default=lambda: PowerFeedSupplyChoices.SUPPLY_AC,
)
phase = ChoiceField(
choices=PowerFeedPhaseChoices,
default=lambda: PowerFeedPhaseChoices.PHASE_SINGLE,
)
tenant = TenantSerializer(
nested=True,
required=False,
allow_null=True
)
class Meta:
model = PowerFeed
fields = [
'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
'amperage', 'max_utilization', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'description',
'tenant', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'cable', '_occupied')

View File

@ -0,0 +1,117 @@
from django.utils.translation import gettext as _
from rest_framework import serializers
from dcim.choices import *
from dcim.constants import *
from dcim.models import Rack, RackReservation, RackRole
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.config import ConfigItem
from tenancy.api.serializers_.tenants import TenantSerializer
from users.api.serializers_.users import UserSerializer
from .sites import LocationSerializer, SiteSerializer
__all__ = (
'RackElevationDetailFilterSerializer',
'RackReservationSerializer',
'RackRoleSerializer',
'RackSerializer',
)
class RackRoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
# Related object counts
rack_count = RelatedObjectCountField('racks')
class Meta:
model = RackRole
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'rack_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count')
class RackSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
site = SiteSerializer(nested=True)
location = LocationSerializer(nested=True, required=False, allow_null=True, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=RackStatusChoices, required=False)
role = RackRoleSerializer(nested=True, required=False, allow_null=True)
type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False, allow_null=True)
facility_id = serializers.CharField(max_length=50, allow_blank=True, allow_null=True, label=_('Facility ID'),
default=None)
width = ChoiceField(choices=RackWidthChoices, required=False)
outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False, allow_null=True)
weight_unit = ChoiceField(choices=WeightUnitChoices, allow_blank=True, required=False, allow_null=True)
# Related object counts
device_count = RelatedObjectCountField('devices')
powerfeed_count = RelatedObjectCountField('powerfeeds')
class Meta:
model = Rack
fields = [
'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'type', 'width', 'u_height', 'starting_unit', 'weight', 'max_weight', 'weight_unit',
'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count')
class RackReservationSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
rack = RackSerializer(nested=True)
user = UserSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = RackReservation
fields = [
'id', 'url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant', 'description',
'comments', 'tags', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'user', 'description', 'units')
class RackElevationDetailFilterSerializer(serializers.Serializer):
q = serializers.CharField(
required=False,
default=None
)
face = serializers.ChoiceField(
choices=DeviceFaceChoices,
default=DeviceFaceChoices.FACE_FRONT
)
render = serializers.ChoiceField(
choices=RackElevationDetailRenderChoices,
default=RackElevationDetailRenderChoices.RENDER_JSON
)
unit_width = serializers.IntegerField(
default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_WIDTH')
)
unit_height = serializers.IntegerField(
default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT')
)
legend_width = serializers.IntegerField(
default=RACK_ELEVATION_DEFAULT_LEGEND_WIDTH
)
margin_width = serializers.IntegerField(
default=RACK_ELEVATION_DEFAULT_MARGIN_WIDTH
)
exclude = serializers.IntegerField(
required=False,
default=None
)
expand_devices = serializers.BooleanField(
required=False,
default=True
)
include_images = serializers.BooleanField(
required=False,
default=True
)

View File

@ -0,0 +1,31 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.choices import *
from netbox.api.fields import ChoiceField
from .devices import DeviceSerializer
__all__ = (
'RackUnitSerializer',
)
class RackUnitSerializer(serializers.Serializer):
"""
A rack unit is an abstraction formed by the set (rack, position, face); it does not exist as a row in the database.
"""
id = serializers.DecimalField(
max_digits=4,
decimal_places=1,
read_only=True
)
name = serializers.CharField(read_only=True)
face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
device = DeviceSerializer(nested=True, read_only=True)
occupied = serializers.BooleanField(read_only=True)
display = serializers.SerializerMethodField(read_only=True)
@extend_schema_field(OpenApiTypes.STR)
def get_display(self, obj):
return obj['name']

View File

@ -0,0 +1,43 @@
from rest_framework import serializers
from dcim.models import DeviceRole, InventoryItemRole
from extras.api.serializers_.provisioning import ConfigTemplateSerializer
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
__all__ = (
'DeviceRoleSerializer',
'InventoryItemRoleSerializer',
)
class DeviceRoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
# Related object counts
device_count = RelatedObjectCountField('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
class Meta:
model = DeviceRole
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags',
'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count')
class InventoryItemRoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
# Related object counts
inventoryitem_count = RelatedObjectCountField('inventory_items')
class Meta:
model = InventoryItemRole
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'inventoryitem_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'inventoryitem_count')

View File

@ -0,0 +1,97 @@
from rest_framework import serializers
from timezone_field.rest_framework import TimeZoneSerializerField
from dcim.choices import *
from dcim.models import Location, Region, Site, SiteGroup
from ipam.api.nested_serializers import NestedASNSerializer
from ipam.models import ASN
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from ..nested_serializers import *
__all__ = (
'LocationSerializer',
'RegionSerializer',
'SiteGroupSerializer',
'SiteSerializer',
)
class RegionSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
parent = NestedRegionSerializer(required=False, allow_null=True, default=None)
site_count = serializers.IntegerField(read_only=True)
class Meta:
model = Region
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'site_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
class SiteGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
parent = NestedSiteGroupSerializer(required=False, allow_null=True, default=None)
site_count = serializers.IntegerField(read_only=True)
class Meta:
model = SiteGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'site_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
class SiteSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
status = ChoiceField(choices=SiteStatusChoices, required=False)
region = RegionSerializer(nested=True, required=False, allow_null=True)
group = SiteGroupSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(required=False, allow_null=True)
time_zone = TimeZoneSerializerField(required=False, allow_null=True)
asns = SerializedPKRelatedField(
queryset=ASN.objects.all(),
serializer=NestedASNSerializer,
required=False,
many=True
)
# Related object counts
circuit_count = RelatedObjectCountField('circuit_terminations')
device_count = RelatedObjectCountField('devices')
prefix_count = RelatedObjectCountField('prefixes')
rack_count = RelatedObjectCountField('racks')
vlan_count = RelatedObjectCountField('vlans')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
class Meta:
model = Site
fields = [
'id', 'url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'time_zone',
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'asns', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count', 'prefix_count', 'rack_count',
'virtualmachine_count', 'vlan_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'slug')
class LocationSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
site = SiteSerializer(nested=True)
parent = NestedLocationSerializer(required=False, allow_null=True)
status = ChoiceField(choices=LocationStatusChoices, required=False)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
rack_count = serializers.IntegerField(read_only=True)
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = Location
fields = [
'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'description', 'tags',
'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')

View File

@ -0,0 +1,25 @@
from rest_framework import serializers
from dcim.models import VirtualChassis
from netbox.api.serializers import NetBoxModelSerializer
from ..nested_serializers import *
__all__ = (
'VirtualChassisSerializer',
)
class VirtualChassisSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
master = NestedDeviceSerializer(required=False, allow_null=True, default=None)
# Counter fields
member_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualChassis
fields = [
'id', 'url', 'display', 'name', 'domain', 'master', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'member_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'master', 'description', 'member_count')

View File

@ -1,666 +1,15 @@
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.api.serializers import DataFileSerializer, DataSourceSerializer, JobSerializer
from core.models import ContentType
from dcim.api.nested_serializers import (
NestedDeviceRoleSerializer, NestedDeviceTypeSerializer, NestedLocationSerializer, NestedPlatformSerializer,
NestedRegionSerializer, NestedSiteSerializer, NestedSiteGroupSerializer,
)
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.choices import *
from extras.models import *
from netbox.api.exceptions import SerializerNotFound
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer
from netbox.api.serializers.features import TaggableModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
from tenancy.models import Tenant, TenantGroup
from users.api.serializers import UserSerializer
from utilities.api import get_serializer_for_model
from virtualization.api.nested_serializers import (
NestedClusterGroupSerializer, NestedClusterSerializer, NestedClusterTypeSerializer,
)
from virtualization.models import Cluster, ClusterGroup, ClusterType
from .serializers_.attachments import *
from .serializers_.bookmarks import *
from .serializers_.change_logging import *
from .serializers_.contenttypes import *
from .serializers_.customfields import *
from .serializers_.customlinks import *
from .serializers_.dashboard import *
from .serializers_.events import *
from .serializers_.exporttemplates import *
from .serializers_.journaling import *
from .serializers_.provisioning import *
from .serializers_.savedfilters import *
from .serializers_.scripts import *
from .serializers_.tags import *
from .nested_serializers import *
__all__ = (
'BookmarkSerializer',
'ConfigContextSerializer',
'ConfigTemplateSerializer',
'ContentTypeSerializer',
'CustomFieldChoiceSetSerializer',
'CustomFieldSerializer',
'CustomLinkSerializer',
'DashboardSerializer',
'EventRuleSerializer',
'ExportTemplateSerializer',
'ImageAttachmentSerializer',
'JournalEntrySerializer',
'ObjectChangeSerializer',
'SavedFilterSerializer',
'ScriptDetailSerializer',
'ScriptInputSerializer',
'ScriptSerializer',
'TagSerializer',
'WebhookSerializer',
)
#
# Event Rules
#
class EventRuleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:eventrule-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('event_rules'),
many=True
)
action_type = ChoiceField(choices=EventRuleActionChoices)
action_object_type = ContentTypeField(
queryset=ContentType.objects.with_feature('event_rules'),
)
action_object = serializers.SerializerMethodField(read_only=True)
class Meta:
model = EventRule
fields = [
'id', 'url', 'display', 'content_types', 'name', 'type_create', 'type_update', 'type_delete',
'type_job_start', 'type_job_end', 'enabled', 'conditions', 'action_type', 'action_object_type',
'action_object_id', 'action_object', 'description', 'custom_fields', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@extend_schema_field(OpenApiTypes.OBJECT)
def get_action_object(self, instance):
context = {'request': self.context['request']}
# We need to manually instantiate the serializer for scripts
if instance.action_type == EventRuleActionChoices.SCRIPT:
script = instance.action_object
instance = script.python_class() if script.python_class else None
return NestedScriptSerializer(instance, context=context).data
else:
serializer = get_serializer_for_model(
model=instance.action_object_type.model_class(),
prefix=NESTED_SERIALIZER_PREFIX
)
return serializer(instance.action_object, context=context).data
#
# Webhooks
#
class WebhookSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:webhook-detail')
class Meta:
model = Webhook
fields = [
'id', 'url', 'display', 'name', 'description', 'payload_url', 'http_method', 'http_content_type',
'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path', 'custom_fields',
'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Custom fields
#
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfieldchoiceset-detail')
base_choices = ChoiceField(
choices=CustomFieldChoiceSetBaseChoices,
required=False
)
extra_choices = serializers.ListField(
child=serializers.ListField(
min_length=2,
max_length=2
)
)
class Meta:
model = CustomFieldChoiceSet
fields = [
'id', 'url', 'display', 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
'choices_count', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
class CustomFieldSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('custom_fields'),
many=True
)
type = ChoiceField(choices=CustomFieldTypeChoices)
object_type = ContentTypeField(
queryset=ContentType.objects.all(),
required=False,
allow_null=True
)
filter_logic = ChoiceField(choices=CustomFieldFilterLogicChoices, required=False)
data_type = serializers.SerializerMethodField()
choice_set = CustomFieldChoiceSetSerializer(
nested=True,
required=False,
allow_null=True
)
ui_visible = ChoiceField(choices=CustomFieldUIVisibleChoices, required=False)
ui_editable = ChoiceField(choices=CustomFieldUIEditableChoices, required=False)
class Meta:
model = CustomField
fields = [
'id', 'url', 'display', 'content_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
'description', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable',
'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
def validate_type(self, value):
if self.instance and self.instance.type != value:
raise serializers.ValidationError(_('Changing the type of custom fields is not supported.'))
return value
@extend_schema_field(OpenApiTypes.STR)
def get_data_type(self, obj):
types = CustomFieldTypeChoices
if obj.type == types.TYPE_INTEGER:
return 'integer'
if obj.type == types.TYPE_DECIMAL:
return 'decimal'
if obj.type == types.TYPE_BOOLEAN:
return 'boolean'
if obj.type in (types.TYPE_JSON, types.TYPE_OBJECT):
return 'object'
if obj.type in (types.TYPE_MULTISELECT, types.TYPE_MULTIOBJECT):
return 'array'
return 'string'
#
# Custom links
#
class CustomLinkSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customlink-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('custom_links'),
many=True
)
class Meta:
model = CustomLink
fields = [
'id', 'url', 'display', 'content_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
'button_class', 'new_window', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name')
#
# Export templates
#
class ExportTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:exporttemplate-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('export_templates'),
many=True
)
data_source = DataSourceSerializer(
nested=True,
required=False
)
data_file = DataFileSerializer(
nested=True,
read_only=True
)
class Meta:
model = ExportTemplate
fields = [
'id', 'url', 'display', 'content_types', 'name', 'description', 'template_code', 'mime_type',
'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', 'data_synced', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Saved filters
#
class SavedFilterSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:savedfilter-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.all(),
many=True
)
class Meta:
model = SavedFilter
fields = [
'id', 'url', 'display', 'content_types', 'name', 'slug', 'description', 'user', 'weight', 'enabled',
'shared', 'parameters', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
#
# Bookmarks
#
class BookmarkSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:bookmark-detail')
object_type = ContentTypeField(
queryset=ContentType.objects.with_feature('bookmarks'),
)
object = serializers.SerializerMethodField(read_only=True)
user = UserSerializer(nested=True)
class Meta:
model = Bookmark
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created',
]
brief_fields = ('id', 'url', 'display', 'object_id', 'object_type')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_object(self, instance):
serializer = get_serializer_for_model(instance.object, prefix=NESTED_SERIALIZER_PREFIX)
return serializer(instance.object, context={'request': self.context['request']}).data
#
# Tags
#
class TagSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:tag-detail')
object_types = ContentTypeField(
queryset=ContentType.objects.with_feature('tags'),
many=True,
required=False
)
# Related object counts
tagged_items = RelatedObjectCountField('extras_taggeditem_items')
class Meta:
model = Tag
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'object_types', 'tagged_items', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description')
#
# Image attachments
#
class ImageAttachmentSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:imageattachment-detail')
content_type = ContentTypeField(
queryset=ContentType.objects.all()
)
parent = serializers.SerializerMethodField(read_only=True)
class Meta:
model = ImageAttachment
fields = [
'id', 'url', 'display', 'content_type', 'object_id', 'parent', 'name', 'image', 'image_height',
'image_width', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'image')
def validate(self, data):
# Validate that the parent object exists
try:
data['content_type'].get_object_for_this_type(id=data['object_id'])
except ObjectDoesNotExist:
raise serializers.ValidationError(
"Invalid parent object: {} ID {}".format(data['content_type'], data['object_id'])
)
# Enforce model validation
super().validate(data)
return data
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_parent(self, obj):
serializer = get_serializer_for_model(obj.parent, prefix=NESTED_SERIALIZER_PREFIX)
return serializer(obj.parent, context={'request': self.context['request']}).data
#
# Journal entries
#
class JournalEntrySerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:journalentry-detail')
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.all()
)
assigned_object = serializers.SerializerMethodField(read_only=True)
created_by = serializers.PrimaryKeyRelatedField(
allow_null=True,
queryset=get_user_model().objects.all(),
required=False,
default=serializers.CurrentUserDefault()
)
kind = ChoiceField(
choices=JournalEntryKindChoices,
required=False
)
class Meta:
model = JournalEntry
fields = [
'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created',
'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'created')
def validate(self, data):
# Validate that the parent object exists
if 'assigned_object_type' in data and 'assigned_object_id' in data:
try:
data['assigned_object_type'].get_object_for_this_type(id=data['assigned_object_id'])
except ObjectDoesNotExist:
raise serializers.ValidationError(
f"Invalid assigned_object: {data['assigned_object_type']} ID {data['assigned_object_id']}"
)
# Enforce model validation
super().validate(data)
return data
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_assigned_object(self, instance):
serializer = get_serializer_for_model(instance.assigned_object_type.model_class(), prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(instance.assigned_object, context=context).data
#
# Config contexts
#
class ConfigContextSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configcontext-detail')
regions = SerializedPKRelatedField(
queryset=Region.objects.all(),
serializer=NestedRegionSerializer,
required=False,
many=True
)
site_groups = SerializedPKRelatedField(
queryset=SiteGroup.objects.all(),
serializer=NestedSiteGroupSerializer,
required=False,
many=True
)
sites = SerializedPKRelatedField(
queryset=Site.objects.all(),
serializer=NestedSiteSerializer,
required=False,
many=True
)
locations = SerializedPKRelatedField(
queryset=Location.objects.all(),
serializer=NestedLocationSerializer,
required=False,
many=True
)
device_types = SerializedPKRelatedField(
queryset=DeviceType.objects.all(),
serializer=NestedDeviceTypeSerializer,
required=False,
many=True
)
roles = SerializedPKRelatedField(
queryset=DeviceRole.objects.all(),
serializer=NestedDeviceRoleSerializer,
required=False,
many=True
)
platforms = SerializedPKRelatedField(
queryset=Platform.objects.all(),
serializer=NestedPlatformSerializer,
required=False,
many=True
)
cluster_types = SerializedPKRelatedField(
queryset=ClusterType.objects.all(),
serializer=NestedClusterTypeSerializer,
required=False,
many=True
)
cluster_groups = SerializedPKRelatedField(
queryset=ClusterGroup.objects.all(),
serializer=NestedClusterGroupSerializer,
required=False,
many=True
)
clusters = SerializedPKRelatedField(
queryset=Cluster.objects.all(),
serializer=NestedClusterSerializer,
required=False,
many=True
)
tenant_groups = SerializedPKRelatedField(
queryset=TenantGroup.objects.all(),
serializer=NestedTenantGroupSerializer,
required=False,
many=True
)
tenants = SerializedPKRelatedField(
queryset=Tenant.objects.all(),
serializer=NestedTenantSerializer,
required=False,
many=True
)
tags = serializers.SlugRelatedField(
queryset=Tag.objects.all(),
slug_field='slug',
required=False,
many=True
)
data_source = DataSourceSerializer(
nested=True,
required=False
)
data_file = DataFileSerializer(
nested=True,
read_only=True
)
class Meta:
model = ConfigContext
fields = [
'id', 'url', 'display', 'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites',
'locations', 'device_types', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
'tenant_groups', 'tenants', 'tags', 'data_source', 'data_path', 'data_file', 'data_synced', 'data',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Config templates
#
class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configtemplate-detail')
data_source = DataSourceSerializer(
nested=True,
required=False
)
data_file = DataFileSerializer(
nested=True,
required=False
)
class Meta:
model = ConfigTemplate
fields = [
'id', 'url', 'display', 'name', 'description', 'environment_params', 'template_code', 'data_source',
'data_path', 'data_file', 'data_synced', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Scripts
#
class ScriptSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:script-detail')
description = serializers.SerializerMethodField(read_only=True)
vars = serializers.SerializerMethodField(read_only=True)
result = JobSerializer(nested=True, read_only=True)
class Meta:
model = Script
fields = [
'id', 'url', 'module', 'name', 'description', 'vars', 'result', 'display', 'is_executable',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_vars(self, obj):
if obj.python_class:
return {
k: v.__class__.__name__ for k, v in obj.python_class()._get_vars().items()
}
else:
return {}
@extend_schema_field(serializers.CharField())
def get_display(self, obj):
return f'{obj.name} ({obj.module})'
@extend_schema_field(serializers.CharField())
def get_description(self, obj):
if obj.python_class:
return obj.python_class().description
else:
return None
class ScriptDetailSerializer(ScriptSerializer):
result = serializers.SerializerMethodField(read_only=True)
@extend_schema_field(JobSerializer())
def get_result(self, obj):
job = obj.jobs.all().order_by('-created').first()
context = {
'request': self.context['request']
}
data = JobSerializer(job, context=context).data
return data
class ScriptInputSerializer(serializers.Serializer):
data = serializers.JSONField()
commit = serializers.BooleanField()
schedule_at = serializers.DateTimeField(required=False, allow_null=True)
interval = serializers.IntegerField(required=False, allow_null=True)
def validate_schedule_at(self, value):
if value and not self.context['script'].scheduling_enabled:
raise serializers.ValidationError(_("Scheduling is not enabled for this script."))
return value
def validate_interval(self, value):
if value and not self.context['script'].scheduling_enabled:
raise serializers.ValidationError(_("Scheduling is not enabled for this script."))
return value
#
# Change logging
#
class ObjectChangeSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:objectchange-detail')
user = UserSerializer(
nested=True,
read_only=True
)
action = ChoiceField(
choices=ObjectChangeActionChoices,
read_only=True
)
changed_object_type = ContentTypeField(
read_only=True
)
changed_object = serializers.SerializerMethodField(
read_only=True
)
class Meta:
model = ObjectChange
fields = [
'id', 'url', 'display', 'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type',
'changed_object_id', 'changed_object', 'prechange_data', 'postchange_data',
]
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_changed_object(self, obj):
"""
Serialize a nested representation of the changed object.
"""
if obj.changed_object is None:
return None
try:
serializer = get_serializer_for_model(obj.changed_object, prefix=NESTED_SERIALIZER_PREFIX)
except SerializerNotFound:
return obj.object_repr
context = {
'request': self.context['request']
}
data = serializer(obj.changed_object, context=context).data
return data
#
# ContentTypes
#
class ContentTypeSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:contenttype-detail')
class Meta:
model = ContentType
fields = ['id', 'url', 'display', 'app_label', 'model']
#
# User dashboard
#
class DashboardSerializer(serializers.ModelSerializer):
class Meta:
model = Dashboard
fields = ('layout', 'config')

View File

@ -0,0 +1,50 @@
from django.core.exceptions import ObjectDoesNotExist
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.models import ContentType
from extras.models import ImageAttachment
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model
__all__ = (
'ImageAttachmentSerializer',
)
class ImageAttachmentSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:imageattachment-detail')
content_type = ContentTypeField(
queryset=ContentType.objects.all()
)
parent = serializers.SerializerMethodField(read_only=True)
class Meta:
model = ImageAttachment
fields = [
'id', 'url', 'display', 'content_type', 'object_id', 'parent', 'name', 'image', 'image_height',
'image_width', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'image')
def validate(self, data):
# Validate that the parent object exists
try:
data['content_type'].get_object_for_this_type(id=data['object_id'])
except ObjectDoesNotExist:
raise serializers.ValidationError(
"Invalid parent object: {} ID {}".format(data['content_type'], data['object_id'])
)
# Enforce model validation
super().validate(data)
return data
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_parent(self, obj):
serializer = get_serializer_for_model(obj.parent, prefix=NESTED_SERIALIZER_PREFIX)
return serializer(obj.parent, context={'request': self.context['request']}).data

View File

@ -0,0 +1,35 @@
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.models import ContentType
from extras.models import Bookmark
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from users.api.serializers_.users import UserSerializer
from utilities.api import get_serializer_for_model
__all__ = (
'BookmarkSerializer',
)
class BookmarkSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:bookmark-detail')
object_type = ContentTypeField(
queryset=ContentType.objects.with_feature('bookmarks'),
)
object = serializers.SerializerMethodField(read_only=True)
user = UserSerializer(nested=True)
class Meta:
model = Bookmark
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created',
]
brief_fields = ('id', 'url', 'display', 'object_id', 'object_type')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_object(self, instance):
serializer = get_serializer_for_model(instance.object, prefix=NESTED_SERIALIZER_PREFIX)
return serializer(instance.object, context={'request': self.context['request']}).data

View File

@ -0,0 +1,59 @@
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from extras.choices import *
from extras.models import ObjectChange
from netbox.api.exceptions import SerializerNotFound
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from users.api.serializers_.users import UserSerializer
from utilities.api import get_serializer_for_model
__all__ = (
'ObjectChangeSerializer',
)
class ObjectChangeSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:objectchange-detail')
user = UserSerializer(
nested=True,
read_only=True
)
action = ChoiceField(
choices=ObjectChangeActionChoices,
read_only=True
)
changed_object_type = ContentTypeField(
read_only=True
)
changed_object = serializers.SerializerMethodField(
read_only=True
)
class Meta:
model = ObjectChange
fields = [
'id', 'url', 'display', 'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type',
'changed_object_id', 'changed_object', 'prechange_data', 'postchange_data',
]
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_changed_object(self, obj):
"""
Serialize a nested representation of the changed object.
"""
if obj.changed_object is None:
return None
try:
serializer = get_serializer_for_model(obj.changed_object, prefix=NESTED_SERIALIZER_PREFIX)
except SerializerNotFound:
return obj.object_repr
context = {
'request': self.context['request']
}
data = serializer(obj.changed_object, context=context).data
return data

View File

@ -0,0 +1,16 @@
from rest_framework import serializers
from core.models import ContentType
from netbox.api.serializers import BaseModelSerializer
__all__ = (
'ContentTypeSerializer',
)
class ContentTypeSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:contenttype-detail')
class Meta:
model = ContentType
fields = ['id', 'url', 'display', 'app_label', 'model']

View File

@ -0,0 +1,91 @@
from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.models import ContentType
from extras.choices import *
from extras.models import CustomField, CustomFieldChoiceSet
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
__all__ = (
'CustomFieldChoiceSetSerializer',
'CustomFieldSerializer',
)
class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfieldchoiceset-detail')
base_choices = ChoiceField(
choices=CustomFieldChoiceSetBaseChoices,
required=False
)
extra_choices = serializers.ListField(
child=serializers.ListField(
min_length=2,
max_length=2
)
)
class Meta:
model = CustomFieldChoiceSet
fields = [
'id', 'url', 'display', 'name', 'description', 'base_choices', 'extra_choices', 'order_alphabetically',
'choices_count', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'choices_count')
class CustomFieldSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('custom_fields'),
many=True
)
type = ChoiceField(choices=CustomFieldTypeChoices)
object_type = ContentTypeField(
queryset=ContentType.objects.all(),
required=False,
allow_null=True
)
filter_logic = ChoiceField(choices=CustomFieldFilterLogicChoices, required=False)
data_type = serializers.SerializerMethodField()
choice_set = CustomFieldChoiceSetSerializer(
nested=True,
required=False,
allow_null=True
)
ui_visible = ChoiceField(choices=CustomFieldUIVisibleChoices, required=False)
ui_editable = ChoiceField(choices=CustomFieldUIEditableChoices, required=False)
class Meta:
model = CustomField
fields = [
'id', 'url', 'display', 'content_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
'description', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable',
'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
def validate_type(self, value):
if self.instance and self.instance.type != value:
raise serializers.ValidationError(_('Changing the type of custom fields is not supported.'))
return value
@extend_schema_field(OpenApiTypes.STR)
def get_data_type(self, obj):
types = CustomFieldTypeChoices
if obj.type == types.TYPE_INTEGER:
return 'integer'
if obj.type == types.TYPE_DECIMAL:
return 'decimal'
if obj.type == types.TYPE_BOOLEAN:
return 'boolean'
if obj.type in (types.TYPE_JSON, types.TYPE_OBJECT):
return 'object'
if obj.type in (types.TYPE_MULTISELECT, types.TYPE_MULTIOBJECT):
return 'array'
return 'string'

View File

@ -0,0 +1,26 @@
from rest_framework import serializers
from core.models import ContentType
from extras.models import CustomLink
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
__all__ = (
'CustomLinkSerializer',
)
class CustomLinkSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customlink-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('custom_links'),
many=True
)
class Meta:
model = CustomLink
fields = [
'id', 'url', 'display', 'content_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
'button_class', 'new_window', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name')

View File

@ -0,0 +1,13 @@
from rest_framework import serializers
from extras.models import Dashboard
__all__ = (
'DashboardSerializer',
)
class DashboardSerializer(serializers.ModelSerializer):
class Meta:
model = Dashboard
fields = ('layout', 'config')

View File

@ -0,0 +1,75 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.models import ContentType
from extras.choices import *
from extras.models import EventRule, Webhook
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model
from ..nested_serializers import *
__all__ = (
'EventRuleSerializer',
'WebhookSerializer',
)
#
# Event Rules
#
class EventRuleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:eventrule-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('event_rules'),
many=True
)
action_type = ChoiceField(choices=EventRuleActionChoices)
action_object_type = ContentTypeField(
queryset=ContentType.objects.with_feature('event_rules'),
)
action_object = serializers.SerializerMethodField(read_only=True)
class Meta:
model = EventRule
fields = [
'id', 'url', 'display', 'content_types', 'name', 'type_create', 'type_update', 'type_delete',
'type_job_start', 'type_job_end', 'enabled', 'conditions', 'action_type', 'action_object_type',
'action_object_id', 'action_object', 'description', 'custom_fields', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@extend_schema_field(OpenApiTypes.OBJECT)
def get_action_object(self, instance):
context = {'request': self.context['request']}
# We need to manually instantiate the serializer for scripts
if instance.action_type == EventRuleActionChoices.SCRIPT:
script = instance.action_object
instance = script.python_class() if script.python_class else None
return NestedScriptSerializer(instance, context=context).data
else:
serializer = get_serializer_for_model(
model=instance.action_object_type.model_class(),
prefix=NESTED_SERIALIZER_PREFIX
)
return serializer(instance.action_object, context=context).data
#
# Webhooks
#
class WebhookSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:webhook-detail')
class Meta:
model = Webhook
fields = [
'id', 'url', 'display', 'name', 'description', 'payload_url', 'http_method', 'http_content_type',
'additional_headers', 'body_template', 'secret', 'ssl_verification', 'ca_file_path', 'custom_fields',
'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -0,0 +1,36 @@
from rest_framework import serializers
from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
from core.models import ContentType
from extras.models import ExportTemplate
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
__all__ = (
'ExportTemplateSerializer',
)
class ExportTemplateSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:exporttemplate-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.with_feature('export_templates'),
many=True
)
data_source = DataSourceSerializer(
nested=True,
required=False
)
data_file = DataFileSerializer(
nested=True,
read_only=True
)
class Meta:
model = ExportTemplate
fields = [
'id', 'url', 'display', 'content_types', 'name', 'description', 'template_code', 'mime_type',
'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', 'data_synced', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -0,0 +1,67 @@
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.models import ContentType
from extras.choices import *
from extras.models import JournalEntry
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model
__all__ = (
'JournalEntrySerializer',
)
class JournalEntrySerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:journalentry-detail')
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.all()
)
assigned_object = serializers.SerializerMethodField(read_only=True)
created_by = serializers.PrimaryKeyRelatedField(
allow_null=True,
queryset=get_user_model().objects.all(),
required=False,
default=serializers.CurrentUserDefault()
)
kind = ChoiceField(
choices=JournalEntryKindChoices,
required=False
)
class Meta:
model = JournalEntry
fields = [
'id', 'url', 'display', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'created',
'created_by', 'kind', 'comments', 'tags', 'custom_fields', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'created')
def validate(self, data):
# Validate that the parent object exists
if 'assigned_object_type' in data and 'assigned_object_id' in data:
try:
data['assigned_object_type'].get_object_for_this_type(id=data['assigned_object_id'])
except ObjectDoesNotExist:
raise serializers.ValidationError(
f"Invalid assigned_object: {data['assigned_object_type']} ID {data['assigned_object_id']}"
)
# Enforce model validation
super().validate(data)
return data
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_assigned_object(self, instance):
serializer = get_serializer_for_model(
instance.assigned_object_type.model_class(),
prefix=NESTED_SERIALIZER_PREFIX
)
context = {'request': self.context['request']}
return serializer(instance.assigned_object, context=context).data

View File

@ -0,0 +1,147 @@
from rest_framework import serializers
from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
from dcim.api.nested_serializers import (
NestedDeviceRoleSerializer, NestedDeviceTypeSerializer, NestedLocationSerializer, NestedPlatformSerializer,
NestedRegionSerializer, NestedSiteSerializer, NestedSiteGroupSerializer,
)
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from extras.models import ConfigContext, ConfigTemplate, Tag
from netbox.api.fields import SerializedPKRelatedField
from netbox.api.serializers import ValidatedModelSerializer
from netbox.api.serializers.features import TaggableModelSerializer
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
from tenancy.models import Tenant, TenantGroup
from virtualization.api.nested_serializers import (
NestedClusterGroupSerializer, NestedClusterSerializer, NestedClusterTypeSerializer,
)
from virtualization.models import Cluster, ClusterGroup, ClusterType
__all__ = (
'ConfigContextSerializer',
'ConfigTemplateSerializer',
)
class ConfigContextSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configcontext-detail')
regions = SerializedPKRelatedField(
queryset=Region.objects.all(),
serializer=NestedRegionSerializer,
required=False,
many=True
)
site_groups = SerializedPKRelatedField(
queryset=SiteGroup.objects.all(),
serializer=NestedSiteGroupSerializer,
required=False,
many=True
)
sites = SerializedPKRelatedField(
queryset=Site.objects.all(),
serializer=NestedSiteSerializer,
required=False,
many=True
)
locations = SerializedPKRelatedField(
queryset=Location.objects.all(),
serializer=NestedLocationSerializer,
required=False,
many=True
)
device_types = SerializedPKRelatedField(
queryset=DeviceType.objects.all(),
serializer=NestedDeviceTypeSerializer,
required=False,
many=True
)
roles = SerializedPKRelatedField(
queryset=DeviceRole.objects.all(),
serializer=NestedDeviceRoleSerializer,
required=False,
many=True
)
platforms = SerializedPKRelatedField(
queryset=Platform.objects.all(),
serializer=NestedPlatformSerializer,
required=False,
many=True
)
cluster_types = SerializedPKRelatedField(
queryset=ClusterType.objects.all(),
serializer=NestedClusterTypeSerializer,
required=False,
many=True
)
cluster_groups = SerializedPKRelatedField(
queryset=ClusterGroup.objects.all(),
serializer=NestedClusterGroupSerializer,
required=False,
many=True
)
clusters = SerializedPKRelatedField(
queryset=Cluster.objects.all(),
serializer=NestedClusterSerializer,
required=False,
many=True
)
tenant_groups = SerializedPKRelatedField(
queryset=TenantGroup.objects.all(),
serializer=NestedTenantGroupSerializer,
required=False,
many=True
)
tenants = SerializedPKRelatedField(
queryset=Tenant.objects.all(),
serializer=NestedTenantSerializer,
required=False,
many=True
)
tags = serializers.SlugRelatedField(
queryset=Tag.objects.all(),
slug_field='slug',
required=False,
many=True
)
data_source = DataSourceSerializer(
nested=True,
required=False
)
data_file = DataFileSerializer(
nested=True,
read_only=True
)
class Meta:
model = ConfigContext
fields = [
'id', 'url', 'display', 'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites',
'locations', 'device_types', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters',
'tenant_groups', 'tenants', 'tags', 'data_source', 'data_path', 'data_file', 'data_synced', 'data',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Config templates
#
class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:configtemplate-detail')
data_source = DataSourceSerializer(
nested=True,
required=False
)
data_file = DataFileSerializer(
nested=True,
required=False
)
class Meta:
model = ConfigTemplate
fields = [
'id', 'url', 'display', 'name', 'description', 'environment_params', 'template_code', 'data_source',
'data_path', 'data_file', 'data_synced', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -0,0 +1,26 @@
from rest_framework import serializers
from core.models import ContentType
from extras.models import SavedFilter
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import ValidatedModelSerializer
__all__ = (
'SavedFilterSerializer',
)
class SavedFilterSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:savedfilter-detail')
content_types = ContentTypeField(
queryset=ContentType.objects.all(),
many=True
)
class Meta:
model = SavedFilter
fields = [
'id', 'url', 'display', 'content_types', 'name', 'slug', 'description', 'user', 'weight', 'enabled',
'shared', 'parameters', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')

View File

@ -0,0 +1,77 @@
from django.utils.translation import gettext as _
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from core.api.serializers_.jobs import JobSerializer
from extras.models import Script
from netbox.api.serializers import ValidatedModelSerializer
__all__ = (
'ScriptDetailSerializer',
'ScriptInputSerializer',
'ScriptSerializer',
)
class ScriptSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:script-detail')
description = serializers.SerializerMethodField(read_only=True)
vars = serializers.SerializerMethodField(read_only=True)
result = JobSerializer(nested=True, read_only=True)
class Meta:
model = Script
fields = [
'id', 'url', 'module', 'name', 'description', 'vars', 'result', 'display', 'is_executable',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_vars(self, obj):
if obj.python_class:
return {
k: v.__class__.__name__ for k, v in obj.python_class()._get_vars().items()
}
else:
return {}
@extend_schema_field(serializers.CharField())
def get_display(self, obj):
return f'{obj.name} ({obj.module})'
@extend_schema_field(serializers.CharField())
def get_description(self, obj):
if obj.python_class:
return obj.python_class().description
else:
return None
class ScriptDetailSerializer(ScriptSerializer):
result = serializers.SerializerMethodField(read_only=True)
@extend_schema_field(JobSerializer())
def get_result(self, obj):
job = obj.jobs.all().order_by('-created').first()
context = {
'request': self.context['request']
}
data = JobSerializer(job, context=context).data
return data
class ScriptInputSerializer(serializers.Serializer):
data = serializers.JSONField()
commit = serializers.BooleanField()
schedule_at = serializers.DateTimeField(required=False, allow_null=True)
interval = serializers.IntegerField(required=False, allow_null=True)
def validate_schedule_at(self, value):
if value and not self.context['script'].scheduling_enabled:
raise serializers.ValidationError(_("Scheduling is not enabled for this script."))
return value
def validate_interval(self, value):
if value and not self.context['script'].scheduling_enabled:
raise serializers.ValidationError(_("Scheduling is not enabled for this script."))
return value

View File

@ -0,0 +1,30 @@
from rest_framework import serializers
from core.models import ContentType
from extras.models import Tag
from netbox.api.fields import ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import ValidatedModelSerializer
__all__ = (
'TagSerializer',
)
class TagSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:tag-detail')
object_types = ContentTypeField(
queryset=ContentType.objects.with_feature('tags'),
many=True,
required=False
)
# Related object counts
tagged_items = RelatedObjectCountField('extras_taggeditem_items')
class Meta:
model = Tag
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'object_types', 'tagged_items', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description')

View File

@ -1,511 +1,8 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.api.serializers import DeviceSerializer, SiteSerializer
from ipam.choices import *
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES
from ipam.models import *
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers import TenantSerializer
from utilities.api import get_serializer_for_model
from virtualization.api.serializers import VirtualMachineSerializer
from vpn.api.nested_serializers import NestedL2VPNTerminationSerializer
from .field_serializers import IPAddressField, IPNetworkField
from .serializers_.asns import *
from .serializers_.vrfs import *
from .serializers_.roles import *
from .serializers_.vlans import *
from .serializers_.ip import *
from .serializers_.fhrpgroups import *
from .serializers_.services import *
from .nested_serializers import *
#
# RIRs
#
class RIRSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
# Related object counts
aggregate_count = RelatedObjectCountField('aggregates')
class Meta:
model = RIR
fields = [
'id', 'url', 'display', 'name', 'slug', 'is_private', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'aggregate_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'aggregate_count')
#
# ASN ranges
#
class ASNRangeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asnrange-detail')
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
asn_count = serializers.IntegerField(read_only=True)
class Meta:
model = ASNRange
fields = [
'id', 'url', 'display', 'name', 'slug', 'rir', 'start', 'end', 'tenant', 'description', 'tags',
'custom_fields', 'created', 'last_updated', 'asn_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# ASNs
#
class ASNSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
rir = RIRSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
# Related object counts
site_count = RelatedObjectCountField('sites')
provider_count = RelatedObjectCountField('providers')
class Meta:
model = ASN
fields = [
'id', 'url', 'display', 'asn', 'rir', 'tenant', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'site_count', 'provider_count',
]
brief_fields = ('id', 'url', 'display', 'asn', 'description')
class AvailableASNSerializer(serializers.Serializer):
"""
Representation of an ASN which does not exist in the database.
"""
asn = serializers.IntegerField(read_only=True)
description = serializers.CharField(required=False)
def to_representation(self, asn):
rir = RIRSerializer(self.context['range'].rir, nested=True, context={
'request': self.context['request']
}).data
return {
'rir': rir,
'asn': asn,
}
#
# VRFs
#
class VRFSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
export_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
# Related object counts
ipaddress_count = RelatedObjectCountField('ip_addresses')
prefix_count = RelatedObjectCountField('prefixes')
class Meta:
model = VRF
fields = [
'id', 'url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments',
'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated', 'ipaddress_count',
'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'rd', 'description', 'prefix_count')
class RouteTargetSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = RouteTarget
fields = [
'id', 'url', 'display', 'name', 'tenant', 'description', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Aggregates
#
class AggregateSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
prefix = IPNetworkField()
class Meta:
model = Aggregate
fields = [
'id', 'url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description')
#
# FHRP Groups
#
class FHRPGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroup-detail')
ip_addresses = NestedIPAddressSerializer(many=True, read_only=True)
class Meta:
model = FHRPGroup
fields = [
'id', 'name', 'url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
]
brief_fields = ('id', 'url', 'display', 'protocol', 'group_id', 'description')
class FHRPGroupAssignmentSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroupassignment-detail')
group = FHRPGroupSerializer(nested=True)
interface_type = ContentTypeField(
queryset=ContentType.objects.all()
)
interface = serializers.SerializerMethodField(read_only=True)
class Meta:
model = FHRPGroupAssignment
fields = [
'id', 'url', 'display', 'group', 'interface_type', 'interface_id', 'interface', 'priority', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'group', 'interface_type', 'interface_id', 'priority')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_interface(self, obj):
if obj.interface is None:
return None
serializer = get_serializer_for_model(obj.interface, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.interface, context=context).data
#
# VLANs
#
class RoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
# Related object counts
prefix_count = RelatedObjectCountField('prefixes')
vlan_count = RelatedObjectCountField('vlans')
class Meta:
model = Role
fields = [
'id', 'url', 'display', 'name', 'slug', 'weight', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'prefix_count', 'vlan_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'prefix_count', 'vlan_count')
class VLANGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
scope_type = ContentTypeField(
queryset=ContentType.objects.filter(
model__in=VLANGROUP_SCOPE_TYPES
),
allow_null=True,
required=False,
default=None
)
scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
scope = serializers.SerializerMethodField(read_only=True)
utilization = serializers.CharField(read_only=True)
# Related object counts
vlan_count = RelatedObjectCountField('vlans')
class Meta:
model = VLANGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'min_vid', 'max_vid',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count')
validators = []
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_scope(self, obj):
if obj.scope_id is None:
return None
serializer = get_serializer_for_model(obj.scope, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.scope, context=context).data
class VLANSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
site = SiteSerializer(nested=True, required=False, allow_null=True)
group = VLANGroupSerializer(nested=True, required=False, allow_null=True, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=VLANStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, allow_null=True)
# Related object counts
prefix_count = RelatedObjectCountField('prefixes')
class Meta:
model = VLAN
fields = [
'id', 'url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description',
'comments', 'l2vpn_termination', 'tags', 'custom_fields', 'created', 'last_updated', 'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'vid', 'name', 'description')
class AvailableVLANSerializer(serializers.Serializer):
"""
Representation of a VLAN which does not exist in the database.
"""
vid = serializers.IntegerField(read_only=True)
group = VLANGroupSerializer(nested=True, read_only=True)
def to_representation(self, instance):
return {
'vid': instance,
'group': VLANGroupSerializer(
self.context['group'],
nested=True,
context={'request': self.context['request']}
).data,
}
class CreateAvailableVLANSerializer(NetBoxModelSerializer):
site = SiteSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=VLANStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = VLAN
fields = [
'name', 'site', 'tenant', 'status', 'role', 'description', 'tags', 'custom_fields',
]
def validate(self, data):
# Bypass model validation since we don't have a VID yet
return data
#
# Prefixes
#
class PrefixSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
site = SiteSerializer(nested=True, required=False, allow_null=True)
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=PrefixStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
children = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(read_only=True)
prefix = IPNetworkField()
class Meta:
model = Prefix
fields = [
'id', 'url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool',
'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'children',
'_depth',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description', '_depth')
class PrefixLengthSerializer(serializers.Serializer):
prefix_length = serializers.IntegerField()
def to_internal_value(self, data):
requested_prefix = data.get('prefix_length')
if requested_prefix is None:
raise serializers.ValidationError({
'prefix_length': 'this field can not be missing'
})
if not isinstance(requested_prefix, int):
raise serializers.ValidationError({
'prefix_length': 'this field must be int type'
})
prefix = self.context.get('prefix')
if prefix.family == 4 and requested_prefix > 32:
raise serializers.ValidationError({
'prefix_length': 'Invalid prefix length ({}) for IPv4'.format((requested_prefix))
})
elif prefix.family == 6 and requested_prefix > 128:
raise serializers.ValidationError({
'prefix_length': 'Invalid prefix length ({}) for IPv6'.format((requested_prefix))
})
return data
class AvailablePrefixSerializer(serializers.Serializer):
"""
Representation of a prefix which does not exist in the database.
"""
family = serializers.IntegerField(read_only=True)
prefix = serializers.CharField(read_only=True)
vrf = VRFSerializer(nested=True, read_only=True)
def to_representation(self, instance):
if self.context.get('vrf'):
vrf = VRFSerializer(self.context['vrf'], nested=True, context={'request': self.context['request']}).data
else:
vrf = None
return {
'family': instance.version,
'prefix': str(instance),
'vrf': vrf,
}
#
# IP ranges
#
class IPRangeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
start_address = IPAddressField()
end_address = IPAddressField()
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=IPRangeStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = IPRange
fields = [
'id', 'url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant', 'status', 'role',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'start_address', 'end_address', 'description')
#
# IP addresses
#
class IPAddressSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
address = IPAddressField()
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=IPAddressStatusChoices, required=False)
role = ChoiceField(choices=IPAddressRoleChoices, allow_blank=True, required=False)
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.filter(IPADDRESS_ASSIGNMENT_MODELS),
required=False,
allow_null=True
)
assigned_object = serializers.SerializerMethodField(read_only=True)
nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
nat_outside = NestedIPAddressSerializer(many=True, read_only=True)
class Meta:
model = IPAddress
fields = [
'id', 'url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'assigned_object_type',
'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside', 'dns_name', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'address', 'description')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_assigned_object(self, obj):
if obj.assigned_object is None:
return None
serializer = get_serializer_for_model(obj.assigned_object, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.assigned_object, context=context).data
class AvailableIPSerializer(serializers.Serializer):
"""
Representation of an IP address which does not exist in the database.
"""
family = serializers.IntegerField(read_only=True)
address = serializers.CharField(read_only=True)
vrf = VRFSerializer(nested=True, read_only=True)
description = serializers.CharField(required=False)
def to_representation(self, instance):
if self.context.get('vrf'):
vrf = VRFSerializer(self.context['vrf'], nested=True, context={'request': self.context['request']}).data
else:
vrf = None
return {
'family': self.context['parent'].family,
'address': f"{instance}/{self.context['parent'].mask_length}",
'vrf': vrf,
}
#
# Services
#
class ServiceTemplateSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:servicetemplate-detail')
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
class Meta:
model = ServiceTemplate
fields = [
'id', 'url', 'display', 'name', 'protocol', 'ports', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')
class ServiceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
device = DeviceSerializer(nested=True, required=False, allow_null=True)
virtual_machine = VirtualMachineSerializer(nested=True, required=False, allow_null=True)
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
ipaddresses = SerializedPKRelatedField(
queryset=IPAddress.objects.all(),
serializer=NestedIPAddressSerializer,
required=False,
many=True
)
class Meta:
model = Service
fields = [
'id', 'url', 'display', 'device', 'virtual_machine', 'name', 'protocol', 'ports', 'ipaddresses',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')

View File

View File

@ -0,0 +1,78 @@
from rest_framework import serializers
from ipam.models import ASN, ASNRange, RIR
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
__all__ = (
'ASNRangeSerializer',
'ASNSerializer',
'AvailableASNSerializer',
'RIRSerializer',
)
class RIRSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
# Related object counts
aggregate_count = RelatedObjectCountField('aggregates')
class Meta:
model = RIR
fields = [
'id', 'url', 'display', 'name', 'slug', 'is_private', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'aggregate_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'aggregate_count')
class ASNRangeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asnrange-detail')
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
asn_count = serializers.IntegerField(read_only=True)
class Meta:
model = ASNRange
fields = [
'id', 'url', 'display', 'name', 'slug', 'rir', 'start', 'end', 'tenant', 'description', 'tags',
'custom_fields', 'created', 'last_updated', 'asn_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ASNSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:asn-detail')
rir = RIRSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
# Related object counts
site_count = RelatedObjectCountField('sites')
provider_count = RelatedObjectCountField('providers')
class Meta:
model = ASN
fields = [
'id', 'url', 'display', 'asn', 'rir', 'tenant', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'site_count', 'provider_count',
]
brief_fields = ('id', 'url', 'display', 'asn', 'description')
class AvailableASNSerializer(serializers.Serializer):
"""
Representation of an ASN which does not exist in the database.
"""
asn = serializers.IntegerField(read_only=True)
description = serializers.CharField(required=False)
def to_representation(self, asn):
rir = RIRSerializer(self.context['range'].rir, nested=True, context={
'request': self.context['request']
}).data
return {
'rir': rir,
'asn': asn,
}

View File

@ -0,0 +1,53 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from ipam.models import FHRPGroup, FHRPGroupAssignment
from netbox.api.fields import ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model
from ..nested_serializers import *
__all__ = (
'FHRPGroupAssignmentSerializer',
'FHRPGroupSerializer',
)
class FHRPGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroup-detail')
ip_addresses = NestedIPAddressSerializer(many=True, read_only=True)
class Meta:
model = FHRPGroup
fields = [
'id', 'name', 'url', 'display', 'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated', 'ip_addresses',
]
brief_fields = ('id', 'url', 'display', 'protocol', 'group_id', 'description')
class FHRPGroupAssignmentSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:fhrpgroupassignment-detail')
group = FHRPGroupSerializer(nested=True)
interface_type = ContentTypeField(
queryset=ContentType.objects.all()
)
interface = serializers.SerializerMethodField(read_only=True)
class Meta:
model = FHRPGroupAssignment
fields = [
'id', 'url', 'display', 'group', 'interface_type', 'interface_id', 'interface', 'priority', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'group', 'interface_type', 'interface_id', 'priority')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_interface(self, obj):
if obj.interface is None:
return None
serializer = get_serializer_for_model(obj.interface, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.interface, context=context).data

View File

@ -0,0 +1,200 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.api.serializers_.sites import SiteSerializer
from ipam.choices import *
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS
from ipam.models import Aggregate, IPAddress, IPRange, Prefix
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from ..field_serializers import IPAddressField, IPNetworkField
from ..nested_serializers import *
from .asns import RIRSerializer
from .vrfs import VRFSerializer
from .roles import RoleSerializer
from .vlans import VLANSerializer
__all__ = (
'AggregateSerializer',
'AvailableIPSerializer',
'AvailablePrefixSerializer',
'IPAddressSerializer',
'IPRangeSerializer',
'PrefixLengthSerializer',
'PrefixSerializer',
)
class AggregateSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
rir = RIRSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
prefix = IPNetworkField()
class Meta:
model = Aggregate
fields = [
'id', 'url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description')
class PrefixSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
site = SiteSerializer(nested=True, required=False, allow_null=True)
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=PrefixStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
children = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(read_only=True)
prefix = IPNetworkField()
class Meta:
model = Prefix
fields = [
'id', 'url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool',
'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'children',
'_depth',
]
brief_fields = ('id', 'url', 'display', 'family', 'prefix', 'description', '_depth')
class PrefixLengthSerializer(serializers.Serializer):
prefix_length = serializers.IntegerField()
def to_internal_value(self, data):
requested_prefix = data.get('prefix_length')
if requested_prefix is None:
raise serializers.ValidationError({
'prefix_length': 'this field can not be missing'
})
if not isinstance(requested_prefix, int):
raise serializers.ValidationError({
'prefix_length': 'this field must be int type'
})
prefix = self.context.get('prefix')
if prefix.family == 4 and requested_prefix > 32:
raise serializers.ValidationError({
'prefix_length': 'Invalid prefix length ({}) for IPv4'.format(requested_prefix)
})
elif prefix.family == 6 and requested_prefix > 128:
raise serializers.ValidationError({
'prefix_length': 'Invalid prefix length ({}) for IPv6'.format(requested_prefix)
})
return data
class AvailablePrefixSerializer(serializers.Serializer):
"""
Representation of a prefix which does not exist in the database.
"""
family = serializers.IntegerField(read_only=True)
prefix = serializers.CharField(read_only=True)
vrf = VRFSerializer(nested=True, read_only=True)
def to_representation(self, instance):
if self.context.get('vrf'):
vrf = VRFSerializer(self.context['vrf'], nested=True, context={'request': self.context['request']}).data
else:
vrf = None
return {
'family': instance.version,
'prefix': str(instance),
'vrf': vrf,
}
#
# IP ranges
#
class IPRangeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
start_address = IPAddressField()
end_address = IPAddressField()
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=IPRangeStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = IPRange
fields = [
'id', 'url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant', 'status', 'role',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'start_address', 'end_address', 'description')
#
# IP addresses
#
class IPAddressSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
address = IPAddressField()
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=IPAddressStatusChoices, required=False)
role = ChoiceField(choices=IPAddressRoleChoices, allow_blank=True, required=False)
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.filter(IPADDRESS_ASSIGNMENT_MODELS),
required=False,
allow_null=True
)
assigned_object = serializers.SerializerMethodField(read_only=True)
nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
nat_outside = NestedIPAddressSerializer(many=True, read_only=True)
class Meta:
model = IPAddress
fields = [
'id', 'url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'assigned_object_type',
'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside', 'dns_name', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'family', 'address', 'description')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_assigned_object(self, obj):
if obj.assigned_object is None:
return None
serializer = get_serializer_for_model(obj.assigned_object, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.assigned_object, context=context).data
class AvailableIPSerializer(serializers.Serializer):
"""
Representation of an IP address which does not exist in the database.
"""
family = serializers.IntegerField(read_only=True)
address = serializers.CharField(read_only=True)
vrf = VRFSerializer(nested=True, read_only=True)
description = serializers.CharField(required=False)
def to_representation(self, instance):
if self.context.get('vrf'):
vrf = VRFSerializer(self.context['vrf'], nested=True, context={'request': self.context['request']}).data
else:
vrf = None
return {
'family': self.context['parent'].family,
'address': f"{instance}/{self.context['parent'].mask_length}",
'vrf': vrf,
}

View File

@ -0,0 +1,25 @@
from rest_framework import serializers
from ipam.models import Role
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
__all__ = (
'RoleSerializer',
)
class RoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
# Related object counts
prefix_count = RelatedObjectCountField('prefixes')
vlan_count = RelatedObjectCountField('vlans')
class Meta:
model = Role
fields = [
'id', 'url', 'display', 'name', 'slug', 'weight', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'prefix_count', 'vlan_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'prefix_count', 'vlan_count')

View File

@ -0,0 +1,48 @@
from rest_framework import serializers
from dcim.api.serializers_.devices import DeviceSerializer
from ipam.choices import *
from ipam.models import IPAddress, Service, ServiceTemplate
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from virtualization.api.serializers_.virtualmachines import VirtualMachineSerializer
from ..nested_serializers import *
__all__ = (
'ServiceSerializer',
'ServiceTemplateSerializer',
)
class ServiceTemplateSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:servicetemplate-detail')
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
class Meta:
model = ServiceTemplate
fields = [
'id', 'url', 'display', 'name', 'protocol', 'ports', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')
class ServiceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
device = DeviceSerializer(nested=True, required=False, allow_null=True)
virtual_machine = VirtualMachineSerializer(nested=True, required=False, allow_null=True)
protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
ipaddresses = SerializedPKRelatedField(
queryset=IPAddress.objects.all(),
serializer=NestedIPAddressSerializer,
required=False,
many=True
)
class Meta:
model = Service
fields = [
'id', 'url', 'display', 'device', 'virtual_machine', 'name', 'protocol', 'ports', 'ipaddresses',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description')

View File

@ -0,0 +1,115 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.api.serializers_.sites import SiteSerializer
from ipam.choices import *
from ipam.constants import VLANGROUP_SCOPE_TYPES
from ipam.models import VLAN, VLANGroup
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
from .roles import RoleSerializer
__all__ = (
'AvailableVLANSerializer',
'CreateAvailableVLANSerializer',
'VLANGroupSerializer',
'VLANSerializer',
)
class VLANGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
scope_type = ContentTypeField(
queryset=ContentType.objects.filter(
model__in=VLANGROUP_SCOPE_TYPES
),
allow_null=True,
required=False,
default=None
)
scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
scope = serializers.SerializerMethodField(read_only=True)
utilization = serializers.CharField(read_only=True)
# Related object counts
vlan_count = RelatedObjectCountField('vlans')
class Meta:
model = VLANGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'min_vid', 'max_vid',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count')
validators = []
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_scope(self, obj):
if obj.scope_id is None:
return None
serializer = get_serializer_for_model(obj.scope, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.scope, context=context).data
class VLANSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
site = SiteSerializer(nested=True, required=False, allow_null=True)
group = VLANGroupSerializer(nested=True, required=False, allow_null=True, default=None)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=VLANStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
# Related object counts
prefix_count = RelatedObjectCountField('prefixes')
class Meta:
model = VLAN
fields = [
'id', 'url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description',
'comments', 'l2vpn_termination', 'tags', 'custom_fields', 'created', 'last_updated', 'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'vid', 'name', 'description')
class AvailableVLANSerializer(serializers.Serializer):
"""
Representation of a VLAN which does not exist in the database.
"""
vid = serializers.IntegerField(read_only=True)
group = VLANGroupSerializer(nested=True, read_only=True)
def to_representation(self, instance):
return {
'vid': instance,
'group': VLANGroupSerializer(
self.context['group'],
nested=True,
context={'request': self.context['request']}
).data,
}
class CreateAvailableVLANSerializer(NetBoxModelSerializer):
site = SiteSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=VLANStatusChoices, required=False)
role = RoleSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = VLAN
fields = [
'name', 'site', 'tenant', 'status', 'role', 'description', 'tags', 'custom_fields',
]
def validate(self, data):
# Bypass model validation since we don't have a VID yet
return data

View File

@ -0,0 +1,55 @@
from rest_framework import serializers
from ipam.models import RouteTarget, VRF
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from ..nested_serializers import *
__all__ = (
'RouteTargetSerializer',
'VRFSerializer',
)
class VRFSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
export_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
# Related object counts
ipaddress_count = RelatedObjectCountField('ip_addresses')
prefix_count = RelatedObjectCountField('prefixes')
class Meta:
model = VRF
fields = [
'id', 'url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'comments',
'import_targets', 'export_targets', 'tags', 'custom_fields', 'created', 'last_updated', 'ipaddress_count',
'prefix_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'rd', 'description', 'prefix_count')
class RouteTargetSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = RouteTarget
fields = [
'id', 'url', 'display', 'name', 'tenant', 'description', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -1,123 +1,3 @@
from django.contrib.auth.models import ContentType
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.choices import ContactPriorityChoices
from tenancy.models import *
from utilities.api import get_serializer_for_model
from .serializers_.tenants import *
from .serializers_.contacts import *
from .nested_serializers import *
#
# Tenants
#
class TenantGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
parent = NestedTenantGroupSerializer(required=False, allow_null=True)
tenant_count = serializers.IntegerField(read_only=True)
class Meta:
model = TenantGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'tenant_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tenant_count', '_depth')
class TenantSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
group = TenantGroupSerializer(nested=True, required=False, allow_null=True)
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
device_count = RelatedObjectCountField('devices')
rack_count = RelatedObjectCountField('racks')
site_count = RelatedObjectCountField('sites')
ipaddress_count = RelatedObjectCountField('ip_addresses')
prefix_count = RelatedObjectCountField('prefixes')
vlan_count = RelatedObjectCountField('vlans')
vrf_count = RelatedObjectCountField('vrfs')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
cluster_count = RelatedObjectCountField('clusters')
class Meta:
model = Tenant
fields = [
'id', 'url', 'display', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'circuit_count', 'device_count', 'ipaddress_count', 'prefix_count', 'rack_count',
'site_count', 'virtualmachine_count', 'vlan_count', 'vrf_count', 'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
#
# Contacts
#
class ContactGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactgroup-detail')
parent = NestedContactGroupSerializer(required=False, allow_null=True, default=None)
contact_count = serializers.IntegerField(read_only=True)
class Meta:
model = ContactGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'contact_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'contact_count', '_depth')
class ContactRoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactrole-detail')
class Meta:
model = ContactRole
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
class ContactSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contact-detail')
group = ContactGroupSerializer(nested=True, required=False, allow_null=True, default=None)
class Meta:
model = Contact
fields = [
'id', 'url', 'display', 'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ContactAssignmentSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactassignment-detail')
content_type = ContentTypeField(
queryset=ContentType.objects.all()
)
object = serializers.SerializerMethodField(read_only=True)
contact = ContactSerializer(nested=True)
role = ContactRoleSerializer(nested=True, required=False, allow_null=True)
priority = ChoiceField(choices=ContactPriorityChoices, allow_blank=True, required=False, default=lambda: '')
class Meta:
model = ContactAssignment
fields = [
'id', 'url', 'display', 'content_type', 'object_id', 'object', 'contact', 'role', 'priority', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'contact', 'role', 'priority')
@extend_schema_field(OpenApiTypes.OBJECT)
def get_object(self, instance):
serializer = get_serializer_for_model(instance.content_type.model_class(), prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(instance.object, context=context).data

View File

@ -0,0 +1,82 @@
from django.contrib.auth.models import ContentType
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.choices import ContactPriorityChoices
from tenancy.models import ContactAssignment, Contact, ContactGroup, ContactRole
from utilities.api import get_serializer_for_model
from ..nested_serializers import *
__all__ = (
'ContactAssignmentSerializer',
'ContactGroupSerializer',
'ContactRoleSerializer',
'ContactSerializer',
)
class ContactGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactgroup-detail')
parent = NestedContactGroupSerializer(required=False, allow_null=True, default=None)
contact_count = serializers.IntegerField(read_only=True)
class Meta:
model = ContactGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'contact_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'contact_count', '_depth')
class ContactRoleSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactrole-detail')
class Meta:
model = ContactRole
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
class ContactSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contact-detail')
group = ContactGroupSerializer(nested=True, required=False, allow_null=True, default=None)
class Meta:
model = Contact
fields = [
'id', 'url', 'display', 'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class ContactAssignmentSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:contactassignment-detail')
content_type = ContentTypeField(
queryset=ContentType.objects.all()
)
object = serializers.SerializerMethodField(read_only=True)
contact = ContactSerializer(nested=True)
role = ContactRoleSerializer(nested=True, required=False, allow_null=True)
priority = ChoiceField(choices=ContactPriorityChoices, allow_blank=True, required=False, default=lambda: '')
class Meta:
model = ContactAssignment
fields = [
'id', 'url', 'display', 'content_type', 'object_id', 'object', 'contact', 'role', 'priority', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'contact', 'role', 'priority')
@extend_schema_field(OpenApiTypes.OBJECT)
def get_object(self, instance):
serializer = get_serializer_for_model(instance.content_type.model_class(), prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(instance.object, context=context).data

View File

@ -0,0 +1,51 @@
from rest_framework import serializers
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from tenancy.models import Tenant, TenantGroup
from ..nested_serializers import *
__all__ = (
'TenantGroupSerializer',
'TenantSerializer',
)
class TenantGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
parent = NestedTenantGroupSerializer(required=False, allow_null=True)
tenant_count = serializers.IntegerField(read_only=True)
class Meta:
model = TenantGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'tenant_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tenant_count', '_depth')
class TenantSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
group = TenantGroupSerializer(nested=True, required=False, allow_null=True)
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
device_count = RelatedObjectCountField('devices')
rack_count = RelatedObjectCountField('racks')
site_count = RelatedObjectCountField('sites')
ipaddress_count = RelatedObjectCountField('ip_addresses')
prefix_count = RelatedObjectCountField('prefixes')
vlan_count = RelatedObjectCountField('vlans')
vrf_count = RelatedObjectCountField('vrfs')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
cluster_count = RelatedObjectCountField('clusters')
class Meta:
model = Tenant
fields = [
'id', 'url', 'display', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'circuit_count', 'device_count', 'ipaddress_count', 'prefix_count', 'rack_count',
'site_count', 'virtualmachine_count', 'vlan_count', 'vrf_count', 'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')

View File

@ -1,190 +1,4 @@
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes
from rest_framework import serializers
from rest_framework.exceptions import AuthenticationFailed, PermissionDenied
from netbox.api.fields import ContentTypeField, IPNetworkSerializer, SerializedPKRelatedField
from netbox.api.serializers import ValidatedModelSerializer
from users.models import ObjectPermission, Token
from .serializers_.users import *
from .serializers_.permissions import *
from .serializers_.tokens import *
from .nested_serializers import *
__all__ = (
'GroupSerializer',
'ObjectPermissionSerializer',
'TokenSerializer',
'UserSerializer',
)
class UserSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:user-detail')
groups = SerializedPKRelatedField(
queryset=Group.objects.all(),
serializer=NestedGroupSerializer,
required=False,
many=True
)
class Meta:
model = get_user_model()
fields = (
'id', 'url', 'display', 'username', 'password', 'first_name', 'last_name', 'email', 'is_staff', 'is_active',
'date_joined', 'last_login', 'groups',
)
brief_fields = ('id', 'url', 'display', 'username')
extra_kwargs = {
'password': {'write_only': True}
}
def create(self, validated_data):
"""
Extract the password from validated data and set it separately to ensure proper hash generation.
"""
password = validated_data.pop('password')
user = super().create(validated_data)
user.set_password(password)
user.save()
return user
def update(self, instance, validated_data):
"""
Ensure proper updated password hash generation.
"""
password = validated_data.pop('password', None)
if password is not None:
instance.set_password(password)
return super().update(instance, validated_data)
@extend_schema_field(OpenApiTypes.STR)
def get_display(self, obj):
if full_name := obj.get_full_name():
return f"{obj.username} ({full_name})"
return obj.username
class GroupSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:group-detail')
user_count = serializers.IntegerField(read_only=True)
class Meta:
model = Group
fields = ('id', 'url', 'display', 'name', 'user_count')
brief_fields = ('id', 'url', 'display', 'name')
class TokenSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:token-detail')
key = serializers.CharField(
min_length=40,
max_length=40,
allow_blank=True,
required=False,
write_only=not settings.ALLOW_TOKEN_RETRIEVAL
)
user = UserSerializer(nested=True)
allowed_ips = serializers.ListField(
child=IPNetworkSerializer(),
required=False,
allow_empty=True,
default=[]
)
class Meta:
model = Token
fields = (
'id', 'url', 'display', 'user', 'created', 'expires', 'last_used', 'key', 'write_enabled', 'description',
'allowed_ips',
)
brief_fields = ('id', 'url', 'display', 'key', 'write_enabled', 'description')
def to_internal_value(self, data):
if 'key' not in data:
data['key'] = Token.generate_key()
return super().to_internal_value(data)
def validate(self, data):
# If the Token is being created on behalf of another user, enforce the grant_token permission.
request = self.context.get('request')
token_user = data.get('user')
if token_user and token_user != request.user and not request.user.has_perm('users.grant_token'):
raise PermissionDenied("This user does not have permission to create tokens for other users.")
return super().validate(data)
class TokenProvisionSerializer(TokenSerializer):
user = UserSerializer(
nested=True,
read_only=True
)
username = serializers.CharField(
write_only=True
)
password = serializers.CharField(
write_only=True
)
last_used = serializers.DateTimeField(
read_only=True
)
key = serializers.CharField(
read_only=True
)
class Meta:
model = Token
fields = (
'id', 'url', 'display', 'user', 'created', 'expires', 'last_used', 'key', 'write_enabled', 'description',
'allowed_ips', 'username', 'password',
)
def validate(self, data):
# Validate the username and password
username = data.pop('username')
password = data.pop('password')
user = authenticate(request=self.context.get('request'), username=username, password=password)
if user is None:
raise AuthenticationFailed("Invalid username/password")
# Inject the user into the validated data
data['user'] = user
return data
class ObjectPermissionSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:objectpermission-detail')
object_types = ContentTypeField(
queryset=ContentType.objects.all(),
many=True
)
groups = SerializedPKRelatedField(
queryset=Group.objects.all(),
serializer=NestedGroupSerializer,
required=False,
many=True
)
users = SerializedPKRelatedField(
queryset=get_user_model().objects.all(),
serializer=NestedUserSerializer,
required=False,
many=True
)
class Meta:
model = ObjectPermission
fields = (
'id', 'url', 'display', 'name', 'description', 'enabled', 'object_types', 'groups', 'users', 'actions',
'constraints',
)
brief_fields = (
'id', 'url', 'display', 'name', 'description', 'enabled', 'object_types', 'groups', 'users', 'actions',
)

View File

@ -0,0 +1,43 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers
from netbox.api.fields import ContentTypeField, SerializedPKRelatedField
from netbox.api.serializers import ValidatedModelSerializer
from users.models import ObjectPermission
from ..nested_serializers import *
__all__ = (
'ObjectPermissionSerializer',
)
class ObjectPermissionSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:objectpermission-detail')
object_types = ContentTypeField(
queryset=ContentType.objects.all(),
many=True
)
groups = SerializedPKRelatedField(
queryset=Group.objects.all(),
serializer=NestedGroupSerializer,
required=False,
many=True
)
users = SerializedPKRelatedField(
queryset=get_user_model().objects.all(),
serializer=NestedUserSerializer,
required=False,
many=True
)
class Meta:
model = ObjectPermission
fields = (
'id', 'url', 'display', 'name', 'description', 'enabled', 'object_types', 'groups', 'users', 'actions',
'constraints',
)
brief_fields = (
'id', 'url', 'display', 'name', 'description', 'enabled', 'object_types', 'groups', 'users', 'actions',
)

View File

@ -0,0 +1,94 @@
from django.conf import settings
from django.contrib.auth import authenticate
from rest_framework import serializers
from rest_framework.exceptions import AuthenticationFailed, PermissionDenied
from netbox.api.fields import IPNetworkSerializer
from netbox.api.serializers import ValidatedModelSerializer
from users.models import Token
from .users import *
__all__ = (
'TokenProvisionSerializer',
'TokenSerializer',
)
class TokenSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:token-detail')
key = serializers.CharField(
min_length=40,
max_length=40,
allow_blank=True,
required=False,
write_only=not settings.ALLOW_TOKEN_RETRIEVAL
)
user = UserSerializer(nested=True)
allowed_ips = serializers.ListField(
child=IPNetworkSerializer(),
required=False,
allow_empty=True,
default=[]
)
class Meta:
model = Token
fields = (
'id', 'url', 'display', 'user', 'created', 'expires', 'last_used', 'key', 'write_enabled', 'description',
'allowed_ips',
)
brief_fields = ('id', 'url', 'display', 'key', 'write_enabled', 'description')
def to_internal_value(self, data):
if 'key' not in data:
data['key'] = Token.generate_key()
return super().to_internal_value(data)
def validate(self, data):
# If the Token is being created on behalf of another user, enforce the grant_token permission.
request = self.context.get('request')
token_user = data.get('user')
if token_user and token_user != request.user and not request.user.has_perm('users.grant_token'):
raise PermissionDenied("This user does not have permission to create tokens for other users.")
return super().validate(data)
class TokenProvisionSerializer(TokenSerializer):
user = UserSerializer(
nested=True,
read_only=True
)
username = serializers.CharField(
write_only=True
)
password = serializers.CharField(
write_only=True
)
last_used = serializers.DateTimeField(
read_only=True
)
key = serializers.CharField(
read_only=True
)
class Meta:
model = Token
fields = (
'id', 'url', 'display', 'user', 'created', 'expires', 'last_used', 'key', 'write_enabled', 'description',
'allowed_ips', 'username', 'password',
)
def validate(self, data):
# Validate the username and password
username = data.pop('username')
password = data.pop('password')
user = authenticate(request=self.context.get('request'), username=username, password=password)
if user is None:
raise AuthenticationFailed("Invalid username/password")
# Inject the user into the validated data
data['user'] = user
return data

View File

@ -0,0 +1,72 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from netbox.api.fields import SerializedPKRelatedField
from netbox.api.serializers import ValidatedModelSerializer
from ..nested_serializers import *
__all__ = (
'GroupSerializer',
'UserSerializer',
)
class UserSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:user-detail')
groups = SerializedPKRelatedField(
queryset=Group.objects.all(),
serializer=NestedGroupSerializer,
required=False,
many=True
)
class Meta:
model = get_user_model()
fields = (
'id', 'url', 'display', 'username', 'password', 'first_name', 'last_name', 'email', 'is_staff', 'is_active',
'date_joined', 'last_login', 'groups',
)
brief_fields = ('id', 'url', 'display', 'username')
extra_kwargs = {
'password': {'write_only': True}
}
def create(self, validated_data):
"""
Extract the password from validated data and set it separately to ensure proper hash generation.
"""
password = validated_data.pop('password')
user = super().create(validated_data)
user.set_password(password)
user.save()
return user
def update(self, instance, validated_data):
"""
Ensure proper updated password hash generation.
"""
password = validated_data.pop('password', None)
if password is not None:
instance.set_password(password)
return super().update(instance, validated_data)
@extend_schema_field(OpenApiTypes.STR)
def get_display(self, obj):
if full_name := obj.get_full_name():
return f"{obj.username} ({full_name})"
return obj.username
class GroupSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:group-detail')
user_count = serializers.IntegerField(read_only=True)
class Meta:
model = Group
fields = ('id', 'url', 'display', 'name', 'user_count')
brief_fields = ('id', 'url', 'display', 'name')

View File

@ -1,187 +1,3 @@
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.api.serializers import DeviceSerializer, DeviceRoleSerializer, PlatformSerializer, SiteSerializer
from dcim.choices import InterfaceModeChoices
from extras.api.serializers import ConfigTemplateSerializer
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer
from ipam.models import VLAN
from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers import TenantSerializer
from virtualization.choices import *
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualDisk, VirtualMachine, VMInterface
from vpn.api.nested_serializers import NestedL2VPNTerminationSerializer
from .serializers_.clusters import *
from .serializers_.virtualmachines import *
from .nested_serializers import *
#
# Clusters
#
class ClusterTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
# Related object counts
cluster_count = RelatedObjectCountField('clusters')
class Meta:
model = ClusterType
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'cluster_count')
class ClusterGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
# Related object counts
cluster_count = RelatedObjectCountField('clusters')
class Meta:
model = ClusterGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'cluster_count')
class ClusterSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
type = ClusterTypeSerializer(nested=True)
group = ClusterGroupSerializer(nested=True, required=False, allow_null=True, default=None)
status = ChoiceField(choices=ClusterStatusChoices, required=False)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
site = SiteSerializer(nested=True, required=False, allow_null=True, default=None)
# Related object counts
device_count = RelatedObjectCountField('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
class Meta:
model = Cluster
fields = [
'id', 'url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'site', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'virtualmachine_count')
#
# Virtual machines
#
class VirtualMachineSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
site = SiteSerializer(nested=True, required=False, allow_null=True)
cluster = ClusterSerializer(nested=True, required=False, allow_null=True)
device = DeviceSerializer(nested=True, required=False, allow_null=True)
role = DeviceRoleSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
platform = PlatformSerializer(nested=True, required=False, allow_null=True)
primary_ip = NestedIPAddressSerializer(read_only=True)
primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
# Counter fields
interface_count = serializers.IntegerField(read_only=True)
virtual_disk_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualMachine
fields = [
'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
'interface_count', 'virtual_disk_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
validators = []
class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
config_context = serializers.SerializerMethodField()
class Meta(VirtualMachineSerializer.Meta):
fields = [
'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created',
'last_updated', 'interface_count', 'virtual_disk_count',
]
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_config_context(self, obj):
return obj.get_config_context()
#
# VM interfaces
#
class VMInterfaceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
virtual_machine = VirtualMachineSerializer(nested=True)
parent = NestedVMInterfaceSerializer(required=False, allow_null=True)
bridge = NestedVMInterfaceSerializer(required=False, allow_null=True)
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
tagged_vlans = SerializedPKRelatedField(
queryset=VLAN.objects.all(),
serializer=NestedVLANSerializer,
required=False,
many=True
)
vrf = NestedVRFSerializer(required=False, allow_null=True)
l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, allow_null=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_null=True
)
class Meta:
model = VMInterface
fields = [
'id', 'url', 'display', 'virtual_machine', 'name', 'enabled', 'parent', 'bridge', 'mtu', 'mac_address',
'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'vrf', 'l2vpn_termination', 'tags', 'custom_fields',
'created', 'last_updated', 'count_ipaddresses', 'count_fhrp_groups',
]
brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description')
def validate(self, data):
# Validate many-to-many VLAN assignments
virtual_machine = self.instance.virtual_machine if self.instance else data.get('virtual_machine')
for vlan in data.get('tagged_vlans', []):
if vlan.site not in [virtual_machine.site, None]:
raise serializers.ValidationError({
'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent virtual "
f"machine, or it must be global."
})
return super().validate(data)
#
# Virtual Disk
#
class VirtualDiskSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualdisk-detail')
virtual_machine = VirtualMachineSerializer(nested=True)
class Meta:
model = VirtualDisk
fields = [
'id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size')

View File

@ -0,0 +1,65 @@
from rest_framework import serializers
from dcim.api.serializers_.sites import SiteSerializer
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from virtualization.choices import *
from virtualization.models import Cluster, ClusterGroup, ClusterType
__all__ = (
'ClusterGroupSerializer',
'ClusterSerializer',
'ClusterTypeSerializer',
)
class ClusterTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
# Related object counts
cluster_count = RelatedObjectCountField('clusters')
class Meta:
model = ClusterType
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'cluster_count')
class ClusterGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
# Related object counts
cluster_count = RelatedObjectCountField('clusters')
class Meta:
model = ClusterGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'cluster_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'cluster_count')
class ClusterSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
type = ClusterTypeSerializer(nested=True)
group = ClusterGroupSerializer(nested=True, required=False, allow_null=True, default=None)
status = ChoiceField(choices=ClusterStatusChoices, required=False)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
site = SiteSerializer(nested=True, required=False, allow_null=True, default=None)
# Related object counts
device_count = RelatedObjectCountField('devices')
virtualmachine_count = RelatedObjectCountField('virtual_machines')
class Meta:
model = Cluster
fields = [
'id', 'url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'site', 'description', 'comments',
'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'virtualmachine_count')

View File

@ -0,0 +1,143 @@
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.api.serializers_.devices import DeviceSerializer
from dcim.api.serializers_.platforms import PlatformSerializer
from dcim.api.serializers_.roles import DeviceRoleSerializer
from dcim.api.serializers_.sites import SiteSerializer
from dcim.choices import InterfaceModeChoices
from extras.api.serializers_.provisioning import ConfigTemplateSerializer
from ipam.api.nested_serializers import NestedVLANSerializer
from ipam.api.serializers_.ip import IPAddressSerializer
from ipam.api.serializers_.vlans import VLANSerializer
from ipam.api.serializers_.vrfs import VRFSerializer
from ipam.models import VLAN
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from virtualization.choices import *
from virtualization.models import VirtualDisk, VirtualMachine, VMInterface
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
from ..nested_serializers import *
from .clusters import ClusterSerializer
__all__ = (
'VMInterfaceSerializer',
'VirtualDiskSerializer',
'VirtualMachineSerializer',
'VirtualMachineWithConfigContextSerializer',
)
class VirtualMachineSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
site = SiteSerializer(nested=True, required=False, allow_null=True)
cluster = ClusterSerializer(nested=True, required=False, allow_null=True)
device = DeviceSerializer(nested=True, required=False, allow_null=True)
role = DeviceRoleSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
platform = PlatformSerializer(nested=True, required=False, allow_null=True)
primary_ip = IPAddressSerializer(nested=True, read_only=True)
primary_ip4 = IPAddressSerializer(nested=True, required=False, allow_null=True)
primary_ip6 = IPAddressSerializer(nested=True, required=False, allow_null=True)
config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
# Counter fields
interface_count = serializers.IntegerField(read_only=True)
virtual_disk_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualMachine
fields = [
'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
'interface_count', 'virtual_disk_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
validators = []
class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
config_context = serializers.SerializerMethodField()
class Meta(VirtualMachineSerializer.Meta):
fields = [
'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created',
'last_updated', 'interface_count', 'virtual_disk_count',
]
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_config_context(self, obj):
return obj.get_config_context()
#
# VM interfaces
#
class VMInterfaceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
virtual_machine = VirtualMachineSerializer(nested=True)
parent = NestedVMInterfaceSerializer(required=False, allow_null=True)
bridge = NestedVMInterfaceSerializer(required=False, allow_null=True)
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
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)
count_ipaddresses = serializers.IntegerField(read_only=True)
count_fhrp_groups = serializers.IntegerField(read_only=True)
mac_address = serializers.CharField(
required=False,
default=None,
allow_null=True
)
class Meta:
model = VMInterface
fields = [
'id', 'url', 'display', 'virtual_machine', 'name', 'enabled', 'parent', 'bridge', 'mtu', 'mac_address',
'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'vrf', 'l2vpn_termination', 'tags', 'custom_fields',
'created', 'last_updated', 'count_ipaddresses', 'count_fhrp_groups',
]
brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description')
def validate(self, data):
# Validate many-to-many VLAN assignments
virtual_machine = self.instance.virtual_machine if self.instance else data.get('virtual_machine')
for vlan in data.get('tagged_vlans', []):
if vlan.site not in [virtual_machine.site, None]:
raise serializers.ValidationError({
'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent virtual "
f"machine, or it must be global."
})
return super().validate(data)
#
# Virtual Disk
#
class VirtualDiskSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualdisk-detail')
virtual_machine = VirtualMachineSerializer(nested=True)
class Meta:
model = VirtualDisk
fields = [
'id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size')

View File

@ -1,297 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from ipam.api.serializers import IPAddressSerializer
from ipam.api.nested_serializers import NestedRouteTargetSerializer
from ipam.models import RouteTarget
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.choices import *
from vpn.models import *
from .serializers_.crypto import *
from .serializers_.tunnels import *
from .serializers_.l2vpn import *
from .nested_serializers import *
__all__ = (
'IKEPolicySerializer',
'IKEProposalSerializer',
'IPSecPolicySerializer',
'IPSecProfileSerializer',
'IPSecProposalSerializer',
'L2VPNSerializer',
'L2VPNTerminationSerializer',
'TunnelGroupSerializer',
'TunnelSerializer',
'TunnelTerminationSerializer',
)
class IKEProposalSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ikeproposal-detail'
)
authentication_method = ChoiceField(
choices=AuthenticationMethodChoices
)
encryption_algorithm = ChoiceField(
choices=EncryptionAlgorithmChoices
)
authentication_algorithm = ChoiceField(
choices=AuthenticationAlgorithmChoices
)
group = ChoiceField(
choices=DHGroupChoices
)
class Meta:
model = IKEProposal
fields = (
'id', 'url', 'display', 'name', 'description', 'authentication_method', 'encryption_algorithm',
'authentication_algorithm', 'group', 'sa_lifetime', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IKEPolicySerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ikepolicy-detail'
)
version = ChoiceField(
choices=IKEVersionChoices
)
mode = ChoiceField(
choices=IKEModeChoices
)
proposals = SerializedPKRelatedField(
queryset=IKEProposal.objects.all(),
serializer=NestedIKEProposalSerializer,
required=False,
many=True
)
class Meta:
model = IKEPolicy
fields = (
'id', 'url', 'display', 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IPSecProposalSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ipsecproposal-detail'
)
encryption_algorithm = ChoiceField(
choices=EncryptionAlgorithmChoices
)
authentication_algorithm = ChoiceField(
choices=AuthenticationAlgorithmChoices
)
class Meta:
model = IPSecProposal
fields = (
'id', 'url', 'display', 'name', 'description', 'encryption_algorithm', 'authentication_algorithm',
'sa_lifetime_seconds', 'sa_lifetime_data', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IPSecPolicySerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ipsecpolicy-detail'
)
proposals = SerializedPKRelatedField(
queryset=IPSecProposal.objects.all(),
serializer=NestedIPSecProposalSerializer,
required=False,
many=True
)
pfs_group = ChoiceField(
choices=DHGroupChoices,
required=False
)
class Meta:
model = IPSecPolicy
fields = (
'id', 'url', 'display', 'name', 'description', 'proposals', 'pfs_group', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IPSecProfileSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ipsecprofile-detail'
)
mode = ChoiceField(
choices=IPSecModeChoices
)
ike_policy = IKEPolicySerializer(
nested=True
)
ipsec_policy = IPSecPolicySerializer(
nested=True
)
class Meta:
model = IPSecProfile
fields = (
'id', 'url', 'display', 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
#
# Tunnels
#
class TunnelGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
# Related object counts
tunnel_count = RelatedObjectCountField('tunnels')
class Meta:
model = TunnelGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'tunnel_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tunnel_count')
class TunnelSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:tunnel-detail'
)
status = ChoiceField(
choices=TunnelStatusChoices
)
group = TunnelGroupSerializer(
nested=True,
required=False,
allow_null=True
)
encapsulation = ChoiceField(
choices=TunnelEncapsulationChoices
)
ipsec_profile = IPSecProfileSerializer(
nested=True,
required=False,
allow_null=True
)
tenant = TenantSerializer(
nested=True,
required=False,
allow_null=True
)
# Related object counts
terminations_count = RelatedObjectCountField('terminations')
class Meta:
model = Tunnel
fields = (
'id', 'url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'terminations_count',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class TunnelTerminationSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:tunneltermination-detail'
)
tunnel = TunnelSerializer(
nested=True
)
role = ChoiceField(
choices=TunnelTerminationRoleChoices
)
termination_type = ContentTypeField(
queryset=ContentType.objects.all()
)
termination = serializers.SerializerMethodField(
read_only=True
)
outside_ip = IPAddressSerializer(
nested=True,
required=False,
allow_null=True
)
class Meta:
model = TunnelTermination
fields = (
'id', 'url', 'display', 'tunnel', 'role', 'termination_type', 'termination_id', 'termination', 'outside_ip',
'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_termination(self, obj):
serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.termination, context=context).data
#
# L2VPN
#
class L2VPNSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:l2vpn-detail')
type = ChoiceField(choices=L2VPNTypeChoices, required=False)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
export_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = L2VPN
fields = [
'id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', 'export_targets',
'description', 'comments', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated'
]
brief_fields = ('id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'description')
class L2VPNTerminationSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:l2vpntermination-detail')
l2vpn = L2VPNSerializer(
nested=True
)
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.all()
)
assigned_object = serializers.SerializerMethodField(read_only=True)
class Meta:
model = L2VPNTermination
fields = [
'id', 'url', 'display', 'l2vpn', 'assigned_object_type', 'assigned_object_id',
'assigned_object', 'tags', 'custom_fields', 'created', 'last_updated'
]
brief_fields = ('id', 'url', 'display', 'l2vpn')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_assigned_object(self, instance):
serializer = get_serializer_for_model(instance.assigned_object, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(instance.assigned_object, context=context).data

View File

View File

@ -0,0 +1,135 @@
from rest_framework import serializers
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from vpn.choices import *
from vpn.models import IKEPolicy, IKEProposal, IPSecPolicy, IPSecProfile, IPSecProposal
from ..nested_serializers import *
__all__ = (
'IKEPolicySerializer',
'IKEProposalSerializer',
'IPSecPolicySerializer',
'IPSecProfileSerializer',
'IPSecProposalSerializer',
)
class IKEProposalSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ikeproposal-detail'
)
authentication_method = ChoiceField(
choices=AuthenticationMethodChoices
)
encryption_algorithm = ChoiceField(
choices=EncryptionAlgorithmChoices
)
authentication_algorithm = ChoiceField(
choices=AuthenticationAlgorithmChoices
)
group = ChoiceField(
choices=DHGroupChoices
)
class Meta:
model = IKEProposal
fields = (
'id', 'url', 'display', 'name', 'description', 'authentication_method', 'encryption_algorithm',
'authentication_algorithm', 'group', 'sa_lifetime', 'comments', 'tags', 'custom_fields', 'created',
'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IKEPolicySerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ikepolicy-detail'
)
version = ChoiceField(
choices=IKEVersionChoices
)
mode = ChoiceField(
choices=IKEModeChoices
)
proposals = SerializedPKRelatedField(
queryset=IKEProposal.objects.all(),
serializer=NestedIKEProposalSerializer,
required=False,
many=True
)
class Meta:
model = IKEPolicy
fields = (
'id', 'url', 'display', 'name', 'description', 'version', 'mode', 'proposals', 'preshared_key', 'comments',
'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IPSecProposalSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ipsecproposal-detail'
)
encryption_algorithm = ChoiceField(
choices=EncryptionAlgorithmChoices
)
authentication_algorithm = ChoiceField(
choices=AuthenticationAlgorithmChoices
)
class Meta:
model = IPSecProposal
fields = (
'id', 'url', 'display', 'name', 'description', 'encryption_algorithm', 'authentication_algorithm',
'sa_lifetime_seconds', 'sa_lifetime_data', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IPSecPolicySerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ipsecpolicy-detail'
)
proposals = SerializedPKRelatedField(
queryset=IPSecProposal.objects.all(),
serializer=NestedIPSecProposalSerializer,
required=False,
many=True
)
pfs_group = ChoiceField(
choices=DHGroupChoices,
required=False
)
class Meta:
model = IPSecPolicy
fields = (
'id', 'url', 'display', 'name', 'description', 'proposals', 'pfs_group', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class IPSecProfileSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:ipsecprofile-detail'
)
mode = ChoiceField(
choices=IPSecModeChoices
)
ike_policy = IKEPolicySerializer(
nested=True
)
ipsec_policy = IPSecPolicySerializer(
nested=True
)
class Meta:
model = IPSecProfile
fields = (
'id', 'url', 'display', 'name', 'description', 'mode', 'ike_policy', 'ipsec_policy', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -0,0 +1,69 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from ipam.api.nested_serializers import NestedRouteTargetSerializer
from ipam.models import RouteTarget
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.choices import *
from vpn.models import L2VPN, L2VPNTermination
__all__ = (
'L2VPNSerializer',
'L2VPNTerminationSerializer',
)
class L2VPNSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:l2vpn-detail')
type = ChoiceField(choices=L2VPNTypeChoices, required=False)
import_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
export_targets = SerializedPKRelatedField(
queryset=RouteTarget.objects.all(),
serializer=NestedRouteTargetSerializer,
required=False,
many=True
)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = L2VPN
fields = [
'id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', 'export_targets',
'description', 'comments', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated'
]
brief_fields = ('id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'description')
class L2VPNTerminationSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:l2vpntermination-detail')
l2vpn = L2VPNSerializer(
nested=True
)
assigned_object_type = ContentTypeField(
queryset=ContentType.objects.all()
)
assigned_object = serializers.SerializerMethodField(read_only=True)
class Meta:
model = L2VPNTermination
fields = [
'id', 'url', 'display', 'l2vpn', 'assigned_object_type', 'assigned_object_id',
'assigned_object', 'tags', 'custom_fields', 'created', 'last_updated'
]
brief_fields = ('id', 'url', 'display', 'l2vpn')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_assigned_object(self, instance):
serializer = get_serializer_for_model(instance.assigned_object, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(instance.assigned_object, context=context).data

View File

@ -0,0 +1,114 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from ipam.api.serializers_.ip import IPAddressSerializer
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from vpn.choices import *
from vpn.models import Tunnel, TunnelGroup, TunnelTermination
from .crypto import IPSecProfileSerializer
__all__ = (
'TunnelGroupSerializer',
'TunnelSerializer',
'TunnelTerminationSerializer',
)
#
# Tunnels
#
class TunnelGroupSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='vpn-api:tunnelgroup-detail')
# Related object counts
tunnel_count = RelatedObjectCountField('tunnels')
class Meta:
model = TunnelGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'tunnel_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tunnel_count')
class TunnelSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:tunnel-detail'
)
status = ChoiceField(
choices=TunnelStatusChoices
)
group = TunnelGroupSerializer(
nested=True,
required=False,
allow_null=True
)
encapsulation = ChoiceField(
choices=TunnelEncapsulationChoices
)
ipsec_profile = IPSecProfileSerializer(
nested=True,
required=False,
allow_null=True
)
tenant = TenantSerializer(
nested=True,
required=False,
allow_null=True
)
# Related object counts
terminations_count = RelatedObjectCountField('terminations')
class Meta:
model = Tunnel
fields = (
'id', 'url', 'display', 'name', 'status', 'group', 'encapsulation', 'ipsec_profile', 'tenant', 'tunnel_id',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'terminations_count',
)
brief_fields = ('id', 'url', 'display', 'name', 'description')
class TunnelTerminationSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='vpn-api:tunneltermination-detail'
)
tunnel = TunnelSerializer(
nested=True
)
role = ChoiceField(
choices=TunnelTerminationRoleChoices
)
termination_type = ContentTypeField(
queryset=ContentType.objects.all()
)
termination = serializers.SerializerMethodField(
read_only=True
)
outside_ip = IPAddressSerializer(
nested=True,
required=False,
allow_null=True
)
class Meta:
model = TunnelTermination
fields = (
'id', 'url', 'display', 'tunnel', 'role', 'termination_type', 'termination_id', 'termination', 'outside_ip',
'tags', 'custom_fields', 'created', 'last_updated',
)
brief_fields = ('id', 'url', 'display')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_termination(self, obj):
serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX)
context = {'request': self.context['request']}
return serializer(obj.termination, context=context).data

View File

@ -1,67 +1,3 @@
from rest_framework import serializers
from dcim.choices import LinkStatusChoices
from dcim.api.serializers import InterfaceSerializer
from ipam.api.serializers import VLANSerializer
from netbox.api.fields import ChoiceField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from tenancy.api.serializers import TenantSerializer
from wireless.choices import *
from wireless.models import *
from .serializers_.wirelesslans import *
from .serializers_.wirelesslinks import *
from .nested_serializers import *
__all__ = (
'WirelessLANGroupSerializer',
'WirelessLANSerializer',
'WirelessLinkSerializer',
)
class WirelessLANGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslangroup-detail')
parent = NestedWirelessLANGroupSerializer(required=False, allow_null=True, default=None)
wirelesslan_count = serializers.IntegerField(read_only=True)
class Meta:
model = WirelessLANGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'wirelesslan_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'wirelesslan_count', '_depth')
class WirelessLANSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
group = WirelessLANGroupSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=WirelessLANStatusChoices, required=False, allow_blank=True)
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
class Meta:
model = WirelessLAN
fields = [
'id', 'url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'tenant', 'auth_type',
'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')
class WirelessLinkSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
status = ChoiceField(choices=LinkStatusChoices, required=False)
interface_a = InterfaceSerializer(nested=True)
interface_b = InterfaceSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
class Meta:
model = WirelessLink
fields = [
'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant', 'auth_type',
'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')

View File

@ -0,0 +1,46 @@
from rest_framework import serializers
from ipam.api.serializers_.vlans import VLANSerializer
from netbox.api.fields import ChoiceField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from wireless.choices import *
from wireless.models import WirelessLAN, WirelessLANGroup
from ..nested_serializers import *
__all__ = (
'WirelessLANGroupSerializer',
'WirelessLANSerializer',
)
class WirelessLANGroupSerializer(NestedGroupModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslangroup-detail')
parent = NestedWirelessLANGroupSerializer(required=False, allow_null=True, default=None)
wirelesslan_count = serializers.IntegerField(read_only=True)
class Meta:
model = WirelessLANGroup
fields = [
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'wirelesslan_count', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'wirelesslan_count', '_depth')
class WirelessLANSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
group = WirelessLANGroupSerializer(nested=True, required=False, allow_null=True)
status = ChoiceField(choices=WirelessLANStatusChoices, required=False, allow_blank=True)
vlan = VLANSerializer(nested=True, required=False, allow_null=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
class Meta:
model = WirelessLAN
fields = [
'id', 'url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'tenant', 'auth_type',
'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')

View File

@ -0,0 +1,31 @@
from rest_framework import serializers
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.choices import LinkStatusChoices
from netbox.api.fields import ChoiceField
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from wireless.choices import *
from wireless.models import WirelessLink
__all__ = (
'WirelessLinkSerializer',
)
class WirelessLinkSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
status = ChoiceField(choices=LinkStatusChoices, required=False)
interface_a = InterfaceSerializer(nested=True)
interface_b = InterfaceSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
class Meta:
model = WirelessLink
fields = [
'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant', 'auth_type',
'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'ssid', 'description')