serializers.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. from rest_framework import serializers
  2. from rest_framework.validators import UniqueTogetherValidator
  3. from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
  4. from circuits.models import Circuit, CircuitTermination
  5. from dcim.constants import *
  6. from dcim.models import (
  7. Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
  8. DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
  9. Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
  10. RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
  11. )
  12. from extras.api.customfields import CustomFieldModelSerializer
  13. from ipam.models import IPAddress, VLAN
  14. from tenancy.api.serializers import NestedTenantSerializer
  15. from users.api.serializers import NestedUserSerializer
  16. from utilities.api import (
  17. ChoiceField, ContentTypeField, SerializedPKRelatedField, TimeZoneField, ValidatedModelSerializer,
  18. WritableNestedSerializer, get_serializer_for_model,
  19. )
  20. from virtualization.models import Cluster
  21. #
  22. # Regions
  23. #
  24. class NestedRegionSerializer(WritableNestedSerializer):
  25. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
  26. class Meta:
  27. model = Region
  28. fields = ['id', 'url', 'name', 'slug']
  29. class RegionSerializer(serializers.ModelSerializer):
  30. parent = NestedRegionSerializer(required=False, allow_null=True)
  31. class Meta:
  32. model = Region
  33. fields = ['id', 'name', 'slug', 'parent']
  34. #
  35. # Sites
  36. #
  37. class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
  38. status = ChoiceField(choices=SITE_STATUS_CHOICES, required=False)
  39. region = NestedRegionSerializer(required=False, allow_null=True)
  40. tenant = NestedTenantSerializer(required=False, allow_null=True)
  41. time_zone = TimeZoneField(required=False)
  42. tags = TagListSerializerField(required=False)
  43. class Meta:
  44. model = Site
  45. fields = [
  46. 'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
  47. 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
  48. 'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'count_prefixes',
  49. 'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
  50. ]
  51. class NestedSiteSerializer(WritableNestedSerializer):
  52. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
  53. class Meta:
  54. model = Site
  55. fields = ['id', 'url', 'name', 'slug']
  56. #
  57. # Rack groups
  58. #
  59. class RackGroupSerializer(ValidatedModelSerializer):
  60. site = NestedSiteSerializer()
  61. class Meta:
  62. model = RackGroup
  63. fields = ['id', 'name', 'slug', 'site']
  64. class NestedRackGroupSerializer(WritableNestedSerializer):
  65. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
  66. class Meta:
  67. model = RackGroup
  68. fields = ['id', 'url', 'name', 'slug']
  69. #
  70. # Rack roles
  71. #
  72. class RackRoleSerializer(ValidatedModelSerializer):
  73. class Meta:
  74. model = RackRole
  75. fields = ['id', 'name', 'slug', 'color']
  76. class NestedRackRoleSerializer(WritableNestedSerializer):
  77. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
  78. class Meta:
  79. model = RackRole
  80. fields = ['id', 'url', 'name', 'slug']
  81. #
  82. # Racks
  83. #
  84. class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
  85. site = NestedSiteSerializer()
  86. group = NestedRackGroupSerializer(required=False, allow_null=True, default=None)
  87. tenant = NestedTenantSerializer(required=False, allow_null=True)
  88. role = NestedRackRoleSerializer(required=False, allow_null=True)
  89. type = ChoiceField(choices=RACK_TYPE_CHOICES, required=False, allow_null=True)
  90. width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
  91. tags = TagListSerializerField(required=False)
  92. class Meta:
  93. model = Rack
  94. fields = [
  95. 'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'serial', 'type', 'width',
  96. 'u_height', 'desc_units', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
  97. ]
  98. # Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
  99. # prevents facility_id from being interpreted as a required field.
  100. validators = [
  101. UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('group', 'name'))
  102. ]
  103. def validate(self, data):
  104. # Validate uniqueness of (group, facility_id) since we omitted the automatically-created validator from Meta.
  105. if data.get('facility_id', None):
  106. validator = UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('group', 'facility_id'))
  107. validator.set_context(self)
  108. validator(data)
  109. # Enforce model validation
  110. super(RackSerializer, self).validate(data)
  111. return data
  112. class NestedRackSerializer(WritableNestedSerializer):
  113. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
  114. class Meta:
  115. model = Rack
  116. fields = ['id', 'url', 'name', 'display_name']
  117. #
  118. # Rack units
  119. #
  120. class NestedDeviceSerializer(WritableNestedSerializer):
  121. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
  122. class Meta:
  123. model = Device
  124. fields = ['id', 'url', 'name', 'display_name']
  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 = serializers.IntegerField(read_only=True)
  132. device = NestedDeviceSerializer(read_only=True)
  133. #
  134. # Rack reservations
  135. #
  136. class RackReservationSerializer(ValidatedModelSerializer):
  137. rack = NestedRackSerializer()
  138. user = NestedUserSerializer()
  139. tenant = NestedTenantSerializer(required=False, allow_null=True)
  140. class Meta:
  141. model = RackReservation
  142. fields = ['id', 'rack', 'units', 'created', 'user', 'tenant', 'description']
  143. #
  144. # Manufacturers
  145. #
  146. class ManufacturerSerializer(ValidatedModelSerializer):
  147. class Meta:
  148. model = Manufacturer
  149. fields = ['id', 'name', 'slug']
  150. class NestedManufacturerSerializer(WritableNestedSerializer):
  151. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
  152. class Meta:
  153. model = Manufacturer
  154. fields = ['id', 'url', 'name', 'slug']
  155. #
  156. # Device types
  157. #
  158. class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
  159. manufacturer = NestedManufacturerSerializer()
  160. interface_ordering = ChoiceField(choices=IFACE_ORDERING_CHOICES, required=False)
  161. subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
  162. instance_count = serializers.IntegerField(source='instances.count', read_only=True)
  163. tags = TagListSerializerField(required=False)
  164. class Meta:
  165. model = DeviceType
  166. fields = [
  167. 'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
  168. 'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'instance_count',
  169. ]
  170. class NestedDeviceTypeSerializer(WritableNestedSerializer):
  171. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
  172. manufacturer = NestedManufacturerSerializer(read_only=True)
  173. class Meta:
  174. model = DeviceType
  175. fields = ['id', 'url', 'manufacturer', 'model', 'slug']
  176. #
  177. # Console port templates
  178. #
  179. class ConsolePortTemplateSerializer(ValidatedModelSerializer):
  180. device_type = NestedDeviceTypeSerializer()
  181. class Meta:
  182. model = ConsolePortTemplate
  183. fields = ['id', 'device_type', 'name']
  184. #
  185. # Console server port templates
  186. #
  187. class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
  188. device_type = NestedDeviceTypeSerializer()
  189. class Meta:
  190. model = ConsoleServerPortTemplate
  191. fields = ['id', 'device_type', 'name']
  192. #
  193. # Power port templates
  194. #
  195. class PowerPortTemplateSerializer(ValidatedModelSerializer):
  196. device_type = NestedDeviceTypeSerializer()
  197. class Meta:
  198. model = PowerPortTemplate
  199. fields = ['id', 'device_type', 'name']
  200. #
  201. # Power outlet templates
  202. #
  203. class PowerOutletTemplateSerializer(ValidatedModelSerializer):
  204. device_type = NestedDeviceTypeSerializer()
  205. class Meta:
  206. model = PowerOutletTemplate
  207. fields = ['id', 'device_type', 'name']
  208. #
  209. # Interface templates
  210. #
  211. class InterfaceTemplateSerializer(ValidatedModelSerializer):
  212. device_type = NestedDeviceTypeSerializer()
  213. form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
  214. class Meta:
  215. model = InterfaceTemplate
  216. fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']
  217. #
  218. # Rear port templates
  219. #
  220. class RearPortTemplateSerializer(ValidatedModelSerializer):
  221. device_type = NestedDeviceTypeSerializer()
  222. type = ChoiceField(choices=PORT_TYPE_CHOICES)
  223. class Meta:
  224. model = RearPortTemplate
  225. fields = ['id', 'device_type', 'name', 'type', 'positions']
  226. class NestedRearPortTemplateSerializer(WritableNestedSerializer):
  227. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
  228. class Meta:
  229. model = RearPortTemplate
  230. fields = ['id', 'url', 'name']
  231. #
  232. # Front port templates
  233. #
  234. class FrontPortTemplateSerializer(ValidatedModelSerializer):
  235. device_type = NestedDeviceTypeSerializer()
  236. type = ChoiceField(choices=PORT_TYPE_CHOICES)
  237. rear_port = NestedRearPortTemplateSerializer()
  238. class Meta:
  239. model = FrontPortTemplate
  240. fields = ['id', 'device_type', 'name', 'type', 'rear_port', 'rear_port_position']
  241. class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
  242. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
  243. class Meta:
  244. model = FrontPortTemplate
  245. fields = ['id', 'url', 'name']
  246. #
  247. # Device bay templates
  248. #
  249. class DeviceBayTemplateSerializer(ValidatedModelSerializer):
  250. device_type = NestedDeviceTypeSerializer()
  251. class Meta:
  252. model = DeviceBayTemplate
  253. fields = ['id', 'device_type', 'name']
  254. #
  255. # Device roles
  256. #
  257. class DeviceRoleSerializer(ValidatedModelSerializer):
  258. class Meta:
  259. model = DeviceRole
  260. fields = ['id', 'name', 'slug', 'color', 'vm_role']
  261. class NestedDeviceRoleSerializer(WritableNestedSerializer):
  262. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
  263. class Meta:
  264. model = DeviceRole
  265. fields = ['id', 'url', 'name', 'slug']
  266. #
  267. # Platforms
  268. #
  269. class PlatformSerializer(ValidatedModelSerializer):
  270. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
  271. class Meta:
  272. model = Platform
  273. fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args']
  274. class NestedPlatformSerializer(WritableNestedSerializer):
  275. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
  276. class Meta:
  277. model = Platform
  278. fields = ['id', 'url', 'name', 'slug']
  279. #
  280. # Devices
  281. #
  282. # Cannot import ipam.api.NestedIPAddressSerializer due to circular dependency
  283. class DeviceIPAddressSerializer(WritableNestedSerializer):
  284. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
  285. class Meta:
  286. model = IPAddress
  287. fields = ['id', 'url', 'family', 'address']
  288. # Cannot import virtualization.api.NestedClusterSerializer due to circular dependency
  289. class NestedClusterSerializer(WritableNestedSerializer):
  290. url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:cluster-detail')
  291. class Meta:
  292. model = Cluster
  293. fields = ['id', 'url', 'name']
  294. # Cannot import NestedVirtualChassisSerializer due to circular dependency
  295. class DeviceVirtualChassisSerializer(WritableNestedSerializer):
  296. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
  297. master = NestedDeviceSerializer()
  298. class Meta:
  299. model = VirtualChassis
  300. fields = ['id', 'url', 'master']
  301. class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
  302. device_type = NestedDeviceTypeSerializer()
  303. device_role = NestedDeviceRoleSerializer()
  304. tenant = NestedTenantSerializer(required=False, allow_null=True)
  305. platform = NestedPlatformSerializer(required=False, allow_null=True)
  306. site = NestedSiteSerializer()
  307. rack = NestedRackSerializer(required=False, allow_null=True)
  308. face = ChoiceField(choices=RACK_FACE_CHOICES, required=False, allow_null=True)
  309. status = ChoiceField(choices=DEVICE_STATUS_CHOICES, required=False)
  310. primary_ip = DeviceIPAddressSerializer(read_only=True)
  311. primary_ip4 = DeviceIPAddressSerializer(required=False, allow_null=True)
  312. primary_ip6 = DeviceIPAddressSerializer(required=False, allow_null=True)
  313. parent_device = serializers.SerializerMethodField()
  314. cluster = NestedClusterSerializer(required=False, allow_null=True)
  315. virtual_chassis = DeviceVirtualChassisSerializer(required=False, allow_null=True)
  316. tags = TagListSerializerField(required=False)
  317. class Meta:
  318. model = Device
  319. fields = [
  320. 'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  321. 'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
  322. 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'custom_fields', 'created',
  323. 'last_updated', 'local_context_data',
  324. ]
  325. validators = []
  326. def validate(self, data):
  327. # Validate uniqueness of (rack, position, face) since we omitted the automatically-created validator from Meta.
  328. if data.get('rack') and data.get('position') and data.get('face'):
  329. validator = UniqueTogetherValidator(queryset=Device.objects.all(), fields=('rack', 'position', 'face'))
  330. validator.set_context(self)
  331. validator(data)
  332. # Enforce model validation
  333. super(DeviceSerializer, self).validate(data)
  334. return data
  335. def get_parent_device(self, obj):
  336. try:
  337. device_bay = obj.parent_bay
  338. except DeviceBay.DoesNotExist:
  339. return None
  340. context = {'request': self.context['request']}
  341. data = NestedDeviceSerializer(instance=device_bay.device, context=context).data
  342. data['device_bay'] = NestedDeviceBaySerializer(instance=device_bay, context=context).data
  343. return data
  344. class DeviceWithConfigContextSerializer(DeviceSerializer):
  345. config_context = serializers.SerializerMethodField()
  346. class Meta(DeviceSerializer.Meta):
  347. fields = [
  348. 'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  349. 'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
  350. 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'custom_fields',
  351. 'config_context', 'created', 'last_updated', 'local_context_data',
  352. ]
  353. def get_config_context(self, obj):
  354. return obj.get_config_context()
  355. #
  356. # Cables
  357. #
  358. class CableSerializer(ValidatedModelSerializer):
  359. termination_a_type = ContentTypeField()
  360. termination_b_type = ContentTypeField()
  361. termination_a = serializers.SerializerMethodField(read_only=True)
  362. termination_b = serializers.SerializerMethodField(read_only=True)
  363. status = ChoiceField(choices=CONNECTION_STATUS_CHOICES)
  364. length_unit = ChoiceField(choices=LENGTH_UNIT_CHOICES)
  365. class Meta:
  366. model = Cable
  367. fields = [
  368. 'id', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type', 'termination_b_id',
  369. 'termination_b', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  370. ]
  371. def _get_termination(self, obj, side):
  372. """
  373. Serialize a nested representation of a termination.
  374. """
  375. if side.lower() not in ['a', 'b']:
  376. raise ValueError("Termination side must be either A or B.")
  377. termination = getattr(obj, 'termination_{}'.format(side.lower()))
  378. if termination is None:
  379. return None
  380. serializer = get_serializer_for_model(termination, prefix='Nested')
  381. context = {'request': self.context['request']}
  382. data = serializer(termination, context=context).data
  383. return data
  384. def get_termination_a(self, obj):
  385. return self._get_termination(obj, 'a')
  386. def get_termination_b(self, obj):
  387. return self._get_termination(obj, 'b')
  388. class TracedCableSerializer(serializers.ModelSerializer):
  389. """
  390. Used only while tracing a cable path.
  391. """
  392. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  393. class Meta:
  394. model = Cable
  395. fields = [
  396. 'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  397. ]
  398. class NestedCableSerializer(serializers.Serializer):
  399. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  400. class Meta:
  401. model = Cable
  402. fields = ['id', 'url', 'label']
  403. #
  404. # Console server ports
  405. #
  406. class ConsoleServerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
  407. device = NestedDeviceSerializer()
  408. cable = NestedCableSerializer(read_only=True)
  409. tags = TagListSerializerField(required=False)
  410. class Meta:
  411. model = ConsoleServerPort
  412. fields = ['id', 'device', 'name', 'connected_endpoint', 'cable', 'tags']
  413. read_only_fields = ['connected_endpoint']
  414. class NestedConsoleServerPortSerializer(WritableNestedSerializer):
  415. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
  416. device = NestedDeviceSerializer(read_only=True)
  417. class Meta:
  418. model = ConsoleServerPort
  419. fields = ['id', 'url', 'device', 'name', 'cable']
  420. #
  421. # Console ports
  422. #
  423. class ConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
  424. device = NestedDeviceSerializer()
  425. connected_endpoint = NestedConsoleServerPortSerializer(required=False, allow_null=True)
  426. cable = NestedCableSerializer(read_only=True)
  427. tags = TagListSerializerField(required=False)
  428. class Meta:
  429. model = ConsolePort
  430. fields = ['id', 'device', 'name', 'connected_endpoint', 'connection_status', 'cable', 'tags']
  431. read_only_fields = ['connected_endpoint']
  432. class NestedConsolePortSerializer(TaggitSerializer, ValidatedModelSerializer):
  433. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
  434. device = NestedDeviceSerializer(read_only=True)
  435. class Meta:
  436. model = ConsolePort
  437. fields = ['id', 'url', 'device', 'name', 'cable']
  438. #
  439. # Power outlets
  440. #
  441. class PowerOutletSerializer(TaggitSerializer, ValidatedModelSerializer):
  442. device = NestedDeviceSerializer()
  443. cable = NestedCableSerializer(read_only=True)
  444. tags = TagListSerializerField(required=False)
  445. class Meta:
  446. model = PowerOutlet
  447. fields = ['id', 'device', 'name', 'connected_endpoint', 'cable', 'tags']
  448. read_only_fields = ['connected_endpoint']
  449. class NestedPowerOutletSerializer(WritableNestedSerializer):
  450. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
  451. device = NestedDeviceSerializer(read_only=True)
  452. class Meta:
  453. model = PowerOutlet
  454. fields = ['id', 'url', 'device', 'name', 'cable']
  455. #
  456. # Power ports
  457. #
  458. class PowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
  459. device = NestedDeviceSerializer()
  460. connected_endpoint = NestedPowerOutletSerializer(required=False, allow_null=True)
  461. cable = NestedCableSerializer(read_only=True)
  462. tags = TagListSerializerField(required=False)
  463. class Meta:
  464. model = PowerPort
  465. fields = ['id', 'device', 'name', 'connected_endpoint', 'connection_status', 'cable', 'tags']
  466. read_only_fields = ['connected_endpoint']
  467. class NestedPowerPortSerializer(TaggitSerializer, ValidatedModelSerializer):
  468. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
  469. device = NestedDeviceSerializer(read_only=True)
  470. class Meta:
  471. model = PowerPort
  472. fields = ['id', 'url', 'device', 'name', 'cable']
  473. #
  474. # Interfaces
  475. #
  476. class NestedInterfaceSerializer(WritableNestedSerializer):
  477. device = NestedDeviceSerializer(read_only=True)
  478. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
  479. class Meta:
  480. model = Interface
  481. fields = ['id', 'url', 'device', 'name', 'cable']
  482. class InterfaceNestedCircuitSerializer(serializers.ModelSerializer):
  483. url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
  484. class Meta:
  485. model = Circuit
  486. fields = ['id', 'url', 'cid']
  487. class InterfaceCircuitTerminationSerializer(WritableNestedSerializer):
  488. circuit = InterfaceNestedCircuitSerializer(read_only=True)
  489. class Meta:
  490. model = CircuitTermination
  491. fields = [
  492. 'id', 'circuit', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'pp_info',
  493. ]
  494. # Cannot import ipam.api.NestedVLANSerializer due to circular dependency
  495. class InterfaceVLANSerializer(WritableNestedSerializer):
  496. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
  497. class Meta:
  498. model = VLAN
  499. fields = ['id', 'url', 'vid', 'name', 'display_name']
  500. class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
  501. device = NestedDeviceSerializer()
  502. form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
  503. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  504. connected_endpoint = serializers.SerializerMethodField(read_only=True)
  505. mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
  506. untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
  507. tagged_vlans = SerializedPKRelatedField(
  508. queryset=VLAN.objects.all(),
  509. serializer=InterfaceVLANSerializer,
  510. required=False,
  511. many=True
  512. )
  513. cable = NestedCableSerializer(read_only=True)
  514. tags = TagListSerializerField(required=False)
  515. class Meta:
  516. model = Interface
  517. fields = [
  518. 'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
  519. 'connected_endpoint', 'cable', 'mode', 'untagged_vlan', 'tagged_vlans', 'tags',
  520. ]
  521. def validate(self, data):
  522. # All associated VLANs be global or assigned to the parent device's site.
  523. device = self.instance.device if self.instance else data.get('device')
  524. untagged_vlan = data.get('untagged_vlan')
  525. if untagged_vlan and untagged_vlan.site not in [device.site, None]:
  526. raise serializers.ValidationError({
  527. 'untagged_vlan': "VLAN {} must belong to the same site as the interface's parent device, or it must be "
  528. "global.".format(untagged_vlan)
  529. })
  530. for vlan in data.get('tagged_vlans', []):
  531. if vlan.site not in [device.site, None]:
  532. raise serializers.ValidationError({
  533. 'tagged_vlans': "VLAN {} must belong to the same site as the interface's parent device, or it must "
  534. "be global.".format(vlan)
  535. })
  536. return super(InterfaceSerializer, self).validate(data)
  537. def get_connected_endpoint(self, obj):
  538. """
  539. Return the appropriate serializer for the type of connected object.
  540. """
  541. if obj.connected_endpoint is None:
  542. return None
  543. serializer = get_serializer_for_model(obj.connected_endpoint, prefix='Nested')
  544. context = {'request': self.context['request']}
  545. data = serializer(obj.connected_endpoint, context=context).data
  546. return data
  547. #
  548. # Rear ports
  549. #
  550. class RearPortSerializer(ValidatedModelSerializer):
  551. device = NestedDeviceSerializer()
  552. type = ChoiceField(choices=PORT_TYPE_CHOICES)
  553. cable = NestedCableSerializer(read_only=True)
  554. tags = TagListSerializerField(required=False)
  555. class Meta:
  556. model = RearPort
  557. fields = ['id', 'device', 'name', 'type', 'positions', 'cable', 'tags']
  558. class NestedRearPortSerializer(WritableNestedSerializer):
  559. device = NestedDeviceSerializer(read_only=True)
  560. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  561. class Meta:
  562. model = RearPort
  563. fields = ['id', 'url', 'device', 'name', 'cable']
  564. #
  565. # Front ports
  566. #
  567. class FrontPortRearPortSerializer(WritableNestedSerializer):
  568. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  569. class Meta:
  570. model = RearPort
  571. fields = ['id', 'url', 'name']
  572. class FrontPortSerializer(ValidatedModelSerializer):
  573. device = NestedDeviceSerializer()
  574. type = ChoiceField(choices=PORT_TYPE_CHOICES)
  575. rear_port = FrontPortRearPortSerializer()
  576. cable = NestedCableSerializer(read_only=True)
  577. tags = TagListSerializerField(required=False)
  578. class Meta:
  579. model = FrontPort
  580. fields = ['id', 'device', 'name', 'type', 'rear_port', 'rear_port_position', 'cable', 'tags']
  581. class NestedFrontPortSerializer(WritableNestedSerializer):
  582. device = NestedDeviceSerializer(read_only=True)
  583. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
  584. class Meta:
  585. model = FrontPort
  586. fields = ['id', 'url', 'device', 'name', 'cable']
  587. #
  588. # Device bays
  589. #
  590. class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
  591. device = NestedDeviceSerializer()
  592. installed_device = NestedDeviceSerializer(required=False, allow_null=True)
  593. tags = TagListSerializerField(required=False)
  594. class Meta:
  595. model = DeviceBay
  596. fields = ['id', 'device', 'name', 'installed_device', 'tags']
  597. class NestedDeviceBaySerializer(WritableNestedSerializer):
  598. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  599. device = NestedDeviceSerializer(read_only=True)
  600. class Meta:
  601. model = DeviceBay
  602. fields = ['id', 'url', 'device', 'name']
  603. #
  604. # Inventory items
  605. #
  606. class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
  607. device = NestedDeviceSerializer()
  608. # Provide a default value to satisfy UniqueTogetherValidator
  609. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  610. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
  611. tags = TagListSerializerField(required=False)
  612. class Meta:
  613. model = InventoryItem
  614. fields = [
  615. 'id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'discovered',
  616. 'description', 'tags',
  617. ]
  618. #
  619. # Interface connections
  620. #
  621. class InterfaceConnectionSerializer(ValidatedModelSerializer):
  622. interface_a = serializers.SerializerMethodField()
  623. interface_b = NestedInterfaceSerializer(source='connected_endpoint')
  624. connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
  625. class Meta:
  626. model = Interface
  627. fields = ['interface_a', 'interface_b', 'connection_status']
  628. def get_interface_a(self, obj):
  629. context = {'request': self.context['request']}
  630. return NestedInterfaceSerializer(instance=obj, context=context).data
  631. #
  632. # Virtual chassis
  633. #
  634. class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
  635. master = NestedDeviceSerializer()
  636. tags = TagListSerializerField(required=False)
  637. class Meta:
  638. model = VirtualChassis
  639. fields = ['id', 'master', 'domain', 'tags']
  640. class NestedVirtualChassisSerializer(WritableNestedSerializer):
  641. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
  642. class Meta:
  643. model = VirtualChassis
  644. fields = ['id', 'url']