serializers.py 28 KB

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