|
|
@@ -2,7 +2,6 @@ from django.conf import settings
|
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
from drf_yasg.utils import swagger_serializer_method
|
|
|
from rest_framework import serializers
|
|
|
-from rest_framework.validators import UniqueTogetherValidator
|
|
|
from timezone_field.rest_framework import TimeZoneSerializerField
|
|
|
|
|
|
from dcim.choices import *
|
|
|
@@ -12,8 +11,7 @@ from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSer
|
|
|
from ipam.models import VLAN
|
|
|
from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
|
|
|
from netbox.api.serializers import (
|
|
|
- NestedGroupModelSerializer, OrganizationalModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer,
|
|
|
- WritableNestedSerializer,
|
|
|
+ NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
|
|
|
)
|
|
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
|
|
from users.api.nested_serializers import NestedUserSerializer
|
|
|
@@ -83,27 +81,27 @@ class ConnectedEndpointSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class RegionSerializer(NestedGroupModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
|
|
- parent = NestedRegionSerializer(required=False, allow_null=True)
|
|
|
+ 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', 'custom_fields', 'created', 'last_updated',
|
|
|
- 'site_count', '_depth',
|
|
|
+ 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
|
|
|
+ 'last_updated', 'site_count', '_depth',
|
|
|
]
|
|
|
|
|
|
|
|
|
class SiteGroupSerializer(NestedGroupModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
|
|
|
- parent = NestedSiteGroupSerializer(required=False, allow_null=True)
|
|
|
+ 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', 'custom_fields', 'created', 'last_updated',
|
|
|
- 'site_count', '_depth',
|
|
|
+ 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
|
|
|
+ 'last_updated', 'site_count', '_depth',
|
|
|
]
|
|
|
|
|
|
|
|
|
@@ -146,20 +144,20 @@ class LocationSerializer(NestedGroupModelSerializer):
|
|
|
class Meta:
|
|
|
model = Location
|
|
|
fields = [
|
|
|
- 'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'tenant', 'description', 'custom_fields',
|
|
|
+ 'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'tenant', 'description', 'tags', 'custom_fields',
|
|
|
'created', 'last_updated', 'rack_count', 'device_count', '_depth',
|
|
|
]
|
|
|
|
|
|
|
|
|
-class RackRoleSerializer(OrganizationalModelSerializer):
|
|
|
+class RackRoleSerializer(PrimaryModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
|
|
rack_count = serializers.IntegerField(read_only=True)
|
|
|
|
|
|
class Meta:
|
|
|
model = RackRole
|
|
|
fields = [
|
|
|
- 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'custom_fields', 'created', 'last_updated',
|
|
|
- 'rack_count',
|
|
|
+ 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
|
|
|
+ 'last_updated', 'rack_count',
|
|
|
]
|
|
|
|
|
|
|
|
|
@@ -171,6 +169,8 @@ class RackSerializer(PrimaryModelSerializer):
|
|
|
status = ChoiceField(choices=RackStatusChoices, required=False)
|
|
|
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
|
|
type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False)
|
|
|
+ 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)
|
|
|
device_count = serializers.IntegerField(read_only=True)
|
|
|
@@ -183,23 +183,6 @@ class RackSerializer(PrimaryModelSerializer):
|
|
|
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
|
|
|
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
|
|
|
]
|
|
|
- # Omit the UniqueTogetherValidator that would be automatically added to validate (location, facility_id). This
|
|
|
- # prevents facility_id from being interpreted as a required field.
|
|
|
- validators = [
|
|
|
- UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('location', 'name'))
|
|
|
- ]
|
|
|
-
|
|
|
- def validate(self, data):
|
|
|
-
|
|
|
- # Validate uniqueness of (location, facility_id) since we omitted the automatically-created validator from Meta.
|
|
|
- if data.get('facility_id', None):
|
|
|
- validator = UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('location', 'facility_id'))
|
|
|
- validator(data, self)
|
|
|
-
|
|
|
- # Enforce model validation
|
|
|
- super().validate(data)
|
|
|
-
|
|
|
- return data
|
|
|
|
|
|
|
|
|
class RackUnitSerializer(serializers.Serializer):
|
|
|
@@ -271,7 +254,7 @@ class RackElevationDetailFilterSerializer(serializers.Serializer):
|
|
|
# Device types
|
|
|
#
|
|
|
|
|
|
-class ManufacturerSerializer(OrganizationalModelSerializer):
|
|
|
+class ManufacturerSerializer(PrimaryModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
|
|
devicetype_count = serializers.IntegerField(read_only=True)
|
|
|
inventoryitem_count = serializers.IntegerField(read_only=True)
|
|
|
@@ -280,7 +263,7 @@ class ManufacturerSerializer(OrganizationalModelSerializer):
|
|
|
class Meta:
|
|
|
model = Manufacturer
|
|
|
fields = [
|
|
|
- 'id', 'url', 'display', 'name', 'slug', 'description', 'custom_fields', 'created', 'last_updated',
|
|
|
+ 'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
|
|
'devicetype_count', 'inventoryitem_count', 'platform_count',
|
|
|
]
|
|
|
|
|
|
@@ -428,7 +411,7 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
|
|
# Devices
|
|
|
#
|
|
|
|
|
|
-class DeviceRoleSerializer(OrganizationalModelSerializer):
|
|
|
+class DeviceRoleSerializer(PrimaryModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
|
|
device_count = serializers.IntegerField(read_only=True)
|
|
|
virtualmachine_count = serializers.IntegerField(read_only=True)
|
|
|
@@ -436,12 +419,12 @@ class DeviceRoleSerializer(OrganizationalModelSerializer):
|
|
|
class Meta:
|
|
|
model = DeviceRole
|
|
|
fields = [
|
|
|
- 'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'description', 'custom_fields', 'created',
|
|
|
- 'last_updated', 'device_count', 'virtualmachine_count',
|
|
|
+ 'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'description', 'tags', 'custom_fields',
|
|
|
+ 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
|
|
]
|
|
|
|
|
|
|
|
|
-class PlatformSerializer(OrganizationalModelSerializer):
|
|
|
+class PlatformSerializer(PrimaryModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
|
|
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
|
|
|
device_count = serializers.IntegerField(read_only=True)
|
|
|
@@ -451,7 +434,7 @@ class PlatformSerializer(OrganizationalModelSerializer):
|
|
|
model = Platform
|
|
|
fields = [
|
|
|
'id', 'url', 'display', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description',
|
|
|
- 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
|
|
+ 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
|
|
|
]
|
|
|
|
|
|
|
|
|
@@ -459,12 +442,13 @@ class DeviceSerializer(PrimaryModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
|
|
device_type = NestedDeviceTypeSerializer()
|
|
|
device_role = NestedDeviceRoleSerializer()
|
|
|
- tenant = NestedTenantSerializer(required=False, allow_null=True)
|
|
|
+ tenant = NestedTenantSerializer(required=False, allow_null=True, default=None)
|
|
|
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
|
|
site = NestedSiteSerializer()
|
|
|
location = NestedLocationSerializer(required=False, allow_null=True, default=None)
|
|
|
- rack = NestedRackSerializer(required=False, allow_null=True)
|
|
|
- face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, required=False)
|
|
|
+ rack = NestedRackSerializer(required=False, allow_null=True, default=None)
|
|
|
+ face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, default='')
|
|
|
+ position = serializers.IntegerField(allow_null=True, label='Position (U)', min_value=1, default=None)
|
|
|
status = ChoiceField(choices=DeviceStatusChoices, required=False)
|
|
|
airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
|
|
|
primary_ip = NestedIPAddressSerializer(read_only=True)
|
|
|
@@ -472,7 +456,8 @@ class DeviceSerializer(PrimaryModelSerializer):
|
|
|
primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
|
|
|
parent_device = serializers.SerializerMethodField()
|
|
|
cluster = NestedClusterSerializer(required=False, allow_null=True)
|
|
|
- virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True)
|
|
|
+ virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True, default=None)
|
|
|
+ vc_position = serializers.IntegerField(allow_null=True, max_value=255, min_value=0, default=None)
|
|
|
|
|
|
class Meta:
|
|
|
model = Device
|
|
|
@@ -482,19 +467,6 @@ class DeviceSerializer(PrimaryModelSerializer):
|
|
|
'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments',
|
|
|
'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
|
|
|
]
|
|
|
- validators = []
|
|
|
-
|
|
|
- def validate(self, data):
|
|
|
-
|
|
|
- # Validate uniqueness of (rack, position, face) since we omitted the automatically-created validator from Meta.
|
|
|
- if data.get('rack') and data.get('position') and data.get('face'):
|
|
|
- validator = UniqueTogetherValidator(queryset=Device.objects.all(), fields=('rack', 'position', 'face'))
|
|
|
- validator(data, self)
|
|
|
-
|
|
|
- # Enforce model validation
|
|
|
- super().validate(data)
|
|
|
-
|
|
|
- return data
|
|
|
|
|
|
@swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
|
|
|
def get_parent_device(self, obj):
|
|
|
@@ -733,7 +705,6 @@ class DeviceBaySerializer(PrimaryModelSerializer):
|
|
|
class InventoryItemSerializer(PrimaryModelSerializer):
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
|
|
|
device = NestedDeviceSerializer()
|
|
|
- # Provide a default value to satisfy UniqueTogetherValidator
|
|
|
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
|
|
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
|
|
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
|
|
@@ -761,14 +732,15 @@ class CableSerializer(PrimaryModelSerializer):
|
|
|
termination_a = serializers.SerializerMethodField(read_only=True)
|
|
|
termination_b = serializers.SerializerMethodField(read_only=True)
|
|
|
status = ChoiceField(choices=LinkStatusChoices, required=False)
|
|
|
+ tenant = NestedTenantSerializer(required=False, allow_null=True)
|
|
|
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
|
|
|
|
|
|
class Meta:
|
|
|
model = Cable
|
|
|
fields = [
|
|
|
'id', 'url', 'display', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
|
|
|
- 'termination_b_id', 'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
|
|
|
- 'custom_fields',
|
|
|
+ 'termination_b_id', 'termination_b', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit',
|
|
|
+ 'tags', 'custom_fields',
|
|
|
]
|
|
|
|
|
|
def _get_termination(self, obj, side):
|