views.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. from rest_framework import generics
  2. from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
  3. from rest_framework.response import Response
  4. from rest_framework.settings import api_settings
  5. from rest_framework.views import APIView
  6. from django.conf import settings
  7. from django.contrib.contenttypes.models import ContentType
  8. from django.http import Http404
  9. from django.shortcuts import get_object_or_404
  10. from dcim.models import (
  11. ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceRole, DeviceType, Interface, InterfaceConnection,
  12. Manufacturer, Module, Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackReservation, RackRole, Region, Site,
  13. VIRTUAL_IFACE_TYPES,
  14. )
  15. from dcim import filters
  16. from extras.api.views import CustomFieldModelAPIView
  17. from extras.api.renderers import BINDZoneRenderer, FlatJSONRenderer
  18. from utilities.api import ServiceUnavailable
  19. from .exceptions import MissingFilterException
  20. from . import serializers
  21. #
  22. # Regions
  23. #
  24. class RegionListView(generics.ListAPIView):
  25. """
  26. List all regions
  27. """
  28. queryset = Region.objects.all()
  29. serializer_class = serializers.RegionSerializer
  30. class RegionDetailView(generics.RetrieveAPIView):
  31. """
  32. Retrieve a single region
  33. """
  34. queryset = Region.objects.all()
  35. serializer_class = serializers.RegionSerializer
  36. #
  37. # Sites
  38. #
  39. class SiteListView(CustomFieldModelAPIView, generics.ListAPIView):
  40. """
  41. List all sites
  42. """
  43. queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values__field')
  44. serializer_class = serializers.SiteSerializer
  45. class SiteDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
  46. """
  47. Retrieve a single site
  48. """
  49. queryset = Site.objects.select_related('tenant').prefetch_related('custom_field_values__field')
  50. serializer_class = serializers.SiteSerializer
  51. #
  52. # Rack groups
  53. #
  54. class RackGroupListView(generics.ListAPIView):
  55. """
  56. List all rack groups
  57. """
  58. queryset = RackGroup.objects.select_related('site')
  59. serializer_class = serializers.RackGroupSerializer
  60. filter_class = filters.RackGroupFilter
  61. class RackGroupDetailView(generics.RetrieveAPIView):
  62. """
  63. Retrieve a single rack group
  64. """
  65. queryset = RackGroup.objects.select_related('site')
  66. serializer_class = serializers.RackGroupSerializer
  67. #
  68. # Rack roles
  69. #
  70. class RackRoleListView(generics.ListAPIView):
  71. """
  72. List all rack roles
  73. """
  74. queryset = RackRole.objects.all()
  75. serializer_class = serializers.RackRoleSerializer
  76. class RackRoleDetailView(generics.RetrieveAPIView):
  77. """
  78. Retrieve a single rack role
  79. """
  80. queryset = RackRole.objects.all()
  81. serializer_class = serializers.RackRoleSerializer
  82. #
  83. # Racks
  84. #
  85. class RackListView(CustomFieldModelAPIView, generics.ListAPIView):
  86. """
  87. List racks (filterable)
  88. """
  89. queryset = Rack.objects.select_related('site', 'group__site', 'tenant')\
  90. .prefetch_related('custom_field_values__field')
  91. serializer_class = serializers.RackSerializer
  92. filter_class = filters.RackFilter
  93. class RackDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
  94. """
  95. Retrieve a single rack
  96. """
  97. queryset = Rack.objects.select_related('site', 'group__site', 'tenant')\
  98. .prefetch_related('custom_field_values__field')
  99. serializer_class = serializers.RackDetailSerializer
  100. #
  101. # Rack units
  102. #
  103. class RackUnitListView(APIView):
  104. """
  105. List rack units (by rack)
  106. """
  107. def get(self, request, pk):
  108. rack = get_object_or_404(Rack, pk=pk)
  109. face = request.GET.get('face', 0)
  110. exclude_pk = request.GET.get('exclude', None)
  111. if exclude_pk is not None:
  112. try:
  113. exclude_pk = int(exclude_pk)
  114. except ValueError:
  115. exclude_pk = None
  116. elevation = rack.get_rack_units(face, exclude_pk)
  117. # Serialize Devices within the rack elevation
  118. for u in elevation:
  119. if u['device']:
  120. u['device'] = serializers.DeviceNestedSerializer(instance=u['device']).data
  121. return Response(elevation)
  122. #
  123. # Rack reservations
  124. #
  125. class RackReservationListView(generics.ListAPIView):
  126. """
  127. List all rack reservation
  128. """
  129. queryset = RackReservation.objects.all()
  130. serializer_class = serializers.RackReservationSerializer
  131. filter_class = filters.RackReservationFilter
  132. class RackReservationDetailView(generics.RetrieveAPIView):
  133. """
  134. Retrieve a single rack reservation
  135. """
  136. queryset = RackReservation.objects.all()
  137. serializer_class = serializers.RackReservationSerializer
  138. #
  139. # Manufacturers
  140. #
  141. class ManufacturerListView(generics.ListAPIView):
  142. """
  143. List all hardware manufacturers
  144. """
  145. queryset = Manufacturer.objects.all()
  146. serializer_class = serializers.ManufacturerSerializer
  147. class ManufacturerDetailView(generics.RetrieveAPIView):
  148. """
  149. Retrieve a single hardware manufacturers
  150. """
  151. queryset = Manufacturer.objects.all()
  152. serializer_class = serializers.ManufacturerSerializer
  153. #
  154. # Device Types
  155. #
  156. class DeviceTypeListView(CustomFieldModelAPIView, generics.ListAPIView):
  157. """
  158. List device types (filterable)
  159. """
  160. queryset = DeviceType.objects.select_related('manufacturer').prefetch_related('custom_field_values__field')
  161. serializer_class = serializers.DeviceTypeSerializer
  162. filter_class = filters.DeviceTypeFilter
  163. class DeviceTypeDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
  164. """
  165. Retrieve a single device type
  166. """
  167. queryset = DeviceType.objects.select_related('manufacturer').prefetch_related('custom_field_values__field')
  168. serializer_class = serializers.DeviceTypeDetailSerializer
  169. #
  170. # Device roles
  171. #
  172. class DeviceRoleListView(generics.ListAPIView):
  173. """
  174. List all device roles
  175. """
  176. queryset = DeviceRole.objects.all()
  177. serializer_class = serializers.DeviceRoleSerializer
  178. class DeviceRoleDetailView(generics.RetrieveAPIView):
  179. """
  180. Retrieve a single device role
  181. """
  182. queryset = DeviceRole.objects.all()
  183. serializer_class = serializers.DeviceRoleSerializer
  184. #
  185. # Platforms
  186. #
  187. class PlatformListView(generics.ListAPIView):
  188. """
  189. List all platforms
  190. """
  191. queryset = Platform.objects.all()
  192. serializer_class = serializers.PlatformSerializer
  193. class PlatformDetailView(generics.RetrieveAPIView):
  194. """
  195. Retrieve a single platform
  196. """
  197. queryset = Platform.objects.all()
  198. serializer_class = serializers.PlatformSerializer
  199. #
  200. # Devices
  201. #
  202. class DeviceListView(CustomFieldModelAPIView, generics.ListAPIView):
  203. """
  204. List devices (filterable)
  205. """
  206. queryset = Device.objects.select_related(
  207. 'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay'
  208. ).prefetch_related(
  209. 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'custom_field_values__field'
  210. )
  211. serializer_class = serializers.DeviceSerializer
  212. filter_class = filters.DeviceFilter
  213. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + [BINDZoneRenderer, FlatJSONRenderer]
  214. class DeviceDetailView(CustomFieldModelAPIView, generics.RetrieveAPIView):
  215. """
  216. Retrieve a single device
  217. """
  218. queryset = Device.objects.select_related(
  219. 'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay'
  220. ).prefetch_related('custom_field_values__field')
  221. serializer_class = serializers.DeviceSerializer
  222. #
  223. # Console ports
  224. #
  225. class ConsolePortListView(generics.ListAPIView):
  226. """
  227. List console ports (by device)
  228. """
  229. serializer_class = serializers.ConsolePortSerializer
  230. def get_queryset(self):
  231. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  232. return ConsolePort.objects.filter(device=device).select_related('cs_port')
  233. class ConsolePortView(generics.RetrieveUpdateDestroyAPIView):
  234. permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
  235. serializer_class = serializers.ConsolePortSerializer
  236. queryset = ConsolePort.objects.all()
  237. #
  238. # Console server ports
  239. #
  240. class ConsoleServerPortListView(generics.ListAPIView):
  241. """
  242. List console server ports (by device)
  243. """
  244. serializer_class = serializers.ConsoleServerPortSerializer
  245. def get_queryset(self):
  246. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  247. return ConsoleServerPort.objects.filter(device=device).select_related('connected_console')
  248. #
  249. # Power ports
  250. #
  251. class PowerPortListView(generics.ListAPIView):
  252. """
  253. List power ports (by device)
  254. """
  255. serializer_class = serializers.PowerPortSerializer
  256. def get_queryset(self):
  257. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  258. return PowerPort.objects.filter(device=device).select_related('power_outlet')
  259. class PowerPortView(generics.RetrieveUpdateDestroyAPIView):
  260. permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
  261. serializer_class = serializers.PowerPortSerializer
  262. queryset = PowerPort.objects.all()
  263. #
  264. # Power outlets
  265. #
  266. class PowerOutletListView(generics.ListAPIView):
  267. """
  268. List power outlets (by device)
  269. """
  270. serializer_class = serializers.PowerOutletSerializer
  271. def get_queryset(self):
  272. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  273. return PowerOutlet.objects.filter(device=device).select_related('connected_port')
  274. #
  275. # Interfaces
  276. #
  277. class InterfaceListView(generics.ListAPIView):
  278. """
  279. List interfaces (by device)
  280. """
  281. serializer_class = serializers.InterfaceSerializer
  282. filter_class = filters.InterfaceFilter
  283. def get_queryset(self):
  284. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  285. queryset = Interface.objects.order_naturally(device.device_type.interface_ordering).filter(device=device)\
  286. .select_related('connected_as_a', 'connected_as_b', 'circuit_termination')
  287. # Filter by type (physical or virtual)
  288. iface_type = self.request.query_params.get('type')
  289. if iface_type == 'physical':
  290. queryset = queryset.exclude(form_factor__in=VIRTUAL_IFACE_TYPES)
  291. elif iface_type == 'virtual':
  292. queryset = queryset.filter(form_factor__in=VIRTUAL_IFACE_TYPES)
  293. elif iface_type is not None:
  294. queryset = queryset.empty()
  295. return queryset
  296. class InterfaceDetailView(generics.RetrieveAPIView):
  297. """
  298. Retrieve a single interface
  299. """
  300. queryset = Interface.objects.select_related('device')
  301. serializer_class = serializers.InterfaceDetailSerializer
  302. class InterfaceConnectionView(generics.RetrieveUpdateDestroyAPIView):
  303. permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
  304. serializer_class = serializers.InterfaceConnectionSerializer
  305. queryset = InterfaceConnection.objects.all()
  306. class InterfaceConnectionListView(generics.ListAPIView):
  307. """
  308. Retrieve a list of all interface connections
  309. """
  310. serializer_class = serializers.InterfaceConnectionSerializer
  311. queryset = InterfaceConnection.objects.all()
  312. #
  313. # Device bays
  314. #
  315. class DeviceBayListView(generics.ListAPIView):
  316. """
  317. List device bays (by device)
  318. """
  319. serializer_class = serializers.DeviceBayNestedSerializer
  320. def get_queryset(self):
  321. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  322. return DeviceBay.objects.filter(device=device).select_related('installed_device')
  323. #
  324. # Modules
  325. #
  326. class ModuleListView(generics.ListAPIView):
  327. """
  328. List device modules (by device)
  329. """
  330. serializer_class = serializers.ModuleSerializer
  331. def get_queryset(self):
  332. device = get_object_or_404(Device, pk=self.kwargs['pk'])
  333. return Module.objects.filter(device=device).select_related('device', 'manufacturer')
  334. #
  335. # Live queries
  336. #
  337. class LLDPNeighborsView(APIView):
  338. """
  339. Retrieve live LLDP neighbors of a device
  340. """
  341. def get(self, request, pk):
  342. device = get_object_or_404(Device, pk=pk)
  343. if not device.primary_ip:
  344. raise ServiceUnavailable(detail="No IP configured for this device.")
  345. RPC = device.get_rpc_client()
  346. if not RPC:
  347. raise ServiceUnavailable(detail="No RPC client available for this platform ({}).".format(device.platform))
  348. # Connect to device and retrieve inventory info
  349. try:
  350. with RPC(device, username=settings.NETBOX_USERNAME, password=settings.NETBOX_PASSWORD) as rpc_client:
  351. lldp_neighbors = rpc_client.get_lldp_neighbors()
  352. except:
  353. raise ServiceUnavailable(detail="Error connecting to the remote device.")
  354. return Response(lldp_neighbors)
  355. #
  356. # Miscellaneous
  357. #
  358. class RelatedConnectionsView(APIView):
  359. """
  360. Retrieve all connections related to a given console/power/interface connection
  361. """
  362. def __init__(self):
  363. super(RelatedConnectionsView, self).__init__()
  364. # Custom fields
  365. self.content_type = ContentType.objects.get_for_model(Device)
  366. self.custom_fields = self.content_type.custom_fields.prefetch_related('choices')
  367. def get(self, request):
  368. peer_device = request.GET.get('peer-device')
  369. peer_interface = request.GET.get('peer-interface')
  370. # Search by interface
  371. if peer_device and peer_interface:
  372. # Determine local interface from peer interface's connection
  373. try:
  374. peer_iface = Interface.objects.get(device__name=peer_device, name=peer_interface)
  375. except Interface.DoesNotExist:
  376. raise Http404()
  377. local_iface = peer_iface.connected_interface
  378. if local_iface:
  379. device = local_iface.device
  380. else:
  381. return Response()
  382. else:
  383. raise MissingFilterException(detail='Must specify search parameters "peer-device" and "peer-interface".')
  384. # Initialize response skeleton
  385. response = {
  386. 'device': serializers.DeviceSerializer(device, context={'view': self}).data,
  387. 'console-ports': [],
  388. 'power-ports': [],
  389. 'interfaces': [],
  390. }
  391. # Console connections
  392. console_ports = ConsolePort.objects.filter(device=device).select_related('cs_port__device')
  393. for cp in console_ports:
  394. data = serializers.ConsolePortSerializer(instance=cp).data
  395. del(data['device'])
  396. response['console-ports'].append(data)
  397. # Power connections
  398. power_ports = PowerPort.objects.filter(device=device).select_related('power_outlet__device')
  399. for pp in power_ports:
  400. data = serializers.PowerPortSerializer(instance=pp).data
  401. del(data['device'])
  402. response['power-ports'].append(data)
  403. # Interface connections
  404. interfaces = Interface.objects.order_naturally(device.device_type.interface_ordering).filter(device=device)\
  405. .select_related('connected_as_a', 'connected_as_b', 'circuit_termination')
  406. for iface in interfaces:
  407. data = serializers.InterfaceDetailSerializer(instance=iface).data
  408. del(data['device'])
  409. response['interfaces'].append(data)
  410. return Response(response)