serializers.py 34 KB

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