serializers.py 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. from django.contrib.contenttypes.models import ContentType
  2. from drf_yasg.utils import swagger_serializer_method
  3. from rest_framework import serializers
  4. from timezone_field.rest_framework import TimeZoneSerializerField
  5. from dcim.choices import *
  6. from dcim.constants import *
  7. from dcim.models import *
  8. from ipam.api.nested_serializers import (
  9. NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer, NestedVRFSerializer,
  10. )
  11. from ipam.models import ASN, VLAN
  12. from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
  13. from netbox.api.serializers import (
  14. NestedGroupModelSerializer, NetBoxModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
  15. )
  16. from netbox.config import ConfigItem
  17. from tenancy.api.nested_serializers import NestedTenantSerializer
  18. from users.api.nested_serializers import NestedUserSerializer
  19. from utilities.api import get_serializer_for_model
  20. from virtualization.api.nested_serializers import NestedClusterSerializer
  21. from wireless.api.nested_serializers import NestedWirelessLANSerializer, NestedWirelessLinkSerializer
  22. from wireless.choices import *
  23. from wireless.models import WirelessLAN
  24. from .nested_serializers import *
  25. class LinkTerminationSerializer(serializers.ModelSerializer):
  26. link_peer_type = serializers.SerializerMethodField(read_only=True)
  27. link_peer = serializers.SerializerMethodField(read_only=True)
  28. _occupied = serializers.SerializerMethodField(read_only=True)
  29. def get_link_peer_type(self, obj):
  30. if obj._link_peer is not None:
  31. return f'{obj._link_peer._meta.app_label}.{obj._link_peer._meta.model_name}'
  32. return None
  33. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  34. def get_link_peer(self, obj):
  35. """
  36. Return the appropriate serializer for the link termination model.
  37. """
  38. if obj._link_peer is not None:
  39. serializer = get_serializer_for_model(obj._link_peer, prefix='Nested')
  40. context = {'request': self.context['request']}
  41. return serializer(obj._link_peer, context=context).data
  42. return None
  43. @swagger_serializer_method(serializer_or_field=serializers.BooleanField)
  44. def get__occupied(self, obj):
  45. return obj._occupied
  46. class ConnectedEndpointSerializer(serializers.ModelSerializer):
  47. connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
  48. connected_endpoint = serializers.SerializerMethodField(read_only=True)
  49. connected_endpoint_reachable = serializers.SerializerMethodField(read_only=True)
  50. def get_connected_endpoint_type(self, obj):
  51. if obj._path is not None and obj._path.destination is not None:
  52. return f'{obj._path.destination._meta.app_label}.{obj._path.destination._meta.model_name}'
  53. return None
  54. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  55. def get_connected_endpoint(self, obj):
  56. """
  57. Return the appropriate serializer for the type of connected object.
  58. """
  59. if obj._path is not None and obj._path.destination is not None:
  60. serializer = get_serializer_for_model(obj._path.destination, prefix='Nested')
  61. context = {'request': self.context['request']}
  62. return serializer(obj._path.destination, context=context).data
  63. return None
  64. @swagger_serializer_method(serializer_or_field=serializers.BooleanField)
  65. def get_connected_endpoint_reachable(self, obj):
  66. if obj._path is not None:
  67. return obj._path.is_active
  68. return None
  69. #
  70. # Regions/sites
  71. #
  72. class RegionSerializer(NestedGroupModelSerializer):
  73. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
  74. parent = NestedRegionSerializer(required=False, allow_null=True, default=None)
  75. site_count = serializers.IntegerField(read_only=True)
  76. class Meta:
  77. model = Region
  78. fields = [
  79. 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
  80. 'last_updated', 'site_count', '_depth',
  81. ]
  82. class SiteGroupSerializer(NestedGroupModelSerializer):
  83. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
  84. parent = NestedSiteGroupSerializer(required=False, allow_null=True, default=None)
  85. site_count = serializers.IntegerField(read_only=True)
  86. class Meta:
  87. model = SiteGroup
  88. fields = [
  89. 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
  90. 'last_updated', 'site_count', '_depth',
  91. ]
  92. class SiteSerializer(NetBoxModelSerializer):
  93. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
  94. status = ChoiceField(choices=SiteStatusChoices, required=False)
  95. region = NestedRegionSerializer(required=False, allow_null=True)
  96. group = NestedSiteGroupSerializer(required=False, allow_null=True)
  97. tenant = NestedTenantSerializer(required=False, allow_null=True)
  98. time_zone = TimeZoneSerializerField(required=False)
  99. asns = SerializedPKRelatedField(
  100. queryset=ASN.objects.all(),
  101. serializer=NestedASNSerializer,
  102. required=False,
  103. many=True
  104. )
  105. # Related object counts
  106. circuit_count = serializers.IntegerField(read_only=True)
  107. device_count = serializers.IntegerField(read_only=True)
  108. prefix_count = serializers.IntegerField(read_only=True)
  109. rack_count = serializers.IntegerField(read_only=True)
  110. virtualmachine_count = serializers.IntegerField(read_only=True)
  111. vlan_count = serializers.IntegerField(read_only=True)
  112. class Meta:
  113. model = Site
  114. fields = [
  115. 'id', 'url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'time_zone',
  116. 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'asns', 'tags',
  117. 'custom_fields', 'created', 'last_updated', 'circuit_count', 'device_count', 'prefix_count', 'rack_count',
  118. 'virtualmachine_count', 'vlan_count',
  119. ]
  120. #
  121. # Racks
  122. #
  123. class LocationSerializer(NestedGroupModelSerializer):
  124. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
  125. site = NestedSiteSerializer()
  126. parent = NestedLocationSerializer(required=False, allow_null=True)
  127. tenant = NestedTenantSerializer(required=False, allow_null=True)
  128. rack_count = serializers.IntegerField(read_only=True)
  129. device_count = serializers.IntegerField(read_only=True)
  130. class Meta:
  131. model = Location
  132. fields = [
  133. 'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'tenant', 'description', 'tags', 'custom_fields',
  134. 'created', 'last_updated', 'rack_count', 'device_count', '_depth',
  135. ]
  136. class RackRoleSerializer(NetBoxModelSerializer):
  137. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
  138. rack_count = serializers.IntegerField(read_only=True)
  139. class Meta:
  140. model = RackRole
  141. fields = [
  142. 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
  143. 'last_updated', 'rack_count',
  144. ]
  145. class RackSerializer(NetBoxModelSerializer):
  146. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
  147. site = NestedSiteSerializer()
  148. location = NestedLocationSerializer(required=False, allow_null=True, default=None)
  149. tenant = NestedTenantSerializer(required=False, allow_null=True)
  150. status = ChoiceField(choices=RackStatusChoices, required=False)
  151. role = NestedRackRoleSerializer(required=False, allow_null=True)
  152. type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False)
  153. facility_id = serializers.CharField(max_length=50, allow_blank=True, allow_null=True, label='Facility ID',
  154. default=None)
  155. width = ChoiceField(choices=RackWidthChoices, required=False)
  156. outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False)
  157. device_count = serializers.IntegerField(read_only=True)
  158. powerfeed_count = serializers.IntegerField(read_only=True)
  159. class Meta:
  160. model = Rack
  161. fields = [
  162. 'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
  163. 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
  164. 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
  165. ]
  166. class RackUnitSerializer(serializers.Serializer):
  167. """
  168. A rack unit is an abstraction formed by the set (rack, position, face); it does not exist as a row in the database.
  169. """
  170. id = serializers.IntegerField(read_only=True)
  171. name = serializers.CharField(read_only=True)
  172. face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
  173. device = NestedDeviceSerializer(read_only=True)
  174. occupied = serializers.BooleanField(read_only=True)
  175. display = serializers.SerializerMethodField(read_only=True)
  176. def get_display(self, obj):
  177. return obj['name']
  178. class RackReservationSerializer(NetBoxModelSerializer):
  179. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
  180. rack = NestedRackSerializer()
  181. user = NestedUserSerializer()
  182. tenant = NestedTenantSerializer(required=False, allow_null=True)
  183. class Meta:
  184. model = RackReservation
  185. fields = [
  186. 'id', 'url', 'display', 'rack', 'units', 'created', 'last_updated', 'user', 'tenant', 'description', 'tags',
  187. 'custom_fields',
  188. ]
  189. class RackElevationDetailFilterSerializer(serializers.Serializer):
  190. q = serializers.CharField(
  191. required=False,
  192. default=None
  193. )
  194. face = serializers.ChoiceField(
  195. choices=DeviceFaceChoices,
  196. default=DeviceFaceChoices.FACE_FRONT
  197. )
  198. render = serializers.ChoiceField(
  199. choices=RackElevationDetailRenderChoices,
  200. default=RackElevationDetailRenderChoices.RENDER_JSON
  201. )
  202. unit_width = serializers.IntegerField(
  203. default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_WIDTH')
  204. )
  205. unit_height = serializers.IntegerField(
  206. default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT')
  207. )
  208. legend_width = serializers.IntegerField(
  209. default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT
  210. )
  211. exclude = serializers.IntegerField(
  212. required=False,
  213. default=None
  214. )
  215. expand_devices = serializers.BooleanField(
  216. required=False,
  217. default=True
  218. )
  219. include_images = serializers.BooleanField(
  220. required=False,
  221. default=True
  222. )
  223. #
  224. # Device/module types
  225. #
  226. class ManufacturerSerializer(NetBoxModelSerializer):
  227. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
  228. devicetype_count = serializers.IntegerField(read_only=True)
  229. inventoryitem_count = serializers.IntegerField(read_only=True)
  230. platform_count = serializers.IntegerField(read_only=True)
  231. class Meta:
  232. model = Manufacturer
  233. fields = [
  234. 'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  235. 'devicetype_count', 'inventoryitem_count', 'platform_count',
  236. ]
  237. class DeviceTypeSerializer(NetBoxModelSerializer):
  238. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
  239. manufacturer = NestedManufacturerSerializer()
  240. subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
  241. airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
  242. device_count = serializers.IntegerField(read_only=True)
  243. class Meta:
  244. model = DeviceType
  245. fields = [
  246. 'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
  247. 'subdevice_role', 'airflow', 'front_image', 'rear_image', 'comments', 'tags', 'custom_fields', 'created',
  248. 'last_updated', 'device_count',
  249. ]
  250. class ModuleTypeSerializer(NetBoxModelSerializer):
  251. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:moduletype-detail')
  252. manufacturer = NestedManufacturerSerializer()
  253. # module_count = serializers.IntegerField(read_only=True)
  254. class Meta:
  255. model = ModuleType
  256. fields = [
  257. 'id', 'url', 'display', 'manufacturer', 'model', 'part_number', 'comments', 'tags', 'custom_fields',
  258. 'created', 'last_updated',
  259. ]
  260. #
  261. # Component templates
  262. #
  263. class ConsolePortTemplateSerializer(ValidatedModelSerializer):
  264. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleporttemplate-detail')
  265. device_type = NestedDeviceTypeSerializer()
  266. type = ChoiceField(
  267. choices=ConsolePortTypeChoices,
  268. allow_blank=True,
  269. required=False
  270. )
  271. class Meta:
  272. model = ConsolePortTemplate
  273. fields = [
  274. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated',
  275. ]
  276. class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
  277. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverporttemplate-detail')
  278. device_type = NestedDeviceTypeSerializer()
  279. type = ChoiceField(
  280. choices=ConsolePortTypeChoices,
  281. allow_blank=True,
  282. required=False
  283. )
  284. class Meta:
  285. model = ConsoleServerPortTemplate
  286. fields = [
  287. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated',
  288. ]
  289. class PowerPortTemplateSerializer(ValidatedModelSerializer):
  290. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
  291. device_type = NestedDeviceTypeSerializer()
  292. type = ChoiceField(
  293. choices=PowerPortTypeChoices,
  294. allow_blank=True,
  295. required=False
  296. )
  297. class Meta:
  298. model = PowerPortTemplate
  299. fields = [
  300. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
  301. 'description', 'created', 'last_updated',
  302. ]
  303. class PowerOutletTemplateSerializer(ValidatedModelSerializer):
  304. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlettemplate-detail')
  305. device_type = NestedDeviceTypeSerializer()
  306. type = ChoiceField(
  307. choices=PowerOutletTypeChoices,
  308. allow_blank=True,
  309. required=False
  310. )
  311. power_port = NestedPowerPortTemplateSerializer(
  312. required=False,
  313. allow_null=True
  314. )
  315. feed_leg = ChoiceField(
  316. choices=PowerOutletFeedLegChoices,
  317. allow_blank=True,
  318. required=False
  319. )
  320. class Meta:
  321. model = PowerOutletTemplate
  322. fields = [
  323. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
  324. 'created', 'last_updated',
  325. ]
  326. class InterfaceTemplateSerializer(ValidatedModelSerializer):
  327. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfacetemplate-detail')
  328. device_type = NestedDeviceTypeSerializer()
  329. type = ChoiceField(choices=InterfaceTypeChoices)
  330. class Meta:
  331. model = InterfaceTemplate
  332. fields = [
  333. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', 'created',
  334. 'last_updated',
  335. ]
  336. class RearPortTemplateSerializer(ValidatedModelSerializer):
  337. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
  338. device_type = NestedDeviceTypeSerializer()
  339. type = ChoiceField(choices=PortTypeChoices)
  340. class Meta:
  341. model = RearPortTemplate
  342. fields = [
  343. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description',
  344. 'created', 'last_updated',
  345. ]
  346. class FrontPortTemplateSerializer(ValidatedModelSerializer):
  347. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
  348. device_type = NestedDeviceTypeSerializer()
  349. type = ChoiceField(choices=PortTypeChoices)
  350. rear_port = NestedRearPortTemplateSerializer()
  351. class Meta:
  352. model = FrontPortTemplate
  353. fields = [
  354. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
  355. 'description', 'created', 'last_updated',
  356. ]
  357. class ModuleBayTemplateSerializer(ValidatedModelSerializer):
  358. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebaytemplate-detail')
  359. device_type = NestedDeviceTypeSerializer()
  360. class Meta:
  361. model = ModuleBayTemplate
  362. fields = [
  363. 'id', 'url', 'display', 'device_type', 'name', 'label', 'position', 'description', 'created',
  364. 'last_updated',
  365. ]
  366. class DeviceBayTemplateSerializer(ValidatedModelSerializer):
  367. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
  368. device_type = NestedDeviceTypeSerializer()
  369. class Meta:
  370. model = DeviceBayTemplate
  371. fields = ['id', 'url', 'display', 'device_type', 'name', 'label', 'description', 'created', 'last_updated']
  372. class InventoryItemTemplateSerializer(ValidatedModelSerializer):
  373. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemtemplate-detail')
  374. device_type = NestedDeviceTypeSerializer()
  375. parent = serializers.PrimaryKeyRelatedField(
  376. queryset=InventoryItemTemplate.objects.all(),
  377. allow_null=True,
  378. default=None
  379. )
  380. role = NestedInventoryItemRoleSerializer(required=False, allow_null=True)
  381. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
  382. component_type = ContentTypeField(
  383. queryset=ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS),
  384. required=False,
  385. allow_null=True
  386. )
  387. component = serializers.SerializerMethodField(read_only=True)
  388. _depth = serializers.IntegerField(source='level', read_only=True)
  389. class Meta:
  390. model = InventoryItemTemplate
  391. fields = [
  392. 'id', 'url', 'display', 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id',
  393. 'description', 'component_type', 'component_id', 'component', 'created', 'last_updated', '_depth',
  394. ]
  395. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  396. def get_component(self, obj):
  397. if obj.component is None:
  398. return None
  399. serializer = get_serializer_for_model(obj.component, prefix='Nested')
  400. context = {'request': self.context['request']}
  401. return serializer(obj.component, context=context).data
  402. #
  403. # Devices
  404. #
  405. class DeviceRoleSerializer(NetBoxModelSerializer):
  406. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
  407. device_count = serializers.IntegerField(read_only=True)
  408. virtualmachine_count = serializers.IntegerField(read_only=True)
  409. class Meta:
  410. model = DeviceRole
  411. fields = [
  412. 'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'description', 'tags', 'custom_fields',
  413. 'created', 'last_updated', 'device_count', 'virtualmachine_count',
  414. ]
  415. class PlatformSerializer(NetBoxModelSerializer):
  416. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
  417. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
  418. device_count = serializers.IntegerField(read_only=True)
  419. virtualmachine_count = serializers.IntegerField(read_only=True)
  420. class Meta:
  421. model = Platform
  422. fields = [
  423. 'id', 'url', 'display', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description',
  424. 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
  425. ]
  426. class DeviceSerializer(NetBoxModelSerializer):
  427. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
  428. device_type = NestedDeviceTypeSerializer()
  429. device_role = NestedDeviceRoleSerializer()
  430. tenant = NestedTenantSerializer(required=False, allow_null=True, default=None)
  431. platform = NestedPlatformSerializer(required=False, allow_null=True)
  432. site = NestedSiteSerializer()
  433. location = NestedLocationSerializer(required=False, allow_null=True, default=None)
  434. rack = NestedRackSerializer(required=False, allow_null=True, default=None)
  435. face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, default='')
  436. position = serializers.IntegerField(allow_null=True, label='Position (U)', min_value=1, default=None)
  437. status = ChoiceField(choices=DeviceStatusChoices, required=False)
  438. airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
  439. primary_ip = NestedIPAddressSerializer(read_only=True)
  440. primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
  441. primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
  442. parent_device = serializers.SerializerMethodField()
  443. cluster = NestedClusterSerializer(required=False, allow_null=True)
  444. virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True, default=None)
  445. vc_position = serializers.IntegerField(allow_null=True, max_value=255, min_value=0, default=None)
  446. class Meta:
  447. model = Device
  448. fields = [
  449. 'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  450. 'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'airflow', 'primary_ip',
  451. 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments',
  452. 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
  453. ]
  454. @swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
  455. def get_parent_device(self, obj):
  456. try:
  457. device_bay = obj.parent_bay
  458. except DeviceBay.DoesNotExist:
  459. return None
  460. context = {'request': self.context['request']}
  461. data = NestedDeviceSerializer(instance=device_bay.device, context=context).data
  462. data['device_bay'] = NestedDeviceBaySerializer(instance=device_bay, context=context).data
  463. return data
  464. class ModuleSerializer(NetBoxModelSerializer):
  465. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:module-detail')
  466. device = NestedDeviceSerializer()
  467. module_bay = NestedModuleBaySerializer()
  468. module_type = NestedModuleTypeSerializer()
  469. class Meta:
  470. model = Module
  471. fields = [
  472. 'id', 'url', 'display', 'device', 'module_bay', 'module_type', 'serial', 'asset_tag', 'comments', 'tags',
  473. 'custom_fields', 'created', 'last_updated',
  474. ]
  475. class DeviceWithConfigContextSerializer(DeviceSerializer):
  476. config_context = serializers.SerializerMethodField()
  477. class Meta(DeviceSerializer.Meta):
  478. fields = [
  479. 'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  480. 'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'airflow', 'primary_ip',
  481. 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments',
  482. 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created', 'last_updated',
  483. ]
  484. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  485. def get_config_context(self, obj):
  486. return obj.get_config_context()
  487. class DeviceNAPALMSerializer(serializers.Serializer):
  488. method = serializers.DictField()
  489. #
  490. # Device components
  491. #
  492. class ConsoleServerPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  493. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
  494. device = NestedDeviceSerializer()
  495. module = ComponentNestedModuleSerializer(
  496. required=False,
  497. allow_null=True
  498. )
  499. type = ChoiceField(
  500. choices=ConsolePortTypeChoices,
  501. allow_blank=True,
  502. required=False
  503. )
  504. speed = ChoiceField(
  505. choices=ConsolePortSpeedChoices,
  506. allow_null=True,
  507. required=False
  508. )
  509. cable = NestedCableSerializer(read_only=True)
  510. class Meta:
  511. model = ConsoleServerPort
  512. fields = [
  513. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
  514. 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type',
  515. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  516. ]
  517. class ConsolePortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  518. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
  519. device = NestedDeviceSerializer()
  520. module = ComponentNestedModuleSerializer(
  521. required=False,
  522. allow_null=True
  523. )
  524. type = ChoiceField(
  525. choices=ConsolePortTypeChoices,
  526. allow_blank=True,
  527. required=False
  528. )
  529. speed = ChoiceField(
  530. choices=ConsolePortSpeedChoices,
  531. allow_null=True,
  532. required=False
  533. )
  534. cable = NestedCableSerializer(read_only=True)
  535. class Meta:
  536. model = ConsolePort
  537. fields = [
  538. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'speed', 'description',
  539. 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type',
  540. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  541. ]
  542. class PowerOutletSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  543. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
  544. device = NestedDeviceSerializer()
  545. module = ComponentNestedModuleSerializer(
  546. required=False,
  547. allow_null=True
  548. )
  549. type = ChoiceField(
  550. choices=PowerOutletTypeChoices,
  551. allow_blank=True,
  552. required=False
  553. )
  554. power_port = NestedPowerPortSerializer(
  555. required=False,
  556. allow_null=True
  557. )
  558. feed_leg = ChoiceField(
  559. choices=PowerOutletFeedLegChoices,
  560. allow_blank=True,
  561. required=False
  562. )
  563. cable = NestedCableSerializer(
  564. read_only=True
  565. )
  566. class Meta:
  567. model = PowerOutlet
  568. fields = [
  569. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'power_port', 'feed_leg',
  570. 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint',
  571. 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
  572. 'last_updated', '_occupied',
  573. ]
  574. class PowerPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  575. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
  576. device = NestedDeviceSerializer()
  577. module = ComponentNestedModuleSerializer(
  578. required=False,
  579. allow_null=True
  580. )
  581. type = ChoiceField(
  582. choices=PowerPortTypeChoices,
  583. allow_blank=True,
  584. required=False
  585. )
  586. cable = NestedCableSerializer(read_only=True)
  587. class Meta:
  588. model = PowerPort
  589. fields = [
  590. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
  591. 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint',
  592. 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
  593. 'last_updated', '_occupied',
  594. ]
  595. class InterfaceSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  596. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
  597. device = NestedDeviceSerializer()
  598. module = ComponentNestedModuleSerializer(
  599. required=False,
  600. allow_null=True
  601. )
  602. type = ChoiceField(choices=InterfaceTypeChoices)
  603. parent = NestedInterfaceSerializer(required=False, allow_null=True)
  604. bridge = NestedInterfaceSerializer(required=False, allow_null=True)
  605. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  606. mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_blank=True)
  607. duplex = ChoiceField(choices=InterfaceDuplexChoices, required=False, allow_blank=True)
  608. rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_blank=True)
  609. rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False, allow_blank=True)
  610. untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
  611. tagged_vlans = SerializedPKRelatedField(
  612. queryset=VLAN.objects.all(),
  613. serializer=NestedVLANSerializer,
  614. required=False,
  615. many=True
  616. )
  617. vrf = NestedVRFSerializer(required=False, allow_null=True)
  618. cable = NestedCableSerializer(read_only=True)
  619. wireless_link = NestedWirelessLinkSerializer(read_only=True)
  620. wireless_lans = SerializedPKRelatedField(
  621. queryset=WirelessLAN.objects.all(),
  622. serializer=NestedWirelessLANSerializer,
  623. required=False,
  624. many=True
  625. )
  626. count_ipaddresses = serializers.IntegerField(read_only=True)
  627. count_fhrp_groups = serializers.IntegerField(read_only=True)
  628. class Meta:
  629. model = Interface
  630. fields = [
  631. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag',
  632. 'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel',
  633. 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected',
  634. 'cable', 'wireless_link', 'link_peer', 'link_peer_type', 'wireless_lans', 'vrf', 'connected_endpoint',
  635. 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
  636. 'last_updated', 'count_ipaddresses', 'count_fhrp_groups', '_occupied',
  637. ]
  638. def validate(self, data):
  639. # Validate many-to-many VLAN assignments
  640. device = self.instance.device if self.instance else data.get('device')
  641. for vlan in data.get('tagged_vlans', []):
  642. if vlan.site not in [device.site, None]:
  643. raise serializers.ValidationError({
  644. 'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent device, or "
  645. f"it must be global."
  646. })
  647. return super().validate(data)
  648. class RearPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
  649. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  650. device = NestedDeviceSerializer()
  651. module = ComponentNestedModuleSerializer(
  652. required=False,
  653. allow_null=True
  654. )
  655. type = ChoiceField(choices=PortTypeChoices)
  656. cable = NestedCableSerializer(read_only=True)
  657. class Meta:
  658. model = RearPort
  659. fields = [
  660. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'description',
  661. 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', 'custom_fields', 'created',
  662. 'last_updated', '_occupied',
  663. ]
  664. class FrontPortRearPortSerializer(WritableNestedSerializer):
  665. """
  666. NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
  667. """
  668. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  669. class Meta:
  670. model = RearPort
  671. fields = ['id', 'url', 'display', 'name', 'label']
  672. class FrontPortSerializer(NetBoxModelSerializer, LinkTerminationSerializer):
  673. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
  674. device = NestedDeviceSerializer()
  675. module = ComponentNestedModuleSerializer(
  676. required=False,
  677. allow_null=True
  678. )
  679. type = ChoiceField(choices=PortTypeChoices)
  680. rear_port = FrontPortRearPortSerializer()
  681. cable = NestedCableSerializer(read_only=True)
  682. class Meta:
  683. model = FrontPort
  684. fields = [
  685. 'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port',
  686. 'rear_port_position', 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags',
  687. 'custom_fields', 'created', 'last_updated', '_occupied',
  688. ]
  689. class ModuleBaySerializer(NetBoxModelSerializer):
  690. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:modulebay-detail')
  691. device = NestedDeviceSerializer()
  692. # installed_module = NestedModuleSerializer(required=False, allow_null=True)
  693. class Meta:
  694. model = ModuleBay
  695. fields = [
  696. 'id', 'url', 'display', 'device', 'name', 'label', 'position', 'description', 'tags', 'custom_fields',
  697. 'created', 'last_updated',
  698. ]
  699. class DeviceBaySerializer(NetBoxModelSerializer):
  700. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
  701. device = NestedDeviceSerializer()
  702. installed_device = NestedDeviceSerializer(required=False, allow_null=True)
  703. class Meta:
  704. model = DeviceBay
  705. fields = [
  706. 'id', 'url', 'display', 'device', 'name', 'label', 'description', 'installed_device', 'tags',
  707. 'custom_fields', 'created', 'last_updated',
  708. ]
  709. class InventoryItemSerializer(NetBoxModelSerializer):
  710. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
  711. device = NestedDeviceSerializer()
  712. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  713. role = NestedInventoryItemRoleSerializer(required=False, allow_null=True)
  714. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
  715. component_type = ContentTypeField(
  716. queryset=ContentType.objects.filter(MODULAR_COMPONENT_MODELS),
  717. required=False,
  718. allow_null=True
  719. )
  720. component = serializers.SerializerMethodField(read_only=True)
  721. _depth = serializers.IntegerField(source='level', read_only=True)
  722. class Meta:
  723. model = InventoryItem
  724. fields = [
  725. 'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial',
  726. 'asset_tag', 'discovered', 'description', 'component_type', 'component_id', 'component', 'tags',
  727. 'custom_fields', 'created', 'last_updated', '_depth',
  728. ]
  729. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  730. def get_component(self, obj):
  731. if obj.component is None:
  732. return None
  733. serializer = get_serializer_for_model(obj.component, prefix='Nested')
  734. context = {'request': self.context['request']}
  735. return serializer(obj.component, context=context).data
  736. #
  737. # Device component roles
  738. #
  739. class InventoryItemRoleSerializer(NetBoxModelSerializer):
  740. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitemrole-detail')
  741. inventoryitem_count = serializers.IntegerField(read_only=True)
  742. class Meta:
  743. model = InventoryItemRole
  744. fields = [
  745. 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
  746. 'last_updated', 'inventoryitem_count',
  747. ]
  748. #
  749. # Cables
  750. #
  751. class CableSerializer(NetBoxModelSerializer):
  752. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  753. termination_a_type = ContentTypeField(
  754. queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
  755. )
  756. termination_b_type = ContentTypeField(
  757. queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
  758. )
  759. termination_a = serializers.SerializerMethodField(read_only=True)
  760. termination_b = serializers.SerializerMethodField(read_only=True)
  761. status = ChoiceField(choices=LinkStatusChoices, required=False)
  762. tenant = NestedTenantSerializer(required=False, allow_null=True)
  763. length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
  764. class Meta:
  765. model = Cable
  766. fields = [
  767. 'id', 'url', 'display', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
  768. 'termination_b_id', 'termination_b', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit',
  769. 'tags', 'custom_fields', 'created', 'last_updated',
  770. ]
  771. def _get_termination(self, obj, side):
  772. """
  773. Serialize a nested representation of a termination.
  774. """
  775. if side.lower() not in ['a', 'b']:
  776. raise ValueError("Termination side must be either A or B.")
  777. termination = getattr(obj, 'termination_{}'.format(side.lower()))
  778. if termination is None:
  779. return None
  780. serializer = get_serializer_for_model(termination, prefix='Nested')
  781. context = {'request': self.context['request']}
  782. data = serializer(termination, context=context).data
  783. return data
  784. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  785. def get_termination_a(self, obj):
  786. return self._get_termination(obj, 'a')
  787. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  788. def get_termination_b(self, obj):
  789. return self._get_termination(obj, 'b')
  790. class TracedCableSerializer(serializers.ModelSerializer):
  791. """
  792. Used only while tracing a cable path.
  793. """
  794. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  795. class Meta:
  796. model = Cable
  797. fields = [
  798. 'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  799. ]
  800. class CablePathSerializer(serializers.ModelSerializer):
  801. origin_type = ContentTypeField(read_only=True)
  802. origin = serializers.SerializerMethodField(read_only=True)
  803. destination_type = ContentTypeField(read_only=True)
  804. destination = serializers.SerializerMethodField(read_only=True)
  805. path = serializers.SerializerMethodField(read_only=True)
  806. class Meta:
  807. model = CablePath
  808. fields = [
  809. 'id', 'origin_type', 'origin', 'destination_type', 'destination', 'path', 'is_active', 'is_split',
  810. ]
  811. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  812. def get_origin(self, obj):
  813. """
  814. Return the appropriate serializer for the origin.
  815. """
  816. serializer = get_serializer_for_model(obj.origin, prefix='Nested')
  817. context = {'request': self.context['request']}
  818. return serializer(obj.origin, context=context).data
  819. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  820. def get_destination(self, obj):
  821. """
  822. Return the appropriate serializer for the destination, if any.
  823. """
  824. if obj.destination_id is not None:
  825. serializer = get_serializer_for_model(obj.destination, prefix='Nested')
  826. context = {'request': self.context['request']}
  827. return serializer(obj.destination, context=context).data
  828. return None
  829. @swagger_serializer_method(serializer_or_field=serializers.ListField)
  830. def get_path(self, obj):
  831. ret = []
  832. for node in obj.get_path():
  833. serializer = get_serializer_for_model(node, prefix='Nested')
  834. context = {'request': self.context['request']}
  835. ret.append(serializer(node, context=context).data)
  836. return ret
  837. #
  838. # Virtual chassis
  839. #
  840. class VirtualChassisSerializer(NetBoxModelSerializer):
  841. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
  842. master = NestedDeviceSerializer(required=False)
  843. member_count = serializers.IntegerField(read_only=True)
  844. class Meta:
  845. model = VirtualChassis
  846. fields = [
  847. 'id', 'url', 'display', 'name', 'domain', 'master', 'tags', 'custom_fields', 'member_count',
  848. 'created', 'last_updated',
  849. ]
  850. #
  851. # Power panels
  852. #
  853. class PowerPanelSerializer(NetBoxModelSerializer):
  854. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
  855. site = NestedSiteSerializer()
  856. location = NestedLocationSerializer(
  857. required=False,
  858. allow_null=True,
  859. default=None
  860. )
  861. powerfeed_count = serializers.IntegerField(read_only=True)
  862. class Meta:
  863. model = PowerPanel
  864. fields = [
  865. 'id', 'url', 'display', 'site', 'location', 'name', 'tags', 'custom_fields', 'powerfeed_count',
  866. 'created', 'last_updated',
  867. ]
  868. class PowerFeedSerializer(NetBoxModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  869. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
  870. power_panel = NestedPowerPanelSerializer()
  871. rack = NestedRackSerializer(
  872. required=False,
  873. allow_null=True,
  874. default=None
  875. )
  876. type = ChoiceField(
  877. choices=PowerFeedTypeChoices,
  878. default=PowerFeedTypeChoices.TYPE_PRIMARY
  879. )
  880. status = ChoiceField(
  881. choices=PowerFeedStatusChoices,
  882. default=PowerFeedStatusChoices.STATUS_ACTIVE
  883. )
  884. supply = ChoiceField(
  885. choices=PowerFeedSupplyChoices,
  886. default=PowerFeedSupplyChoices.SUPPLY_AC
  887. )
  888. phase = ChoiceField(
  889. choices=PowerFeedPhaseChoices,
  890. default=PowerFeedPhaseChoices.PHASE_SINGLE
  891. )
  892. cable = NestedCableSerializer(read_only=True)
  893. class Meta:
  894. model = PowerFeed
  895. fields = [
  896. 'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
  897. 'amperage', 'max_utilization', 'comments', 'mark_connected', 'cable', 'link_peer', 'link_peer_type',
  898. 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields',
  899. 'created', 'last_updated', '_occupied',
  900. ]