serializers.py 46 KB

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