views.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. from rest_framework.decorators import detail_route
  2. from rest_framework.permissions import IsAuthenticated
  3. from rest_framework.response import Response
  4. from rest_framework.viewsets import ModelViewSet, ViewSet
  5. from django.conf import settings
  6. from django.shortcuts import get_object_or_404
  7. from dcim.models import (
  8. ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
  9. DeviceBayTemplate, DeviceRole, DeviceType, Interface, InterfaceConnection, InterfaceTemplate, Manufacturer, Module,
  10. Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation,
  11. RackRole, Region, Site,
  12. )
  13. from dcim import filters
  14. from extras.api.serializers import RenderedGraphSerializer
  15. from extras.api.views import CustomFieldModelViewSet
  16. from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
  17. from utilities.api import ServiceUnavailable, WritableSerializerMixin
  18. from .exceptions import MissingFilterException
  19. from . import serializers
  20. #
  21. # Regions
  22. #
  23. class RegionViewSet(WritableSerializerMixin, ModelViewSet):
  24. queryset = Region.objects.all()
  25. serializer_class = serializers.RegionSerializer
  26. write_serializer_class = serializers.WritableRegionSerializer
  27. #
  28. # Sites
  29. #
  30. class SiteViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
  31. queryset = Site.objects.select_related('region', 'tenant')
  32. serializer_class = serializers.SiteSerializer
  33. filter_class = filters.SiteFilter
  34. write_serializer_class = serializers.WritableSiteSerializer
  35. @detail_route()
  36. def graphs(self, request, pk=None):
  37. """
  38. A convenience method for rendering graphs for a particular site.
  39. """
  40. site = get_object_or_404(Site, pk=pk)
  41. queryset = Graph.objects.filter(type=GRAPH_TYPE_SITE)
  42. serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': site})
  43. return Response(serializer.data)
  44. #
  45. # Rack groups
  46. #
  47. class RackGroupViewSet(WritableSerializerMixin, ModelViewSet):
  48. queryset = RackGroup.objects.select_related('site')
  49. serializer_class = serializers.RackGroupSerializer
  50. filter_class = filters.RackGroupFilter
  51. write_serializer_class = serializers.WritableRackGroupSerializer
  52. #
  53. # Rack roles
  54. #
  55. class RackRoleViewSet(ModelViewSet):
  56. queryset = RackRole.objects.all()
  57. serializer_class = serializers.RackRoleSerializer
  58. #
  59. # Racks
  60. #
  61. class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
  62. queryset = Rack.objects.select_related('site', 'group__site', 'tenant')
  63. serializer_class = serializers.RackSerializer
  64. write_serializer_class = serializers.WritableRackSerializer
  65. filter_class = filters.RackFilter
  66. @detail_route(url_path='rack-units')
  67. def rack_units(self, request, pk=None):
  68. """
  69. List rack units (by rack)
  70. """
  71. rack = get_object_or_404(Rack, pk=pk)
  72. face = request.GET.get('face', 0)
  73. exclude_pk = request.GET.get('exclude', None)
  74. if exclude_pk is not None:
  75. try:
  76. exclude_pk = int(exclude_pk)
  77. except ValueError:
  78. exclude_pk = None
  79. elevation = rack.get_rack_units(face, exclude_pk)
  80. page = self.paginate_queryset(elevation)
  81. if page is not None:
  82. rack_units = serializers.RackUnitSerializer(page, many=True, context={'request': request})
  83. return self.get_paginated_response(rack_units.data)
  84. #
  85. # Rack reservations
  86. #
  87. class RackReservationViewSet(WritableSerializerMixin, ModelViewSet):
  88. queryset = RackReservation.objects.select_related('rack')
  89. serializer_class = serializers.RackReservationSerializer
  90. write_serializer_class = serializers.WritableRackReservationSerializer
  91. filter_class = filters.RackReservationFilter
  92. # Assign user from request
  93. def perform_create(self, serializer):
  94. serializer.save(user=self.request.user)
  95. #
  96. # Manufacturers
  97. #
  98. class ManufacturerViewSet(ModelViewSet):
  99. queryset = Manufacturer.objects.all()
  100. serializer_class = serializers.ManufacturerSerializer
  101. #
  102. # Device types
  103. #
  104. class DeviceTypeViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
  105. queryset = DeviceType.objects.select_related('manufacturer')
  106. serializer_class = serializers.DeviceTypeSerializer
  107. write_serializer_class = serializers.WritableDeviceTypeSerializer
  108. #
  109. # Device type components
  110. #
  111. class ConsolePortTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  112. queryset = ConsolePortTemplate.objects.select_related('device_type__manufacturer')
  113. serializer_class = serializers.ConsolePortTemplateSerializer
  114. write_serializer_class = serializers.WritableConsolePortTemplateSerializer
  115. filter_class = filters.ConsolePortTemplateFilter
  116. class ConsoleServerPortTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  117. queryset = ConsoleServerPortTemplate.objects.select_related('device_type__manufacturer')
  118. serializer_class = serializers.ConsoleServerPortTemplateSerializer
  119. write_serializer_class = serializers.WritableConsoleServerPortTemplateSerializer
  120. filter_class = filters.ConsoleServerPortTemplateFilter
  121. class PowerPortTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  122. queryset = PowerPortTemplate.objects.select_related('device_type__manufacturer')
  123. serializer_class = serializers.PowerPortTemplateSerializer
  124. write_serializer_class = serializers.WritablePowerPortTemplateSerializer
  125. filter_class = filters.PowerPortTemplateFilter
  126. class PowerOutletTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  127. queryset = PowerOutletTemplate.objects.select_related('device_type__manufacturer')
  128. serializer_class = serializers.PowerOutletTemplateSerializer
  129. write_serializer_class = serializers.WritablePowerOutletTemplateSerializer
  130. filter_class = filters.PowerOutletTemplateFilter
  131. class InterfaceTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  132. queryset = InterfaceTemplate.objects.select_related('device_type__manufacturer')
  133. serializer_class = serializers.InterfaceTemplateSerializer
  134. write_serializer_class = serializers.WritableInterfaceTemplateSerializer
  135. filter_class = filters.InterfaceTemplateFilter
  136. class DeviceBayTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  137. queryset = DeviceBayTemplate.objects.select_related('device_type__manufacturer')
  138. serializer_class = serializers.DeviceBayTemplateSerializer
  139. write_serializer_class = serializers.WritableDeviceBayTemplateSerializer
  140. filter_class = filters.DeviceBayTemplateFilter
  141. #
  142. # Device roles
  143. #
  144. class DeviceRoleViewSet(ModelViewSet):
  145. queryset = DeviceRole.objects.all()
  146. serializer_class = serializers.DeviceRoleSerializer
  147. #
  148. # Platforms
  149. #
  150. class PlatformViewSet(ModelViewSet):
  151. queryset = Platform.objects.all()
  152. serializer_class = serializers.PlatformSerializer
  153. #
  154. # Devices
  155. #
  156. class DeviceViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
  157. queryset = Device.objects.select_related(
  158. 'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay',
  159. ).prefetch_related(
  160. 'primary_ip4__nat_outside', 'primary_ip6__nat_outside',
  161. )
  162. serializer_class = serializers.DeviceSerializer
  163. write_serializer_class = serializers.WritableDeviceSerializer
  164. filter_class = filters.DeviceFilter
  165. @detail_route(url_path='lldp-neighbors')
  166. def lldp_neighbors(self, request, pk):
  167. """
  168. Retrieve live LLDP neighbors of a device
  169. """
  170. device = get_object_or_404(Device, pk=pk)
  171. if not device.primary_ip:
  172. raise ServiceUnavailable("No IP configured for this device.")
  173. RPC = device.get_rpc_client()
  174. if not RPC:
  175. raise ServiceUnavailable("No RPC client available for this platform ({}).".format(device.platform))
  176. # Connect to device and retrieve inventory info
  177. try:
  178. with RPC(device, username=settings.NETBOX_USERNAME, password=settings.NETBOX_PASSWORD) as rpc_client:
  179. lldp_neighbors = rpc_client.get_lldp_neighbors()
  180. except:
  181. raise ServiceUnavailable("Error connecting to the remote device.")
  182. return Response(lldp_neighbors)
  183. #
  184. # Device components
  185. #
  186. class ConsolePortViewSet(WritableSerializerMixin, ModelViewSet):
  187. queryset = ConsolePort.objects.select_related('device', 'cs_port__device')
  188. serializer_class = serializers.ConsolePortSerializer
  189. write_serializer_class = serializers.WritableConsolePortSerializer
  190. filter_class = filters.ConsolePortFilter
  191. class ConsoleServerPortViewSet(WritableSerializerMixin, ModelViewSet):
  192. queryset = ConsoleServerPort.objects.select_related('device', 'connected_console__device')
  193. serializer_class = serializers.ConsoleServerPortSerializer
  194. write_serializer_class = serializers.WritableConsoleServerPortSerializer
  195. filter_class = filters.ConsoleServerPortFilter
  196. class PowerPortViewSet(WritableSerializerMixin, ModelViewSet):
  197. queryset = PowerPort.objects.select_related('device', 'power_outlet__device')
  198. serializer_class = serializers.PowerPortSerializer
  199. write_serializer_class = serializers.WritablePowerPortSerializer
  200. filter_class = filters.PowerPortFilter
  201. class PowerOutletViewSet(WritableSerializerMixin, ModelViewSet):
  202. queryset = PowerOutlet.objects.select_related('device', 'connected_port__device')
  203. serializer_class = serializers.PowerOutletSerializer
  204. write_serializer_class = serializers.WritablePowerOutletSerializer
  205. filter_class = filters.PowerOutletFilter
  206. class InterfaceViewSet(WritableSerializerMixin, ModelViewSet):
  207. queryset = Interface.objects.select_related('device')
  208. serializer_class = serializers.InterfaceSerializer
  209. write_serializer_class = serializers.WritableInterfaceSerializer
  210. filter_class = filters.InterfaceFilter
  211. @detail_route()
  212. def graphs(self, request, pk=None):
  213. """
  214. A convenience method for rendering graphs for a particular interface.
  215. """
  216. interface = get_object_or_404(Interface, pk=pk)
  217. queryset = Graph.objects.filter(type=GRAPH_TYPE_INTERFACE)
  218. serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': interface})
  219. return Response(serializer.data)
  220. class DeviceBayViewSet(WritableSerializerMixin, ModelViewSet):
  221. queryset = DeviceBay.objects.select_related('installed_device')
  222. serializer_class = serializers.DeviceBaySerializer
  223. write_serializer_class = serializers.WritableDeviceBaySerializer
  224. filter_class = filters.DeviceBayFilter
  225. class ModuleViewSet(WritableSerializerMixin, ModelViewSet):
  226. queryset = Module.objects.select_related('device', 'manufacturer')
  227. serializer_class = serializers.ModuleSerializer
  228. write_serializer_class = serializers.WritableModuleSerializer
  229. filter_class = filters.ModuleFilter
  230. #
  231. # Interface connections
  232. #
  233. class InterfaceConnectionViewSet(WritableSerializerMixin, ModelViewSet):
  234. queryset = InterfaceConnection.objects.select_related('interface_a__device', 'interface_b__device')
  235. serializer_class = serializers.InterfaceConnectionSerializer
  236. write_serializer_class = serializers.WritableInterfaceConnectionSerializer
  237. #
  238. # Miscellaneous
  239. #
  240. class ConnectedDeviceViewSet(ViewSet):
  241. """
  242. This endpoint allows a user to determine what device (if any) is connected to a given peer device and peer
  243. interface. This is useful in a situation where a device boots with no configuration, but can detect its neighbors
  244. via a protocol such as LLDP. Two query parameters must be included in the request:
  245. * `peer-device`: The name of the peer device
  246. * `peer-interface`: The name of the peer interface
  247. """
  248. permission_classes = [IsAuthenticated]
  249. def list(self, request):
  250. peer_device_name = request.query_params.get('peer-device')
  251. peer_interface_name = request.query_params.get('peer-interface')
  252. if not peer_device_name or not peer_interface_name:
  253. raise MissingFilterException(detail='Request must include "peer-device" and "peer-interface" filters.')
  254. # Determine local interface from peer interface's connection
  255. peer_interface = get_object_or_404(Interface, device__name=peer_device_name, name=peer_interface_name)
  256. local_interface = peer_interface.connected_interface
  257. if local_interface is None:
  258. return Response()
  259. return Response(serializers.DeviceSerializer(local_interface.device, context={'request': request}).data)