serializers.py 36 KB

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