diff --git a/netbox/vapor/api/serializers.py b/netbox/vapor/api/serializers.py index 5dd1b88c5..e34bd3997 100644 --- a/netbox/vapor/api/serializers.py +++ b/netbox/vapor/api/serializers.py @@ -1,24 +1,112 @@ from rest_framework import serializers from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField +from drf_yasg.utils import swagger_serializer_method +from dcim.constants import CONNECTION_STATUS_CHOICES, IFACE_TYPE_CHOICES from extras.api.customfields import CustomFieldModelSerializer from dcim.api.nested_serializers import ( NestedDeviceSerializer, - NestedInterfaceSerializer, NestedCableSerializer, ) from dcim.api.serializers import ( InterfaceConnectionSerializer, - ConnectedEndpointSerializer, IFACE_TYPE_CHOICES, IFACE_MODE_CHOICES, ) from dcim.models import Interface -from ipam.api.nested_serializers import NestedVLANSerializer -from ipam.models import VLAN +from ipam.api.nested_serializers import NestedPrefixSerializer +from ipam.models import VLAN, Prefix from tenancy.api.nested_serializers import NestedTenantGroupSerializer -from utilities.api import ChoiceField, ValidatedModelSerializer, SerializedPKRelatedField +from utilities.api import ChoiceField, ValidatedModelSerializer, SerializedPKRelatedField, WritableNestedSerializer from tenancy.models import Tenant as Customer +from utilities.utils import dynamic_import + + +def get_serializer_for_model(model, prefix=''): + """ + Dynamically resolve and return the appropriate serializer for a model. + """ + app_name, model_name = model._meta.label.split('.') + serializer_name = '{}.api.serializers.{}{}Serializer'.format( + app_name, prefix, model_name + ) + + override_serializer_name = 'vapor.api.serializers.{}{}Serializer'.format( + prefix, model_name + ) + try: + return dynamic_import(override_serializer_name) + except AttributeError: + pass + + try: + return dynamic_import(serializer_name) + except AttributeError: + raise SerializerNotFound( + "Could not determine serializer for {}.{} with prefix '{}'".format(app_name, model_name, prefix) + ) + + +class NestedVLANSerializer(WritableNestedSerializer): + url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail') + + prefixes = SerializedPKRelatedField( + queryset=Prefix.objects.all(), + serializer=NestedPrefixSerializer, + required=False, + many=True, + ) + + class Meta: + model = VLAN + fields = ['id', 'url', 'vid', 'name', 'display_name', 'prefixes'] + + +class NestedInterfaceSerializer(WritableNestedSerializer): + device = NestedDeviceSerializer(read_only=True) + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail') + connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True) + type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False) + + untagged_vlan = NestedVLANSerializer(required=False, allow_null=True) + tagged_vlans = SerializedPKRelatedField( + queryset=VLAN.objects.all(), + serializer=NestedVLANSerializer, + required=False, + many=True, + ) + + class Meta: + model = Interface + fields = ['id', 'url', 'device', 'name', 'cable', 'connection_status', 'type', 'untagged_vlan', 'tagged_vlans'] + + +class ConnectedEndpointSerializer(ValidatedModelSerializer): + connected_endpoint_type = serializers.SerializerMethodField(read_only=True) + connected_endpoint = serializers.SerializerMethodField(read_only=True) + connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True) + + def get_connected_endpoint_type(self, obj): + if hasattr(obj, 'connected_endpoint') and obj.connected_endpoint is not None: + return '{}.{}'.format( + obj.connected_endpoint._meta.app_label, + obj.connected_endpoint._meta.model_name + ) + return None + + @swagger_serializer_method(serializer_or_field=serializers.DictField) + def get_connected_endpoint(self, obj): + """ + Return the appropriate serializer for the type of connected object. + """ + if getattr(obj, 'connected_endpoint', None) is None: + return None + + serializer = get_serializer_for_model(obj.connected_endpoint, prefix='Nested') + context = {'request': self.context['request']} + data = serializer(obj.connected_endpoint, context=context).data + + return data class CustomerSerializer(TaggitSerializer, CustomFieldModelSerializer):