device_components.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. from django.utils.translation import gettext as _
  2. from django.contrib.contenttypes.models import ContentType
  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.gfk_fields import GFKSerializerField
  15. from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
  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. status = ChoiceField(choices=PowerOutletStatusChoices, required=False)
  142. class Meta:
  143. model = PowerOutlet
  144. fields = [
  145. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'status', 'color',
  146. 'power_port', 'feed_leg', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
  147. 'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable',
  148. 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  149. ]
  150. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  151. class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, ConnectedEndpointsSerializer):
  152. device = DeviceSerializer(nested=True)
  153. vdcs = SerializedPKRelatedField(
  154. queryset=VirtualDeviceContext.objects.all(),
  155. serializer=VirtualDeviceContextSerializer,
  156. nested=True,
  157. required=False,
  158. many=True
  159. )
  160. module = ModuleSerializer(
  161. nested=True,
  162. fields=('id', 'url', 'display', 'device', 'module_bay'),
  163. required=False,
  164. allow_null=True
  165. )
  166. type = ChoiceField(choices=InterfaceTypeChoices)
  167. parent = NestedInterfaceSerializer(required=False, allow_null=True)
  168. bridge = NestedInterfaceSerializer(required=False, allow_null=True)
  169. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  170. mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_blank=True)
  171. duplex = ChoiceField(choices=InterfaceDuplexChoices, required=False, allow_blank=True, allow_null=True)
  172. rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_blank=True)
  173. rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False, allow_blank=True)
  174. poe_mode = ChoiceField(choices=InterfacePoEModeChoices, required=False, allow_blank=True)
  175. poe_type = ChoiceField(choices=InterfacePoETypeChoices, required=False, allow_blank=True)
  176. untagged_vlan = VLANSerializer(nested=True, required=False, allow_null=True)
  177. tagged_vlans = SerializedPKRelatedField(
  178. queryset=VLAN.objects.all(),
  179. serializer=VLANSerializer,
  180. nested=True,
  181. required=False,
  182. many=True
  183. )
  184. qinq_svlan = VLANSerializer(nested=True, required=False, allow_null=True)
  185. vlan_translation_policy = VLANTranslationPolicySerializer(nested=True, required=False, allow_null=True)
  186. vrf = VRFSerializer(nested=True, required=False, allow_null=True)
  187. l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
  188. wireless_link = NestedWirelessLinkSerializer(read_only=True, allow_null=True)
  189. wireless_lans = SerializedPKRelatedField(
  190. queryset=WirelessLAN.objects.all(),
  191. serializer=WirelessLANSerializer,
  192. nested=True,
  193. required=False,
  194. many=True
  195. )
  196. count_ipaddresses = serializers.IntegerField(read_only=True)
  197. count_fhrp_groups = serializers.IntegerField(read_only=True)
  198. # Maintains backward compatibility with NetBox <v4.2
  199. mac_address = serializers.CharField(allow_null=True, read_only=True)
  200. primary_mac_address = MACAddressSerializer(nested=True, required=False, allow_null=True)
  201. mac_addresses = MACAddressSerializer(many=True, nested=True, read_only=True, allow_null=True)
  202. wwn = serializers.CharField(required=False, default=None, allow_blank=True, allow_null=True)
  203. class Meta:
  204. model = Interface
  205. fields = [
  206. 'id', 'url', 'display_url', 'display', 'device', 'vdcs', 'module', 'name', 'label', 'type', 'enabled',
  207. 'parent', 'bridge', 'lag', 'mtu', 'mac_address', 'primary_mac_address', 'mac_addresses', 'speed', 'duplex',
  208. 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'poe_mode', 'poe_type',
  209. 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan',
  210. 'vlan_translation_policy', 'mark_connected', 'cable', 'cable_end', 'wireless_link', 'link_peers',
  211. 'link_peers_type', 'wireless_lans', 'vrf', 'l2vpn_termination', 'connected_endpoints',
  212. 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', 'created',
  213. 'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
  214. ]
  215. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  216. def validate(self, data):
  217. if not self.nested:
  218. # Validate 802.1q mode and vlan(s)
  219. mode = None
  220. tagged_vlans = []
  221. # Gather Information
  222. if self.instance:
  223. mode = data.get('mode') if 'mode' in data.keys() else self.instance.mode
  224. untagged_vlan = data.get('untagged_vlan') if 'untagged_vlan' in data.keys() else \
  225. self.instance.untagged_vlan
  226. qinq_svlan = data.get('qinq_svlan') if 'qinq_svlan' in data.keys() else \
  227. self.instance.qinq_svlan
  228. tagged_vlans = data.get('tagged_vlans') if 'tagged_vlans' in data.keys() else \
  229. self.instance.tagged_vlans.all()
  230. else:
  231. mode = data.get('mode', None)
  232. untagged_vlan = data.get('untagged_vlan') if 'untagged_vlan' in data.keys() else None
  233. qinq_svlan = data.get('qinq_svlan') if 'qinq_svlan' in data.keys() else None
  234. tagged_vlans = data.get('tagged_vlans') if 'tagged_vlans' in data.keys() else None
  235. errors = {}
  236. # Non Q-in-Q mode with service vlan set
  237. if mode != InterfaceModeChoices.MODE_Q_IN_Q and qinq_svlan:
  238. errors.update({
  239. 'qinq_svlan': _("Interface mode does not support q-in-q service vlan")
  240. })
  241. # Routed mode
  242. if not mode:
  243. # Untagged vlan
  244. if untagged_vlan:
  245. errors.update({
  246. 'untagged_vlan': _("Interface mode does not support untagged vlan")
  247. })
  248. # Tagged vlan
  249. if tagged_vlans:
  250. errors.update({
  251. 'tagged_vlans': _("Interface mode does not support tagged vlans")
  252. })
  253. # Non-tagged mode
  254. elif mode in (InterfaceModeChoices.MODE_TAGGED_ALL, InterfaceModeChoices.MODE_ACCESS) and tagged_vlans:
  255. errors.update({
  256. 'tagged_vlans': _("Interface mode does not support tagged vlans")
  257. })
  258. if errors:
  259. raise serializers.ValidationError(errors)
  260. # Validate many-to-many VLAN assignments
  261. device = self.instance.device if self.instance else data.get('device')
  262. for vlan in data.get('tagged_vlans', []):
  263. if vlan.site not in [device.site, None]:
  264. raise serializers.ValidationError({
  265. 'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent device, "
  266. f"or it must be global."
  267. })
  268. return super().validate(data)
  269. class RearPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
  270. device = DeviceSerializer(nested=True)
  271. module = ModuleSerializer(
  272. nested=True,
  273. fields=('id', 'url', 'display', 'device', 'module_bay'),
  274. required=False,
  275. allow_null=True
  276. )
  277. type = ChoiceField(choices=PortTypeChoices)
  278. class Meta:
  279. model = RearPort
  280. fields = [
  281. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions',
  282. 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags',
  283. 'custom_fields', 'created', 'last_updated', '_occupied',
  284. ]
  285. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  286. class FrontPortRearPortSerializer(WritableNestedSerializer):
  287. """
  288. NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
  289. """
  290. class Meta:
  291. model = RearPort
  292. fields = ['id', 'url', 'display_url', 'display', 'name', 'label', 'description']
  293. class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer):
  294. device = DeviceSerializer(nested=True)
  295. module = ModuleSerializer(
  296. nested=True,
  297. fields=('id', 'url', 'display', 'device', 'module_bay'),
  298. required=False,
  299. allow_null=True
  300. )
  301. type = ChoiceField(choices=PortTypeChoices)
  302. rear_port = FrontPortRearPortSerializer()
  303. class Meta:
  304. model = FrontPort
  305. fields = [
  306. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
  307. 'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
  308. 'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  309. ]
  310. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied')
  311. class ModuleBaySerializer(NetBoxModelSerializer):
  312. device = DeviceSerializer(nested=True)
  313. module = ModuleSerializer(
  314. nested=True,
  315. fields=('id', 'url', 'display'),
  316. required=False,
  317. allow_null=True,
  318. default=None
  319. )
  320. installed_module = ModuleSerializer(
  321. nested=True,
  322. fields=('id', 'url', 'display', 'serial', 'description'),
  323. required=False,
  324. allow_null=True
  325. )
  326. class Meta:
  327. model = ModuleBay
  328. fields = [
  329. 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'installed_module', 'label', 'position',
  330. 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  331. ]
  332. brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
  333. class DeviceBaySerializer(NetBoxModelSerializer):
  334. device = DeviceSerializer(nested=True)
  335. installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
  336. class Meta:
  337. model = DeviceBay
  338. fields = [
  339. 'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'description', 'installed_device',
  340. 'tags', 'custom_fields', 'created', 'last_updated',
  341. ]
  342. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
  343. class InventoryItemSerializer(NetBoxModelSerializer):
  344. device = DeviceSerializer(nested=True)
  345. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  346. role = InventoryItemRoleSerializer(nested=True, required=False, allow_null=True)
  347. manufacturer = ManufacturerSerializer(nested=True, required=False, allow_null=True, default=None)
  348. component_type = ContentTypeField(
  349. queryset=ContentType.objects.filter(MODULAR_COMPONENT_MODELS),
  350. required=False,
  351. allow_null=True
  352. )
  353. component = GFKSerializerField(read_only=True)
  354. _depth = serializers.IntegerField(source='level', read_only=True)
  355. status = ChoiceField(choices=InventoryItemStatusChoices, required=False)
  356. class Meta:
  357. model = InventoryItem
  358. fields = [
  359. 'id', 'url', 'display_url', 'display', 'device', 'parent', 'name', 'label', 'status', 'role',
  360. 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered', 'description', 'component_type',
  361. 'component_id', 'component', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
  362. ]
  363. brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', '_depth')