device_components.py 15 KB

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