mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-19 05:21:55 -06:00
Merge pull request #2024 from digitalocean/1794-writable-nested-serializers
1794 writable nested serializers
This commit is contained in:
commit
7805848e6c
@ -7,7 +7,7 @@ from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
|
|||||||
from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
|
from dcim.api.serializers import NestedSiteSerializer, InterfaceSerializer
|
||||||
from extras.api.customfields import CustomFieldModelSerializer
|
from extras.api.customfields import CustomFieldModelSerializer
|
||||||
from tenancy.api.serializers import NestedTenantSerializer
|
from tenancy.api.serializers import NestedTenantSerializer
|
||||||
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
|
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer, WritableNestedSerializer
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -24,7 +24,7 @@ class ProviderSerializer(CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedProviderSerializer(serializers.ModelSerializer):
|
class NestedProviderSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -32,16 +32,6 @@ class NestedProviderSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'slug']
|
fields = ['id', 'url', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class WritableProviderSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Provider
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
|
||||||
'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Circuit types
|
# Circuit types
|
||||||
#
|
#
|
||||||
@ -53,7 +43,7 @@ class CircuitTypeSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedCircuitTypeSerializer(serializers.ModelSerializer):
|
class NestedCircuitTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -67,9 +57,9 @@ class NestedCircuitTypeSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class CircuitSerializer(CustomFieldModelSerializer):
|
class CircuitSerializer(CustomFieldModelSerializer):
|
||||||
provider = NestedProviderSerializer()
|
provider = NestedProviderSerializer()
|
||||||
status = ChoiceFieldSerializer(choices=CIRCUIT_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=CIRCUIT_STATUS_CHOICES, required=False)
|
||||||
type = NestedCircuitTypeSerializer()
|
type = NestedCircuitTypeSerializer()
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Circuit
|
model = Circuit
|
||||||
@ -79,7 +69,7 @@ class CircuitSerializer(CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedCircuitSerializer(serializers.ModelSerializer):
|
class NestedCircuitSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -87,33 +77,14 @@ class NestedCircuitSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'cid']
|
fields = ['id', 'url', 'cid']
|
||||||
|
|
||||||
|
|
||||||
class WritableCircuitSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Circuit
|
|
||||||
fields = [
|
|
||||||
'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
|
|
||||||
'comments', 'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Circuit Terminations
|
# Circuit Terminations
|
||||||
#
|
#
|
||||||
|
|
||||||
class CircuitTerminationSerializer(serializers.ModelSerializer):
|
class CircuitTerminationSerializer(ValidatedModelSerializer):
|
||||||
circuit = NestedCircuitSerializer()
|
circuit = NestedCircuitSerializer()
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
interface = InterfaceSerializer()
|
interface = InterfaceSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = CircuitTermination
|
|
||||||
fields = [
|
|
||||||
'id', 'circuit', 'term_side', 'site', 'interface', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class WritableCircuitTerminationSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
|
@ -30,7 +30,6 @@ class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
|
|||||||
class ProviderViewSet(CustomFieldModelViewSet):
|
class ProviderViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Provider.objects.all()
|
queryset = Provider.objects.all()
|
||||||
serializer_class = serializers.ProviderSerializer
|
serializer_class = serializers.ProviderSerializer
|
||||||
write_serializer_class = serializers.WritableProviderSerializer
|
|
||||||
filter_class = filters.ProviderFilter
|
filter_class = filters.ProviderFilter
|
||||||
|
|
||||||
@detail_route()
|
@detail_route()
|
||||||
@ -61,7 +60,6 @@ class CircuitTypeViewSet(ModelViewSet):
|
|||||||
class CircuitViewSet(CustomFieldModelViewSet):
|
class CircuitViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
|
queryset = Circuit.objects.select_related('type', 'tenant', 'provider')
|
||||||
serializer_class = serializers.CircuitSerializer
|
serializer_class = serializers.CircuitSerializer
|
||||||
write_serializer_class = serializers.WritableCircuitSerializer
|
|
||||||
filter_class = filters.CircuitFilter
|
filter_class = filters.CircuitFilter
|
||||||
|
|
||||||
|
|
||||||
@ -72,5 +70,4 @@ class CircuitViewSet(CustomFieldModelViewSet):
|
|||||||
class CircuitTerminationViewSet(ModelViewSet):
|
class CircuitTerminationViewSet(ModelViewSet):
|
||||||
queryset = CircuitTermination.objects.select_related('circuit', 'site', 'interface__device')
|
queryset = CircuitTermination.objects.select_related('circuit', 'site', 'interface__device')
|
||||||
serializer_class = serializers.CircuitTerminationSerializer
|
serializer_class = serializers.CircuitTerminationSerializer
|
||||||
write_serializer_class = serializers.WritableCircuitTerminationSerializer
|
|
||||||
filter_class = filters.CircuitTerminationFilter
|
filter_class = filters.CircuitTerminationFilter
|
||||||
|
@ -20,7 +20,9 @@ from extras.api.customfields import CustomFieldModelSerializer
|
|||||||
from ipam.models import IPAddress, VLAN
|
from ipam.models import IPAddress, VLAN
|
||||||
from tenancy.api.serializers import NestedTenantSerializer
|
from tenancy.api.serializers import NestedTenantSerializer
|
||||||
from users.api.serializers import NestedUserSerializer
|
from users.api.serializers import NestedUserSerializer
|
||||||
from utilities.api import ChoiceFieldSerializer, TimeZoneField, ValidatedModelSerializer
|
from utilities.api import (
|
||||||
|
ChoiceFieldSerializer, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer, WritableNestedSerializer,
|
||||||
|
)
|
||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +30,7 @@ from virtualization.models import Cluster
|
|||||||
# Regions
|
# Regions
|
||||||
#
|
#
|
||||||
|
|
||||||
class NestedRegionSerializer(serializers.ModelSerializer):
|
class NestedRegionSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -37,14 +39,7 @@ class NestedRegionSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class RegionSerializer(serializers.ModelSerializer):
|
class RegionSerializer(serializers.ModelSerializer):
|
||||||
parent = NestedRegionSerializer()
|
parent = NestedRegionSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Region
|
|
||||||
fields = ['id', 'name', 'slug', 'parent']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableRegionSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Region
|
model = Region
|
||||||
@ -56,9 +51,9 @@ class WritableRegionSerializer(ValidatedModelSerializer):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class SiteSerializer(CustomFieldModelSerializer):
|
class SiteSerializer(CustomFieldModelSerializer):
|
||||||
status = ChoiceFieldSerializer(choices=SITE_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=SITE_STATUS_CHOICES, required=False)
|
||||||
region = NestedRegionSerializer()
|
region = NestedRegionSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
time_zone = TimeZoneField(required=False)
|
time_zone = TimeZoneField(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -71,7 +66,7 @@ class SiteSerializer(CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedSiteSerializer(serializers.ModelSerializer):
|
class NestedSiteSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -79,23 +74,11 @@ class NestedSiteSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'slug']
|
fields = ['id', 'url', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class WritableSiteSerializer(CustomFieldModelSerializer):
|
|
||||||
time_zone = TimeZoneField(required=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Site
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
|
|
||||||
'physical_address', 'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments',
|
|
||||||
'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rack groups
|
# Rack groups
|
||||||
#
|
#
|
||||||
|
|
||||||
class RackGroupSerializer(serializers.ModelSerializer):
|
class RackGroupSerializer(ValidatedModelSerializer):
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -103,7 +86,7 @@ class RackGroupSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug', 'site']
|
fields = ['id', 'name', 'slug', 'site']
|
||||||
|
|
||||||
|
|
||||||
class NestedRackGroupSerializer(serializers.ModelSerializer):
|
class NestedRackGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -111,13 +94,6 @@ class NestedRackGroupSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'slug']
|
fields = ['id', 'url', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class WritableRackGroupSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RackGroup
|
|
||||||
fields = ['id', 'name', 'slug', 'site']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rack roles
|
# Rack roles
|
||||||
#
|
#
|
||||||
@ -129,7 +105,7 @@ class RackRoleSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug', 'color']
|
fields = ['id', 'name', 'slug', 'color']
|
||||||
|
|
||||||
|
|
||||||
class NestedRackRoleSerializer(serializers.ModelSerializer):
|
class NestedRackRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -143,11 +119,11 @@ class NestedRackRoleSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class RackSerializer(CustomFieldModelSerializer):
|
class RackSerializer(CustomFieldModelSerializer):
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
group = NestedRackGroupSerializer()
|
group = NestedRackGroupSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
role = NestedRackRoleSerializer()
|
role = NestedRackRoleSerializer(required=False, allow_null=True)
|
||||||
type = ChoiceFieldSerializer(choices=RACK_TYPE_CHOICES)
|
type = ChoiceFieldSerializer(choices=RACK_TYPE_CHOICES, required=False)
|
||||||
width = ChoiceFieldSerializer(choices=RACK_WIDTH_CHOICES)
|
width = ChoiceFieldSerializer(choices=RACK_WIDTH_CHOICES, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Rack
|
model = Rack
|
||||||
@ -155,24 +131,6 @@ class RackSerializer(CustomFieldModelSerializer):
|
|||||||
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width',
|
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width',
|
||||||
'u_height', 'desc_units', 'comments', 'custom_fields', 'created', 'last_updated',
|
'u_height', 'desc_units', 'comments', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedRackSerializer(serializers.ModelSerializer):
|
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Rack
|
|
||||||
fields = ['id', 'url', 'name', 'display_name']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableRackSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Rack
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'facility_id', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width', 'u_height',
|
|
||||||
'desc_units', 'comments', 'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
# Omit the UniqueTogetherValidator that would be automatically added to validate (site, facility_id). This
|
# Omit the UniqueTogetherValidator that would be automatically added to validate (site, facility_id). This
|
||||||
# prevents facility_id from being interpreted as a required field.
|
# prevents facility_id from being interpreted as a required field.
|
||||||
validators = [
|
validators = [
|
||||||
@ -188,16 +146,24 @@ class WritableRackSerializer(CustomFieldModelSerializer):
|
|||||||
validator(data)
|
validator(data)
|
||||||
|
|
||||||
# Enforce model validation
|
# Enforce model validation
|
||||||
super(WritableRackSerializer, self).validate(data)
|
super(RackSerializer, self).validate(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class NestedRackSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Rack
|
||||||
|
fields = ['id', 'url', 'name', 'display_name']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rack units
|
# Rack units
|
||||||
#
|
#
|
||||||
|
|
||||||
class NestedDeviceSerializer(serializers.ModelSerializer):
|
class NestedDeviceSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -219,23 +185,16 @@ class RackUnitSerializer(serializers.Serializer):
|
|||||||
# Rack reservations
|
# Rack reservations
|
||||||
#
|
#
|
||||||
|
|
||||||
class RackReservationSerializer(serializers.ModelSerializer):
|
class RackReservationSerializer(ValidatedModelSerializer):
|
||||||
rack = NestedRackSerializer()
|
rack = NestedRackSerializer()
|
||||||
user = NestedUserSerializer()
|
user = NestedUserSerializer()
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RackReservation
|
model = RackReservation
|
||||||
fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description']
|
fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description']
|
||||||
|
|
||||||
|
|
||||||
class WritableRackReservationSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RackReservation
|
|
||||||
fields = ['id', 'rack', 'units', 'user', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Manufacturers
|
# Manufacturers
|
||||||
#
|
#
|
||||||
@ -247,7 +206,7 @@ class ManufacturerSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedManufacturerSerializer(serializers.ModelSerializer):
|
class NestedManufacturerSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -261,8 +220,8 @@ class NestedManufacturerSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class DeviceTypeSerializer(CustomFieldModelSerializer):
|
class DeviceTypeSerializer(CustomFieldModelSerializer):
|
||||||
manufacturer = NestedManufacturerSerializer()
|
manufacturer = NestedManufacturerSerializer()
|
||||||
interface_ordering = ChoiceFieldSerializer(choices=IFACE_ORDERING_CHOICES)
|
interface_ordering = ChoiceFieldSerializer(choices=IFACE_ORDERING_CHOICES, required=False)
|
||||||
subdevice_role = ChoiceFieldSerializer(choices=SUBDEVICE_ROLE_CHOICES)
|
subdevice_role = ChoiceFieldSerializer(choices=SUBDEVICE_ROLE_CHOICES, required=False)
|
||||||
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
|
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -274,30 +233,20 @@ class DeviceTypeSerializer(CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceTypeSerializer(serializers.ModelSerializer):
|
class NestedDeviceTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
|
||||||
manufacturer = NestedManufacturerSerializer()
|
manufacturer = NestedManufacturerSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = ['id', 'url', 'manufacturer', 'model', 'slug']
|
fields = ['id', 'url', 'manufacturer', 'model', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class WritableDeviceTypeSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = DeviceType
|
|
||||||
fields = [
|
|
||||||
'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
|
|
||||||
'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'custom_fields',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Console port templates
|
# Console port templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsolePortTemplateSerializer(serializers.ModelSerializer):
|
class ConsolePortTemplateSerializer(ValidatedModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -305,18 +254,11 @@ class ConsolePortTemplateSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'device_type', 'name']
|
fields = ['id', 'device_type', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableConsolePortTemplateSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ConsolePortTemplate
|
|
||||||
fields = ['id', 'device_type', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Console server port templates
|
# Console server port templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsoleServerPortTemplateSerializer(serializers.ModelSerializer):
|
class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -324,18 +266,11 @@ class ConsoleServerPortTemplateSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'device_type', 'name']
|
fields = ['id', 'device_type', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ConsoleServerPortTemplate
|
|
||||||
fields = ['id', 'device_type', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Power port templates
|
# Power port templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class PowerPortTemplateSerializer(serializers.ModelSerializer):
|
class PowerPortTemplateSerializer(ValidatedModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -343,18 +278,11 @@ class PowerPortTemplateSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'device_type', 'name']
|
fields = ['id', 'device_type', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritablePowerPortTemplateSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = PowerPortTemplate
|
|
||||||
fields = ['id', 'device_type', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Power outlet templates
|
# Power outlet templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class PowerOutletTemplateSerializer(serializers.ModelSerializer):
|
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -362,27 +290,13 @@ class PowerOutletTemplateSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'device_type', 'name']
|
fields = ['id', 'device_type', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritablePowerOutletTemplateSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = PowerOutletTemplate
|
|
||||||
fields = ['id', 'device_type', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Interface templates
|
# Interface templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class InterfaceTemplateSerializer(serializers.ModelSerializer):
|
class InterfaceTemplateSerializer(ValidatedModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
|
form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES, required=False)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = InterfaceTemplate
|
|
||||||
fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableInterfaceTemplateSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceTemplate
|
model = InterfaceTemplate
|
||||||
@ -393,7 +307,7 @@ class WritableInterfaceTemplateSerializer(ValidatedModelSerializer):
|
|||||||
# Device bay templates
|
# Device bay templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class DeviceBayTemplateSerializer(serializers.ModelSerializer):
|
class DeviceBayTemplateSerializer(ValidatedModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -401,13 +315,6 @@ class DeviceBayTemplateSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'device_type', 'name']
|
fields = ['id', 'device_type', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableDeviceBayTemplateSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = DeviceBayTemplate
|
|
||||||
fields = ['id', 'device_type', 'name']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device roles
|
# Device roles
|
||||||
#
|
#
|
||||||
@ -419,7 +326,7 @@ class DeviceRoleSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug', 'color', 'vm_role']
|
fields = ['id', 'name', 'slug', 'color', 'vm_role']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceRoleSerializer(serializers.ModelSerializer):
|
class NestedDeviceRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -431,15 +338,15 @@ class NestedDeviceRoleSerializer(serializers.ModelSerializer):
|
|||||||
# Platforms
|
# Platforms
|
||||||
#
|
#
|
||||||
|
|
||||||
class PlatformSerializer(serializers.ModelSerializer):
|
class PlatformSerializer(ValidatedModelSerializer):
|
||||||
manufacturer = NestedManufacturerSerializer()
|
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Platform
|
model = Platform
|
||||||
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'rpc_client']
|
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'rpc_client']
|
||||||
|
|
||||||
|
|
||||||
class NestedPlatformSerializer(serializers.ModelSerializer):
|
class NestedPlatformSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -447,13 +354,6 @@ class NestedPlatformSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'slug']
|
fields = ['id', 'url', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class WritablePlatformSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Platform
|
|
||||||
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'rpc_client']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Devices
|
# Devices
|
||||||
#
|
#
|
||||||
@ -489,18 +389,18 @@ class DeviceVirtualChassisSerializer(serializers.ModelSerializer):
|
|||||||
class DeviceSerializer(CustomFieldModelSerializer):
|
class DeviceSerializer(CustomFieldModelSerializer):
|
||||||
device_type = NestedDeviceTypeSerializer()
|
device_type = NestedDeviceTypeSerializer()
|
||||||
device_role = NestedDeviceRoleSerializer()
|
device_role = NestedDeviceRoleSerializer()
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
platform = NestedPlatformSerializer()
|
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
rack = NestedRackSerializer()
|
rack = NestedRackSerializer(required=False, allow_null=True)
|
||||||
face = ChoiceFieldSerializer(choices=RACK_FACE_CHOICES)
|
face = ChoiceFieldSerializer(choices=RACK_FACE_CHOICES, required=False)
|
||||||
status = ChoiceFieldSerializer(choices=DEVICE_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=DEVICE_STATUS_CHOICES, required=False)
|
||||||
primary_ip = DeviceIPAddressSerializer()
|
primary_ip = DeviceIPAddressSerializer(read_only=True)
|
||||||
primary_ip4 = DeviceIPAddressSerializer()
|
primary_ip4 = DeviceIPAddressSerializer(required=False, allow_null=True)
|
||||||
primary_ip6 = DeviceIPAddressSerializer()
|
primary_ip6 = DeviceIPAddressSerializer(required=False, allow_null=True)
|
||||||
parent_device = serializers.SerializerMethodField()
|
parent_device = serializers.SerializerMethodField()
|
||||||
cluster = NestedClusterSerializer()
|
cluster = NestedClusterSerializer(required=False, allow_null=True)
|
||||||
virtual_chassis = DeviceVirtualChassisSerializer()
|
virtual_chassis = DeviceVirtualChassisSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
@ -510,27 +410,6 @@ class DeviceSerializer(CustomFieldModelSerializer):
|
|||||||
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'custom_fields', 'created',
|
'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'custom_fields', 'created',
|
||||||
'last_updated',
|
'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class WritableDeviceSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Device
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag', 'site', 'rack',
|
|
||||||
'position', 'face', 'status', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position',
|
|
||||||
'vc_priority', 'comments', 'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
validators = []
|
validators = []
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
@ -542,16 +421,26 @@ class WritableDeviceSerializer(CustomFieldModelSerializer):
|
|||||||
validator(data)
|
validator(data)
|
||||||
|
|
||||||
# Enforce model validation
|
# Enforce model validation
|
||||||
super(WritableDeviceSerializer, self).validate(data)
|
super(DeviceSerializer, self).validate(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Console server ports
|
# Console server ports
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsoleServerPortSerializer(serializers.ModelSerializer):
|
class ConsoleServerPortSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -560,27 +449,22 @@ class ConsoleServerPortSerializer(serializers.ModelSerializer):
|
|||||||
read_only_fields = ['connected_console']
|
read_only_fields = ['connected_console']
|
||||||
|
|
||||||
|
|
||||||
class WritableConsoleServerPortSerializer(ValidatedModelSerializer):
|
class NestedConsoleServerPortSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
|
||||||
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
fields = ['id', 'device', 'name']
|
fields = ['id', 'url', 'device', 'name']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Console ports
|
# Console ports
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsolePortSerializer(serializers.ModelSerializer):
|
class ConsolePortSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
cs_port = ConsoleServerPortSerializer()
|
cs_port = NestedConsoleServerPortSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ConsolePort
|
|
||||||
fields = ['id', 'device', 'name', 'cs_port', 'connection_status']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableConsolePortSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
@ -591,7 +475,7 @@ class WritableConsolePortSerializer(ValidatedModelSerializer):
|
|||||||
# Power outlets
|
# Power outlets
|
||||||
#
|
#
|
||||||
|
|
||||||
class PowerOutletSerializer(serializers.ModelSerializer):
|
class PowerOutletSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -600,27 +484,22 @@ class PowerOutletSerializer(serializers.ModelSerializer):
|
|||||||
read_only_fields = ['connected_port']
|
read_only_fields = ['connected_port']
|
||||||
|
|
||||||
|
|
||||||
class WritablePowerOutletSerializer(ValidatedModelSerializer):
|
class NestedPowerOutletSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
|
||||||
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
fields = ['id', 'device', 'name']
|
fields = ['id', 'url', 'device', 'name']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Power ports
|
# Power ports
|
||||||
#
|
#
|
||||||
|
|
||||||
class PowerPortSerializer(serializers.ModelSerializer):
|
class PowerPortSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
power_outlet = PowerOutletSerializer()
|
power_outlet = NestedPowerOutletSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = PowerPort
|
|
||||||
fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']
|
|
||||||
|
|
||||||
|
|
||||||
class WritablePowerPortSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
@ -631,12 +510,13 @@ class WritablePowerPortSerializer(ValidatedModelSerializer):
|
|||||||
# Interfaces
|
# Interfaces
|
||||||
#
|
#
|
||||||
|
|
||||||
class NestedInterfaceSerializer(serializers.ModelSerializer):
|
class NestedInterfaceSerializer(WritableNestedSerializer):
|
||||||
|
device = NestedDeviceSerializer(read_only=True)
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ['id', 'url', 'name']
|
fields = ['id', 'url', 'device', 'name']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
|
class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
|
||||||
@ -647,8 +527,8 @@ class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'cid']
|
fields = ['id', 'url', 'cid']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceCircuitTerminationSerializer(serializers.ModelSerializer):
|
class InterfaceCircuitTerminationSerializer(WritableNestedSerializer):
|
||||||
circuit = InterfaceNestedCircuitSerializer()
|
circuit = InterfaceNestedCircuitSerializer(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CircuitTermination
|
model = CircuitTermination
|
||||||
@ -658,7 +538,7 @@ class InterfaceCircuitTerminationSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
# Cannot import ipam.api.NestedVLANSerializer due to circular dependency
|
# Cannot import ipam.api.NestedVLANSerializer due to circular dependency
|
||||||
class InterfaceVLANSerializer(serializers.ModelSerializer):
|
class InterfaceVLANSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -666,16 +546,21 @@ class InterfaceVLANSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceSerializer(serializers.ModelSerializer):
|
class InterfaceSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
|
form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES, required=False)
|
||||||
lag = NestedInterfaceSerializer()
|
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||||
is_connected = serializers.SerializerMethodField(read_only=True)
|
is_connected = serializers.SerializerMethodField(read_only=True)
|
||||||
interface_connection = serializers.SerializerMethodField(read_only=True)
|
interface_connection = serializers.SerializerMethodField(read_only=True)
|
||||||
circuit_termination = InterfaceCircuitTerminationSerializer()
|
circuit_termination = InterfaceCircuitTerminationSerializer(read_only=True)
|
||||||
untagged_vlan = InterfaceVLANSerializer()
|
mode = ChoiceFieldSerializer(choices=IFACE_MODE_CHOICES, required=False)
|
||||||
mode = ChoiceFieldSerializer(choices=IFACE_MODE_CHOICES)
|
untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
|
||||||
tagged_vlans = InterfaceVLANSerializer(many=True)
|
tagged_vlans = SerializedPKRelatedField(
|
||||||
|
queryset=VLAN.objects.all(),
|
||||||
|
serializer=InterfaceVLANSerializer,
|
||||||
|
required=False,
|
||||||
|
many=True
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
@ -684,51 +569,6 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
|||||||
'is_connected', 'interface_connection', 'circuit_termination', 'mode', 'untagged_vlan', 'tagged_vlans',
|
'is_connected', 'interface_connection', 'circuit_termination', 'mode', 'untagged_vlan', 'tagged_vlans',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_is_connected(self, obj):
|
|
||||||
"""
|
|
||||||
Return True if the interface has a connected interface or circuit termination.
|
|
||||||
"""
|
|
||||||
if obj.connection:
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
circuit_termination = obj.circuit_termination
|
|
||||||
return True
|
|
||||||
except CircuitTermination.DoesNotExist:
|
|
||||||
pass
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_interface_connection(self, obj):
|
|
||||||
if obj.connection:
|
|
||||||
return OrderedDict((
|
|
||||||
('interface', PeerInterfaceSerializer(obj.connected_interface, context=self.context).data),
|
|
||||||
('status', obj.connection.connection_status),
|
|
||||||
))
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class PeerInterfaceSerializer(serializers.ModelSerializer):
|
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
|
||||||
device = NestedDeviceSerializer()
|
|
||||||
form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
|
|
||||||
lag = NestedInterfaceSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Interface
|
|
||||||
fields = [
|
|
||||||
'id', 'url', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only',
|
|
||||||
'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class WritableInterfaceSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Interface
|
|
||||||
fields = [
|
|
||||||
'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
|
|
||||||
'mode', 'untagged_vlan', 'tagged_vlans',
|
|
||||||
]
|
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
|
||||||
# All associated VLANs be global or assigned to the parent device's site.
|
# All associated VLANs be global or assigned to the parent device's site.
|
||||||
@ -746,23 +586,44 @@ class WritableInterfaceSerializer(ValidatedModelSerializer):
|
|||||||
"be global.".format(vlan)
|
"be global.".format(vlan)
|
||||||
})
|
})
|
||||||
|
|
||||||
return super(WritableInterfaceSerializer, self).validate(data)
|
return super(InterfaceSerializer, self).validate(data)
|
||||||
|
|
||||||
|
def get_is_connected(self, obj):
|
||||||
|
"""
|
||||||
|
Return True if the interface has a connected interface or circuit termination.
|
||||||
|
"""
|
||||||
|
if obj.connection:
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
circuit_termination = obj.circuit_termination
|
||||||
|
return True
|
||||||
|
except CircuitTermination.DoesNotExist:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_interface_connection(self, obj):
|
||||||
|
if obj.connection:
|
||||||
|
return OrderedDict((
|
||||||
|
('interface', NestedInterfaceSerializer(obj.connected_interface, context=self.context).data),
|
||||||
|
('status', obj.connection.connection_status),
|
||||||
|
))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device bays
|
# Device bays
|
||||||
#
|
#
|
||||||
|
|
||||||
class DeviceBaySerializer(serializers.ModelSerializer):
|
class DeviceBaySerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
installed_device = NestedDeviceSerializer()
|
installed_device = NestedDeviceSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
fields = ['id', 'device', 'name', 'installed_device']
|
fields = ['id', 'device', 'name', 'installed_device']
|
||||||
|
|
||||||
|
|
||||||
class NestedDeviceBaySerializer(serializers.ModelSerializer):
|
class NestedDeviceBaySerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -770,32 +631,15 @@ class NestedDeviceBaySerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name']
|
fields = ['id', 'url', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableDeviceBaySerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = DeviceBay
|
|
||||||
fields = ['id', 'device', 'name', 'installed_device']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Inventory items
|
# Inventory items
|
||||||
#
|
#
|
||||||
|
|
||||||
class InventoryItemSerializer(serializers.ModelSerializer):
|
class InventoryItemSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
manufacturer = NestedManufacturerSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = InventoryItem
|
|
||||||
fields = [
|
|
||||||
'id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
|
|
||||||
'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class WritableInventoryItemSerializer(ValidatedModelSerializer):
|
|
||||||
# Provide a default value to satisfy UniqueTogetherValidator
|
# Provide a default value to satisfy UniqueTogetherValidator
|
||||||
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
|
||||||
|
manufacturer = NestedManufacturerSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
@ -809,17 +653,17 @@ class WritableInventoryItemSerializer(ValidatedModelSerializer):
|
|||||||
# Interface connections
|
# Interface connections
|
||||||
#
|
#
|
||||||
|
|
||||||
class InterfaceConnectionSerializer(serializers.ModelSerializer):
|
class InterfaceConnectionSerializer(ValidatedModelSerializer):
|
||||||
interface_a = PeerInterfaceSerializer()
|
interface_a = NestedInterfaceSerializer()
|
||||||
interface_b = PeerInterfaceSerializer()
|
interface_b = NestedInterfaceSerializer()
|
||||||
connection_status = ChoiceFieldSerializer(choices=CONNECTION_STATUS_CHOICES)
|
connection_status = ChoiceFieldSerializer(choices=CONNECTION_STATUS_CHOICES, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InterfaceConnection
|
model = InterfaceConnection
|
||||||
fields = ['id', 'interface_a', 'interface_b', 'connection_status']
|
fields = ['id', 'interface_a', 'interface_b', 'connection_status']
|
||||||
|
|
||||||
|
|
||||||
class NestedInterfaceConnectionSerializer(serializers.ModelSerializer):
|
class NestedInterfaceConnectionSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfaceconnection-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfaceconnection-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -827,18 +671,11 @@ class NestedInterfaceConnectionSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'connection_status']
|
fields = ['id', 'url', 'connection_status']
|
||||||
|
|
||||||
|
|
||||||
class WritableInterfaceConnectionSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = InterfaceConnection
|
|
||||||
fields = ['id', 'interface_a', 'interface_b', 'connection_status']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
#
|
#
|
||||||
|
|
||||||
class VirtualChassisSerializer(serializers.ModelSerializer):
|
class VirtualChassisSerializer(ValidatedModelSerializer):
|
||||||
master = NestedDeviceSerializer()
|
master = NestedDeviceSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -846,16 +683,9 @@ class VirtualChassisSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'master', 'domain']
|
fields = ['id', 'master', 'domain']
|
||||||
|
|
||||||
|
|
||||||
class NestedVirtualChassisSerializer(serializers.ModelSerializer):
|
class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VirtualChassis
|
model = VirtualChassis
|
||||||
fields = ['id', 'url']
|
fields = ['id', 'url']
|
||||||
|
|
||||||
|
|
||||||
class WritableVirtualChassisSerializer(ValidatedModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VirtualChassis
|
|
||||||
fields = ['id', 'master', 'domain']
|
|
||||||
|
@ -52,7 +52,6 @@ class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
|
|||||||
class RegionViewSet(ModelViewSet):
|
class RegionViewSet(ModelViewSet):
|
||||||
queryset = Region.objects.all()
|
queryset = Region.objects.all()
|
||||||
serializer_class = serializers.RegionSerializer
|
serializer_class = serializers.RegionSerializer
|
||||||
write_serializer_class = serializers.WritableRegionSerializer
|
|
||||||
filter_class = filters.RegionFilter
|
filter_class = filters.RegionFilter
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +62,6 @@ class RegionViewSet(ModelViewSet):
|
|||||||
class SiteViewSet(CustomFieldModelViewSet):
|
class SiteViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Site.objects.select_related('region', 'tenant')
|
queryset = Site.objects.select_related('region', 'tenant')
|
||||||
serializer_class = serializers.SiteSerializer
|
serializer_class = serializers.SiteSerializer
|
||||||
write_serializer_class = serializers.WritableSiteSerializer
|
|
||||||
filter_class = filters.SiteFilter
|
filter_class = filters.SiteFilter
|
||||||
|
|
||||||
@detail_route()
|
@detail_route()
|
||||||
@ -84,7 +82,6 @@ class SiteViewSet(CustomFieldModelViewSet):
|
|||||||
class RackGroupViewSet(ModelViewSet):
|
class RackGroupViewSet(ModelViewSet):
|
||||||
queryset = RackGroup.objects.select_related('site')
|
queryset = RackGroup.objects.select_related('site')
|
||||||
serializer_class = serializers.RackGroupSerializer
|
serializer_class = serializers.RackGroupSerializer
|
||||||
write_serializer_class = serializers.WritableRackGroupSerializer
|
|
||||||
filter_class = filters.RackGroupFilter
|
filter_class = filters.RackGroupFilter
|
||||||
|
|
||||||
|
|
||||||
@ -105,7 +102,6 @@ class RackRoleViewSet(ModelViewSet):
|
|||||||
class RackViewSet(CustomFieldModelViewSet):
|
class RackViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
|
queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
|
||||||
serializer_class = serializers.RackSerializer
|
serializer_class = serializers.RackSerializer
|
||||||
write_serializer_class = serializers.WritableRackSerializer
|
|
||||||
filter_class = filters.RackFilter
|
filter_class = filters.RackFilter
|
||||||
|
|
||||||
@detail_route()
|
@detail_route()
|
||||||
@ -136,7 +132,6 @@ class RackViewSet(CustomFieldModelViewSet):
|
|||||||
class RackReservationViewSet(ModelViewSet):
|
class RackReservationViewSet(ModelViewSet):
|
||||||
queryset = RackReservation.objects.select_related('rack', 'user', 'tenant')
|
queryset = RackReservation.objects.select_related('rack', 'user', 'tenant')
|
||||||
serializer_class = serializers.RackReservationSerializer
|
serializer_class = serializers.RackReservationSerializer
|
||||||
write_serializer_class = serializers.WritableRackReservationSerializer
|
|
||||||
filter_class = filters.RackReservationFilter
|
filter_class = filters.RackReservationFilter
|
||||||
|
|
||||||
# Assign user from request
|
# Assign user from request
|
||||||
@ -161,7 +156,6 @@ class ManufacturerViewSet(ModelViewSet):
|
|||||||
class DeviceTypeViewSet(CustomFieldModelViewSet):
|
class DeviceTypeViewSet(CustomFieldModelViewSet):
|
||||||
queryset = DeviceType.objects.select_related('manufacturer')
|
queryset = DeviceType.objects.select_related('manufacturer')
|
||||||
serializer_class = serializers.DeviceTypeSerializer
|
serializer_class = serializers.DeviceTypeSerializer
|
||||||
write_serializer_class = serializers.WritableDeviceTypeSerializer
|
|
||||||
filter_class = filters.DeviceTypeFilter
|
filter_class = filters.DeviceTypeFilter
|
||||||
|
|
||||||
|
|
||||||
@ -172,42 +166,36 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
|
|||||||
class ConsolePortTemplateViewSet(ModelViewSet):
|
class ConsolePortTemplateViewSet(ModelViewSet):
|
||||||
queryset = ConsolePortTemplate.objects.select_related('device_type__manufacturer')
|
queryset = ConsolePortTemplate.objects.select_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.ConsolePortTemplateSerializer
|
serializer_class = serializers.ConsolePortTemplateSerializer
|
||||||
write_serializer_class = serializers.WritableConsolePortTemplateSerializer
|
|
||||||
filter_class = filters.ConsolePortTemplateFilter
|
filter_class = filters.ConsolePortTemplateFilter
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateViewSet(ModelViewSet):
|
class ConsoleServerPortTemplateViewSet(ModelViewSet):
|
||||||
queryset = ConsoleServerPortTemplate.objects.select_related('device_type__manufacturer')
|
queryset = ConsoleServerPortTemplate.objects.select_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.ConsoleServerPortTemplateSerializer
|
serializer_class = serializers.ConsoleServerPortTemplateSerializer
|
||||||
write_serializer_class = serializers.WritableConsoleServerPortTemplateSerializer
|
|
||||||
filter_class = filters.ConsoleServerPortTemplateFilter
|
filter_class = filters.ConsoleServerPortTemplateFilter
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateViewSet(ModelViewSet):
|
class PowerPortTemplateViewSet(ModelViewSet):
|
||||||
queryset = PowerPortTemplate.objects.select_related('device_type__manufacturer')
|
queryset = PowerPortTemplate.objects.select_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.PowerPortTemplateSerializer
|
serializer_class = serializers.PowerPortTemplateSerializer
|
||||||
write_serializer_class = serializers.WritablePowerPortTemplateSerializer
|
|
||||||
filter_class = filters.PowerPortTemplateFilter
|
filter_class = filters.PowerPortTemplateFilter
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateViewSet(ModelViewSet):
|
class PowerOutletTemplateViewSet(ModelViewSet):
|
||||||
queryset = PowerOutletTemplate.objects.select_related('device_type__manufacturer')
|
queryset = PowerOutletTemplate.objects.select_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.PowerOutletTemplateSerializer
|
serializer_class = serializers.PowerOutletTemplateSerializer
|
||||||
write_serializer_class = serializers.WritablePowerOutletTemplateSerializer
|
|
||||||
filter_class = filters.PowerOutletTemplateFilter
|
filter_class = filters.PowerOutletTemplateFilter
|
||||||
|
|
||||||
|
|
||||||
class InterfaceTemplateViewSet(ModelViewSet):
|
class InterfaceTemplateViewSet(ModelViewSet):
|
||||||
queryset = InterfaceTemplate.objects.select_related('device_type__manufacturer')
|
queryset = InterfaceTemplate.objects.select_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.InterfaceTemplateSerializer
|
serializer_class = serializers.InterfaceTemplateSerializer
|
||||||
write_serializer_class = serializers.WritableInterfaceTemplateSerializer
|
|
||||||
filter_class = filters.InterfaceTemplateFilter
|
filter_class = filters.InterfaceTemplateFilter
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateViewSet(ModelViewSet):
|
class DeviceBayTemplateViewSet(ModelViewSet):
|
||||||
queryset = DeviceBayTemplate.objects.select_related('device_type__manufacturer')
|
queryset = DeviceBayTemplate.objects.select_related('device_type__manufacturer')
|
||||||
serializer_class = serializers.DeviceBayTemplateSerializer
|
serializer_class = serializers.DeviceBayTemplateSerializer
|
||||||
write_serializer_class = serializers.WritableDeviceBayTemplateSerializer
|
|
||||||
filter_class = filters.DeviceBayTemplateFilter
|
filter_class = filters.DeviceBayTemplateFilter
|
||||||
|
|
||||||
|
|
||||||
@ -228,7 +216,6 @@ class DeviceRoleViewSet(ModelViewSet):
|
|||||||
class PlatformViewSet(ModelViewSet):
|
class PlatformViewSet(ModelViewSet):
|
||||||
queryset = Platform.objects.all()
|
queryset = Platform.objects.all()
|
||||||
serializer_class = serializers.PlatformSerializer
|
serializer_class = serializers.PlatformSerializer
|
||||||
write_serializer_class = serializers.WritablePlatformSerializer
|
|
||||||
filter_class = filters.PlatformFilter
|
filter_class = filters.PlatformFilter
|
||||||
|
|
||||||
|
|
||||||
@ -244,7 +231,6 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
|||||||
'primary_ip4__nat_outside', 'primary_ip6__nat_outside',
|
'primary_ip4__nat_outside', 'primary_ip6__nat_outside',
|
||||||
)
|
)
|
||||||
serializer_class = serializers.DeviceSerializer
|
serializer_class = serializers.DeviceSerializer
|
||||||
write_serializer_class = serializers.WritableDeviceSerializer
|
|
||||||
filter_class = filters.DeviceFilter
|
filter_class = filters.DeviceFilter
|
||||||
|
|
||||||
@detail_route(url_path='napalm')
|
@detail_route(url_path='napalm')
|
||||||
@ -318,35 +304,30 @@ class DeviceViewSet(CustomFieldModelViewSet):
|
|||||||
class ConsolePortViewSet(ModelViewSet):
|
class ConsolePortViewSet(ModelViewSet):
|
||||||
queryset = ConsolePort.objects.select_related('device', 'cs_port__device')
|
queryset = ConsolePort.objects.select_related('device', 'cs_port__device')
|
||||||
serializer_class = serializers.ConsolePortSerializer
|
serializer_class = serializers.ConsolePortSerializer
|
||||||
write_serializer_class = serializers.WritableConsolePortSerializer
|
|
||||||
filter_class = filters.ConsolePortFilter
|
filter_class = filters.ConsolePortFilter
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortViewSet(ModelViewSet):
|
class ConsoleServerPortViewSet(ModelViewSet):
|
||||||
queryset = ConsoleServerPort.objects.select_related('device', 'connected_console__device')
|
queryset = ConsoleServerPort.objects.select_related('device', 'connected_console__device')
|
||||||
serializer_class = serializers.ConsoleServerPortSerializer
|
serializer_class = serializers.ConsoleServerPortSerializer
|
||||||
write_serializer_class = serializers.WritableConsoleServerPortSerializer
|
|
||||||
filter_class = filters.ConsoleServerPortFilter
|
filter_class = filters.ConsoleServerPortFilter
|
||||||
|
|
||||||
|
|
||||||
class PowerPortViewSet(ModelViewSet):
|
class PowerPortViewSet(ModelViewSet):
|
||||||
queryset = PowerPort.objects.select_related('device', 'power_outlet__device')
|
queryset = PowerPort.objects.select_related('device', 'power_outlet__device')
|
||||||
serializer_class = serializers.PowerPortSerializer
|
serializer_class = serializers.PowerPortSerializer
|
||||||
write_serializer_class = serializers.WritablePowerPortSerializer
|
|
||||||
filter_class = filters.PowerPortFilter
|
filter_class = filters.PowerPortFilter
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletViewSet(ModelViewSet):
|
class PowerOutletViewSet(ModelViewSet):
|
||||||
queryset = PowerOutlet.objects.select_related('device', 'connected_port__device')
|
queryset = PowerOutlet.objects.select_related('device', 'connected_port__device')
|
||||||
serializer_class = serializers.PowerOutletSerializer
|
serializer_class = serializers.PowerOutletSerializer
|
||||||
write_serializer_class = serializers.WritablePowerOutletSerializer
|
|
||||||
filter_class = filters.PowerOutletFilter
|
filter_class = filters.PowerOutletFilter
|
||||||
|
|
||||||
|
|
||||||
class InterfaceViewSet(ModelViewSet):
|
class InterfaceViewSet(ModelViewSet):
|
||||||
queryset = Interface.objects.select_related('device')
|
queryset = Interface.objects.select_related('device')
|
||||||
serializer_class = serializers.InterfaceSerializer
|
serializer_class = serializers.InterfaceSerializer
|
||||||
write_serializer_class = serializers.WritableInterfaceSerializer
|
|
||||||
filter_class = filters.InterfaceFilter
|
filter_class = filters.InterfaceFilter
|
||||||
|
|
||||||
@detail_route()
|
@detail_route()
|
||||||
@ -363,14 +344,12 @@ class InterfaceViewSet(ModelViewSet):
|
|||||||
class DeviceBayViewSet(ModelViewSet):
|
class DeviceBayViewSet(ModelViewSet):
|
||||||
queryset = DeviceBay.objects.select_related('installed_device')
|
queryset = DeviceBay.objects.select_related('installed_device')
|
||||||
serializer_class = serializers.DeviceBaySerializer
|
serializer_class = serializers.DeviceBaySerializer
|
||||||
write_serializer_class = serializers.WritableDeviceBaySerializer
|
|
||||||
filter_class = filters.DeviceBayFilter
|
filter_class = filters.DeviceBayFilter
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemViewSet(ModelViewSet):
|
class InventoryItemViewSet(ModelViewSet):
|
||||||
queryset = InventoryItem.objects.select_related('device', 'manufacturer')
|
queryset = InventoryItem.objects.select_related('device', 'manufacturer')
|
||||||
serializer_class = serializers.InventoryItemSerializer
|
serializer_class = serializers.InventoryItemSerializer
|
||||||
write_serializer_class = serializers.WritableInventoryItemSerializer
|
|
||||||
filter_class = filters.InventoryItemFilter
|
filter_class = filters.InventoryItemFilter
|
||||||
|
|
||||||
|
|
||||||
@ -393,7 +372,6 @@ class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
|
|||||||
class InterfaceConnectionViewSet(ModelViewSet):
|
class InterfaceConnectionViewSet(ModelViewSet):
|
||||||
queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
|
queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
|
||||||
serializer_class = serializers.InterfaceConnectionSerializer
|
serializer_class = serializers.InterfaceConnectionSerializer
|
||||||
write_serializer_class = serializers.WritableInterfaceConnectionSerializer
|
|
||||||
filter_class = filters.InterfaceConnectionFilter
|
filter_class = filters.InterfaceConnectionFilter
|
||||||
|
|
||||||
|
|
||||||
@ -404,7 +382,6 @@ class InterfaceConnectionViewSet(ModelViewSet):
|
|||||||
class VirtualChassisViewSet(ModelViewSet):
|
class VirtualChassisViewSet(ModelViewSet):
|
||||||
queryset = VirtualChassis.objects.all()
|
queryset = VirtualChassis.objects.all()
|
||||||
serializer_class = serializers.VirtualChassisSerializer
|
serializer_class = serializers.VirtualChassisSerializer
|
||||||
write_serializer_class = serializers.WritableVirtualChassisSerializer
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -2327,8 +2327,8 @@ class InterfaceTest(HttpStatusMixin, APITestCase):
|
|||||||
'device': self.device.pk,
|
'device': self.device.pk,
|
||||||
'name': 'Test Interface 4',
|
'name': 'Test Interface 4',
|
||||||
'mode': IFACE_MODE_TAGGED,
|
'mode': IFACE_MODE_TAGGED,
|
||||||
|
'untagged_vlan': self.vlan3.id,
|
||||||
'tagged_vlans': [self.vlan1.id, self.vlan2.id],
|
'tagged_vlans': [self.vlan1.id, self.vlan2.id],
|
||||||
'untagged_vlan': self.vlan3.id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
url = reverse('dcim-api:interface-list')
|
url = reverse('dcim-api:interface-list')
|
||||||
@ -2336,11 +2336,10 @@ class InterfaceTest(HttpStatusMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(Interface.objects.count(), 4)
|
self.assertEqual(Interface.objects.count(), 4)
|
||||||
interface5 = Interface.objects.get(pk=response.data['id'])
|
self.assertEqual(response.data['device']['id'], data['device'])
|
||||||
self.assertEqual(interface5.device_id, data['device'])
|
self.assertEqual(response.data['name'], data['name'])
|
||||||
self.assertEqual(interface5.name, data['name'])
|
self.assertEqual(response.data['untagged_vlan']['id'], data['untagged_vlan'])
|
||||||
self.assertEqual(interface5.tagged_vlans.count(), 2)
|
self.assertEqual([v['id'] for v in response.data['tagged_vlans']], data['tagged_vlans'])
|
||||||
self.assertEqual(interface5.untagged_vlan.id, data['untagged_vlan'])
|
|
||||||
|
|
||||||
def test_create_interface_bulk(self):
|
def test_create_interface_bulk(self):
|
||||||
|
|
||||||
@ -2375,22 +2374,22 @@ class InterfaceTest(HttpStatusMixin, APITestCase):
|
|||||||
'device': self.device.pk,
|
'device': self.device.pk,
|
||||||
'name': 'Test Interface 4',
|
'name': 'Test Interface 4',
|
||||||
'mode': IFACE_MODE_TAGGED,
|
'mode': IFACE_MODE_TAGGED,
|
||||||
'tagged_vlans': [self.vlan1.id],
|
|
||||||
'untagged_vlan': self.vlan2.id,
|
'untagged_vlan': self.vlan2.id,
|
||||||
|
'tagged_vlans': [self.vlan1.id],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'device': self.device.pk,
|
'device': self.device.pk,
|
||||||
'name': 'Test Interface 5',
|
'name': 'Test Interface 5',
|
||||||
'mode': IFACE_MODE_TAGGED,
|
'mode': IFACE_MODE_TAGGED,
|
||||||
'tagged_vlans': [self.vlan1.id],
|
|
||||||
'untagged_vlan': self.vlan2.id,
|
'untagged_vlan': self.vlan2.id,
|
||||||
|
'tagged_vlans': [self.vlan1.id],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'device': self.device.pk,
|
'device': self.device.pk,
|
||||||
'name': 'Test Interface 6',
|
'name': 'Test Interface 6',
|
||||||
'mode': IFACE_MODE_TAGGED,
|
'mode': IFACE_MODE_TAGGED,
|
||||||
'tagged_vlans': [self.vlan1.id],
|
|
||||||
'untagged_vlan': self.vlan2.id,
|
'untagged_vlan': self.vlan2.id,
|
||||||
|
'tagged_vlans': [self.vlan1.id],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2399,15 +2398,10 @@ class InterfaceTest(HttpStatusMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(Interface.objects.count(), 6)
|
self.assertEqual(Interface.objects.count(), 6)
|
||||||
self.assertEqual(response.data[0]['name'], data[0]['name'])
|
for i in range(0, 3):
|
||||||
self.assertEqual(response.data[1]['name'], data[1]['name'])
|
self.assertEqual(response.data[i]['name'], data[i]['name'])
|
||||||
self.assertEqual(response.data[2]['name'], data[2]['name'])
|
self.assertEqual([v['id'] for v in response.data[i]['tagged_vlans']], data[i]['tagged_vlans'])
|
||||||
self.assertEqual(len(response.data[0]['tagged_vlans']), 1)
|
self.assertEqual(response.data[i]['untagged_vlan']['id'], data[i]['untagged_vlan'])
|
||||||
self.assertEqual(len(response.data[1]['tagged_vlans']), 1)
|
|
||||||
self.assertEqual(len(response.data[2]['tagged_vlans']), 1)
|
|
||||||
self.assertEqual(response.data[0]['untagged_vlan'], self.vlan2.id)
|
|
||||||
self.assertEqual(response.data[1]['untagged_vlan'], self.vlan2.id)
|
|
||||||
self.assertEqual(response.data[2]['untagged_vlan'], self.vlan2.id)
|
|
||||||
|
|
||||||
def test_update_interface(self):
|
def test_update_interface(self):
|
||||||
|
|
||||||
@ -2852,9 +2846,9 @@ class InterfaceConnectionTest(HttpStatusMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(InterfaceConnection.objects.count(), 6)
|
self.assertEqual(InterfaceConnection.objects.count(), 6)
|
||||||
self.assertEqual(response.data[0]['interface_a'], data[0]['interface_a'])
|
for i in range(0, 3):
|
||||||
self.assertEqual(response.data[1]['interface_a'], data[1]['interface_a'])
|
self.assertEqual(response.data[i]['interface_a']['id'], data[i]['interface_a'])
|
||||||
self.assertEqual(response.data[2]['interface_a'], data[2]['interface_a'])
|
self.assertEqual(response.data[i]['interface_b']['id'], data[i]['interface_b'])
|
||||||
|
|
||||||
def test_update_interfaceconnection(self):
|
def test_update_interfaceconnection(self):
|
||||||
|
|
||||||
@ -3052,12 +3046,9 @@ class VirtualChassisTest(HttpStatusMixin, APITestCase):
|
|||||||
response = self.client.post(url, data, format='json', **self.header)
|
response = self.client.post(url, data, format='json', **self.header)
|
||||||
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
self.assertHttpStatus(response, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(VirtualChassis.objects.count(), 5)
|
self.assertEqual(VirtualChassis.objects.count(), 5)
|
||||||
self.assertEqual(response.data[0]['master'], data[0]['master'])
|
for i in range(0, 3):
|
||||||
self.assertEqual(response.data[0]['domain'], data[0]['domain'])
|
self.assertEqual(response.data[i]['master']['id'], data[i]['master'])
|
||||||
self.assertEqual(response.data[1]['master'], data[1]['master'])
|
self.assertEqual(response.data[i]['domain'], data[i]['domain'])
|
||||||
self.assertEqual(response.data[1]['domain'], data[1]['domain'])
|
|
||||||
self.assertEqual(response.data[2]['master'], data[2]['master'])
|
|
||||||
self.assertEqual(response.data[2]['domain'], data[2]['domain'])
|
|
||||||
|
|
||||||
def test_update_virtualchassis(self):
|
def test_update_virtualchassis(self):
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from utilities.api import ChoiceFieldSerializer, ContentTypeFieldSerializer, Val
|
|||||||
# Graphs
|
# Graphs
|
||||||
#
|
#
|
||||||
|
|
||||||
class GraphSerializer(serializers.ModelSerializer):
|
class GraphSerializer(ValidatedModelSerializer):
|
||||||
type = ChoiceFieldSerializer(choices=GRAPH_TYPE_CHOICES)
|
type = ChoiceFieldSerializer(choices=GRAPH_TYPE_CHOICES)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -23,13 +23,6 @@ class GraphSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'type', 'weight', 'name', 'source', 'link']
|
fields = ['id', 'type', 'weight', 'name', 'source', 'link']
|
||||||
|
|
||||||
|
|
||||||
class WritableGraphSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Graph
|
|
||||||
fields = ['id', 'type', 'weight', 'name', 'source', 'link']
|
|
||||||
|
|
||||||
|
|
||||||
class RenderedGraphSerializer(serializers.ModelSerializer):
|
class RenderedGraphSerializer(serializers.ModelSerializer):
|
||||||
embed_url = serializers.SerializerMethodField()
|
embed_url = serializers.SerializerMethodField()
|
||||||
embed_link = serializers.SerializerMethodField()
|
embed_link = serializers.SerializerMethodField()
|
||||||
@ -50,7 +43,7 @@ class RenderedGraphSerializer(serializers.ModelSerializer):
|
|||||||
# Export templates
|
# Export templates
|
||||||
#
|
#
|
||||||
|
|
||||||
class ExportTemplateSerializer(serializers.ModelSerializer):
|
class ExportTemplateSerializer(ValidatedModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExportTemplate
|
model = ExportTemplate
|
||||||
@ -61,7 +54,7 @@ class ExportTemplateSerializer(serializers.ModelSerializer):
|
|||||||
# Topology maps
|
# Topology maps
|
||||||
#
|
#
|
||||||
|
|
||||||
class TopologyMapSerializer(serializers.ModelSerializer):
|
class TopologyMapSerializer(ValidatedModelSerializer):
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -69,23 +62,34 @@ class TopologyMapSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug', 'site', 'device_patterns', 'description']
|
fields = ['id', 'name', 'slug', 'site', 'device_patterns', 'description']
|
||||||
|
|
||||||
|
|
||||||
class WritableTopologyMapSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = TopologyMap
|
|
||||||
fields = ['id', 'name', 'slug', 'site', 'device_patterns', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Image attachments
|
# Image attachments
|
||||||
#
|
#
|
||||||
|
|
||||||
class ImageAttachmentSerializer(serializers.ModelSerializer):
|
class ImageAttachmentSerializer(ValidatedModelSerializer):
|
||||||
parent = serializers.SerializerMethodField()
|
content_type = ContentTypeFieldSerializer()
|
||||||
|
parent = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ImageAttachment
|
model = ImageAttachment
|
||||||
fields = ['id', 'parent', 'name', 'image', 'image_height', 'image_width', 'created']
|
fields = [
|
||||||
|
'id', 'content_type', 'object_id', 'parent', 'name', 'image', 'image_height', 'image_width', 'created',
|
||||||
|
]
|
||||||
|
|
||||||
|
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(ImageAttachmentSerializer, self).validate(data)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
def get_parent(self, obj):
|
def get_parent(self, obj):
|
||||||
|
|
||||||
@ -102,29 +106,6 @@ class ImageAttachmentSerializer(serializers.ModelSerializer):
|
|||||||
return serializer(obj.parent, context={'request': self.context['request']}).data
|
return serializer(obj.parent, context={'request': self.context['request']}).data
|
||||||
|
|
||||||
|
|
||||||
class WritableImageAttachmentSerializer(ValidatedModelSerializer):
|
|
||||||
content_type = ContentTypeFieldSerializer()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ImageAttachment
|
|
||||||
fields = ['id', 'content_type', 'object_id', '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(WritableImageAttachmentSerializer, self).validate(data)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Reports
|
# Reports
|
||||||
#
|
#
|
||||||
|
@ -67,7 +67,6 @@ class CustomFieldModelViewSet(ModelViewSet):
|
|||||||
class GraphViewSet(ModelViewSet):
|
class GraphViewSet(ModelViewSet):
|
||||||
queryset = Graph.objects.all()
|
queryset = Graph.objects.all()
|
||||||
serializer_class = serializers.GraphSerializer
|
serializer_class = serializers.GraphSerializer
|
||||||
write_serializer_class = serializers.WritableGraphSerializer
|
|
||||||
filter_class = filters.GraphFilter
|
filter_class = filters.GraphFilter
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +87,6 @@ class ExportTemplateViewSet(ModelViewSet):
|
|||||||
class TopologyMapViewSet(ModelViewSet):
|
class TopologyMapViewSet(ModelViewSet):
|
||||||
queryset = TopologyMap.objects.select_related('site')
|
queryset = TopologyMap.objects.select_related('site')
|
||||||
serializer_class = serializers.TopologyMapSerializer
|
serializer_class = serializers.TopologyMapSerializer
|
||||||
write_serializer_class = serializers.WritableTopologyMapSerializer
|
|
||||||
filter_class = filters.TopologyMapFilter
|
filter_class = filters.TopologyMapFilter
|
||||||
|
|
||||||
@detail_route()
|
@detail_route()
|
||||||
@ -118,7 +116,6 @@ class TopologyMapViewSet(ModelViewSet):
|
|||||||
class ImageAttachmentViewSet(ModelViewSet):
|
class ImageAttachmentViewSet(ModelViewSet):
|
||||||
queryset = ImageAttachment.objects.all()
|
queryset = ImageAttachment.objects.all()
|
||||||
serializer_class = serializers.ImageAttachmentSerializer
|
serializer_class = serializers.ImageAttachmentSerializer
|
||||||
write_serializer_class = serializers.WritableImageAttachmentSerializer
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -14,7 +14,7 @@ from ipam.constants import (
|
|||||||
)
|
)
|
||||||
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
||||||
from tenancy.api.serializers import NestedTenantSerializer
|
from tenancy.api.serializers import NestedTenantSerializer
|
||||||
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
|
from utilities.api import ChoiceFieldSerializer, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer
|
||||||
from virtualization.api.serializers import NestedVirtualMachineSerializer
|
from virtualization.api.serializers import NestedVirtualMachineSerializer
|
||||||
|
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ from virtualization.api.serializers import NestedVirtualMachineSerializer
|
|||||||
#
|
#
|
||||||
|
|
||||||
class VRFSerializer(CustomFieldModelSerializer):
|
class VRFSerializer(CustomFieldModelSerializer):
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VRF
|
model = VRF
|
||||||
@ -33,7 +33,7 @@ class VRFSerializer(CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedVRFSerializer(serializers.ModelSerializer):
|
class NestedVRFSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -41,15 +41,6 @@ class NestedVRFSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'rd']
|
fields = ['id', 'url', 'name', 'rd']
|
||||||
|
|
||||||
|
|
||||||
class WritableVRFSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VRF
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Roles
|
# Roles
|
||||||
#
|
#
|
||||||
@ -61,7 +52,7 @@ class RoleSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug', 'weight']
|
fields = ['id', 'name', 'slug', 'weight']
|
||||||
|
|
||||||
|
|
||||||
class NestedRoleSerializer(serializers.ModelSerializer):
|
class NestedRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -80,7 +71,7 @@ class RIRSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug', 'is_private']
|
fields = ['id', 'name', 'slug', 'is_private']
|
||||||
|
|
||||||
|
|
||||||
class NestedRIRSerializer(serializers.ModelSerializer):
|
class NestedRIRSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -100,9 +91,10 @@ class AggregateSerializer(CustomFieldModelSerializer):
|
|||||||
fields = [
|
fields = [
|
||||||
'id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_fields', 'created', 'last_updated',
|
'id', 'family', 'prefix', 'rir', 'date_added', 'description', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
read_only_fields = ['family']
|
||||||
|
|
||||||
|
|
||||||
class NestedAggregateSerializer(serializers.ModelSerializer):
|
class NestedAggregateSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
|
||||||
|
|
||||||
class Meta(AggregateSerializer.Meta):
|
class Meta(AggregateSerializer.Meta):
|
||||||
@ -110,34 +102,12 @@ class NestedAggregateSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'family', 'prefix']
|
fields = ['id', 'url', 'family', 'prefix']
|
||||||
|
|
||||||
|
|
||||||
class WritableAggregateSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Aggregate
|
|
||||||
fields = ['id', 'prefix', 'rir', 'date_added', 'description', 'custom_fields', 'created', 'last_updated']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# VLAN groups
|
# VLAN groups
|
||||||
#
|
#
|
||||||
|
|
||||||
class VLANGroupSerializer(serializers.ModelSerializer):
|
class VLANGroupSerializer(ValidatedModelSerializer):
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VLANGroup
|
|
||||||
fields = ['id', 'name', 'slug', 'site']
|
|
||||||
|
|
||||||
|
|
||||||
class NestedVLANGroupSerializer(serializers.ModelSerializer):
|
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VLANGroup
|
|
||||||
fields = ['id', 'url', 'name', 'slug']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableVLANGroupSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLANGroup
|
model = VLANGroup
|
||||||
@ -154,21 +124,29 @@ class WritableVLANGroupSerializer(serializers.ModelSerializer):
|
|||||||
validator(data)
|
validator(data)
|
||||||
|
|
||||||
# Enforce model validation
|
# Enforce model validation
|
||||||
super(WritableVLANGroupSerializer, self).validate(data)
|
super(VLANGroupSerializer, self).validate(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class NestedVLANGroupSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VLANGroup
|
||||||
|
fields = ['id', 'url', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# VLANs
|
# VLANs
|
||||||
#
|
#
|
||||||
|
|
||||||
class VLANSerializer(CustomFieldModelSerializer):
|
class VLANSerializer(CustomFieldModelSerializer):
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||||
group = NestedVLANGroupSerializer()
|
group = NestedVLANGroupSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
status = ChoiceFieldSerializer(choices=VLAN_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=VLAN_STATUS_CHOICES, required=False)
|
||||||
role = NestedRoleSerializer()
|
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLAN
|
model = VLAN
|
||||||
@ -176,24 +154,6 @@ class VLANSerializer(CustomFieldModelSerializer):
|
|||||||
'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name',
|
'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'display_name',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedVLANSerializer(serializers.ModelSerializer):
|
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VLAN
|
|
||||||
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableVLANSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VLAN
|
|
||||||
fields = [
|
|
||||||
'id', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'custom_fields', 'created',
|
|
||||||
'last_updated',
|
|
||||||
]
|
|
||||||
validators = []
|
validators = []
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
@ -206,22 +166,30 @@ class WritableVLANSerializer(CustomFieldModelSerializer):
|
|||||||
validator(data)
|
validator(data)
|
||||||
|
|
||||||
# Enforce model validation
|
# Enforce model validation
|
||||||
super(WritableVLANSerializer, self).validate(data)
|
super(VLANSerializer, self).validate(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class NestedVLANSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VLAN
|
||||||
|
fields = ['id', 'url', 'vid', 'name', 'display_name']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prefixes
|
# Prefixes
|
||||||
#
|
#
|
||||||
|
|
||||||
class PrefixSerializer(CustomFieldModelSerializer):
|
class PrefixSerializer(CustomFieldModelSerializer):
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||||
vrf = NestedVRFSerializer()
|
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
vlan = NestedVLANSerializer()
|
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||||
status = ChoiceFieldSerializer(choices=PREFIX_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=PREFIX_STATUS_CHOICES, required=False)
|
||||||
role = NestedRoleSerializer()
|
role = NestedRoleSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Prefix
|
model = Prefix
|
||||||
@ -229,9 +197,10 @@ class PrefixSerializer(CustomFieldModelSerializer):
|
|||||||
'id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
|
'id', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
|
||||||
'custom_fields', 'created', 'last_updated',
|
'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
read_only_fields = ['family']
|
||||||
|
|
||||||
|
|
||||||
class NestedPrefixSerializer(serializers.ModelSerializer):
|
class NestedPrefixSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -239,16 +208,6 @@ class NestedPrefixSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'family', 'prefix']
|
fields = ['id', 'url', 'family', 'prefix']
|
||||||
|
|
||||||
|
|
||||||
class WritablePrefixSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Prefix
|
|
||||||
fields = [
|
|
||||||
'id', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool', 'description',
|
|
||||||
'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class AvailablePrefixSerializer(serializers.Serializer):
|
class AvailablePrefixSerializer(serializers.Serializer):
|
||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
@ -288,11 +247,11 @@ class IPAddressInterfaceSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class IPAddressSerializer(CustomFieldModelSerializer):
|
class IPAddressSerializer(CustomFieldModelSerializer):
|
||||||
vrf = NestedVRFSerializer()
|
vrf = NestedVRFSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
status = ChoiceFieldSerializer(choices=IPADDRESS_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=IPADDRESS_STATUS_CHOICES, required=False)
|
||||||
role = ChoiceFieldSerializer(choices=IPADDRESS_ROLE_CHOICES)
|
role = ChoiceFieldSerializer(choices=IPADDRESS_ROLE_CHOICES, required=False)
|
||||||
interface = IPAddressInterfaceSerializer()
|
interface = IPAddressInterfaceSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
@ -300,9 +259,10 @@ class IPAddressSerializer(CustomFieldModelSerializer):
|
|||||||
'id', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'interface', 'description', 'nat_inside',
|
'id', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'interface', 'description', 'nat_inside',
|
||||||
'nat_outside', 'custom_fields', 'created', 'last_updated',
|
'nat_outside', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
read_only_fields = ['family']
|
||||||
|
|
||||||
|
|
||||||
class NestedIPAddressSerializer(serializers.ModelSerializer):
|
class NestedIPAddressSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -310,18 +270,8 @@ class NestedIPAddressSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'family', 'address']
|
fields = ['id', 'url', 'family', 'address']
|
||||||
|
|
||||||
|
|
||||||
IPAddressSerializer._declared_fields['nat_inside'] = NestedIPAddressSerializer()
|
IPAddressSerializer._declared_fields['nat_inside'] = NestedIPAddressSerializer(required=False, allow_null=True)
|
||||||
IPAddressSerializer._declared_fields['nat_outside'] = NestedIPAddressSerializer()
|
IPAddressSerializer._declared_fields['nat_outside'] = NestedIPAddressSerializer(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class WritableIPAddressSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = IPAddress
|
|
||||||
fields = [
|
|
||||||
'id', 'address', 'vrf', 'tenant', 'status', 'role', 'interface', 'description', 'nat_inside',
|
|
||||||
'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class AvailableIPSerializer(serializers.Serializer):
|
class AvailableIPSerializer(serializers.Serializer):
|
||||||
@ -342,22 +292,16 @@ class AvailableIPSerializer(serializers.Serializer):
|
|||||||
# Services
|
# Services
|
||||||
#
|
#
|
||||||
|
|
||||||
class ServiceSerializer(serializers.ModelSerializer):
|
class ServiceSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer(required=False, allow_null=True)
|
||||||
virtual_machine = NestedVirtualMachineSerializer()
|
virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)
|
||||||
protocol = ChoiceFieldSerializer(choices=IP_PROTOCOL_CHOICES)
|
protocol = ChoiceFieldSerializer(choices=IP_PROTOCOL_CHOICES)
|
||||||
ipaddresses = NestedIPAddressSerializer(many=True)
|
ipaddresses = SerializedPKRelatedField(
|
||||||
|
queryset=IPAddress.objects.all(),
|
||||||
class Meta:
|
serializer=NestedIPAddressSerializer,
|
||||||
model = Service
|
required=False,
|
||||||
fields = [
|
many=True
|
||||||
'id', 'device', 'virtual_machine', 'name', 'port', 'protocol', 'ipaddresses', 'description', 'created',
|
)
|
||||||
'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Figure out how to use model validation with ManyToManyFields. Calling clean() yields a ValueError.
|
|
||||||
class WritableServiceSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Service
|
model = Service
|
||||||
|
@ -35,7 +35,6 @@ class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
|
|||||||
class VRFViewSet(CustomFieldModelViewSet):
|
class VRFViewSet(CustomFieldModelViewSet):
|
||||||
queryset = VRF.objects.select_related('tenant')
|
queryset = VRF.objects.select_related('tenant')
|
||||||
serializer_class = serializers.VRFSerializer
|
serializer_class = serializers.VRFSerializer
|
||||||
write_serializer_class = serializers.WritableVRFSerializer
|
|
||||||
filter_class = filters.VRFFilter
|
filter_class = filters.VRFFilter
|
||||||
|
|
||||||
|
|
||||||
@ -56,7 +55,6 @@ class RIRViewSet(ModelViewSet):
|
|||||||
class AggregateViewSet(CustomFieldModelViewSet):
|
class AggregateViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Aggregate.objects.select_related('rir')
|
queryset = Aggregate.objects.select_related('rir')
|
||||||
serializer_class = serializers.AggregateSerializer
|
serializer_class = serializers.AggregateSerializer
|
||||||
write_serializer_class = serializers.WritableAggregateSerializer
|
|
||||||
filter_class = filters.AggregateFilter
|
filter_class = filters.AggregateFilter
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +75,6 @@ class RoleViewSet(ModelViewSet):
|
|||||||
class PrefixViewSet(CustomFieldModelViewSet):
|
class PrefixViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
|
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
|
||||||
serializer_class = serializers.PrefixSerializer
|
serializer_class = serializers.PrefixSerializer
|
||||||
write_serializer_class = serializers.WritablePrefixSerializer
|
|
||||||
filter_class = filters.PrefixFilter
|
filter_class = filters.PrefixFilter
|
||||||
|
|
||||||
@detail_route(url_path='available-prefixes', methods=['get', 'post'])
|
@detail_route(url_path='available-prefixes', methods=['get', 'post'])
|
||||||
@ -120,9 +117,9 @@ class PrefixViewSet(CustomFieldModelViewSet):
|
|||||||
|
|
||||||
# Initialize the serializer with a list or a single object depending on what was requested
|
# Initialize the serializer with a list or a single object depending on what was requested
|
||||||
if isinstance(request.data, list):
|
if isinstance(request.data, list):
|
||||||
serializer = serializers.WritablePrefixSerializer(data=requested_prefixes, many=True)
|
serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True)
|
||||||
else:
|
else:
|
||||||
serializer = serializers.WritablePrefixSerializer(data=requested_prefixes[0])
|
serializer = serializers.PrefixSerializer(data=requested_prefixes[0])
|
||||||
|
|
||||||
# Create the new Prefix(es)
|
# Create the new Prefix(es)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
@ -177,9 +174,9 @@ class PrefixViewSet(CustomFieldModelViewSet):
|
|||||||
|
|
||||||
# Initialize the serializer with a list or a single object depending on what was requested
|
# Initialize the serializer with a list or a single object depending on what was requested
|
||||||
if isinstance(request.data, list):
|
if isinstance(request.data, list):
|
||||||
serializer = serializers.WritableIPAddressSerializer(data=requested_ips, many=True)
|
serializer = serializers.IPAddressSerializer(data=requested_ips, many=True)
|
||||||
else:
|
else:
|
||||||
serializer = serializers.WritableIPAddressSerializer(data=requested_ips[0])
|
serializer = serializers.IPAddressSerializer(data=requested_ips[0])
|
||||||
|
|
||||||
# Create the new IP address(es)
|
# Create the new IP address(es)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
@ -223,7 +220,6 @@ class IPAddressViewSet(CustomFieldModelViewSet):
|
|||||||
'nat_outside'
|
'nat_outside'
|
||||||
)
|
)
|
||||||
serializer_class = serializers.IPAddressSerializer
|
serializer_class = serializers.IPAddressSerializer
|
||||||
write_serializer_class = serializers.WritableIPAddressSerializer
|
|
||||||
filter_class = filters.IPAddressFilter
|
filter_class = filters.IPAddressFilter
|
||||||
|
|
||||||
|
|
||||||
@ -234,7 +230,6 @@ class IPAddressViewSet(CustomFieldModelViewSet):
|
|||||||
class VLANGroupViewSet(ModelViewSet):
|
class VLANGroupViewSet(ModelViewSet):
|
||||||
queryset = VLANGroup.objects.select_related('site')
|
queryset = VLANGroup.objects.select_related('site')
|
||||||
serializer_class = serializers.VLANGroupSerializer
|
serializer_class = serializers.VLANGroupSerializer
|
||||||
write_serializer_class = serializers.WritableVLANGroupSerializer
|
|
||||||
filter_class = filters.VLANGroupFilter
|
filter_class = filters.VLANGroupFilter
|
||||||
|
|
||||||
|
|
||||||
@ -245,7 +240,6 @@ class VLANGroupViewSet(ModelViewSet):
|
|||||||
class VLANViewSet(CustomFieldModelViewSet):
|
class VLANViewSet(CustomFieldModelViewSet):
|
||||||
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
|
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
|
||||||
serializer_class = serializers.VLANSerializer
|
serializer_class = serializers.VLANSerializer
|
||||||
write_serializer_class = serializers.WritableVLANSerializer
|
|
||||||
filter_class = filters.VLANFilter
|
filter_class = filters.VLANFilter
|
||||||
|
|
||||||
|
|
||||||
@ -256,5 +250,4 @@ class VLANViewSet(CustomFieldModelViewSet):
|
|||||||
class ServiceViewSet(ModelViewSet):
|
class ServiceViewSet(ModelViewSet):
|
||||||
queryset = Service.objects.select_related('device')
|
queryset = Service.objects.select_related('device')
|
||||||
serializer_class = serializers.ServiceSerializer
|
serializer_class = serializers.ServiceSerializer
|
||||||
write_serializer_class = serializers.WritableServiceSerializer
|
|
||||||
filter_class = filters.ServiceFilter
|
filter_class = filters.ServiceFilter
|
||||||
|
@ -5,7 +5,7 @@ from rest_framework.validators import UniqueTogetherValidator
|
|||||||
|
|
||||||
from dcim.api.serializers import NestedDeviceSerializer
|
from dcim.api.serializers import NestedDeviceSerializer
|
||||||
from secrets.models import Secret, SecretRole
|
from secrets.models import Secret, SecretRole
|
||||||
from utilities.api import ValidatedModelSerializer
|
from utilities.api import ValidatedModelSerializer, WritableNestedSerializer
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -19,7 +19,7 @@ class SecretRoleSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedSecretRoleSerializer(serializers.ModelSerializer):
|
class NestedSecretRoleSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secretrole-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secretrole-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -31,16 +31,9 @@ class NestedSecretRoleSerializer(serializers.ModelSerializer):
|
|||||||
# Secrets
|
# Secrets
|
||||||
#
|
#
|
||||||
|
|
||||||
class SecretSerializer(serializers.ModelSerializer):
|
class SecretSerializer(ValidatedModelSerializer):
|
||||||
device = NestedDeviceSerializer()
|
device = NestedDeviceSerializer()
|
||||||
role = NestedSecretRoleSerializer()
|
role = NestedSecretRoleSerializer()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Secret
|
|
||||||
fields = ['id', 'device', 'role', 'name', 'plaintext', 'hash', 'created', 'last_updated']
|
|
||||||
|
|
||||||
|
|
||||||
class WritableSecretSerializer(serializers.ModelSerializer):
|
|
||||||
plaintext = serializers.CharField()
|
plaintext = serializers.CharField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -64,6 +57,6 @@ class WritableSecretSerializer(serializers.ModelSerializer):
|
|||||||
validator(data)
|
validator(data)
|
||||||
|
|
||||||
# Enforce model validation
|
# Enforce model validation
|
||||||
super(WritableSecretSerializer, self).validate(data)
|
super(SecretSerializer, self).validate(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -51,7 +51,6 @@ class SecretViewSet(ModelViewSet):
|
|||||||
'role__users', 'role__groups',
|
'role__users', 'role__groups',
|
||||||
)
|
)
|
||||||
serializer_class = serializers.SecretSerializer
|
serializer_class = serializers.SecretSerializer
|
||||||
write_serializer_class = serializers.WritableSecretSerializer
|
|
||||||
filter_class = filters.SecretFilter
|
filter_class = filters.SecretFilter
|
||||||
|
|
||||||
master_key = None
|
master_key = None
|
||||||
|
@ -4,7 +4,7 @@ from rest_framework import serializers
|
|||||||
|
|
||||||
from extras.api.customfields import CustomFieldModelSerializer
|
from extras.api.customfields import CustomFieldModelSerializer
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.api import ValidatedModelSerializer
|
from utilities.api import ValidatedModelSerializer, WritableNestedSerializer
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -18,7 +18,7 @@ class TenantGroupSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedTenantGroupSerializer(serializers.ModelSerializer):
|
class NestedTenantGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -31,23 +31,16 @@ class NestedTenantGroupSerializer(serializers.ModelSerializer):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class TenantSerializer(CustomFieldModelSerializer):
|
class TenantSerializer(CustomFieldModelSerializer):
|
||||||
group = NestedTenantGroupSerializer()
|
group = NestedTenantGroupSerializer(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tenant
|
model = Tenant
|
||||||
fields = ['id', 'name', 'slug', 'group', 'description', 'comments', 'custom_fields', 'created', 'last_updated']
|
fields = ['id', 'name', 'slug', 'group', 'description', 'comments', 'custom_fields', 'created', 'last_updated']
|
||||||
|
|
||||||
|
|
||||||
class NestedTenantSerializer(serializers.ModelSerializer):
|
class NestedTenantSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tenant
|
model = Tenant
|
||||||
fields = ['id', 'url', 'name', 'slug']
|
fields = ['id', 'url', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class WritableTenantSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Tenant
|
|
||||||
fields = ['id', 'name', 'slug', 'group', 'description', 'comments', 'custom_fields', 'created', 'last_updated']
|
|
||||||
|
@ -32,5 +32,4 @@ class TenantGroupViewSet(ModelViewSet):
|
|||||||
class TenantViewSet(CustomFieldModelViewSet):
|
class TenantViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Tenant.objects.select_related('group')
|
queryset = Tenant.objects.select_related('group')
|
||||||
serializer_class = serializers.TenantSerializer
|
serializer_class = serializers.TenantSerializer
|
||||||
write_serializer_class = serializers.WritableTenantSerializer
|
|
||||||
filter_class = filters.TenantFilter
|
filter_class = filters.TenantFilter
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from rest_framework import serializers
|
|
||||||
|
from utilities.api import WritableNestedSerializer
|
||||||
|
|
||||||
|
|
||||||
class NestedUserSerializer(serializers.ModelSerializer):
|
class NestedUserSerializer(WritableNestedSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -5,11 +5,13 @@ import pytz
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models import ManyToManyField
|
from django.db.models import ManyToManyField
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from rest_framework import mixins
|
from rest_framework import mixins
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
from rest_framework.permissions import BasePermission
|
from rest_framework.permissions import BasePermission
|
||||||
|
from rest_framework.relations import PrimaryKeyRelatedField
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import Field, ModelSerializer, ValidationError
|
from rest_framework.serializers import Field, ModelSerializer, ValidationError
|
||||||
from rest_framework.viewsets import GenericViewSet, ViewSet
|
from rest_framework.viewsets import GenericViewSet, ViewSet
|
||||||
@ -36,6 +38,77 @@ class IsAuthenticatedOrLoginNotRequired(BasePermission):
|
|||||||
return request.user.is_authenticated
|
return request.user.is_authenticated
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Fields
|
||||||
|
#
|
||||||
|
|
||||||
|
class ChoiceFieldSerializer(Field):
|
||||||
|
"""
|
||||||
|
Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
|
||||||
|
"""
|
||||||
|
def __init__(self, choices, **kwargs):
|
||||||
|
self._choices = dict()
|
||||||
|
for k, v in choices:
|
||||||
|
# Unpack grouped choices
|
||||||
|
if type(v) in [list, tuple]:
|
||||||
|
for k2, v2 in v:
|
||||||
|
self._choices[k2] = v2
|
||||||
|
else:
|
||||||
|
self._choices[k] = v
|
||||||
|
super(ChoiceFieldSerializer, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def to_representation(self, obj):
|
||||||
|
return {'value': obj, 'label': self._choices[obj]}
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeFieldSerializer(Field):
|
||||||
|
"""
|
||||||
|
Represent a ContentType as '<app_label>.<model>'
|
||||||
|
"""
|
||||||
|
def to_representation(self, obj):
|
||||||
|
return "{}.{}".format(obj.app_label, obj.model)
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
app_label, model = data.split('.')
|
||||||
|
try:
|
||||||
|
return ContentType.objects.get_by_natural_key(app_label=app_label, model=model)
|
||||||
|
except ContentType.DoesNotExist:
|
||||||
|
raise ValidationError("Invalid content type")
|
||||||
|
|
||||||
|
|
||||||
|
class TimeZoneField(Field):
|
||||||
|
"""
|
||||||
|
Represent a pytz time zone.
|
||||||
|
"""
|
||||||
|
def to_representation(self, obj):
|
||||||
|
return obj.zone if obj else None
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
if not data:
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
return pytz.timezone(str(data))
|
||||||
|
except pytz.exceptions.UnknownTimeZoneError:
|
||||||
|
raise ValidationError('Invalid time zone "{}"'.format(data))
|
||||||
|
|
||||||
|
|
||||||
|
class SerializedPKRelatedField(PrimaryKeyRelatedField):
|
||||||
|
"""
|
||||||
|
Extends PrimaryKeyRelatedField to return a serialized object on read. This is useful for representing related
|
||||||
|
objects in a ManyToManyField while still allowing a set of primary keys to be written.
|
||||||
|
"""
|
||||||
|
def __init__(self, serializer, **kwargs):
|
||||||
|
self.serializer = serializer
|
||||||
|
self.pk_field = kwargs.pop('pk_field', None)
|
||||||
|
super(SerializedPKRelatedField, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def to_representation(self, value):
|
||||||
|
return self.serializer(value, context={'request': self.context['request']}).data
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Serializers
|
# Serializers
|
||||||
#
|
#
|
||||||
@ -67,58 +140,17 @@ class ValidatedModelSerializer(ModelSerializer):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class ChoiceFieldSerializer(Field):
|
class WritableNestedSerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Represent a ChoiceField as {'value': <DB value>, 'label': <string>}.
|
Returns a nested representation of an object on read, but accepts only a primary key on write.
|
||||||
"""
|
"""
|
||||||
def __init__(self, choices, **kwargs):
|
|
||||||
self._choices = dict()
|
|
||||||
for k, v in choices:
|
|
||||||
# Unpack grouped choices
|
|
||||||
if type(v) in [list, tuple]:
|
|
||||||
for k2, v2 in v:
|
|
||||||
self._choices[k2] = v2
|
|
||||||
else:
|
|
||||||
self._choices[k] = v
|
|
||||||
super(ChoiceFieldSerializer, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
def to_representation(self, obj):
|
|
||||||
return {'value': obj, 'label': self._choices[obj]}
|
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
return self._choices.get(data)
|
if data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
class ContentTypeFieldSerializer(Field):
|
|
||||||
"""
|
|
||||||
Represent a ContentType as '<app_label>.<model>'
|
|
||||||
"""
|
|
||||||
def to_representation(self, obj):
|
|
||||||
return "{}.{}".format(obj.app_label, obj.model)
|
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
|
||||||
app_label, model = data.split('.')
|
|
||||||
try:
|
try:
|
||||||
return ContentType.objects.get_by_natural_key(app_label=app_label, model=model)
|
return self.Meta.model.objects.get(pk=data)
|
||||||
except ContentType.DoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
raise ValidationError("Invalid content type")
|
raise ValidationError("Invalid ID")
|
||||||
|
|
||||||
|
|
||||||
class TimeZoneField(Field):
|
|
||||||
"""
|
|
||||||
Represent a pytz time zone.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def to_representation(self, obj):
|
|
||||||
return obj.zone if obj else None
|
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
|
||||||
if not data:
|
|
||||||
return ""
|
|
||||||
try:
|
|
||||||
return pytz.timezone(str(data))
|
|
||||||
except pytz.exceptions.UnknownTimeZoneError:
|
|
||||||
raise ValidationError('Invalid time zone "{}"'.format(data))
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -132,16 +164,8 @@ class ModelViewSet(mixins.CreateModelMixin,
|
|||||||
mixins.ListModelMixin,
|
mixins.ListModelMixin,
|
||||||
GenericViewSet):
|
GenericViewSet):
|
||||||
"""
|
"""
|
||||||
Substitute DRF's built-in ModelViewSet for our own, which introduces a bit of additional functionality:
|
Accept either a single object or a list of objects to create.
|
||||||
1. Use an alternate serializer (if provided) for write operations
|
|
||||||
2. Accept either a single object or a list of objects to create
|
|
||||||
"""
|
"""
|
||||||
def get_serializer_class(self):
|
|
||||||
# Check for a different serializer to use for write operations
|
|
||||||
if self.action in WRITE_OPERATIONS and hasattr(self, 'write_serializer_class'):
|
|
||||||
return self.write_serializer_class
|
|
||||||
return self.serializer_class
|
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
# If a list of objects has been provided, initialize the serializer with many=True
|
# If a list of objects has been provided, initialize the serializer with many=True
|
||||||
if isinstance(kwargs.get('data', {}), list):
|
if isinstance(kwargs.get('data', {}), list):
|
||||||
|
@ -8,7 +8,7 @@ from dcim.models import Interface
|
|||||||
from extras.api.customfields import CustomFieldModelSerializer
|
from extras.api.customfields import CustomFieldModelSerializer
|
||||||
from ipam.models import IPAddress
|
from ipam.models import IPAddress
|
||||||
from tenancy.api.serializers import NestedTenantSerializer
|
from tenancy.api.serializers import NestedTenantSerializer
|
||||||
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
|
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer, WritableNestedSerializer
|
||||||
from virtualization.constants import VM_STATUS_CHOICES
|
from virtualization.constants import VM_STATUS_CHOICES
|
||||||
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ class ClusterTypeSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedClusterTypeSerializer(serializers.ModelSerializer):
|
class NestedClusterTypeSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -43,7 +43,7 @@ class ClusterGroupSerializer(ValidatedModelSerializer):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class NestedClusterGroupSerializer(serializers.ModelSerializer):
|
class NestedClusterGroupSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -57,15 +57,15 @@ class NestedClusterGroupSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class ClusterSerializer(CustomFieldModelSerializer):
|
class ClusterSerializer(CustomFieldModelSerializer):
|
||||||
type = NestedClusterTypeSerializer()
|
type = NestedClusterTypeSerializer()
|
||||||
group = NestedClusterGroupSerializer()
|
group = NestedClusterGroupSerializer(required=False, allow_null=True)
|
||||||
site = NestedSiteSerializer()
|
site = NestedSiteSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cluster
|
model = Cluster
|
||||||
fields = ['id', 'name', 'type', 'group', 'site', 'comments', 'custom_fields', 'created', 'last_updated']
|
fields = ['id', 'name', 'type', 'group', 'site', 'comments', 'custom_fields', 'created', 'last_updated']
|
||||||
|
|
||||||
|
|
||||||
class NestedClusterSerializer(serializers.ModelSerializer):
|
class NestedClusterSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -73,13 +73,6 @@ class NestedClusterSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name']
|
fields = ['id', 'url', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableClusterSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Cluster
|
|
||||||
fields = ['id', 'name', 'type', 'group', 'site', 'comments', 'custom_fields', 'created', 'last_updated']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Virtual machines
|
# Virtual machines
|
||||||
#
|
#
|
||||||
@ -94,14 +87,14 @@ class VirtualMachineIPAddressSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class VirtualMachineSerializer(CustomFieldModelSerializer):
|
class VirtualMachineSerializer(CustomFieldModelSerializer):
|
||||||
status = ChoiceFieldSerializer(choices=VM_STATUS_CHOICES)
|
status = ChoiceFieldSerializer(choices=VM_STATUS_CHOICES, required=False)
|
||||||
cluster = NestedClusterSerializer()
|
cluster = NestedClusterSerializer(required=False, allow_null=True)
|
||||||
role = NestedDeviceRoleSerializer()
|
role = NestedDeviceRoleSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer()
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
platform = NestedPlatformSerializer()
|
platform = NestedPlatformSerializer(required=False, allow_null=True)
|
||||||
primary_ip = VirtualMachineIPAddressSerializer()
|
primary_ip = VirtualMachineIPAddressSerializer(read_only=True)
|
||||||
primary_ip4 = VirtualMachineIPAddressSerializer()
|
primary_ip4 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
|
||||||
primary_ip6 = VirtualMachineIPAddressSerializer()
|
primary_ip6 = VirtualMachineIPAddressSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VirtualMachine
|
model = VirtualMachine
|
||||||
@ -111,7 +104,7 @@ class VirtualMachineSerializer(CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedVirtualMachineSerializer(serializers.ModelSerializer):
|
class NestedVirtualMachineSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -119,22 +112,13 @@ class NestedVirtualMachineSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'url', 'name']
|
fields = ['id', 'url', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableVirtualMachineSerializer(CustomFieldModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = VirtualMachine
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus',
|
|
||||||
'memory', 'disk', 'comments', 'custom_fields', 'created', 'last_updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# VM interfaces
|
# VM interfaces
|
||||||
#
|
#
|
||||||
|
|
||||||
class InterfaceSerializer(serializers.ModelSerializer):
|
class InterfaceSerializer(ValidatedModelSerializer):
|
||||||
virtual_machine = NestedVirtualMachineSerializer()
|
virtual_machine = NestedVirtualMachineSerializer()
|
||||||
|
form_factor = serializers.IntegerField(default=IFACE_FF_VIRTUAL)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
@ -143,19 +127,9 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NestedInterfaceSerializer(serializers.ModelSerializer):
|
class NestedInterfaceSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:interface-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:interface-detail')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ['id', 'url', 'name']
|
fields = ['id', 'url', 'name']
|
||||||
|
|
||||||
|
|
||||||
class WritableInterfaceSerializer(ValidatedModelSerializer):
|
|
||||||
form_factor = serializers.IntegerField(default=IFACE_FF_VIRTUAL)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Interface
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'virtual_machine', 'form_factor', 'enabled', 'mac_address', 'mtu', 'description',
|
|
||||||
]
|
|
||||||
|
@ -37,7 +37,6 @@ class ClusterGroupViewSet(ModelViewSet):
|
|||||||
class ClusterViewSet(CustomFieldModelViewSet):
|
class ClusterViewSet(CustomFieldModelViewSet):
|
||||||
queryset = Cluster.objects.select_related('type', 'group')
|
queryset = Cluster.objects.select_related('type', 'group')
|
||||||
serializer_class = serializers.ClusterSerializer
|
serializer_class = serializers.ClusterSerializer
|
||||||
write_serializer_class = serializers.WritableClusterSerializer
|
|
||||||
filter_class = filters.ClusterFilter
|
filter_class = filters.ClusterFilter
|
||||||
|
|
||||||
|
|
||||||
@ -48,12 +47,10 @@ class ClusterViewSet(CustomFieldModelViewSet):
|
|||||||
class VirtualMachineViewSet(CustomFieldModelViewSet):
|
class VirtualMachineViewSet(CustomFieldModelViewSet):
|
||||||
queryset = VirtualMachine.objects.all()
|
queryset = VirtualMachine.objects.all()
|
||||||
serializer_class = serializers.VirtualMachineSerializer
|
serializer_class = serializers.VirtualMachineSerializer
|
||||||
write_serializer_class = serializers.WritableVirtualMachineSerializer
|
|
||||||
filter_class = filters.VirtualMachineFilter
|
filter_class = filters.VirtualMachineFilter
|
||||||
|
|
||||||
|
|
||||||
class InterfaceViewSet(ModelViewSet):
|
class InterfaceViewSet(ModelViewSet):
|
||||||
queryset = Interface.objects.filter(virtual_machine__isnull=False).select_related('virtual_machine')
|
queryset = Interface.objects.filter(virtual_machine__isnull=False).select_related('virtual_machine')
|
||||||
serializer_class = serializers.InterfaceSerializer
|
serializer_class = serializers.InterfaceSerializer
|
||||||
write_serializer_class = serializers.WritableInterfaceSerializer
|
|
||||||
filter_class = filters.InterfaceFilter
|
filter_class = filters.InterfaceFilter
|
||||||
|
@ -5,7 +5,7 @@ django-filter>=1.1.0
|
|||||||
django-mptt>=0.9.0
|
django-mptt>=0.9.0
|
||||||
django-tables2>=1.19.0
|
django-tables2>=1.19.0
|
||||||
django-timezone-field>=2.0
|
django-timezone-field>=2.0
|
||||||
djangorestframework>=3.7.7
|
djangorestframework>=3.7.7,<3.8.2
|
||||||
drf-yasg[validation]>=1.4.4
|
drf-yasg[validation]>=1.4.4
|
||||||
graphviz>=0.8.2
|
graphviz>=0.8.2
|
||||||
Markdown>=2.6.11
|
Markdown>=2.6.11
|
||||||
|
Loading…
Reference in New Issue
Block a user