serializers.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. from django.contrib.contenttypes.models import ContentType
  2. from drf_yasg.utils import swagger_serializer_method
  3. from rest_framework import serializers
  4. from timezone_field.rest_framework import TimeZoneSerializerField
  5. from dcim.choices import *
  6. from dcim.constants import *
  7. from dcim.models import *
  8. from ipam.api.nested_serializers import NestedASNSerializer, NestedIPAddressSerializer, NestedVLANSerializer
  9. from ipam.models import ASN, VLAN
  10. from netbox.api import ChoiceField, ContentTypeField, SerializedPKRelatedField
  11. from netbox.api.serializers import (
  12. NestedGroupModelSerializer, PrimaryModelSerializer, ValidatedModelSerializer, WritableNestedSerializer,
  13. )
  14. from netbox.config import ConfigItem
  15. from tenancy.api.nested_serializers import NestedTenantSerializer
  16. from users.api.nested_serializers import NestedUserSerializer
  17. from utilities.api import get_serializer_for_model
  18. from virtualization.api.nested_serializers import NestedClusterSerializer
  19. from wireless.api.nested_serializers import NestedWirelessLANSerializer, NestedWirelessLinkSerializer
  20. from wireless.choices import *
  21. from wireless.models import WirelessLAN
  22. from .nested_serializers import *
  23. class LinkTerminationSerializer(serializers.ModelSerializer):
  24. link_peer_type = serializers.SerializerMethodField(read_only=True)
  25. link_peer = serializers.SerializerMethodField(read_only=True)
  26. _occupied = serializers.SerializerMethodField(read_only=True)
  27. def get_link_peer_type(self, obj):
  28. if obj._link_peer is not None:
  29. return f'{obj._link_peer._meta.app_label}.{obj._link_peer._meta.model_name}'
  30. return None
  31. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  32. def get_link_peer(self, obj):
  33. """
  34. Return the appropriate serializer for the link termination model.
  35. """
  36. if obj._link_peer is not None:
  37. serializer = get_serializer_for_model(obj._link_peer, prefix='Nested')
  38. context = {'request': self.context['request']}
  39. return serializer(obj._link_peer, context=context).data
  40. return None
  41. @swagger_serializer_method(serializer_or_field=serializers.BooleanField)
  42. def get__occupied(self, obj):
  43. return obj._occupied
  44. class ConnectedEndpointSerializer(serializers.ModelSerializer):
  45. connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
  46. connected_endpoint = serializers.SerializerMethodField(read_only=True)
  47. connected_endpoint_reachable = serializers.SerializerMethodField(read_only=True)
  48. def get_connected_endpoint_type(self, obj):
  49. if obj._path is not None and obj._path.destination is not None:
  50. return f'{obj._path.destination._meta.app_label}.{obj._path.destination._meta.model_name}'
  51. return None
  52. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  53. def get_connected_endpoint(self, obj):
  54. """
  55. Return the appropriate serializer for the type of connected object.
  56. """
  57. if obj._path is not None and obj._path.destination is not None:
  58. serializer = get_serializer_for_model(obj._path.destination, prefix='Nested')
  59. context = {'request': self.context['request']}
  60. return serializer(obj._path.destination, context=context).data
  61. return None
  62. @swagger_serializer_method(serializer_or_field=serializers.BooleanField)
  63. def get_connected_endpoint_reachable(self, obj):
  64. if obj._path is not None:
  65. return obj._path.is_active
  66. return None
  67. #
  68. # Regions/sites
  69. #
  70. class RegionSerializer(NestedGroupModelSerializer):
  71. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
  72. parent = NestedRegionSerializer(required=False, allow_null=True, default=None)
  73. site_count = serializers.IntegerField(read_only=True)
  74. class Meta:
  75. model = Region
  76. fields = [
  77. 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
  78. 'last_updated', 'site_count', '_depth',
  79. ]
  80. class SiteGroupSerializer(NestedGroupModelSerializer):
  81. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
  82. parent = NestedSiteGroupSerializer(required=False, allow_null=True, default=None)
  83. site_count = serializers.IntegerField(read_only=True)
  84. class Meta:
  85. model = SiteGroup
  86. fields = [
  87. 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', 'created',
  88. 'last_updated', 'site_count', '_depth',
  89. ]
  90. class SiteSerializer(PrimaryModelSerializer):
  91. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
  92. status = ChoiceField(choices=SiteStatusChoices, required=False)
  93. region = NestedRegionSerializer(required=False, allow_null=True)
  94. group = NestedSiteGroupSerializer(required=False, allow_null=True)
  95. tenant = NestedTenantSerializer(required=False, allow_null=True)
  96. time_zone = TimeZoneSerializerField(required=False)
  97. asns = SerializedPKRelatedField(
  98. queryset=ASN.objects.all(),
  99. serializer=NestedASNSerializer,
  100. required=False,
  101. many=True
  102. )
  103. # Related object counts
  104. circuit_count = serializers.IntegerField(read_only=True)
  105. device_count = serializers.IntegerField(read_only=True)
  106. prefix_count = serializers.IntegerField(read_only=True)
  107. rack_count = serializers.IntegerField(read_only=True)
  108. virtualmachine_count = serializers.IntegerField(read_only=True)
  109. vlan_count = serializers.IntegerField(read_only=True)
  110. class Meta:
  111. model = Site
  112. fields = [
  113. 'id', 'url', 'display', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'asn', 'asns',
  114. 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name',
  115. 'contact_phone', 'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
  116. 'circuit_count', 'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
  117. ]
  118. #
  119. # Racks
  120. #
  121. class LocationSerializer(NestedGroupModelSerializer):
  122. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:location-detail')
  123. site = NestedSiteSerializer()
  124. parent = NestedLocationSerializer(required=False, allow_null=True)
  125. tenant = NestedTenantSerializer(required=False, allow_null=True)
  126. rack_count = serializers.IntegerField(read_only=True)
  127. device_count = serializers.IntegerField(read_only=True)
  128. class Meta:
  129. model = Location
  130. fields = [
  131. 'id', 'url', 'display', 'name', 'slug', 'site', 'parent', 'tenant', 'description', 'tags', 'custom_fields',
  132. 'created', 'last_updated', 'rack_count', 'device_count', '_depth',
  133. ]
  134. class RackRoleSerializer(PrimaryModelSerializer):
  135. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
  136. rack_count = serializers.IntegerField(read_only=True)
  137. class Meta:
  138. model = RackRole
  139. fields = [
  140. 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
  141. 'last_updated', 'rack_count',
  142. ]
  143. class RackSerializer(PrimaryModelSerializer):
  144. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
  145. site = NestedSiteSerializer()
  146. location = NestedLocationSerializer(required=False, allow_null=True, default=None)
  147. tenant = NestedTenantSerializer(required=False, allow_null=True)
  148. status = ChoiceField(choices=RackStatusChoices, required=False)
  149. role = NestedRackRoleSerializer(required=False, allow_null=True)
  150. type = ChoiceField(choices=RackTypeChoices, allow_blank=True, required=False)
  151. facility_id = serializers.CharField(max_length=50, allow_blank=True, allow_null=True, label='Facility ID',
  152. default=None)
  153. width = ChoiceField(choices=RackWidthChoices, required=False)
  154. outer_unit = ChoiceField(choices=RackDimensionUnitChoices, allow_blank=True, required=False)
  155. device_count = serializers.IntegerField(read_only=True)
  156. powerfeed_count = serializers.IntegerField(read_only=True)
  157. class Meta:
  158. model = Rack
  159. fields = [
  160. 'id', 'url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial',
  161. 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
  162. 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
  163. ]
  164. class RackUnitSerializer(serializers.Serializer):
  165. """
  166. A rack unit is an abstraction formed by the set (rack, position, face); it does not exist as a row in the database.
  167. """
  168. id = serializers.IntegerField(read_only=True)
  169. name = serializers.CharField(read_only=True)
  170. face = ChoiceField(choices=DeviceFaceChoices, read_only=True)
  171. device = NestedDeviceSerializer(read_only=True)
  172. occupied = serializers.BooleanField(read_only=True)
  173. display = serializers.SerializerMethodField(read_only=True)
  174. def get_display(self, obj):
  175. return obj['name']
  176. class RackReservationSerializer(PrimaryModelSerializer):
  177. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackreservation-detail')
  178. rack = NestedRackSerializer()
  179. user = NestedUserSerializer()
  180. tenant = NestedTenantSerializer(required=False, allow_null=True)
  181. class Meta:
  182. model = RackReservation
  183. fields = [
  184. 'id', 'url', 'display', 'rack', 'units', 'created', 'user', 'tenant', 'description', 'tags',
  185. 'custom_fields',
  186. ]
  187. class RackElevationDetailFilterSerializer(serializers.Serializer):
  188. q = serializers.CharField(
  189. required=False,
  190. default=None
  191. )
  192. face = serializers.ChoiceField(
  193. choices=DeviceFaceChoices,
  194. default=DeviceFaceChoices.FACE_FRONT
  195. )
  196. render = serializers.ChoiceField(
  197. choices=RackElevationDetailRenderChoices,
  198. default=RackElevationDetailRenderChoices.RENDER_JSON
  199. )
  200. unit_width = serializers.IntegerField(
  201. default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_WIDTH')
  202. )
  203. unit_height = serializers.IntegerField(
  204. default=ConfigItem('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT')
  205. )
  206. legend_width = serializers.IntegerField(
  207. default=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT
  208. )
  209. exclude = serializers.IntegerField(
  210. required=False,
  211. default=None
  212. )
  213. expand_devices = serializers.BooleanField(
  214. required=False,
  215. default=True
  216. )
  217. include_images = serializers.BooleanField(
  218. required=False,
  219. default=True
  220. )
  221. #
  222. # Device types
  223. #
  224. class ManufacturerSerializer(PrimaryModelSerializer):
  225. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
  226. devicetype_count = serializers.IntegerField(read_only=True)
  227. inventoryitem_count = serializers.IntegerField(read_only=True)
  228. platform_count = serializers.IntegerField(read_only=True)
  229. class Meta:
  230. model = Manufacturer
  231. fields = [
  232. 'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
  233. 'devicetype_count', 'inventoryitem_count', 'platform_count',
  234. ]
  235. class DeviceTypeSerializer(PrimaryModelSerializer):
  236. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
  237. manufacturer = NestedManufacturerSerializer()
  238. subdevice_role = ChoiceField(choices=SubdeviceRoleChoices, allow_blank=True, required=False)
  239. airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
  240. device_count = serializers.IntegerField(read_only=True)
  241. class Meta:
  242. model = DeviceType
  243. fields = [
  244. 'id', 'url', 'display', 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth',
  245. 'subdevice_role', 'airflow', 'front_image', 'rear_image', 'comments', 'tags', 'custom_fields', 'created',
  246. 'last_updated', 'device_count',
  247. ]
  248. class ConsolePortTemplateSerializer(ValidatedModelSerializer):
  249. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleporttemplate-detail')
  250. device_type = NestedDeviceTypeSerializer()
  251. type = ChoiceField(
  252. choices=ConsolePortTypeChoices,
  253. allow_blank=True,
  254. required=False
  255. )
  256. class Meta:
  257. model = ConsolePortTemplate
  258. fields = [
  259. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated',
  260. ]
  261. class ConsoleServerPortTemplateSerializer(ValidatedModelSerializer):
  262. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverporttemplate-detail')
  263. device_type = NestedDeviceTypeSerializer()
  264. type = ChoiceField(
  265. choices=ConsolePortTypeChoices,
  266. allow_blank=True,
  267. required=False
  268. )
  269. class Meta:
  270. model = ConsoleServerPortTemplate
  271. fields = [
  272. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'description', 'created', 'last_updated',
  273. ]
  274. class PowerPortTemplateSerializer(ValidatedModelSerializer):
  275. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerporttemplate-detail')
  276. device_type = NestedDeviceTypeSerializer()
  277. type = ChoiceField(
  278. choices=PowerPortTypeChoices,
  279. allow_blank=True,
  280. required=False
  281. )
  282. class Meta:
  283. model = PowerPortTemplate
  284. fields = [
  285. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw',
  286. 'description', 'created', 'last_updated',
  287. ]
  288. class PowerOutletTemplateSerializer(ValidatedModelSerializer):
  289. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlettemplate-detail')
  290. device_type = NestedDeviceTypeSerializer()
  291. type = ChoiceField(
  292. choices=PowerOutletTypeChoices,
  293. allow_blank=True,
  294. required=False
  295. )
  296. power_port = NestedPowerPortTemplateSerializer(
  297. required=False,
  298. allow_null=True
  299. )
  300. feed_leg = ChoiceField(
  301. choices=PowerOutletFeedLegChoices,
  302. allow_blank=True,
  303. required=False
  304. )
  305. class Meta:
  306. model = PowerOutletTemplate
  307. fields = [
  308. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
  309. 'created', 'last_updated',
  310. ]
  311. class InterfaceTemplateSerializer(ValidatedModelSerializer):
  312. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interfacetemplate-detail')
  313. device_type = NestedDeviceTypeSerializer()
  314. type = ChoiceField(choices=InterfaceTypeChoices)
  315. class Meta:
  316. model = InterfaceTemplate
  317. fields = [
  318. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'mgmt_only', 'description', 'created',
  319. 'last_updated',
  320. ]
  321. class RearPortTemplateSerializer(ValidatedModelSerializer):
  322. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearporttemplate-detail')
  323. device_type = NestedDeviceTypeSerializer()
  324. type = ChoiceField(choices=PortTypeChoices)
  325. class Meta:
  326. model = RearPortTemplate
  327. fields = [
  328. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'positions', 'description',
  329. 'created', 'last_updated',
  330. ]
  331. class FrontPortTemplateSerializer(ValidatedModelSerializer):
  332. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontporttemplate-detail')
  333. device_type = NestedDeviceTypeSerializer()
  334. type = ChoiceField(choices=PortTypeChoices)
  335. rear_port = NestedRearPortTemplateSerializer()
  336. class Meta:
  337. model = FrontPortTemplate
  338. fields = [
  339. 'id', 'url', 'display', 'device_type', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
  340. 'description', 'created', 'last_updated',
  341. ]
  342. class DeviceBayTemplateSerializer(ValidatedModelSerializer):
  343. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebaytemplate-detail')
  344. device_type = NestedDeviceTypeSerializer()
  345. class Meta:
  346. model = DeviceBayTemplate
  347. fields = ['id', 'url', 'display', 'device_type', 'name', 'label', 'description', 'created', 'last_updated']
  348. #
  349. # Devices
  350. #
  351. class DeviceRoleSerializer(PrimaryModelSerializer):
  352. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
  353. device_count = serializers.IntegerField(read_only=True)
  354. virtualmachine_count = serializers.IntegerField(read_only=True)
  355. class Meta:
  356. model = DeviceRole
  357. fields = [
  358. 'id', 'url', 'display', 'name', 'slug', 'color', 'vm_role', 'description', 'tags', 'custom_fields',
  359. 'created', 'last_updated', 'device_count', 'virtualmachine_count',
  360. ]
  361. class PlatformSerializer(PrimaryModelSerializer):
  362. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
  363. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
  364. device_count = serializers.IntegerField(read_only=True)
  365. virtualmachine_count = serializers.IntegerField(read_only=True)
  366. class Meta:
  367. model = Platform
  368. fields = [
  369. 'id', 'url', 'display', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description',
  370. 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count',
  371. ]
  372. class DeviceSerializer(PrimaryModelSerializer):
  373. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail')
  374. device_type = NestedDeviceTypeSerializer()
  375. device_role = NestedDeviceRoleSerializer()
  376. tenant = NestedTenantSerializer(required=False, allow_null=True, default=None)
  377. platform = NestedPlatformSerializer(required=False, allow_null=True)
  378. site = NestedSiteSerializer()
  379. location = NestedLocationSerializer(required=False, allow_null=True, default=None)
  380. rack = NestedRackSerializer(required=False, allow_null=True, default=None)
  381. face = ChoiceField(choices=DeviceFaceChoices, allow_blank=True, default='')
  382. position = serializers.IntegerField(allow_null=True, label='Position (U)', min_value=1, default=None)
  383. status = ChoiceField(choices=DeviceStatusChoices, required=False)
  384. airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
  385. primary_ip = NestedIPAddressSerializer(read_only=True)
  386. primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True)
  387. primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True)
  388. parent_device = serializers.SerializerMethodField()
  389. cluster = NestedClusterSerializer(required=False, allow_null=True)
  390. virtual_chassis = NestedVirtualChassisSerializer(required=False, allow_null=True, default=None)
  391. vc_position = serializers.IntegerField(allow_null=True, max_value=255, min_value=0, default=None)
  392. class Meta:
  393. model = Device
  394. fields = [
  395. 'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  396. 'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'airflow', 'primary_ip',
  397. 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments',
  398. 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated',
  399. ]
  400. @swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
  401. def get_parent_device(self, obj):
  402. try:
  403. device_bay = obj.parent_bay
  404. except DeviceBay.DoesNotExist:
  405. return None
  406. context = {'request': self.context['request']}
  407. data = NestedDeviceSerializer(instance=device_bay.device, context=context).data
  408. data['device_bay'] = NestedDeviceBaySerializer(instance=device_bay, context=context).data
  409. return data
  410. class DeviceWithConfigContextSerializer(DeviceSerializer):
  411. config_context = serializers.SerializerMethodField()
  412. class Meta(DeviceSerializer.Meta):
  413. fields = [
  414. 'id', 'url', 'display', 'name', 'device_type', 'device_role', 'tenant', 'platform', 'serial', 'asset_tag',
  415. 'site', 'location', 'rack', 'position', 'face', 'parent_device', 'status', 'primary_ip', 'primary_ip4',
  416. 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'local_context_data',
  417. 'tags', 'custom_fields', 'config_context', 'created', 'last_updated',
  418. ]
  419. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  420. def get_config_context(self, obj):
  421. return obj.get_config_context()
  422. class DeviceNAPALMSerializer(serializers.Serializer):
  423. method = serializers.DictField()
  424. #
  425. # Device components
  426. #
  427. class ConsoleServerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  428. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleserverport-detail')
  429. device = NestedDeviceSerializer()
  430. type = ChoiceField(
  431. choices=ConsolePortTypeChoices,
  432. allow_blank=True,
  433. required=False
  434. )
  435. speed = ChoiceField(
  436. choices=ConsolePortSpeedChoices,
  437. allow_null=True,
  438. required=False
  439. )
  440. cable = NestedCableSerializer(read_only=True)
  441. class Meta:
  442. model = ConsoleServerPort
  443. fields = [
  444. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected',
  445. 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type',
  446. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  447. ]
  448. class ConsolePortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  449. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:consoleport-detail')
  450. device = NestedDeviceSerializer()
  451. type = ChoiceField(
  452. choices=ConsolePortTypeChoices,
  453. allow_blank=True,
  454. required=False
  455. )
  456. speed = ChoiceField(
  457. choices=ConsolePortSpeedChoices,
  458. allow_null=True,
  459. required=False
  460. )
  461. cable = NestedCableSerializer(read_only=True)
  462. class Meta:
  463. model = ConsolePort
  464. fields = [
  465. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected',
  466. 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type',
  467. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  468. ]
  469. class PowerOutletSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  470. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:poweroutlet-detail')
  471. device = NestedDeviceSerializer()
  472. type = ChoiceField(
  473. choices=PowerOutletTypeChoices,
  474. allow_blank=True,
  475. required=False
  476. )
  477. power_port = NestedPowerPortSerializer(
  478. required=False,
  479. allow_null=True
  480. )
  481. feed_leg = ChoiceField(
  482. choices=PowerOutletFeedLegChoices,
  483. allow_blank=True,
  484. required=False
  485. )
  486. cable = NestedCableSerializer(
  487. read_only=True
  488. )
  489. class Meta:
  490. model = PowerOutlet
  491. fields = [
  492. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
  493. 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type',
  494. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  495. ]
  496. class PowerPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  497. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerport-detail')
  498. device = NestedDeviceSerializer()
  499. type = ChoiceField(
  500. choices=PowerPortTypeChoices,
  501. allow_blank=True,
  502. required=False
  503. )
  504. cable = NestedCableSerializer(read_only=True)
  505. class Meta:
  506. model = PowerPort
  507. fields = [
  508. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description',
  509. 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint', 'connected_endpoint_type',
  510. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
  511. ]
  512. class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  513. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
  514. device = NestedDeviceSerializer()
  515. type = ChoiceField(choices=InterfaceTypeChoices)
  516. parent = NestedInterfaceSerializer(required=False, allow_null=True)
  517. bridge = NestedInterfaceSerializer(required=False, allow_null=True)
  518. lag = NestedInterfaceSerializer(required=False, allow_null=True)
  519. mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
  520. rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_null=True)
  521. rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False)
  522. untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
  523. tagged_vlans = SerializedPKRelatedField(
  524. queryset=VLAN.objects.all(),
  525. serializer=NestedVLANSerializer,
  526. required=False,
  527. many=True
  528. )
  529. cable = NestedCableSerializer(read_only=True)
  530. wireless_link = NestedWirelessLinkSerializer(read_only=True)
  531. wireless_lans = SerializedPKRelatedField(
  532. queryset=WirelessLAN.objects.all(),
  533. serializer=NestedWirelessLANSerializer,
  534. required=False,
  535. many=True
  536. )
  537. count_ipaddresses = serializers.IntegerField(read_only=True)
  538. count_fhrp_groups = serializers.IntegerField(read_only=True)
  539. class Meta:
  540. model = Interface
  541. fields = [
  542. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag', 'mtu',
  543. 'mac_address', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency',
  544. 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable', 'wireless_link',
  545. 'link_peer', 'link_peer_type', 'wireless_lans', 'connected_endpoint', 'connected_endpoint_type',
  546. 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', 'count_ipaddresses',
  547. 'count_fhrp_groups', '_occupied',
  548. ]
  549. def validate(self, data):
  550. # Validate many-to-many VLAN assignments
  551. device = self.instance.device if self.instance else data.get('device')
  552. for vlan in data.get('tagged_vlans', []):
  553. if vlan.site not in [device.site, None]:
  554. raise serializers.ValidationError({
  555. 'tagged_vlans': f"VLAN {vlan} must belong to the same site as the interface's parent device, or "
  556. f"it must be global."
  557. })
  558. return super().validate(data)
  559. class RearPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer):
  560. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  561. device = NestedDeviceSerializer()
  562. type = ChoiceField(choices=PortTypeChoices)
  563. cable = NestedCableSerializer(read_only=True)
  564. class Meta:
  565. model = RearPort
  566. fields = [
  567. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'positions', 'description',
  568. 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', 'custom_fields', 'created',
  569. 'last_updated', '_occupied',
  570. ]
  571. class FrontPortRearPortSerializer(WritableNestedSerializer):
  572. """
  573. NestedRearPortSerializer but with parent device omitted (since front and rear ports must belong to same device)
  574. """
  575. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
  576. class Meta:
  577. model = RearPort
  578. fields = ['id', 'url', 'display', 'name', 'label']
  579. class FrontPortSerializer(PrimaryModelSerializer, LinkTerminationSerializer):
  580. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:frontport-detail')
  581. device = NestedDeviceSerializer()
  582. type = ChoiceField(choices=PortTypeChoices)
  583. rear_port = FrontPortRearPortSerializer()
  584. cable = NestedCableSerializer(read_only=True)
  585. class Meta:
  586. model = FrontPort
  587. fields = [
  588. 'id', 'url', 'display', 'device', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position',
  589. 'description', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'tags', 'custom_fields',
  590. 'created', 'last_updated', '_occupied',
  591. ]
  592. class DeviceBaySerializer(PrimaryModelSerializer):
  593. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
  594. device = NestedDeviceSerializer()
  595. installed_device = NestedDeviceSerializer(required=False, allow_null=True)
  596. class Meta:
  597. model = DeviceBay
  598. fields = [
  599. 'id', 'url', 'display', 'device', 'name', 'label', 'description', 'installed_device', 'tags',
  600. 'custom_fields', 'created', 'last_updated',
  601. ]
  602. #
  603. # Inventory items
  604. #
  605. class InventoryItemSerializer(PrimaryModelSerializer):
  606. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:inventoryitem-detail')
  607. device = NestedDeviceSerializer()
  608. parent = serializers.PrimaryKeyRelatedField(queryset=InventoryItem.objects.all(), allow_null=True, default=None)
  609. manufacturer = NestedManufacturerSerializer(required=False, allow_null=True, default=None)
  610. _depth = serializers.IntegerField(source='level', read_only=True)
  611. class Meta:
  612. model = InventoryItem
  613. fields = [
  614. 'id', 'url', 'display', 'device', 'parent', 'name', 'label', 'manufacturer', 'part_id', 'serial',
  615. 'asset_tag', 'discovered', 'description', 'tags', 'custom_fields', 'created', 'last_updated', '_depth',
  616. ]
  617. #
  618. # Cables
  619. #
  620. class CableSerializer(PrimaryModelSerializer):
  621. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  622. termination_a_type = ContentTypeField(
  623. queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
  624. )
  625. termination_b_type = ContentTypeField(
  626. queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
  627. )
  628. termination_a = serializers.SerializerMethodField(read_only=True)
  629. termination_b = serializers.SerializerMethodField(read_only=True)
  630. status = ChoiceField(choices=LinkStatusChoices, required=False)
  631. tenant = NestedTenantSerializer(required=False, allow_null=True)
  632. length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
  633. class Meta:
  634. model = Cable
  635. fields = [
  636. 'id', 'url', 'display', 'termination_a_type', 'termination_a_id', 'termination_a', 'termination_b_type',
  637. 'termination_b_id', 'termination_b', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit',
  638. 'tags', 'custom_fields',
  639. ]
  640. def _get_termination(self, obj, side):
  641. """
  642. Serialize a nested representation of a termination.
  643. """
  644. if side.lower() not in ['a', 'b']:
  645. raise ValueError("Termination side must be either A or B.")
  646. termination = getattr(obj, 'termination_{}'.format(side.lower()))
  647. if termination is None:
  648. return None
  649. serializer = get_serializer_for_model(termination, prefix='Nested')
  650. context = {'request': self.context['request']}
  651. data = serializer(termination, context=context).data
  652. return data
  653. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  654. def get_termination_a(self, obj):
  655. return self._get_termination(obj, 'a')
  656. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  657. def get_termination_b(self, obj):
  658. return self._get_termination(obj, 'b')
  659. class TracedCableSerializer(serializers.ModelSerializer):
  660. """
  661. Used only while tracing a cable path.
  662. """
  663. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
  664. class Meta:
  665. model = Cable
  666. fields = [
  667. 'id', 'url', 'type', 'status', 'label', 'color', 'length', 'length_unit',
  668. ]
  669. class CablePathSerializer(serializers.ModelSerializer):
  670. origin_type = ContentTypeField(read_only=True)
  671. origin = serializers.SerializerMethodField(read_only=True)
  672. destination_type = ContentTypeField(read_only=True)
  673. destination = serializers.SerializerMethodField(read_only=True)
  674. path = serializers.SerializerMethodField(read_only=True)
  675. class Meta:
  676. model = CablePath
  677. fields = [
  678. 'id', 'origin_type', 'origin', 'destination_type', 'destination', 'path', 'is_active', 'is_split',
  679. ]
  680. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  681. def get_origin(self, obj):
  682. """
  683. Return the appropriate serializer for the origin.
  684. """
  685. serializer = get_serializer_for_model(obj.origin, prefix='Nested')
  686. context = {'request': self.context['request']}
  687. return serializer(obj.origin, context=context).data
  688. @swagger_serializer_method(serializer_or_field=serializers.DictField)
  689. def get_destination(self, obj):
  690. """
  691. Return the appropriate serializer for the destination, if any.
  692. """
  693. if obj.destination_id is not None:
  694. serializer = get_serializer_for_model(obj.destination, prefix='Nested')
  695. context = {'request': self.context['request']}
  696. return serializer(obj.destination, context=context).data
  697. return None
  698. @swagger_serializer_method(serializer_or_field=serializers.ListField)
  699. def get_path(self, obj):
  700. ret = []
  701. for node in obj.get_path():
  702. serializer = get_serializer_for_model(node, prefix='Nested')
  703. context = {'request': self.context['request']}
  704. ret.append(serializer(node, context=context).data)
  705. return ret
  706. #
  707. # Virtual chassis
  708. #
  709. class VirtualChassisSerializer(PrimaryModelSerializer):
  710. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
  711. master = NestedDeviceSerializer(required=False)
  712. member_count = serializers.IntegerField(read_only=True)
  713. class Meta:
  714. model = VirtualChassis
  715. fields = ['id', 'url', 'display', 'name', 'domain', 'master', 'tags', 'custom_fields', 'member_count']
  716. #
  717. # Power panels
  718. #
  719. class PowerPanelSerializer(PrimaryModelSerializer):
  720. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
  721. site = NestedSiteSerializer()
  722. location = NestedLocationSerializer(
  723. required=False,
  724. allow_null=True,
  725. default=None
  726. )
  727. powerfeed_count = serializers.IntegerField(read_only=True)
  728. class Meta:
  729. model = PowerPanel
  730. fields = ['id', 'url', 'display', 'site', 'location', 'name', 'tags', 'custom_fields', 'powerfeed_count']
  731. class PowerFeedSerializer(PrimaryModelSerializer, LinkTerminationSerializer, ConnectedEndpointSerializer):
  732. url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
  733. power_panel = NestedPowerPanelSerializer()
  734. rack = NestedRackSerializer(
  735. required=False,
  736. allow_null=True,
  737. default=None
  738. )
  739. type = ChoiceField(
  740. choices=PowerFeedTypeChoices,
  741. default=PowerFeedTypeChoices.TYPE_PRIMARY
  742. )
  743. status = ChoiceField(
  744. choices=PowerFeedStatusChoices,
  745. default=PowerFeedStatusChoices.STATUS_ACTIVE
  746. )
  747. supply = ChoiceField(
  748. choices=PowerFeedSupplyChoices,
  749. default=PowerFeedSupplyChoices.SUPPLY_AC
  750. )
  751. phase = ChoiceField(
  752. choices=PowerFeedPhaseChoices,
  753. default=PowerFeedPhaseChoices.PHASE_SINGLE
  754. )
  755. cable = NestedCableSerializer(read_only=True)
  756. class Meta:
  757. model = PowerFeed
  758. fields = [
  759. 'id', 'url', 'display', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
  760. 'amperage', 'max_utilization', 'comments', 'mark_connected', 'cable', 'link_peer', 'link_peer_type',
  761. 'connected_endpoint', 'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields',
  762. 'created', 'last_updated', '_occupied',
  763. ]