views.py 14 KB

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