serializers.py 26 KB

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