serializers.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  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 rest_framework.validators import UniqueTogetherValidator
  6. from dcim.choices import *
  7. from dcim.constants import *
  8. from dcim.models import (
  9. Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
  10. DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
  11. Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
  12. PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
  13. VirtualChassis,
  14. )
  15. from extras.api.customfields import CustomFieldModelSerializer
  16. from extras.api.serializers import TaggedObjectSerializer
  17. from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
  18. from ipam.models import VLAN
  19. from tenancy.api.nested_serializers import NestedTenantSerializer
  20. from users.api.nested_serializers import NestedUserSerializer
  21. from utilities.api import (
  22. ChoiceField, ContentTypeField, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer,
  23. WritableNestedSerializer, get_serializer_for_model,
  24. )
  25. from virtualization.api.nested_serializers import NestedClusterSerializer
  26. from .nested_serializers import *
  27. class ConnectedEndpointSerializer(ValidatedModelSerializer):
  28. connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
  29. connected_endpoint = serializers.SerializerMethodField(read_only=True)
  30. connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True)
  31. def get_connected_endpoint_type(self, obj):
  32. if hasattr(obj, 'connected_endpoint') and obj.connected_endpoint is not None:
  33. return '{}.{}'.format(
  34. obj.connected_endpoint._meta.app_label,
  35. obj.connected_endpoint._meta.model_name
  36. )
  37. return None
  38. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  39. def get_connected_endpoint(self, obj):
  40. """
  41. Return the appropriate serializer for the type of connected object.
  42. """
  43. if getattr(obj, 'connected_endpoint', None) is None:
  44. return None
  45. serializer = get_serializer_for_model(obj.connected_endpoint, prefix='Nested')
  46. context = {'request': self.context['request']}
  47. data = serializer(obj.connected_endpoint, context=context).data
  48. return data
  49. #
  50. # Regions/sites
  51. #
  52. class RegionSerializer(serializers.ModelSerializer):
  53. parent = NestedRegionSerializer(required=False, allow_null=True)
  54. site_count = serializers.IntegerField(read_only=True)
  55. class Meta:
  56. model = Region
  57. fields = ['id', 'name', 'slug', 'parent', 'description', 'site_count']
  58. class SiteSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
  59. status = ChoiceField(choices=SiteStatusChoices, required=False)
  60. region = NestedRegionSerializer(required=False, allow_null=True)
  61. tenant = NestedTenantSerializer(required=False, allow_null=True)
  62. time_zone = TimeZoneField(required=False)
  63. circuit_count = serializers.IntegerField(read_only=True)
  64. device_count = serializers.IntegerField(read_only=True)
  65. prefix_count = serializers.IntegerField(read_only=True)
  66. rack_count = serializers.IntegerField(read_only=True)
  67. virtualmachine_count = serializers.IntegerField(read_only=True)
  68. vlan_count = serializers.IntegerField(read_only=True)
  69. class Meta:
  70. model = Site
  71. fields = [
  72. 'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
  73. 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
  74. 'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
  75. 'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
  76. ]
  77. #
  78. # Racks
  79. #
  80. class RackGroupSerializer(ValidatedModelSerializer):
  81. site = NestedSiteSerializer()
  82. parent = NestedRackGroupSerializer(required=False, allow_null=True)
  83. rack_count = serializers.IntegerField(read_only=True)
  84. class Meta:
  85. model = RackGroup
  86. fields = ['id', 'name', 'slug', 'site', 'parent', 'description', 'rack_count']
  87. class RackRoleSerializer(ValidatedModelSerializer):
  88. rack_count = serializers.IntegerField(read_only=True)
  89. class Meta:
  90. model = RackRole
  91. fields = ['id', 'name', 'slug', 'color', 'description', 'rack_count']
  92. class RackSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
  93. site = NestedSiteSerializer()
  94. group = NestedRackGroupSerializer(required=False, allow_null=True, default=None)
  95. tenant = NestedTenantSerializer(required=False, allow_null=True)
  96. status = ChoiceField(choices=RackStatusChoices, required=False)
  97. role = NestedRackRoleSerializer(required=False, allow_null=True)
  98. type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False)
  99. width = ChoiceField(choices=RackWidthChoices, required=False)
  100. outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False)
  101. device_count = serializers.IntegerField(read_only=True)
  102. powerfeed_count = serializers.IntegerField(read_only=True)
  103. class Meta:
  104. model = Rack
  105. fields = [
  106. 'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial',
  107. 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
  108. 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
  109. ]
  110. # Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
  111. # prevents facility_id from being interpreted as a required field.
  112. validators = [
  113. UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('group', 'name'))
  114. ]
  115. def validate(self, data):
  116. # Validate uniqueness of (group, facility_id) since we omitted the automatically-created validator from Meta.
  117. if data.get('facility_id', None):
  118. validator = UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('group', 'facility_id'))
  119. validator(data, self)
  120. # Enforce model validation
  121. super().validate(data)
  122. return data
  123. class RackUnitSerializer(serializers.Serializer):
  124. """
  125. A rack unit is an abstraction formed by the set (rack, position, face); it does not exist as a row in the database.
  126. """
  127. id = serializers.IntegerField(read_only=True)
  128. name = serializers.CharField(read_only=True)
  129. face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
  130. device = NestedDeviceSerializer(read_only=True)
  131. class RackReservationSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  132. rack = NestedRackSerializer()
  133. user = NestedUserSerializer()
  134. tenant = NestedTenantSerializer(required=False, allow_null=True)
  135. class Meta:
  136. model = RackReservation
  137. fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description', 'tags']
  138. class RackElevationDetailFilterSerializer(serializers.Serializer):
  139. q = serializers.CharField(
  140. required=False,
  141. default=None
  142. )
  143. face = serializers.ChoiceField(
  144. choices=DeviceFaceChoices,
  145. default=DeviceFaceChoices.FACE_FRONT
  146. )
  147. render = serializers.ChoiceField(
  148. choices=RackElevationDetailRenderChoices,
  149. default=RackElevationDetailRenderChoices.RENDER_JSON
  150. )
  151. unit_width = serializers.IntegerField(
  152. default=settings.RACK_ELEVATION_DEFAULT_UNIT_WIDTH
  153. )
  154. unit_height = serializers.IntegerField(
  155. default=settings.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT
  156. )
  157. legend_width = serializers.IntegerField(
  158. default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT
  159. )
  160. exclude = serializers.IntegerField(
  161. required=False,
  162. default=None
  163. )
  164. expand_devices = serializers.BooleanField(
  165. required=False,
  166. default=True
  167. )
  168. include_images = serializers.BooleanField(
  169. required=False,
  170. default=True
  171. )
  172. #
  173. # Device types
  174. #
  175. class ManufacturerSerializer(ValidatedModelSerializer):
  176. devicetype_count = serializers.IntegerField(read_only=True)
  177. inventoryitem_count = serializers.IntegerField(read_only=True)
  178. platform_count = serializers.IntegerField(read_only=True)
  179. class Meta:
  180. model = Manufacturer
  181. fields = [
  182. 'id', 'name', 'slug', 'description', 'devicetype_count', 'inventoryitem_count', 'platform_count',
  183. ]
  184. class DeviceTypeSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
  185. manufacturer = NestedManufacturerSerializer()
  186. subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
  187. device_count = serializers.IntegerField(read_only=True)
  188. class Meta:
  189. model = DeviceType
  190. fields = [
  191. 'id', 'manufacturer', 'model', 'slug', 'display_name', 'part_number', 'u_height', 'is_full_depth',
  192. 'subdevice_role', 'front_image', 'rear_image', 'comments', 'tags', 'custom_fields', 'created',
  193. 'last_updated', 'device_count',
  194. ]
  195. class ConsolePortTemplateSerializer(ValidatedModelSerializer):
  196. device_type = NestedDeviceTypeSerializer()
  197. type = ChoiceField(
  198. choices=ConsolePortTypeChoices,
  199. allow_blank=True,
  200. required=False
  201. )
  202. class Meta:
  203. model = ConsolePortTemplate
  204. fields = ['id', 'device_type', 'name', 'label', 'type', 'description']
  205. class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
  206. device_type = NestedDeviceTypeSerializer()
  207. type = ChoiceField(
  208. choices=ConsolePortTypeChoices,
  209. allow_blank=True,
  210. required=False
  211. )
  212. class Meta:
  213. model = ConsoleServerPortTemplate
  214. fields = ['id', 'device_type', 'name', 'label', 'type', 'description']
  215. class PowerPortTemplateSerializer(ValidatedModelSerializer):
  216. device_type = NestedDeviceTypeSerializer()
  217. type = ChoiceField(
  218. choices=PowerPortTypeChoices,
  219. allow_blank=True,
  220. required=False
  221. )
  222. class Meta:
  223. model = PowerPortTemplate
  224. fields = ['id', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description']
  225. class PowerOutletTemplateSerializer(ValidatedModelSerializer):
  226. device_type = NestedDeviceTypeSerializer()
  227. type = ChoiceField(
  228. choices=PowerOutletTypeChoices,
  229. allow_blank=True,
  230. required=False
  231. )
  232. power_port = NestedPowerPortTemplateSerializer(
  233. required=False
  234. )
  235. feed_leg = ChoiceField(
  236. choices=PowerOutletFeedLegChoices,
  237. allow_blank=True,
  238. required=False
  239. )
  240. class Meta:
  241. model = PowerOutletTemplate
  242. fields = ['id', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description']
  243. class InterfaceTemplateSerializer(ValidatedModelSerializer):
  244. device_type = NestedDeviceTypeSerializer()
  245. type = ChoiceField(choices=InterfaceTypeChoices)
  246. class Meta:
  247. model = InterfaceTemplate
  248. fields = ['id', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description']
  249. class RearPortTemplateSerializer(ValidatedModelSerializer):
  250. device_type = NestedDeviceTypeSerializer()
  251. type = ChoiceField(choices=PortTypeChoices)
  252. class Meta:
  253. model = RearPortTemplate
  254. fields = ['id', 'device_type', 'name', 'type', 'positions', 'description']
  255. class FrontPortTemplateSerializer(ValidatedModelSerializer):
  256. device_type = NestedDeviceTypeSerializer()
  257. type = ChoiceField(choices=PortTypeChoices)
  258. rear_port = NestedRearPortTemplateSerializer()
  259. class Meta:
  260. model = FrontPortTemplate
  261. fields = ['id', 'device_type', 'name', 'type', 'rear_port', 'rear_port_position', 'description']
  262. class DeviceBayTemplateSerializer(ValidatedModelSerializer):
  263. device_type = NestedDeviceTypeSerializer()
  264. class Meta:
  265. model = DeviceBayTemplate
  266. fields = ['id', 'device_type', 'name', 'label', 'description']
  267. #
  268. # Devices
  269. #
  270. class DeviceRoleSerializer(ValidatedModelSerializer):
  271. device_count = serializers.IntegerField(read_only=True)
  272. virtualmachine_count = serializers.IntegerField(read_only=True)
  273. class Meta:
  274. model = DeviceRole
  275. fields = [
  276. 'id', 'name', 'slug', 'color', 'vm_role', 'description', 'device_count', 'virtualmachine_count',
  277. ]
  278. class PlatformSerializer(ValidatedModelSerializer):
  279. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
  280. device_count = serializers.IntegerField(read_only=True)
  281. virtualmachine_count = serializers.IntegerField(read_only=True)
  282. class Meta:
  283. model = Platform
  284. fields = [
  285. 'id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description', 'device_count',
  286. 'virtualmachine_count',
  287. ]
  288. class DeviceSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
  289. device_type = NestedDeviceTypeSerializer()
  290. device_role = NestedDeviceRoleSerializer()
  291. tenant = NestedTenantSerializer(required=False, allow_null=True)
  292. platform = NestedPlatformSerializer(required=False, allow_null=True)
  293. site = NestedSiteSerializer()
  294. rack = NestedRackSerializer(required=False, allow_null=True)
  295. face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, required=False)
  296. status = ChoiceField(choices=DeviceStatusChoices, required=False)
  297. primary_ip = NestedIPAddressSerializer(read_only=True)
  298. primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
  299. primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
  300. parent_device = serializers.SerializerMethodField()
  301. cluster = NestedClusterSerializer(required=False, allow_null=True)
  302. virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True)
  303. class Meta:
  304. model = Device
  305. fields = [
  306. 'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  307. 'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
  308. 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'local_context_data', 'tags',
  309. 'custom_fields', 'created', 'last_updated',
  310. ]
  311. validators = []
  312. def validate(self, data):
  313. # Validate uniqueness of (rack, position, face) since we omitted the automatically-created validator from Meta.
  314. if data.get('rack') and data.get('position') and data.get('face'):
  315. validator = UniqueTogetherValidator(queryset=Device.objects.all(), fields=('rack', 'position', 'face'))
  316. validator(data, self)
  317. # Enforce model validation
  318. super().validate(data)
  319. return data
  320. @swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
  321. def get_parent_device(self, obj):
  322. try:
  323. device_bay = obj.parent_bay
  324. except DeviceBay.DoesNotExist:
  325. return None
  326. context = {'request': self.context['request']}
  327. data = NestedDeviceSerializer(instance=device_bay.device, context=context).data
  328. data['device_bay'] = NestedDeviceBaySerializer(instance=device_bay, context=context).data
  329. return data
  330. class DeviceWithConfigContextSerializer(DeviceSerializer):
  331. config_context = serializers.SerializerMethodField()
  332. class Meta(DeviceSerializer.Meta):
  333. fields = [
  334. 'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  335. 'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
  336. 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'local_context_data', 'tags',
  337. 'custom_fields', 'config_context', 'created', 'last_updated',
  338. ]
  339. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  340. def get_config_context(self, obj):
  341. return obj.get_config_context()
  342. class DeviceNAPALMSerializer(serializers.Serializer):
  343. method = serializers.DictField()
  344. class ConsoleServerPortSerializer(TaggedObjectSerializer, ConnectedEndpointSerializer):
  345. device = NestedDeviceSerializer()
  346. type = ChoiceField(
  347. choices=ConsolePortTypeChoices,
  348. allow_blank=True,
  349. required=False
  350. )
  351. cable = NestedCableSerializer(read_only=True)
  352. class Meta:
  353. model = ConsoleServerPort
  354. fields = [
  355. 'id', 'device', 'name', 'label', 'type', 'description', 'connected_endpoint_type', 'connected_endpoint',
  356. 'connection_status', 'cable', 'tags',
  357. ]
  358. class ConsolePortSerializer(TaggedObjectSerializer, ConnectedEndpointSerializer):
  359. device = NestedDeviceSerializer()
  360. type = ChoiceField(
  361. choices=ConsolePortTypeChoices,
  362. allow_blank=True,
  363. required=False
  364. )
  365. cable = NestedCableSerializer(read_only=True)
  366. class Meta:
  367. model = ConsolePort
  368. fields = [
  369. 'id', 'device', 'name', 'label', 'type', 'description', 'connected_endpoint_type', 'connected_endpoint',
  370. 'connection_status', 'cable', 'tags',
  371. ]
  372. class PowerOutletSerializer(TaggedObjectSerializer, ConnectedEndpointSerializer):
  373. device = NestedDeviceSerializer()
  374. type = ChoiceField(
  375. choices=PowerOutletTypeChoices,
  376. allow_blank=True,
  377. required=False
  378. )
  379. power_port = NestedPowerPortSerializer(
  380. required=False
  381. )
  382. feed_leg = ChoiceField(
  383. choices=PowerOutletFeedLegChoices,
  384. allow_blank=True,
  385. required=False
  386. )
  387. cable = NestedCableSerializer(
  388. read_only=True
  389. )
  390. class Meta:
  391. model = PowerOutlet
  392. fields = [
  393. 'id', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
  394. 'connected_endpoint', 'connection_status', 'cable', 'tags',
  395. ]
  396. class PowerPortSerializer(TaggedObjectSerializer, ConnectedEndpointSerializer):
  397. device = NestedDeviceSerializer()
  398. type = ChoiceField(
  399. choices=PowerPortTypeChoices,
  400. allow_blank=True,
  401. required=False
  402. )
  403. cable = NestedCableSerializer(read_only=True)
  404. class Meta:
  405. model = PowerPort
  406. fields = [
  407. 'id', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type',
  408. 'connected_endpoint', 'connection_status', 'cable', 'tags',
  409. ]
  410. class InterfaceSerializer(TaggedObjectSerializer, ConnectedEndpointSerializer):
  411. device = NestedDeviceSerializer()
  412. type = ChoiceField(choices=InterfaceTypeChoices)
  413. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  414. mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
  415. untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
  416. tagged_vlans = SerializedPKRelatedField(
  417. queryset=VLAN.objects.all(),
  418. serializer=NestedVLANSerializer,
  419. required=False,
  420. many=True
  421. )
  422. cable = NestedCableSerializer(read_only=True)
  423. count_ipaddresses = serializers.IntegerField(read_only=True)
  424. class Meta:
  425. model = Interface
  426. fields = [
  427. 'id', 'device', 'name', 'label', 'type', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
  428. 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode', 'untagged_vlan',
  429. 'tagged_vlans', 'tags', 'count_ipaddresses',
  430. ]
  431. # TODO: This validation should be handled by Interface.clean()
  432. def validate(self, data):
  433. # All associated VLANs be global or assigned to the parent device's site.
  434. device = self.instance.device if self.instance else data.get('device')
  435. untagged_vlan = data.get('untagged_vlan')
  436. if untagged_vlan and untagged_vlan.site not in [device.site, None]:
  437. raise serializers.ValidationError({
  438. 'untagged_vlan': "VLAN {} must belong to the same site as the interface's parent device, or it must be "
  439. "global.".format(untagged_vlan)
  440. })
  441. for vlan in data.get('tagged_vlans', []):
  442. if vlan.site not in [device.site, None]:
  443. raise serializers.ValidationError({
  444. 'tagged_vlans': "VLAN {} must belong to the same site as the interface's parent device, or it must "
  445. "be global.".format(vlan)
  446. })
  447. return super().validate(data)
  448. class RearPortSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  449. device = NestedDeviceSerializer()
  450. type = ChoiceField(choices=PortTypeChoices)
  451. cable = NestedCableSerializer(read_only=True)
  452. class Meta:
  453. model = RearPort
  454. fields = ['id', 'device', 'name', 'type', 'positions', 'description', 'cable', 'tags']
  455. class FrontPortRearPortSerializer(WritableNestedSerializer):
  456. """
  457. NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
  458. """
  459. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  460. class Meta:
  461. model = RearPort
  462. fields = ['id', 'url', 'name']
  463. class FrontPortSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  464. device = NestedDeviceSerializer()
  465. type = ChoiceField(choices=PortTypeChoices)
  466. rear_port = FrontPortRearPortSerializer()
  467. cable = NestedCableSerializer(read_only=True)
  468. class Meta:
  469. model = FrontPort
  470. fields = ['id', 'device', 'name', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'tags']
  471. class DeviceBaySerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  472. device = NestedDeviceSerializer()
  473. installed_device = NestedDeviceSerializer(required=False, allow_null=True)
  474. class Meta:
  475. model = DeviceBay
  476. fields = ['id', 'device', 'name', 'label', 'description', 'installed_device', 'tags']
  477. #
  478. # Inventory items
  479. #
  480. class InventoryItemSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  481. device = NestedDeviceSerializer()
  482. # Provide a default value to satisfy UniqueTogetherValidator
  483. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  484. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
  485. class Meta:
  486. model = InventoryItem
  487. fields = [
  488. 'id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
  489. 'description', 'tags',
  490. ]
  491. #
  492. # Cables
  493. #
  494. class CableSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  495. termination_a_type = ContentTypeField(
  496. queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
  497. )
  498. termination_b_type = ContentTypeField(
  499. queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
  500. )
  501. termination_a = serializers.SerializerMethodField(read_only=True)
  502. termination_b = serializers.SerializerMethodField(read_only=True)
  503. status = ChoiceField(choices=CableStatusChoices, required=False)
  504. length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
  505. class Meta:
  506. model = Cable
  507. fields = [
  508. 'id', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type', 'termination_b_id',
  509. 'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit', 'tags',
  510. ]
  511. def _get_termination(self, obj, side):
  512. """
  513. Serialize a nested representation of a termination.
  514. """
  515. if side.lower() not in ['a', 'b']:
  516. raise ValueError("Termination side must be either A or B.")
  517. termination = getattr(obj, 'termination_{}'.format(side.lower()))
  518. if termination is None:
  519. return None
  520. serializer = get_serializer_for_model(termination, prefix='Nested')
  521. context = {'request': self.context['request']}
  522. data = serializer(termination, context=context).data
  523. return data
  524. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  525. def get_termination_a(self, obj):
  526. return self._get_termination(obj, 'a')
  527. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  528. def get_termination_b(self, obj):
  529. return self._get_termination(obj, 'b')
  530. class TracedCableSerializer(serializers.ModelSerializer):
  531. """
  532. Used only while tracing a cable path.
  533. """
  534. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  535. class Meta:
  536. model = Cable
  537. fields = [
  538. 'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  539. ]
  540. #
  541. # Interface connections
  542. #
  543. class InterfaceConnectionSerializer(ValidatedModelSerializer):
  544. interface_a = serializers.SerializerMethodField()
  545. interface_b = NestedInterfaceSerializer(source='connected_endpoint')
  546. connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
  547. class Meta:
  548. model = Interface
  549. fields = ['interface_a', 'interface_b', 'connection_status']
  550. @swagger_serializer_method(serializer_or_field=NestedInterfaceSerializer)
  551. def get_interface_a(self, obj):
  552. context = {'request': self.context['request']}
  553. return NestedInterfaceSerializer(instance=obj, context=context).data
  554. #
  555. # Virtual chassis
  556. #
  557. class VirtualChassisSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  558. master = NestedDeviceSerializer(required=False)
  559. member_count = serializers.IntegerField(read_only=True)
  560. class Meta:
  561. model = VirtualChassis
  562. fields = ['id', 'name', 'domain', 'master', 'tags', 'member_count']
  563. #
  564. # Power panels
  565. #
  566. class PowerPanelSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
  567. site = NestedSiteSerializer()
  568. rack_group = NestedRackGroupSerializer(
  569. required=False,
  570. allow_null=True,
  571. default=None
  572. )
  573. powerfeed_count = serializers.IntegerField(read_only=True)
  574. class Meta:
  575. model = PowerPanel
  576. fields = ['id', 'site', 'rack_group', 'name', 'tags', 'powerfeed_count']
  577. class PowerFeedSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
  578. power_panel = NestedPowerPanelSerializer()
  579. rack = NestedRackSerializer(
  580. required=False,
  581. allow_null=True,
  582. default=None
  583. )
  584. type = ChoiceField(
  585. choices=PowerFeedTypeChoices,
  586. default=PowerFeedTypeChoices.TYPE_PRIMARY
  587. )
  588. status = ChoiceField(
  589. choices=PowerFeedStatusChoices,
  590. default=PowerFeedStatusChoices.STATUS_ACTIVE
  591. )
  592. supply = ChoiceField(
  593. choices=PowerFeedSupplyChoices,
  594. default=PowerFeedSupplyChoices.SUPPLY_AC
  595. )
  596. phase = ChoiceField(
  597. choices=PowerFeedPhaseChoices,
  598. default=PowerFeedPhaseChoices.PHASE_SINGLE
  599. )
  600. class Meta:
  601. model = PowerFeed
  602. fields = [
  603. 'id', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
  604. 'max_utilization', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
  605. ]