device_components.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. from django.contrib.contenttypes.models import ContentType
  2. from drf_spectacular.utils import extend_schema_field
  3. from rest_framework import serializers
  4. from dcim.choices import *
  5. from dcim.constants import *
  6. from dcim.models import (
  7. ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, ModuleBay, PowerOutlet, PowerPort,
  8. RearPort, VirtualDeviceContext,
  9. )
  10. from ipam.api.serializers_.vlans import VLANSerializer, VLANTranslationPolicySerializer
  11. from ipam.api.serializers_.vrfs import VRFSerializer
  12. from ipam.models import VLAN
  13. from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField
  14. from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
  15. from utilities.api import get_serializer_for_model
  16. from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
  17. from wireless.api.serializers_.nested import NestedWirelessLinkSerializer
  18. from wireless.api.serializers_.wirelesslans import WirelessLANSerializer
  19. from wireless.choices import *
  20. from wireless.models import WirelessLAN
  21. from .base import ConnectedEndpointsSerializer
  22. from .cables import CabledObjectSerializer
  23. from .devices import DeviceSerializer, MACAddressSerializer, ModuleSerializer, VirtualDeviceContextSerializer
  24. from .manufacturers import ManufacturerSerializer
  25. from .nested import NestedInterfaceSerializer
  26. from .roles import InventoryItemRoleSerializer
  27. __all__ = (
  28. 'ConsolePortSerializer',
  29. 'ConsoleServerPortSerializer',
  30. 'DeviceBaySerializer',
  31. 'FrontPortSerializer',
  32. 'InterfaceSerializer',
  33. 'InventoryItemSerializer',
  34. 'ModuleBaySerializer',
  35. 'PowerOutletSerializer',
  36. 'PowerPortSerializer',
  37. 'RearPortSerializer',
  38. )
  39. class ConsoleServerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
  40. device = DeviceSerializer(nested=True)
  41. module = ModuleSerializer(
  42. nested=True,
  43. fields=('id', 'url', 'display', 'device', 'module_bay'),
  44. required=False,
  45. allow_null=True
  46. )
  47. type = ChoiceField(
  48. choices=ConsolePortTypeChoices,
  49. allow_blank=True,
  50. required=False
  51. )
  52. speed = ChoiceField(
  53. choices=ConsolePortSpeedChoices,
  54. allow_null=True,
  55. required=False
  56. )
  57. class Meta:
  58. model = ConsoleServerPort
  59. fields = [
  60. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
  61. 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
  62. 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
  63. 'last_updated', '_occupied',
  64. ]
  65. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  66. class ConsolePortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
  67. device = DeviceSerializer(nested=True)
  68. module = ModuleSerializer(
  69. nested=True,
  70. fields=('id', 'url', 'display', 'device', 'module_bay'),
  71. required=False,
  72. allow_null=True
  73. )
  74. type = ChoiceField(
  75. choices=ConsolePortTypeChoices,
  76. allow_blank=True,
  77. required=False
  78. )
  79. speed = ChoiceField(
  80. choices=ConsolePortSpeedChoices,
  81. allow_null=True,
  82. required=False
  83. )
  84. class Meta:
  85. model = ConsolePort
  86. fields = [
  87. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
  88. 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'connected_endpoints',
  89. 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
  90. 'last_updated', '_occupied',
  91. ]
  92. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  93. class PowerPortSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
  94. device = DeviceSerializer(nested=True)
  95. module = ModuleSerializer(
  96. nested=True,
  97. fields=('id', 'url', 'display', 'device', 'module_bay'),
  98. required=False,
  99. allow_null=True
  100. )
  101. type = ChoiceField(
  102. choices=PowerPortTypeChoices,
  103. allow_blank=True,
  104. required=False,
  105. allow_null=True
  106. )
  107. class Meta:
  108. model = PowerPort
  109. fields = [
  110. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw',
  111. 'allocated_draw', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
  112. 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
  113. 'created', 'last_updated', '_occupied',
  114. ]
  115. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  116. class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
  117. device = DeviceSerializer(nested=True)
  118. module = ModuleSerializer(
  119. nested=True,
  120. fields=('id', 'url', 'display', 'device', 'module_bay'),
  121. required=False,
  122. allow_null=True
  123. )
  124. type = ChoiceField(
  125. choices=PowerOutletTypeChoices,
  126. allow_blank=True,
  127. required=False,
  128. allow_null=True
  129. )
  130. power_port = PowerPortSerializer(
  131. nested=True,
  132. required=False,
  133. allow_null=True
  134. )
  135. feed_leg = ChoiceField(
  136. choices=PowerOutletFeedLegChoices,
  137. allow_blank=True,
  138. required=False,
  139. allow_null=True
  140. )
  141. class Meta:
  142. model = PowerOutlet
  143. fields = [
  144. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'power_port',
  145. 'feed_leg', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type',
  146. 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields',
  147. 'created', 'last_updated', '_occupied',
  148. ]
  149. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  150. class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
  151. device = DeviceSerializer(nested=True)
  152. vdcs = SerializedPKRelatedField(
  153. queryset=VirtualDeviceContext.objects.all(),
  154. serializer=VirtualDeviceContextSerializer,
  155. nested=True,
  156. required=False,
  157. many=True
  158. )
  159. module = ModuleSerializer(
  160. nested=True,
  161. fields=('id', 'url', 'display', 'device', 'module_bay'),
  162. required=False,
  163. allow_null=True
  164. )
  165. type = ChoiceField(choices=InterfaceTypeChoices)
  166. parent = NestedInterfaceSerializer(required=False, allow_null=True)
  167. bridge = NestedInterfaceSerializer(required=False, allow_null=True)
  168. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  169. mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_blank=True)
  170. duplex = ChoiceField(choices=InterfaceDuplexChoices, required=False, allow_blank=True, allow_null=True)
  171. rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_blank=True)
  172. rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False, allow_blank=True)
  173. poe_mode = ChoiceField(choices=InterfacePoEModeChoices, required=False, allow_blank=True)
  174. poe_type = ChoiceField(choices=InterfacePoETypeChoices, required=False, allow_blank=True)
  175. untagged_vlan = VLANSerializer(nested=True, required=False, allow_null=True)
  176. tagged_vlans = SerializedPKRelatedField(
  177. queryset=VLAN.objects.all(),
  178. serializer=VLANSerializer,
  179. nested=True,
  180. required=False,
  181. many=True
  182. )
  183. qinq_svlan = VLANSerializer(nested=True, required=False, allow_null=True)
  184. vlan_translation_policy = VLANTranslationPolicySerializer(nested=True, required=False, allow_null=True)
  185. vrf = VRFSerializer(nested=True, required=False, allow_null=True)
  186. l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
  187. wireless_link = NestedWirelessLinkSerializer(read_only=True, allow_null=True)
  188. wireless_lans = SerializedPKRelatedField(
  189. queryset=WirelessLAN.objects.all(),
  190. serializer=WirelessLANSerializer,
  191. nested=True,
  192. required=False,
  193. many=True
  194. )
  195. count_ipaddresses = serializers.IntegerField(read_only=True)
  196. count_fhrp_groups = serializers.IntegerField(read_only=True)
  197. # Maintains backward compatibility with NetBox <v4.2
  198. mac_address = serializers.CharField(allow_null=True, read_only=True)
  199. primary_mac_address = MACAddressSerializer(nested=True, required=False, allow_null=True)
  200. mac_addresses = MACAddressSerializer(many=True, nested=True, read_only=True, allow_null=True)
  201. wwn = serializers.CharField(required=False, default=None, allow_blank=True, allow_null=True)
  202. class Meta:
  203. model = Interface
  204. fields = [
  205. 'id', 'url', 'display_url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled',
  206. 'parent', 'bridge', 'lag', 'mtu', 'mac_address', 'primary_mac_address', 'mac_addresses', 'speed', 'duplex',
  207. 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'poe_mode', 'poe_type',
  208. 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan',
  209. 'vlan_translation_policy', 'mark_connected', 'cable', 'cable_end', 'wireless_link', 'link_peers',
  210. 'link_peers_type', 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints',
  211. 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
  212. 'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
  213. ]
  214. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  215. def validate(self, data):
  216. # Validate many-to-many VLAN assignments
  217. if not self.nested:
  218. device = self.instance.device if self.instance else data.get('device')
  219. for vlan in data.get('tagged_vlans', []):
  220. if vlan.site not in [device.site, None]:
  221. raise serializers.ValidationError({
  222. 'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent device, "
  223. f"or it must be global."
  224. })
  225. return super().validate(data)
  226. class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
  227. device = DeviceSerializer(nested=True)
  228. module = ModuleSerializer(
  229. nested=True,
  230. fields=('id', 'url', 'display', 'device', 'module_bay'),
  231. required=False,
  232. allow_null=True
  233. )
  234. type = ChoiceField(choices=PortTypeChoices)
  235. class Meta:
  236. model = RearPort
  237. fields = [
  238. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions',
  239. 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags',
  240. 'custom_fields', 'created', 'last_updated', '_occupied',
  241. ]
  242. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  243. class FrontPortRearPortSerializer(WritableNestedSerializer):
  244. """
  245. NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
  246. """
  247. class Meta:
  248. model = RearPort
  249. fields = ['id', 'url', 'display_url', 'display', 'name', 'label', 'description']
  250. class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
  251. device = DeviceSerializer(nested=True)
  252. module = ModuleSerializer(
  253. nested=True,
  254. fields=('id', 'url', 'display', 'device', 'module_bay'),
  255. required=False,
  256. allow_null=True
  257. )
  258. type = ChoiceField(choices=PortTypeChoices)
  259. rear_port = FrontPortRearPortSerializer()
  260. class Meta:
  261. model = FrontPort
  262. fields = [
  263. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
  264. 'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
  265. 'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  266. ]
  267. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  268. class ModuleBaySerializer(NetBoxModelSerializer):
  269. device = DeviceSerializer(nested=True)
  270. module = ModuleSerializer(
  271. nested=True,
  272. fields=('id', 'url', 'display'),
  273. required=False,
  274. allow_null=True,
  275. default=None
  276. )
  277. installed_module = ModuleSerializer(
  278. nested=True,
  279. fields=('id', 'url', 'display', 'serial', 'description'),
  280. required=False,
  281. allow_null=True
  282. )
  283. class Meta:
  284. model = ModuleBay
  285. fields = [
  286. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'installed_module', 'label', 'position',
  287. 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  288. ]
  289. brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
  290. class DeviceBaySerializer(NetBoxModelSerializer):
  291. device = DeviceSerializer(nested=True)
  292. installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
  293. class Meta:
  294. model = DeviceBay
  295. fields = [
  296. 'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'description', 'installed_device',
  297. 'tags', 'custom_fields', 'created', 'last_updated',
  298. ]
  299. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
  300. class InventoryItemSerializer(NetBoxModelSerializer):
  301. device = DeviceSerializer(nested=True)
  302. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  303. role = InventoryItemRoleSerializer(nested=True, required=False, allow_null=True)
  304. manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True, default=None)
  305. component_type = ContentTypeField(
  306. queryset=ContentType.objects.filter(MODULAR_COMPONENT_MODELS),
  307. required=False,
  308. allow_null=True
  309. )
  310. component = serializers.SerializerMethodField(read_only=True, allow_null=True)
  311. _depth = serializers.IntegerField(source='level', read_only=True)
  312. status = ChoiceField(choices=InventoryItemStatusChoices, required=False)
  313. class Meta:
  314. model = InventoryItem
  315. fields = [
  316. 'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'status', 'role', 'manufacturer',
  317. 'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type', 'component_id',
  318. 'component', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
  319. ]
  320. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')
  321. @extend_schema_field(serializers.JSONField(allow_null=True))
  322. def get_component(self, obj):
  323. if obj.component is None:
  324. return None
  325. serializer = get_serializer_for_model(obj.component)
  326. context = {'request': self.context['request']}
  327. return serializer(obj.component, nested=True, context=context).data