serializers.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. from drf_spectacular.utils import extend_schema_field
  2. from rest_framework import serializers
  3. from dcim.api.nested_serializers import (
  4. NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer,
  5. )
  6. from dcim.choices import InterfaceModeChoices
  7. from extras.api.nested_serializers import NestedConfigTemplateSerializer
  8. from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer
  9. from ipam.models import VLAN
  10. from netbox.api.fields import ChoiceField, RelatedObjectCountField, SerializedPKRelatedField
  11. from netbox.api.serializers import NetBoxModelSerializer
  12. from tenancy.api.nested_serializers import NestedTenantSerializer
  13. from virtualization.choices import *
  14. from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualDisk, VirtualMachine, VMInterface
  15. from vpn.api.nested_serializers import NestedL2VPNTerminationSerializer
  16. from .nested_serializers import *
  17. #
  18. # Clusters
  19. #
  20. class ClusterTypeSerializer(NetBoxModelSerializer):
  21. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustertype-detail')
  22. # Related object counts
  23. cluster_count = RelatedObjectCountField('clusters')
  24. class Meta:
  25. model = ClusterType
  26. fields = [
  27. 'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  28. 'cluster_count',
  29. ]
  30. class ClusterGroupSerializer(NetBoxModelSerializer):
  31. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:clustergroup-detail')
  32. # Related object counts
  33. cluster_count = RelatedObjectCountField('clusters')
  34. class Meta:
  35. model = ClusterGroup
  36. fields = [
  37. 'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  38. 'cluster_count',
  39. ]
  40. class ClusterSerializer(NetBoxModelSerializer):
  41. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
  42. type = NestedClusterTypeSerializer()
  43. group = NestedClusterGroupSerializer(required=False, allow_null=True, default=None)
  44. status = ChoiceField(choices=ClusterStatusChoices, required=False)
  45. tenant = NestedTenantSerializer(required=False, allow_null=True)
  46. site = NestedSiteSerializer(required=False, allow_null=True, default=None)
  47. # Related object counts
  48. device_count = RelatedObjectCountField('devices')
  49. virtualmachine_count = RelatedObjectCountField('virtual_machines')
  50. class Meta:
  51. model = Cluster
  52. fields = [
  53. 'id', 'url', 'display', 'name', 'type', 'group', 'status', 'tenant', 'site', 'description', 'comments',
  54. 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
  55. ]
  56. #
  57. # Virtual machines
  58. #
  59. class VirtualMachineSerializer(NetBoxModelSerializer):
  60. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
  61. status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
  62. site = NestedSiteSerializer(required=False, allow_null=True)
  63. cluster = NestedClusterSerializer(required=False, allow_null=True)
  64. device = NestedDeviceSerializer(required=False, allow_null=True)
  65. role = NestedDeviceRoleSerializer(required=False, allow_null=True)
  66. tenant = NestedTenantSerializer(required=False, allow_null=True)
  67. platform = NestedPlatformSerializer(required=False, allow_null=True)
  68. primary_ip = NestedIPAddressSerializer(read_only=True)
  69. primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
  70. primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
  71. config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None)
  72. # Counter fields
  73. interface_count = serializers.IntegerField(read_only=True)
  74. virtual_disk_count = serializers.IntegerField(read_only=True)
  75. class Meta:
  76. model = VirtualMachine
  77. fields = [
  78. 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
  79. 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
  80. 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
  81. 'interface_count', 'virtual_disk_count',
  82. ]
  83. validators = []
  84. class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
  85. config_context = serializers.SerializerMethodField()
  86. class Meta(VirtualMachineSerializer.Meta):
  87. fields = [
  88. 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
  89. 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
  90. 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created', 'last_updated',
  91. 'interface_count', 'virtual_disk_count',
  92. ]
  93. @extend_schema_field(serializers.JSONField(allow_null=True))
  94. def get_config_context(self, obj):
  95. return obj.get_config_context()
  96. #
  97. # VM interfaces
  98. #
  99. class VMInterfaceSerializer(NetBoxModelSerializer):
  100. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
  101. virtual_machine = NestedVirtualMachineSerializer()
  102. parent = NestedVMInterfaceSerializer(required=False, allow_null=True)
  103. bridge = NestedVMInterfaceSerializer(required=False, allow_null=True)
  104. mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
  105. untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
  106. tagged_vlans = SerializedPKRelatedField(
  107. queryset=VLAN.objects.all(),
  108. serializer=NestedVLANSerializer,
  109. required=False,
  110. many=True
  111. )
  112. vrf = NestedVRFSerializer(required=False, allow_null=True)
  113. l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, allow_null=True)
  114. count_ipaddresses = serializers.IntegerField(read_only=True)
  115. count_fhrp_groups = serializers.IntegerField(read_only=True)
  116. mac_address = serializers.CharField(
  117. required=False,
  118. default=None,
  119. allow_null=True
  120. )
  121. class Meta:
  122. model = VMInterface
  123. fields = [
  124. 'id', 'url', 'display', 'virtual_machine', 'name', 'enabled', 'parent', 'bridge', 'mtu', 'mac_address',
  125. 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'vrf', 'l2vpn_termination', 'tags', 'custom_fields',
  126. 'created', 'last_updated', 'count_ipaddresses', 'count_fhrp_groups',
  127. ]
  128. def validate(self, data):
  129. # Validate many-to-many VLAN assignments
  130. virtual_machine = self.instance.virtual_machine if self.instance else data.get('virtual_machine')
  131. for vlan in data.get('tagged_vlans', []):
  132. if vlan.site not in [virtual_machine.site, None]:
  133. raise serializers.ValidationError({
  134. 'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent virtual "
  135. f"machine, or it must be global."
  136. })
  137. return super().validate(data)
  138. #
  139. # Virtual Disk
  140. #
  141. class VirtualDiskSerializer(NetBoxModelSerializer):
  142. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualdisk-detail')
  143. virtual_machine = NestedVirtualMachineSerializer()
  144. class Meta:
  145. model = VirtualDisk
  146. fields = [
  147. 'id', 'url', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields', 'created',
  148. 'last_updated',
  149. ]