serializers.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. from collections import OrderedDict
  2. from django.contrib.contenttypes.models import ContentType
  3. from drf_yasg.utils import swagger_serializer_method
  4. from rest_framework import serializers
  5. from rest_framework.validators import UniqueTogetherValidator
  6. from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerializer
  7. from ipam.choices import *
  8. from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS, VLANGROUP_SCOPE_TYPES
  9. from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
  10. from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
  11. from netbox.api.serializers import OrganizationalModelSerializer
  12. from netbox.api.serializers import PrimaryModelSerializer
  13. from tenancy.api.nested_serializers import NestedTenantSerializer
  14. from utilities.api import get_serializer_for_model
  15. from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
  16. from .nested_serializers import *
  17. #
  18. # VRFs
  19. #
  20. class VRFSerializer(PrimaryModelSerializer):
  21. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vrf-detail')
  22. tenant = NestedTenantSerializer(required=False, allow_null=True)
  23. import_targets = SerializedPKRelatedField(
  24. queryset=RouteTarget.objects.all(),
  25. serializer=NestedRouteTargetSerializer,
  26. required=False,
  27. many=True
  28. )
  29. export_targets = SerializedPKRelatedField(
  30. queryset=RouteTarget.objects.all(),
  31. serializer=NestedRouteTargetSerializer,
  32. required=False,
  33. many=True
  34. )
  35. ipaddress_count = serializers.IntegerField(read_only=True)
  36. prefix_count = serializers.IntegerField(read_only=True)
  37. class Meta:
  38. model = VRF
  39. fields = [
  40. 'id', 'url', 'display', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'import_targets',
  41. 'export_targets', 'tags', 'display_name', 'custom_fields', 'created', 'last_updated', 'ipaddress_count',
  42. 'prefix_count',
  43. ]
  44. #
  45. # Route targets
  46. #
  47. class RouteTargetSerializer(PrimaryModelSerializer):
  48. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
  49. tenant = NestedTenantSerializer(required=False, allow_null=True)
  50. class Meta:
  51. model = RouteTarget
  52. fields = [
  53. 'id', 'url', 'display', 'name', 'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  54. ]
  55. #
  56. # RIRs/aggregates
  57. #
  58. class RIRSerializer(OrganizationalModelSerializer):
  59. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:rir-detail')
  60. aggregate_count = serializers.IntegerField(read_only=True)
  61. class Meta:
  62. model = RIR
  63. fields = [
  64. 'id', 'url', 'display', 'name', 'slug', 'is_private', 'description', 'custom_fields', 'created',
  65. 'last_updated', 'aggregate_count',
  66. ]
  67. class AggregateSerializer(PrimaryModelSerializer):
  68. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:aggregate-detail')
  69. family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
  70. rir = NestedRIRSerializer()
  71. tenant = NestedTenantSerializer(required=False, allow_null=True)
  72. class Meta:
  73. model = Aggregate
  74. fields = [
  75. 'id', 'url', 'display', 'family', 'prefix', 'rir', 'tenant', 'date_added', 'description', 'tags',
  76. 'custom_fields', 'created', 'last_updated',
  77. ]
  78. read_only_fields = ['family']
  79. #
  80. # VLANs
  81. #
  82. class RoleSerializer(OrganizationalModelSerializer):
  83. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:role-detail')
  84. prefix_count = serializers.IntegerField(read_only=True)
  85. vlan_count = serializers.IntegerField(read_only=True)
  86. class Meta:
  87. model = Role
  88. fields = [
  89. 'id', 'url', 'display', 'name', 'slug', 'weight', 'description', 'custom_fields', 'created', 'last_updated',
  90. 'prefix_count', 'vlan_count',
  91. ]
  92. class VLANGroupSerializer(OrganizationalModelSerializer):
  93. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlangroup-detail')
  94. scope_type = ContentTypeField(
  95. queryset=ContentType.objects.filter(
  96. model__in=VLANGROUP_SCOPE_TYPES
  97. ),
  98. required=False
  99. )
  100. scope = serializers.SerializerMethodField(read_only=True)
  101. vlan_count = serializers.IntegerField(read_only=True)
  102. class Meta:
  103. model = VLANGroup
  104. fields = [
  105. 'id', 'url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'description', 'custom_fields',
  106. 'created', 'last_updated', 'vlan_count',
  107. ]
  108. validators = []
  109. def validate(self, data):
  110. # Validate uniqueness of name and slug if a site has been assigned.
  111. if data.get('site', None):
  112. for field in ['name', 'slug']:
  113. validator = UniqueTogetherValidator(queryset=VLANGroup.objects.all(), fields=('site', field))
  114. validator(data, self)
  115. # Enforce model validation
  116. super().validate(data)
  117. return data
  118. def get_scope(self, obj):
  119. if obj.scope_id is None:
  120. return None
  121. serializer = get_serializer_for_model(obj.scope, prefix='Nested')
  122. context = {'request': self.context['request']}
  123. return serializer(obj.scope, context=context).data
  124. class VLANSerializer(PrimaryModelSerializer):
  125. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
  126. site = NestedSiteSerializer(required=False, allow_null=True)
  127. group = NestedVLANGroupSerializer(required=False, allow_null=True)
  128. tenant = NestedTenantSerializer(required=False, allow_null=True)
  129. status = ChoiceField(choices=VLANStatusChoices, required=False)
  130. role = NestedRoleSerializer(required=False, allow_null=True)
  131. prefix_count = serializers.IntegerField(read_only=True)
  132. class Meta:
  133. model = VLAN
  134. fields = [
  135. 'id', 'url', 'display', 'site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'tags',
  136. 'display_name', 'custom_fields', 'created', 'last_updated', 'prefix_count',
  137. ]
  138. validators = []
  139. def validate(self, data):
  140. # Validate uniqueness of vid and name if a group has been assigned.
  141. if data.get('group', None):
  142. for field in ['vid', 'name']:
  143. validator = UniqueTogetherValidator(queryset=VLAN.objects.all(), fields=('group', field))
  144. validator(data, self)
  145. # Enforce model validation
  146. super().validate(data)
  147. return data
  148. #
  149. # Prefixes
  150. #
  151. class PrefixSerializer(PrimaryModelSerializer):
  152. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:prefix-detail')
  153. family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
  154. site = NestedSiteSerializer(required=False, allow_null=True)
  155. vrf = NestedVRFSerializer(required=False, allow_null=True)
  156. tenant = NestedTenantSerializer(required=False, allow_null=True)
  157. vlan = NestedVLANSerializer(required=False, allow_null=True)
  158. status = ChoiceField(choices=PrefixStatusChoices, required=False)
  159. role = NestedRoleSerializer(required=False, allow_null=True)
  160. children = serializers.IntegerField(read_only=True)
  161. _depth = serializers.IntegerField(read_only=True)
  162. class Meta:
  163. model = Prefix
  164. fields = [
  165. 'id', 'url', 'display', 'family', 'prefix', 'site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'is_pool',
  166. 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'children', '_depth',
  167. ]
  168. read_only_fields = ['family']
  169. class PrefixLengthSerializer(serializers.Serializer):
  170. prefix_length = serializers.IntegerField()
  171. def to_internal_value(self, data):
  172. requested_prefix = data.get('prefix_length')
  173. if requested_prefix is None:
  174. raise serializers.ValidationError({
  175. 'prefix_length': 'this field can not be missing'
  176. })
  177. if not isinstance(requested_prefix, int):
  178. raise serializers.ValidationError({
  179. 'prefix_length': 'this field must be int type'
  180. })
  181. prefix = self.context.get('prefix')
  182. if prefix.family == 4 and requested_prefix > 32:
  183. raise serializers.ValidationError({
  184. 'prefix_length': 'Invalid prefix length ({}) for IPv4'.format((requested_prefix))
  185. })
  186. elif prefix.family == 6 and requested_prefix > 128:
  187. raise serializers.ValidationError({
  188. 'prefix_length': 'Invalid prefix length ({}) for IPv6'.format((requested_prefix))
  189. })
  190. return data
  191. class AvailablePrefixSerializer(serializers.Serializer):
  192. """
  193. Representation of a prefix which does not exist in the database.
  194. """
  195. family = serializers.IntegerField(read_only=True)
  196. prefix = serializers.CharField(read_only=True)
  197. vrf = NestedVRFSerializer(read_only=True)
  198. def to_representation(self, instance):
  199. if self.context.get('vrf'):
  200. vrf = NestedVRFSerializer(self.context['vrf'], context={'request': self.context['request']}).data
  201. else:
  202. vrf = None
  203. return OrderedDict([
  204. ('family', instance.version),
  205. ('prefix', str(instance)),
  206. ('vrf', vrf),
  207. ])
  208. #
  209. # IP addresses
  210. #
  211. class IPAddressSerializer(PrimaryModelSerializer):
  212. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
  213. family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True)
  214. vrf = NestedVRFSerializer(required=False, allow_null=True)
  215. tenant = NestedTenantSerializer(required=False, allow_null=True)
  216. status = ChoiceField(choices=IPAddressStatusChoices, required=False)
  217. role = ChoiceField(choices=IPAddressRoleChoices, allow_blank=True, required=False)
  218. assigned_object_type = ContentTypeField(
  219. queryset=ContentType.objects.filter(IPADDRESS_ASSIGNMENT_MODELS),
  220. required=False,
  221. allow_null=True
  222. )
  223. assigned_object = serializers.SerializerMethodField(read_only=True)
  224. nat_inside = NestedIPAddressSerializer(required=False, allow_null=True)
  225. nat_outside = NestedIPAddressSerializer(read_only=True)
  226. class Meta:
  227. model = IPAddress
  228. fields = [
  229. 'id', 'url', 'display', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'assigned_object_type',
  230. 'assigned_object_id', 'assigned_object', 'nat_inside', 'nat_outside', 'dns_name', 'description', 'tags',
  231. 'custom_fields', 'created', 'last_updated',
  232. ]
  233. read_only_fields = ['family']
  234. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  235. def get_assigned_object(self, obj):
  236. if obj.assigned_object is None:
  237. return None
  238. serializer = get_serializer_for_model(obj.assigned_object, prefix='Nested')
  239. context = {'request': self.context['request']}
  240. return serializer(obj.assigned_object, context=context).data
  241. class AvailableIPSerializer(serializers.Serializer):
  242. """
  243. Representation of an IP address which does not exist in the database.
  244. """
  245. family = serializers.IntegerField(read_only=True)
  246. address = serializers.CharField(read_only=True)
  247. vrf = NestedVRFSerializer(read_only=True)
  248. def to_representation(self, instance):
  249. if self.context.get('vrf'):
  250. vrf = NestedVRFSerializer(self.context['vrf'], context={'request': self.context['request']}).data
  251. else:
  252. vrf = None
  253. return OrderedDict([
  254. ('family', self.context['prefix'].version),
  255. ('address', '{}/{}'.format(instance, self.context['prefix'].prefixlen)),
  256. ('vrf', vrf),
  257. ])
  258. #
  259. # Services
  260. #
  261. class ServiceSerializer(PrimaryModelSerializer):
  262. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:service-detail')
  263. device = NestedDeviceSerializer(required=False, allow_null=True)
  264. virtual_machine = NestedVirtualMachineSerializer(required=False, allow_null=True)
  265. protocol = ChoiceField(choices=ServiceProtocolChoices, required=False)
  266. ipaddresses = SerializedPKRelatedField(
  267. queryset=IPAddress.objects.all(),
  268. serializer=NestedIPAddressSerializer,
  269. required=False,
  270. many=True
  271. )
  272. class Meta:
  273. model = Service
  274. fields = [
  275. 'id', 'url', 'display', 'device', 'virtual_machine', 'name', 'ports', 'protocol', 'ipaddresses',
  276. 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  277. ]