serializers.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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 ConsoleServerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
  289. device = NestedDeviceSerializer()
  290. cable = NestedCableSerializer(read_only=True)
  291. tags = TagListSerializerField(required=False)
  292. class Meta:
  293. model = ConsoleServerPort
  294. fields = [
  295. 'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
  296. 'cable', 'tags',
  297. ]
  298. class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
  299. device = NestedDeviceSerializer()
  300. cable = NestedCableSerializer(read_only=True)
  301. tags = TagListSerializerField(required=False)
  302. class Meta:
  303. model = ConsolePort
  304. fields = [
  305. 'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
  306. 'cable', 'tags',
  307. ]
  308. class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
  309. device = NestedDeviceSerializer()
  310. power_port = NestedPowerPortSerializer(
  311. required=False
  312. )
  313. feed_leg = ChoiceField(
  314. choices=POWERFEED_LEG_CHOICES,
  315. required=False,
  316. allow_null=True
  317. )
  318. cable = NestedCableSerializer(
  319. read_only=True
  320. )
  321. tags = TagListSerializerField(
  322. required=False
  323. )
  324. class Meta:
  325. model = PowerOutlet
  326. fields = [
  327. 'id', 'device', 'name', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
  328. 'connected_endpoint', 'connection_status', 'cable', 'tags',
  329. ]
  330. class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
  331. device = NestedDeviceSerializer()
  332. cable = NestedCableSerializer(read_only=True)
  333. tags = TagListSerializerField(required=False)
  334. class Meta:
  335. model = PowerPort
  336. fields = [
  337. 'id', 'device', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
  338. 'cable', 'tags',
  339. ]
  340. class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
  341. device = NestedDeviceSerializer()
  342. type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
  343. # TODO: Remove in v2.7 (backward-compatibility for form_factor)
  344. form_factor = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
  345. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  346. mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
  347. untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
  348. tagged_vlans = SerializedPKRelatedField(
  349. queryset=VLAN.objects.all(),
  350. serializer=NestedVLANSerializer,
  351. required=False,
  352. many=True
  353. )
  354. cable = NestedCableSerializer(read_only=True)
  355. tags = TagListSerializerField(required=False)
  356. class Meta:
  357. model = Interface
  358. fields = [
  359. 'id', 'device', 'name', 'type', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only',
  360. 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode',
  361. 'untagged_vlan', 'tagged_vlans', 'tags', 'count_ipaddresses',
  362. ]
  363. # TODO: This validation should be handled by Interface.clean()
  364. def validate(self, data):
  365. # All associated VLANs be global or assigned to the parent device's site.
  366. device = self.instance.device if self.instance else data.get('device')
  367. untagged_vlan = data.get('untagged_vlan')
  368. if untagged_vlan and untagged_vlan.site not in [device.site, None]:
  369. raise serializers.ValidationError({
  370. 'untagged_vlan': "VLAN {} must belong to the same site as the interface's parent device, or it must be "
  371. "global.".format(untagged_vlan)
  372. })
  373. for vlan in data.get('tagged_vlans', []):
  374. if vlan.site not in [device.site, None]:
  375. raise serializers.ValidationError({
  376. 'tagged_vlans': "VLAN {} must belong to the same site as the interface's parent device, or it must "
  377. "be global.".format(vlan)
  378. })
  379. return super().validate(data)
  380. class RearPortSerializer(ValidatedModelSerializer):
  381. device = NestedDeviceSerializer()
  382. type = ChoiceField(choices=PORT_TYPE_CHOICES)
  383. cable = NestedCableSerializer(read_only=True)
  384. tags = TagListSerializerField(required=False)
  385. class Meta:
  386. model = RearPort
  387. fields = ['id', 'device', 'name', 'type', 'positions', 'description', 'cable', 'tags']
  388. class FrontPortRearPortSerializer(WritableNestedSerializer):
  389. """
  390. NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
  391. """
  392. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  393. class Meta:
  394. model = RearPort
  395. fields = ['id', 'url', 'name']
  396. class FrontPortSerializer(ValidatedModelSerializer):
  397. device = NestedDeviceSerializer()
  398. type = ChoiceField(choices=PORT_TYPE_CHOICES)
  399. rear_port = FrontPortRearPortSerializer()
  400. cable = NestedCableSerializer(read_only=True)
  401. tags = TagListSerializerField(required=False)
  402. class Meta:
  403. model = FrontPort
  404. fields = ['id', 'device', 'name', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'tags']
  405. class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
  406. device = NestedDeviceSerializer()
  407. installed_device = NestedDeviceSerializer(required=False, allow_null=True)
  408. tags = TagListSerializerField(required=False)
  409. class Meta:
  410. model = DeviceBay
  411. fields = ['id', 'device', 'name', 'description', 'installed_device', 'tags']
  412. #
  413. # Inventory items
  414. #
  415. class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
  416. device = NestedDeviceSerializer()
  417. # Provide a default value to satisfy UniqueTogetherValidator
  418. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  419. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
  420. tags = TagListSerializerField(required=False)
  421. class Meta:
  422. model = InventoryItem
  423. fields = [
  424. 'id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
  425. 'description', 'tags',
  426. ]
  427. #
  428. # Cables
  429. #
  430. class CableSerializer(ValidatedModelSerializer):
  431. termination_a_type = ContentTypeField(
  432. queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
  433. )
  434. termination_b_type = ContentTypeField(
  435. queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
  436. )
  437. termination_a = serializers.SerializerMethodField(read_only=True)
  438. termination_b = serializers.SerializerMethodField(read_only=True)
  439. status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
  440. length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False, allow_null=True)
  441. class Meta:
  442. model = Cable
  443. fields = [
  444. 'id', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type', 'termination_b_id',
  445. 'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  446. ]
  447. def _get_termination(self, obj, side):
  448. """
  449. Serialize a nested representation of a termination.
  450. """
  451. if side.lower() not in ['a', 'b']:
  452. raise ValueError("Termination side must be either A or B.")
  453. termination = getattr(obj, 'termination_{}'.format(side.lower()))
  454. if termination is None:
  455. return None
  456. serializer = get_serializer_for_model(termination, prefix='Nested')
  457. context = {'request': self.context['request']}
  458. data = serializer(termination, context=context).data
  459. return data
  460. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  461. def get_termination_a(self, obj):
  462. return self._get_termination(obj, 'a')
  463. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  464. def get_termination_b(self, obj):
  465. return self._get_termination(obj, 'b')
  466. class TracedCableSerializer(serializers.ModelSerializer):
  467. """
  468. Used only while tracing a cable path.
  469. """
  470. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  471. class Meta:
  472. model = Cable
  473. fields = [
  474. 'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  475. ]
  476. #
  477. # Interface connections
  478. #
  479. class InterfaceConnectionSerializer(ValidatedModelSerializer):
  480. interface_a = serializers.SerializerMethodField()
  481. interface_b = NestedInterfaceSerializer(source='connected_endpoint')
  482. connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
  483. class Meta:
  484. model = Interface
  485. fields = ['interface_a', 'interface_b', 'connection_status']
  486. @swagger_serializer_method(serializer_or_field=NestedInterfaceSerializer)
  487. def get_interface_a(self, obj):
  488. context = {'request': self.context['request']}
  489. return NestedInterfaceSerializer(instance=obj, context=context).data
  490. #
  491. # Virtual chassis
  492. #
  493. class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
  494. master = NestedDeviceSerializer()
  495. tags = TagListSerializerField(required=False)
  496. member_count = serializers.IntegerField(read_only=True)
  497. class Meta:
  498. model = VirtualChassis
  499. fields = ['id', 'master', 'domain', 'tags', 'member_count']
  500. #
  501. # Power panels
  502. #
  503. class PowerPanelSerializer(ValidatedModelSerializer):
  504. site = NestedSiteSerializer()
  505. rack_group = NestedRackGroupSerializer(
  506. required=False,
  507. allow_null=True,
  508. default=None
  509. )
  510. powerfeed_count = serializers.IntegerField(read_only=True)
  511. class Meta:
  512. model = PowerPanel
  513. fields = ['id', 'site', 'rack_group', 'name', 'powerfeed_count']
  514. class PowerFeedSerializer(TaggitSerializer, CustomFieldModelSerializer):
  515. power_panel = NestedPowerPanelSerializer()
  516. rack = NestedRackSerializer(
  517. required=False,
  518. allow_null=True,
  519. default=None
  520. )
  521. type = ChoiceField(
  522. choices=POWERFEED_TYPE_CHOICES,
  523. default=POWERFEED_TYPE_PRIMARY
  524. )
  525. status = ChoiceField(
  526. choices=POWERFEED_STATUS_CHOICES,
  527. default=POWERFEED_STATUS_ACTIVE
  528. )
  529. supply = ChoiceField(
  530. choices=POWERFEED_SUPPLY_CHOICES,
  531. default=POWERFEED_SUPPLY_AC
  532. )
  533. phase = ChoiceField(
  534. choices=POWERFEED_PHASE_CHOICES,
  535. default=POWERFEED_PHASE_SINGLE
  536. )
  537. tags = TagListSerializerField(
  538. required=False
  539. )
  540. class Meta:
  541. model = PowerFeed
  542. fields = [
  543. 'id', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
  544. 'power_factor', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
  545. ]