serializers.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. from rest_framework import serializers
  2. from rest_framework.validators import UniqueTogetherValidator
  3. from ipam.models import IPAddress
  4. from dcim.models import (
  5. CONNECTION_STATUS_CHOICES, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device,
  6. DeviceBay, DeviceBayTemplate, DeviceType, DeviceRole, IFACE_FF_CHOICES, IFACE_ORDERING_CHOICES, Interface,
  7. InterfaceConnection, InterfaceTemplate, Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
  8. PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RACK_FACE_CHOICES, RACK_TYPE_CHOICES,
  9. RACK_WIDTH_CHOICES, Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHOICES,
  10. )
  11. from extras.api.customfields import CustomFieldModelSerializer
  12. from tenancy.api.serializers import NestedTenantSerializer
  13. from utilities.api import ChoiceFieldSerializer
  14. #
  15. # Regions
  16. #
  17. class NestedRegionSerializer(serializers.ModelSerializer):
  18. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
  19. class Meta:
  20. model = Region
  21. fields = ['id', 'url', 'name', 'slug']
  22. class RegionSerializer(serializers.ModelSerializer):
  23. parent = NestedRegionSerializer()
  24. class Meta:
  25. model = Region
  26. fields = ['id', 'name', 'slug', 'parent']
  27. class WritableRegionSerializer(serializers.ModelSerializer):
  28. class Meta:
  29. model = Region
  30. fields = ['id', 'name', 'slug', 'parent']
  31. #
  32. # Sites
  33. #
  34. class SiteSerializer(CustomFieldModelSerializer):
  35. region = NestedRegionSerializer()
  36. tenant = NestedTenantSerializer()
  37. class Meta:
  38. model = Site
  39. fields = [
  40. 'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
  41. 'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields', 'count_prefixes',
  42. 'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
  43. ]
  44. class NestedSiteSerializer(serializers.ModelSerializer):
  45. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
  46. class Meta:
  47. model = Site
  48. fields = ['id', 'url', 'name', 'slug']
  49. class WritableSiteSerializer(CustomFieldModelSerializer):
  50. class Meta:
  51. model = Site
  52. fields = [
  53. 'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
  54. 'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
  55. ]
  56. #
  57. # Rack groups
  58. #
  59. class RackGroupSerializer(serializers.ModelSerializer):
  60. site = NestedSiteSerializer()
  61. class Meta:
  62. model = RackGroup
  63. fields = ['id', 'name', 'slug', 'site']
  64. class NestedRackGroupSerializer(serializers.ModelSerializer):
  65. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
  66. class Meta:
  67. model = RackGroup
  68. fields = ['id', 'url', 'name', 'slug']
  69. class WritableRackGroupSerializer(serializers.ModelSerializer):
  70. class Meta:
  71. model = RackGroup
  72. fields = ['id', 'name', 'slug', 'site']
  73. #
  74. # Rack roles
  75. #
  76. class RackRoleSerializer(serializers.ModelSerializer):
  77. class Meta:
  78. model = RackRole
  79. fields = ['id', 'name', 'slug', 'color']
  80. class NestedRackRoleSerializer(serializers.ModelSerializer):
  81. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
  82. class Meta:
  83. model = RackRole
  84. fields = ['id', 'url', 'name', 'slug']
  85. #
  86. # Racks
  87. #
  88. class RackSerializer(CustomFieldModelSerializer):
  89. site = NestedSiteSerializer()
  90. group = NestedRackGroupSerializer()
  91. tenant = NestedTenantSerializer()
  92. role = NestedRackRoleSerializer()
  93. type = ChoiceFieldSerializer(choices=RACK_TYPE_CHOICES)
  94. width = ChoiceFieldSerializer(choices=RACK_WIDTH_CHOICES)
  95. class Meta:
  96. model = Rack
  97. fields = [
  98. 'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height',
  99. 'desc_units', 'comments', 'custom_fields',
  100. ]
  101. class NestedRackSerializer(serializers.ModelSerializer):
  102. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
  103. class Meta:
  104. model = Rack
  105. fields = ['id', 'url', 'name', 'display_name']
  106. class WritableRackSerializer(CustomFieldModelSerializer):
  107. class Meta:
  108. model = Rack
  109. fields = [
  110. 'id', 'name', 'facility_id', 'site', 'group', 'tenant', 'role', 'type', 'width', 'u_height', 'desc_units',
  111. 'comments', 'custom_fields',
  112. ]
  113. # Omit the UniqueTogetherValidator that would be automatically added to validate (site, facility_id). This
  114. # prevents facility_id from being interpreted as a required field.
  115. validators = [
  116. UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('site', 'name'))
  117. ]
  118. def validate(self, data):
  119. # Validate uniqueness of (site, facility_id) since we omitted the automatically-created validator from Meta.
  120. if data.get('facility_id', None):
  121. validator = UniqueTogetherValidator(queryset=Rack.objects.all(), fields=('site', 'facility_id'))
  122. validator.set_context(self)
  123. validator(data)
  124. return data
  125. #
  126. # Rack units
  127. #
  128. class NestedDeviceSerializer(serializers.ModelSerializer):
  129. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
  130. class Meta:
  131. model = Device
  132. fields = ['id', 'url', 'name', 'display_name']
  133. class RackUnitSerializer(serializers.Serializer):
  134. """
  135. A rack unit is an abstraction formed by the set (rack, position, face); it does not exist as a row in the database.
  136. """
  137. id = serializers.IntegerField(read_only=True)
  138. name = serializers.CharField(read_only=True)
  139. face = serializers.IntegerField(read_only=True)
  140. device = NestedDeviceSerializer(read_only=True)
  141. #
  142. # Rack reservations
  143. #
  144. class RackReservationSerializer(serializers.ModelSerializer):
  145. rack = NestedRackSerializer()
  146. class Meta:
  147. model = RackReservation
  148. fields = ['id', 'rack', 'units', 'created', 'user', 'description']
  149. class WritableRackReservationSerializer(serializers.ModelSerializer):
  150. class Meta:
  151. model = RackReservation
  152. fields = ['id', 'rack', 'units', 'description']
  153. #
  154. # Manufacturers
  155. #
  156. class ManufacturerSerializer(serializers.ModelSerializer):
  157. class Meta:
  158. model = Manufacturer
  159. fields = ['id', 'name', 'slug']
  160. class NestedManufacturerSerializer(serializers.ModelSerializer):
  161. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
  162. class Meta:
  163. model = Manufacturer
  164. fields = ['id', 'url', 'name', 'slug']
  165. #
  166. # Device types
  167. #
  168. class DeviceTypeSerializer(CustomFieldModelSerializer):
  169. manufacturer = NestedManufacturerSerializer()
  170. interface_ordering = ChoiceFieldSerializer(choices=IFACE_ORDERING_CHOICES)
  171. subdevice_role = ChoiceFieldSerializer(choices=SUBDEVICE_ROLE_CHOICES)
  172. instance_count = serializers.IntegerField(source='instances.count', read_only=True)
  173. class Meta:
  174. model = DeviceType
  175. fields = [
  176. 'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
  177. 'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'custom_fields',
  178. 'instance_count',
  179. ]
  180. class NestedDeviceTypeSerializer(serializers.ModelSerializer):
  181. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
  182. manufacturer = NestedManufacturerSerializer()
  183. class Meta:
  184. model = DeviceType
  185. fields = ['id', 'url', 'manufacturer', 'model', 'slug']
  186. class WritableDeviceTypeSerializer(CustomFieldModelSerializer):
  187. class Meta:
  188. model = DeviceType
  189. fields = [
  190. 'id', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'interface_ordering',
  191. 'is_console_server', 'is_pdu', 'is_network_device', 'subdevice_role', 'comments', 'custom_fields',
  192. ]
  193. #
  194. # Console port templates
  195. #
  196. class ConsolePortTemplateSerializer(serializers.ModelSerializer):
  197. device_type = NestedDeviceTypeSerializer()
  198. class Meta:
  199. model = ConsolePortTemplate
  200. fields = ['id', 'device_type', 'name']
  201. class WritableConsolePortTemplateSerializer(serializers.ModelSerializer):
  202. class Meta:
  203. model = ConsolePortTemplate
  204. fields = ['id', 'device_type', 'name']
  205. #
  206. # Console server port templates
  207. #
  208. class ConsoleServerPortTemplateSerializer(serializers.ModelSerializer):
  209. device_type = NestedDeviceTypeSerializer()
  210. class Meta:
  211. model = ConsoleServerPortTemplate
  212. fields = ['id', 'device_type', 'name']
  213. class WritableConsoleServerPortTemplateSerializer(serializers.ModelSerializer):
  214. class Meta:
  215. model = ConsoleServerPortTemplate
  216. fields = ['id', 'device_type', 'name']
  217. #
  218. # Power port templates
  219. #
  220. class PowerPortTemplateSerializer(serializers.ModelSerializer):
  221. device_type = NestedDeviceTypeSerializer()
  222. class Meta:
  223. model = PowerPortTemplate
  224. fields = ['id', 'device_type', 'name']
  225. class WritablePowerPortTemplateSerializer(serializers.ModelSerializer):
  226. class Meta:
  227. model = PowerPortTemplate
  228. fields = ['id', 'device_type', 'name']
  229. #
  230. # Power outlet templates
  231. #
  232. class PowerOutletTemplateSerializer(serializers.ModelSerializer):
  233. device_type = NestedDeviceTypeSerializer()
  234. class Meta:
  235. model = PowerOutletTemplate
  236. fields = ['id', 'device_type', 'name']
  237. class WritablePowerOutletTemplateSerializer(serializers.ModelSerializer):
  238. class Meta:
  239. model = PowerOutletTemplate
  240. fields = ['id', 'device_type', 'name']
  241. #
  242. # Interface templates
  243. #
  244. class InterfaceTemplateSerializer(serializers.ModelSerializer):
  245. device_type = NestedDeviceTypeSerializer()
  246. form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
  247. class Meta:
  248. model = InterfaceTemplate
  249. fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']
  250. class WritableInterfaceTemplateSerializer(serializers.ModelSerializer):
  251. class Meta:
  252. model = InterfaceTemplate
  253. fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']
  254. #
  255. # Device bay templates
  256. #
  257. class DeviceBayTemplateSerializer(serializers.ModelSerializer):
  258. device_type = NestedDeviceTypeSerializer()
  259. class Meta:
  260. model = DeviceBayTemplate
  261. fields = ['id', 'device_type', 'name']
  262. class WritableDeviceBayTemplateSerializer(serializers.ModelSerializer):
  263. class Meta:
  264. model = DeviceBayTemplate
  265. fields = ['id', 'device_type', 'name']
  266. #
  267. # Device roles
  268. #
  269. class DeviceRoleSerializer(serializers.ModelSerializer):
  270. class Meta:
  271. model = DeviceRole
  272. fields = ['id', 'name', 'slug', 'color']
  273. class NestedDeviceRoleSerializer(serializers.ModelSerializer):
  274. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
  275. class Meta:
  276. model = DeviceRole
  277. fields = ['id', 'url', 'name', 'slug']
  278. #
  279. # Platforms
  280. #
  281. class PlatformSerializer(serializers.ModelSerializer):
  282. class Meta:
  283. model = Platform
  284. fields = ['id', 'name', 'slug', 'rpc_client']
  285. class NestedPlatformSerializer(serializers.ModelSerializer):
  286. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
  287. class Meta:
  288. model = Platform
  289. fields = ['id', 'url', 'name', 'slug']
  290. #
  291. # Devices
  292. #
  293. # Cannot import ipam.api.NestedIPAddressSerializer due to circular dependency
  294. class DeviceIPAddressSerializer(serializers.ModelSerializer):
  295. url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail')
  296. class Meta:
  297. model = IPAddress
  298. fields = ['id', 'url', 'family', 'address']
  299. class DeviceSerializer(CustomFieldModelSerializer):
  300. device_type = NestedDeviceTypeSerializer()
  301. device_role = NestedDeviceRoleSerializer()
  302. tenant = NestedTenantSerializer()
  303. platform = NestedPlatformSerializer()
  304. site = NestedSiteSerializer()
  305. rack = NestedRackSerializer()
  306. face = ChoiceFieldSerializer(choices=RACK_FACE_CHOICES)
  307. status = ChoiceFieldSerializer(choices=STATUS_CHOICES)
  308. primary_ip = DeviceIPAddressSerializer()
  309. primary_ip4 = DeviceIPAddressSerializer()
  310. primary_ip6 = DeviceIPAddressSerializer()
  311. parent_device = serializers.SerializerMethodField()
  312. class Meta:
  313. model = Device
  314. fields = [
  315. 'id', 'name', 'display_name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  316. 'site', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4', 'primary_ip6',
  317. 'comments', 'custom_fields',
  318. ]
  319. def get_parent_device(self, obj):
  320. try:
  321. device_bay = obj.parent_bay
  322. except DeviceBay.DoesNotExist:
  323. return None
  324. return {
  325. 'id': device_bay.device.pk,
  326. 'name': device_bay.device.name,
  327. 'device_bay': {
  328. 'id': device_bay.pk,
  329. 'name': device_bay.name,
  330. }
  331. }
  332. class WritableDeviceSerializer(CustomFieldModelSerializer):
  333. class Meta:
  334. model = Device
  335. fields = [
  336. 'id', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag', 'site', 'rack',
  337. 'position', 'face', 'status', 'primary_ip4', 'primary_ip6', 'comments', 'custom_fields',
  338. ]
  339. validators = []
  340. def validate(self, data):
  341. # Validate uniqueness of (rack, position, face) since we omitted the automatically-created validator from Meta.
  342. if data.get('rack') and data.get('position') and data.get('face'):
  343. validator = UniqueTogetherValidator(queryset=Device.objects.all(), fields=('rack', 'position', 'face'))
  344. validator.set_context(self)
  345. validator(data)
  346. return data
  347. #
  348. # Console server ports
  349. #
  350. class ConsoleServerPortSerializer(serializers.ModelSerializer):
  351. device = NestedDeviceSerializer()
  352. class Meta:
  353. model = ConsoleServerPort
  354. fields = ['id', 'device', 'name', 'connected_console']
  355. read_only_fields = ['connected_console']
  356. class WritableConsoleServerPortSerializer(serializers.ModelSerializer):
  357. class Meta:
  358. model = ConsoleServerPort
  359. fields = ['id', 'device', 'name']
  360. #
  361. # Console ports
  362. #
  363. class ConsolePortSerializer(serializers.ModelSerializer):
  364. device = NestedDeviceSerializer()
  365. cs_port = ConsoleServerPortSerializer()
  366. class Meta:
  367. model = ConsolePort
  368. fields = ['id', 'device', 'name', 'cs_port', 'connection_status']
  369. class WritableConsolePortSerializer(serializers.ModelSerializer):
  370. class Meta:
  371. model = ConsolePort
  372. fields = ['id', 'device', 'name', 'cs_port', 'connection_status']
  373. #
  374. # Power outlets
  375. #
  376. class PowerOutletSerializer(serializers.ModelSerializer):
  377. device = NestedDeviceSerializer()
  378. class Meta:
  379. model = PowerOutlet
  380. fields = ['id', 'device', 'name', 'connected_port']
  381. read_only_fields = ['connected_port']
  382. class WritablePowerOutletSerializer(serializers.ModelSerializer):
  383. class Meta:
  384. model = PowerOutlet
  385. fields = ['id', 'device', 'name']
  386. #
  387. # Power ports
  388. #
  389. class PowerPortSerializer(serializers.ModelSerializer):
  390. device = NestedDeviceSerializer()
  391. power_outlet = PowerOutletSerializer()
  392. class Meta:
  393. model = PowerPort
  394. fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']
  395. class WritablePowerPortSerializer(serializers.ModelSerializer):
  396. class Meta:
  397. model = PowerPort
  398. fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']
  399. #
  400. # Interfaces
  401. #
  402. class NestedInterfaceSerializer(serializers.ModelSerializer):
  403. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
  404. class Meta:
  405. model = Interface
  406. fields = ['id', 'url', 'name']
  407. class InterfaceSerializer(serializers.ModelSerializer):
  408. device = NestedDeviceSerializer()
  409. form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
  410. lag = NestedInterfaceSerializer()
  411. connection = serializers.SerializerMethodField(read_only=True)
  412. connected_interface = serializers.SerializerMethodField(read_only=True)
  413. class Meta:
  414. model = Interface
  415. fields = [
  416. 'id', 'device', 'name', 'form_factor', 'lag', 'mac_address', 'mgmt_only', 'description', 'connection',
  417. 'connected_interface',
  418. ]
  419. def get_connection(self, obj):
  420. if obj.connection:
  421. return NestedInterfaceConnectionSerializer(obj.connection, context=self.context).data
  422. return None
  423. def get_connected_interface(self, obj):
  424. if obj.connected_interface:
  425. return PeerInterfaceSerializer(obj.connected_interface, context=self.context).data
  426. return None
  427. class PeerInterfaceSerializer(serializers.ModelSerializer):
  428. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
  429. device = NestedDeviceSerializer()
  430. form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
  431. lag = NestedInterfaceSerializer()
  432. class Meta:
  433. model = Interface
  434. fields = ['id', 'url', 'device', 'name', 'form_factor', 'lag', 'mac_address', 'mgmt_only', 'description']
  435. class WritableInterfaceSerializer(serializers.ModelSerializer):
  436. class Meta:
  437. model = Interface
  438. fields = ['id', 'device', 'name', 'form_factor', 'lag', 'mac_address', 'mgmt_only', 'description']
  439. #
  440. # Device bays
  441. #
  442. class DeviceBaySerializer(serializers.ModelSerializer):
  443. device = NestedDeviceSerializer()
  444. installed_device = NestedDeviceSerializer()
  445. class Meta:
  446. model = DeviceBay
  447. fields = ['id', 'device', 'name', 'installed_device']
  448. class WritableDeviceBaySerializer(serializers.ModelSerializer):
  449. class Meta:
  450. model = DeviceBay
  451. fields = ['id', 'device', 'name', 'installed_device']
  452. #
  453. # Inventory items
  454. #
  455. class InventoryItemSerializer(serializers.ModelSerializer):
  456. device = NestedDeviceSerializer()
  457. manufacturer = NestedManufacturerSerializer()
  458. class Meta:
  459. model = InventoryItem
  460. fields = ['id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'discovered']
  461. class WritableInventoryItemSerializer(serializers.ModelSerializer):
  462. class Meta:
  463. model = InventoryItem
  464. fields = ['id', 'device', 'parent', 'name', 'manufacturer', 'part_id', 'serial', 'discovered']
  465. #
  466. # Interface connections
  467. #
  468. class InterfaceConnectionSerializer(serializers.ModelSerializer):
  469. interface_a = PeerInterfaceSerializer()
  470. interface_b = PeerInterfaceSerializer()
  471. connection_status = ChoiceFieldSerializer(choices=CONNECTION_STATUS_CHOICES)
  472. class Meta:
  473. model = InterfaceConnection
  474. fields = ['id', 'interface_a', 'interface_b', 'connection_status']
  475. class NestedInterfaceConnectionSerializer(serializers.ModelSerializer):
  476. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfaceconnection-detail')
  477. class Meta:
  478. model = InterfaceConnection
  479. fields = ['id', 'url', 'connection_status']
  480. class WritableInterfaceConnectionSerializer(serializers.ModelSerializer):
  481. class Meta:
  482. model = InterfaceConnection
  483. fields = ['id', 'interface_a', 'interface_b', 'connection_status']