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:
@ -1,144 +1,3 @@
|
|||||||
from rest_framework import serializers
|
from .serializers_.providers import *
|
||||||
|
from .serializers_.circuits import *
|
||||||
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 .nested_serializers 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')
|
|
||||||
|
0
netbox/circuits/api/serializers_/__init__.py
Normal file
0
netbox/circuits/api/serializers_/__init__.py
Normal file
81
netbox/circuits/api/serializers_/circuits.py
Normal file
81
netbox/circuits/api/serializers_/circuits.py
Normal 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')
|
67
netbox/circuits/api/serializers_/providers.py
Normal file
67
netbox/circuits/api/serializers_/providers.py
Normal 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')
|
@ -1,75 +1,3 @@
|
|||||||
from rest_framework import serializers
|
from .serializers_.data import *
|
||||||
|
from .serializers_.jobs import *
|
||||||
from core.choices import *
|
from .nested_serializers 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')
|
|
||||||
|
0
netbox/core/api/serializers_/__init__.py
Normal file
0
netbox/core/api/serializers_/__init__.py
Normal file
53
netbox/core/api/serializers_/data.py
Normal file
53
netbox/core/api/serializers_/data.py
Normal 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')
|
31
netbox/core/api/serializers_/jobs.py
Normal file
31
netbox/core/api/serializers_/jobs.py
Normal 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
0
netbox/dcim/api/serializers_/__init__.py
Normal file
0
netbox/dcim/api/serializers_/__init__.py
Normal file
38
netbox/dcim/api/serializers_/base.py
Normal file
38
netbox/dcim/api/serializers_/base.py
Normal 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
|
127
netbox/dcim/api/serializers_/cables.py
Normal file
127
netbox/dcim/api/serializers_/cables.py
Normal 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
|
366
netbox/dcim/api/serializers_/device_components.py
Normal file
366
netbox/dcim/api/serializers_/device_components.py
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from drf_spectacular.utils import extend_schema_field
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from dcim.choices import *
|
||||||
|
from dcim.constants import *
|
||||||
|
from dcim.models import (
|
||||||
|
ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
|
||||||
|
RearPort, VirtualDeviceContext,
|
||||||
|
)
|
||||||
|
from ipam.api.serializers_.vlans import VLANSerializer
|
||||||
|
from ipam.api.serializers_.vrfs import VRFSerializer
|
||||||
|
from ipam.api.nested_serializers import NestedVLANSerializer
|
||||||
|
from ipam.models import VLAN
|
||||||
|
from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
||||||
|
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
||||||
|
from netbox.constants import NESTED_SERIALIZER_PREFIX
|
||||||
|
from utilities.api import get_serializer_for_model
|
||||||
|
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
|
||||||
|
from wireless.api.nested_serializers import NestedWirelessLANSerializer, NestedWirelessLinkSerializer
|
||||||
|
from wireless.choices import *
|
||||||
|
from wireless.models import WirelessLAN
|
||||||
|
from .base import ConnectedEndpointsSerializer
|
||||||
|
from .cables import CabledObjectSerializer
|
||||||
|
from .devices import DeviceSerializer, ModuleSerializer
|
||||||
|
from .manufacturers import ManufacturerSerializer
|
||||||
|
from .roles import InventoryItemRoleSerializer
|
||||||
|
from ..nested_serializers import *
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ConsolePortSerializer',
|
||||||
|
'ConsoleServerPortSerializer',
|
||||||
|
'DeviceBaySerializer',
|
||||||
|
'FrontPortSerializer',
|
||||||
|
'InterfaceSerializer',
|
||||||
|
'InventoryItemSerializer',
|
||||||
|
'ModuleBaySerializer',
|
||||||
|
'PowerOutletSerializer',
|
||||||
|
'PowerPortSerializer',
|
||||||
|
'RearPortSerializer',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(
|
||||||
|
choices=ConsolePortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
speed = ChoiceField(
|
||||||
|
choices=ConsolePortSpeedChoices,
|
||||||
|
allow_null=True,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConsoleServerPort
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||||
|
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||||
|
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||||
|
'last_updated', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
|
||||||
|
class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(
|
||||||
|
choices=ConsolePortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
speed = ChoiceField(
|
||||||
|
choices=ConsolePortSpeedChoices,
|
||||||
|
allow_null=True,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConsolePort
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
|
||||||
|
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
|
||||||
|
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||||
|
'last_updated', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
|
||||||
|
class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(
|
||||||
|
choices=PowerPortTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PowerPort
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
|
||||||
|
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
||||||
|
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||||
|
'created', 'last_updated', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
|
||||||
|
class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(
|
||||||
|
choices=PowerOutletTypeChoices,
|
||||||
|
allow_blank=True,
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
power_port = PowerPortSerializer(
|
||||||
|
nested=True,
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
feed_leg = ChoiceField(
|
||||||
|
choices=PowerOutletFeedLegChoices,
|
||||||
|
allow_blank=True,
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PowerOutlet
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg',
|
||||||
|
'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
|
||||||
|
'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
|
||||||
|
'created', 'last_updated', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
vdcs = SerializedPKRelatedField(
|
||||||
|
queryset=VirtualDeviceContext.objects.all(),
|
||||||
|
serializer=NestedVirtualDeviceContextSerializer,
|
||||||
|
required=False,
|
||||||
|
many=True
|
||||||
|
)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(choices=InterfaceTypeChoices)
|
||||||
|
parent = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||||
|
bridge = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||||
|
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||||
|
mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_blank=True)
|
||||||
|
duplex = ChoiceField(choices=InterfaceDuplexChoices, required=False, allow_blank=True, allow_null=True)
|
||||||
|
rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_blank=True)
|
||||||
|
rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False, allow_blank=True)
|
||||||
|
poe_mode = ChoiceField(choices=InterfacePoEModeChoices, required=False, allow_blank=True)
|
||||||
|
poe_type = ChoiceField(choices=InterfacePoETypeChoices, required=False, allow_blank=True)
|
||||||
|
untagged_vlan = VLANSerializer(nested=True, required=False, allow_null=True)
|
||||||
|
tagged_vlans = SerializedPKRelatedField(
|
||||||
|
queryset=VLAN.objects.all(),
|
||||||
|
serializer=NestedVLANSerializer,
|
||||||
|
required=False,
|
||||||
|
many=True
|
||||||
|
)
|
||||||
|
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||||
|
l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
|
||||||
|
wireless_link = NestedWirelessLinkSerializer(read_only=True, allow_null=True)
|
||||||
|
wireless_lans = SerializedPKRelatedField(
|
||||||
|
queryset=WirelessLAN.objects.all(),
|
||||||
|
serializer=NestedWirelessLANSerializer,
|
||||||
|
required=False,
|
||||||
|
many=True
|
||||||
|
)
|
||||||
|
count_ipaddresses = serializers.IntegerField(read_only=True)
|
||||||
|
count_fhrp_groups = serializers.IntegerField(read_only=True)
|
||||||
|
mac_address = serializers.CharField(
|
||||||
|
required=False,
|
||||||
|
default=None,
|
||||||
|
allow_blank=True,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
wwn = serializers.CharField(required=False, default=None, allow_blank=True, allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Interface
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge',
|
||||||
|
'lag', 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role',
|
||||||
|
'rf_channel', 'poe_mode', 'poe_type', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
|
||||||
|
'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'cable_end', 'wireless_link', 'link_peers',
|
||||||
|
'link_peers_type', 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints',
|
||||||
|
'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
|
||||||
|
'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
|
||||||
|
# Validate many-to-many VLAN assignments
|
||||||
|
if not self.nested:
|
||||||
|
device = self.instance.device if self.instance else data.get('device')
|
||||||
|
for vlan in data.get('tagged_vlans', []):
|
||||||
|
if vlan.site not in [device.site, None]:
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent device, "
|
||||||
|
f"or it must be global."
|
||||||
|
})
|
||||||
|
|
||||||
|
return super().validate(data)
|
||||||
|
|
||||||
|
|
||||||
|
class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(choices=PortTypeChoices)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RearPort
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'description',
|
||||||
|
'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags', 'custom_fields', 'created',
|
||||||
|
'last_updated', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortRearPortSerializer(WritableNestedSerializer):
|
||||||
|
"""
|
||||||
|
NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
|
||||||
|
"""
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RearPort
|
||||||
|
fields = ['id', 'url', 'display', 'name', 'label', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'device', 'module_bay'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
type = ChoiceField(choices=PortTypeChoices)
|
||||||
|
rear_port = FrontPortRearPortSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = FrontPort
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
|
||||||
|
'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
|
||||||
|
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleBaySerializer(NetBoxModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
installed_module = ModuleSerializer(
|
||||||
|
nested=True,
|
||||||
|
requested_fields=('id', 'url', 'display', 'serial', 'description'),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ModuleBay
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'name', 'installed_module', 'label', 'position', 'description', 'tags',
|
||||||
|
'custom_fields', 'created', 'last_updated',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBaySerializer(NetBoxModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = DeviceBay
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'name', 'label', 'description', 'installed_device', 'tags',
|
||||||
|
'custom_fields', 'created', 'last_updated',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryItemSerializer(NetBoxModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
|
||||||
|
device = DeviceSerializer(nested=True)
|
||||||
|
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
||||||
|
role = InventoryItemRoleSerializer(nested=True, required=False, allow_null=True)
|
||||||
|
manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True, default=None)
|
||||||
|
component_type = ContentTypeField(
|
||||||
|
queryset=ContentType.objects.filter(MODULAR_COMPONENT_MODELS),
|
||||||
|
required=False,
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
component = serializers.SerializerMethodField(read_only=True)
|
||||||
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = InventoryItem
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial',
|
||||||
|
'asset_tag', 'discovered', 'description', 'component_type', 'component_id', 'component', 'tags',
|
||||||
|
'custom_fields', 'created', 'last_updated', '_depth',
|
||||||
|
]
|
||||||
|
brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
|
||||||
|
|
||||||
|
@extend_schema_field(serializers.JSONField(allow_null=True))
|
||||||
|
def get_component(self, obj):
|
||||||
|
if obj.component is None:
|
||||||
|
return None
|
||||||
|
serializer = get_serializer_for_model(obj.component, prefix=NESTED_SERIALIZER_PREFIX)
|
||||||
|
context = {'request': self.context['request']}
|
||||||
|
return serializer(obj.component, context=context).data
|
165
netbox/dcim/api/serializers_/devices.py
Normal file
165
netbox/dcim/api/serializers_/devices.py
Normal 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')
|
328
netbox/dcim/api/serializers_/devicetype_components.py
Normal file
328
netbox/dcim/api/serializers_/devicetype_components.py
Normal 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
|
74
netbox/dcim/api/serializers_/devicetypes.py
Normal file
74
netbox/dcim/api/serializers_/devicetypes.py
Normal 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')
|
26
netbox/dcim/api/serializers_/manufacturers.py
Normal file
26
netbox/dcim/api/serializers_/manufacturers.py
Normal 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')
|
29
netbox/dcim/api/serializers_/platforms.py
Normal file
29
netbox/dcim/api/serializers_/platforms.py
Normal 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')
|
80
netbox/dcim/api/serializers_/power.py
Normal file
80
netbox/dcim/api/serializers_/power.py
Normal 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')
|
117
netbox/dcim/api/serializers_/racks.py
Normal file
117
netbox/dcim/api/serializers_/racks.py
Normal 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
|
||||||
|
)
|
31
netbox/dcim/api/serializers_/rackunits.py
Normal file
31
netbox/dcim/api/serializers_/rackunits.py
Normal 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']
|
43
netbox/dcim/api/serializers_/roles.py
Normal file
43
netbox/dcim/api/serializers_/roles.py
Normal 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')
|
97
netbox/dcim/api/serializers_/sites.py
Normal file
97
netbox/dcim/api/serializers_/sites.py
Normal 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')
|
25
netbox/dcim/api/serializers_/virtualchassis.py
Normal file
25
netbox/dcim/api/serializers_/virtualchassis.py
Normal 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')
|
@ -1,666 +1,15 @@
|
|||||||
from django.contrib.auth import get_user_model
|
from .serializers_.attachments import *
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from .serializers_.bookmarks import *
|
||||||
from django.utils.translation import gettext as _
|
from .serializers_.change_logging import *
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from .serializers_.contenttypes import *
|
||||||
from drf_spectacular.utils import extend_schema_field
|
from .serializers_.customfields import *
|
||||||
from rest_framework import serializers
|
from .serializers_.customlinks import *
|
||||||
|
from .serializers_.dashboard import *
|
||||||
from core.api.serializers import DataFileSerializer, DataSourceSerializer, JobSerializer
|
from .serializers_.events import *
|
||||||
from core.models import ContentType
|
from .serializers_.exporttemplates import *
|
||||||
from dcim.api.nested_serializers import (
|
from .serializers_.journaling import *
|
||||||
NestedDeviceRoleSerializer, NestedDeviceTypeSerializer, NestedLocationSerializer, NestedPlatformSerializer,
|
from .serializers_.provisioning import *
|
||||||
NestedRegionSerializer, NestedSiteSerializer, NestedSiteGroupSerializer,
|
from .serializers_.savedfilters import *
|
||||||
)
|
from .serializers_.scripts import *
|
||||||
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
|
from .serializers_.tags import *
|
||||||
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 .nested_serializers 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')
|
|
||||||
|
0
netbox/extras/api/serializers_/__init__.py
Normal file
0
netbox/extras/api/serializers_/__init__.py
Normal file
50
netbox/extras/api/serializers_/attachments.py
Normal file
50
netbox/extras/api/serializers_/attachments.py
Normal 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
|
35
netbox/extras/api/serializers_/bookmarks.py
Normal file
35
netbox/extras/api/serializers_/bookmarks.py
Normal 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
|
59
netbox/extras/api/serializers_/change_logging.py
Normal file
59
netbox/extras/api/serializers_/change_logging.py
Normal 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
|
16
netbox/extras/api/serializers_/contenttypes.py
Normal file
16
netbox/extras/api/serializers_/contenttypes.py
Normal 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']
|
91
netbox/extras/api/serializers_/customfields.py
Normal file
91
netbox/extras/api/serializers_/customfields.py
Normal 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'
|
26
netbox/extras/api/serializers_/customlinks.py
Normal file
26
netbox/extras/api/serializers_/customlinks.py
Normal 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')
|
13
netbox/extras/api/serializers_/dashboard.py
Normal file
13
netbox/extras/api/serializers_/dashboard.py
Normal 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')
|
75
netbox/extras/api/serializers_/events.py
Normal file
75
netbox/extras/api/serializers_/events.py
Normal 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')
|
36
netbox/extras/api/serializers_/exporttemplates.py
Normal file
36
netbox/extras/api/serializers_/exporttemplates.py
Normal 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')
|
67
netbox/extras/api/serializers_/journaling.py
Normal file
67
netbox/extras/api/serializers_/journaling.py
Normal 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
|
147
netbox/extras/api/serializers_/provisioning.py
Normal file
147
netbox/extras/api/serializers_/provisioning.py
Normal 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')
|
26
netbox/extras/api/serializers_/savedfilters.py
Normal file
26
netbox/extras/api/serializers_/savedfilters.py
Normal 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')
|
77
netbox/extras/api/serializers_/scripts.py
Normal file
77
netbox/extras/api/serializers_/scripts.py
Normal 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
|
30
netbox/extras/api/serializers_/tags.py
Normal file
30
netbox/extras/api/serializers_/tags.py
Normal 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')
|
@ -1,511 +1,8 @@
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from .serializers_.asns import *
|
||||||
from drf_spectacular.utils import extend_schema_field
|
from .serializers_.vrfs import *
|
||||||
from rest_framework import serializers
|
from .serializers_.roles import *
|
||||||
|
from .serializers_.vlans import *
|
||||||
from dcim.api.serializers import DeviceSerializer, SiteSerializer
|
from .serializers_.ip import *
|
||||||
from ipam.choices import *
|
from .serializers_.fhrpgroups import *
|
||||||
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES
|
from .serializers_.services import *
|
||||||
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 .nested_serializers 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')
|
|
||||||
|
0
netbox/ipam/api/serializers_/__init__.py
Normal file
0
netbox/ipam/api/serializers_/__init__.py
Normal file
78
netbox/ipam/api/serializers_/asns.py
Normal file
78
netbox/ipam/api/serializers_/asns.py
Normal 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,
|
||||||
|
}
|
53
netbox/ipam/api/serializers_/fhrpgroups.py
Normal file
53
netbox/ipam/api/serializers_/fhrpgroups.py
Normal 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
|
200
netbox/ipam/api/serializers_/ip.py
Normal file
200
netbox/ipam/api/serializers_/ip.py
Normal 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,
|
||||||
|
}
|
25
netbox/ipam/api/serializers_/roles.py
Normal file
25
netbox/ipam/api/serializers_/roles.py
Normal 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')
|
48
netbox/ipam/api/serializers_/services.py
Normal file
48
netbox/ipam/api/serializers_/services.py
Normal 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')
|
115
netbox/ipam/api/serializers_/vlans.py
Normal file
115
netbox/ipam/api/serializers_/vlans.py
Normal 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
|
55
netbox/ipam/api/serializers_/vrfs.py
Normal file
55
netbox/ipam/api/serializers_/vrfs.py
Normal 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')
|
@ -1,123 +1,3 @@
|
|||||||
from django.contrib.auth.models import ContentType
|
from .serializers_.tenants import *
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from .serializers_.contacts import *
|
||||||
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 .nested_serializers 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
|
|
||||||
|
0
netbox/tenancy/api/serializers_/__init__.py
Normal file
0
netbox/tenancy/api/serializers_/__init__.py
Normal file
82
netbox/tenancy/api/serializers_/contacts.py
Normal file
82
netbox/tenancy/api/serializers_/contacts.py
Normal 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
|
51
netbox/tenancy/api/serializers_/tenants.py
Normal file
51
netbox/tenancy/api/serializers_/tenants.py
Normal 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')
|
@ -1,190 +1,4 @@
|
|||||||
from django.conf import settings
|
from .serializers_.users import *
|
||||||
from django.contrib.auth import authenticate
|
from .serializers_.permissions import *
|
||||||
from django.contrib.auth import get_user_model
|
from .serializers_.tokens import *
|
||||||
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 .nested_serializers 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',
|
|
||||||
)
|
|
||||||
|
0
netbox/users/api/serializers_/__init__.py
Normal file
0
netbox/users/api/serializers_/__init__.py
Normal file
43
netbox/users/api/serializers_/permissions.py
Normal file
43
netbox/users/api/serializers_/permissions.py
Normal 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',
|
||||||
|
)
|
94
netbox/users/api/serializers_/tokens.py
Normal file
94
netbox/users/api/serializers_/tokens.py
Normal 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
|
72
netbox/users/api/serializers_/users.py
Normal file
72
netbox/users/api/serializers_/users.py
Normal 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')
|
@ -1,187 +1,3 @@
|
|||||||
from drf_spectacular.utils import extend_schema_field
|
from .serializers_.clusters import *
|
||||||
from rest_framework import serializers
|
from .serializers_.virtualmachines import *
|
||||||
|
|
||||||
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 .nested_serializers 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')
|
|
||||||
|
0
netbox/virtualization/api/serializers_/__init__.py
Normal file
0
netbox/virtualization/api/serializers_/__init__.py
Normal file
65
netbox/virtualization/api/serializers_/clusters.py
Normal file
65
netbox/virtualization/api/serializers_/clusters.py
Normal 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')
|
143
netbox/virtualization/api/serializers_/virtualmachines.py
Normal file
143
netbox/virtualization/api/serializers_/virtualmachines.py
Normal 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')
|
@ -1,297 +1,4 @@
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from .serializers_.crypto import *
|
||||||
from drf_spectacular.utils import extend_schema_field
|
from .serializers_.tunnels import *
|
||||||
from rest_framework import serializers
|
from .serializers_.l2vpn import *
|
||||||
|
|
||||||
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 .nested_serializers 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
|
|
||||||
|
0
netbox/vpn/api/serializers_/__init__.py
Normal file
0
netbox/vpn/api/serializers_/__init__.py
Normal file
135
netbox/vpn/api/serializers_/crypto.py
Normal file
135
netbox/vpn/api/serializers_/crypto.py
Normal 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')
|
69
netbox/vpn/api/serializers_/l2vpn.py
Normal file
69
netbox/vpn/api/serializers_/l2vpn.py
Normal 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
|
114
netbox/vpn/api/serializers_/tunnels.py
Normal file
114
netbox/vpn/api/serializers_/tunnels.py
Normal 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
|
@ -1,67 +1,3 @@
|
|||||||
from rest_framework import serializers
|
from .serializers_.wirelesslans import *
|
||||||
|
from .serializers_.wirelesslinks import *
|
||||||
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 .nested_serializers 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')
|
|
||||||
|
0
netbox/wireless/api/serializers_/__init__.py
Normal file
0
netbox/wireless/api/serializers_/__init__.py
Normal file
46
netbox/wireless/api/serializers_/wirelesslans.py
Normal file
46
netbox/wireless/api/serializers_/wirelesslans.py
Normal 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')
|
31
netbox/wireless/api/serializers_/wirelesslinks.py
Normal file
31
netbox/wireless/api/serializers_/wirelesslinks.py
Normal 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')
|
Reference in New Issue
Block a user