serializers.py 23 KB

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