2
0

serializers.py 26 KB

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