serializers.py 23 KB

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