virtualmachines.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. from drf_spectacular.utils import extend_schema_field
  2. from rest_framework import serializers
  3. from dcim.api.serializers_.devices import DeviceSerializer
  4. from dcim.api.serializers_.platforms import PlatformSerializer
  5. from dcim.api.serializers_.roles import DeviceRoleSerializer
  6. from dcim.api.serializers_.sites import SiteSerializer
  7. from dcim.choices import InterfaceModeChoices
  8. from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
  9. from ipam.api.serializers_.ip import IPAddressSerializer
  10. from ipam.api.serializers_.vlans import VLANSerializer
  11. from ipam.api.serializers_.vrfs import VRFSerializer
  12. from ipam.models import VLAN
  13. from netbox.api.fields import ChoiceField, SerializedPKRelatedField
  14. from netbox.api.serializers import NetBoxModelSerializer
  15. from tenancy.api.serializers_.tenants import TenantSerializer
  16. from virtualization.choices import *
  17. from virtualization.models import VirtualDisk, VirtualMachine, VMInterface
  18. from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
  19. from .clusters import ClusterSerializer
  20. from ..nested_serializers import *
  21. __all__ = (
  22. 'VMInterfaceSerializer',
  23. 'VirtualDiskSerializer',
  24. 'VirtualMachineSerializer',
  25. 'VirtualMachineWithConfigContextSerializer',
  26. )
  27. class VirtualMachineSerializer(NetBoxModelSerializer):
  28. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualmachine-detail')
  29. status = ChoiceField(choices=VirtualMachineStatusChoices, required=False)
  30. site = SiteSerializer(nested=True, required=False, allow_null=True, default=None)
  31. cluster = ClusterSerializer(nested=True, required=False, allow_null=True, default=None)
  32. device = DeviceSerializer(nested=True, required=False, allow_null=True, default=None)
  33. role = DeviceRoleSerializer(nested=True, required=False, allow_null=True)
  34. tenant = TenantSerializer(nested=True, required=False, allow_null=True, default=None)
  35. platform = PlatformSerializer(nested=True, required=False, allow_null=True)
  36. primary_ip = IPAddressSerializer(nested=True, read_only=True, allow_null=True)
  37. primary_ip4 = IPAddressSerializer(nested=True, required=False, allow_null=True)
  38. primary_ip6 = IPAddressSerializer(nested=True, required=False, allow_null=True)
  39. config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None)
  40. # Counter fields
  41. interface_count = serializers.IntegerField(read_only=True)
  42. virtual_disk_count = serializers.IntegerField(read_only=True)
  43. class Meta:
  44. model = VirtualMachine
  45. fields = [
  46. 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
  47. 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
  48. 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
  49. 'interface_count', 'virtual_disk_count',
  50. ]
  51. brief_fields = ('id', 'url', 'display', 'name', 'description')
  52. class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
  53. config_context = serializers.SerializerMethodField()
  54. class Meta(VirtualMachineSerializer.Meta):
  55. fields = [
  56. 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform',
  57. 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments',
  58. 'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created',
  59. 'last_updated', 'interface_count', 'virtual_disk_count',
  60. ]
  61. @extend_schema_field(serializers.JSONField(allow_null=True))
  62. def get_config_context(self, obj):
  63. return obj.get_config_context()
  64. #
  65. # VM interfaces
  66. #
  67. class VMInterfaceSerializer(NetBoxModelSerializer):
  68. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
  69. virtual_machine = VirtualMachineSerializer(nested=True)
  70. parent = NestedVMInterfaceSerializer(required=False, allow_null=True)
  71. bridge = NestedVMInterfaceSerializer(required=False, allow_null=True)
  72. mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
  73. untagged_vlan = VLANSerializer(nested=True, required=False, allow_null=True)
  74. tagged_vlans = SerializedPKRelatedField(
  75. queryset=VLAN.objects.all(),
  76. serializer=VLANSerializer,
  77. nested=True,
  78. required=False,
  79. many=True
  80. )
  81. vrf = VRFSerializer(nested=True, required=False, allow_null=True)
  82. l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
  83. count_ipaddresses = serializers.IntegerField(read_only=True)
  84. count_fhrp_groups = serializers.IntegerField(read_only=True)
  85. mac_address = serializers.CharField(
  86. required=False,
  87. default=None,
  88. allow_null=True
  89. )
  90. class Meta:
  91. model = VMInterface
  92. fields = [
  93. 'id', 'url', 'display', 'virtual_machine', 'name', 'enabled', 'parent', 'bridge', 'mtu', 'mac_address',
  94. 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'vrf', 'l2vpn_termination', 'tags', 'custom_fields',
  95. 'created', 'last_updated', 'count_ipaddresses', 'count_fhrp_groups',
  96. ]
  97. brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description')
  98. def validate(self, data):
  99. # Validate many-to-many VLAN assignments
  100. virtual_machine = self.instance.virtual_machine if self.instance else data.get('virtual_machine')
  101. for vlan in data.get('tagged_vlans', []):
  102. if vlan.site not in [virtual_machine.site, None]:
  103. raise serializers.ValidationError({
  104. 'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent virtual "
  105. f"machine, or it must be global."
  106. })
  107. return super().validate(data)
  108. #
  109. # Virtual Disk
  110. #
  111. class VirtualDiskSerializer(NetBoxModelSerializer):
  112. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:virtualdisk-detail')
  113. virtual_machine = VirtualMachineSerializer(nested=True)
  114. class Meta:
  115. model = VirtualDisk
  116. fields = [
  117. 'id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size', 'tags', 'custom_fields',
  118. 'created', 'last_updated',
  119. ]
  120. brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description', 'size')