serializers.py 25 KB

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