serializers.py 26 KB

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