serializers.py 35 KB

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