views.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. from __future__ import unicode_literals
  2. from django.conf import settings
  3. from django.shortcuts import get_object_or_404
  4. from rest_framework import status
  5. from rest_framework.decorators import detail_route
  6. from rest_framework.exceptions import PermissionDenied
  7. from rest_framework.response import Response
  8. from extras.api.views import CustomFieldModelViewSet
  9. from ipam import filters
  10. from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
  11. from utilities.api import FieldChoicesViewSet, ModelViewSet
  12. from . import serializers
  13. #
  14. # Field choices
  15. #
  16. class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
  17. fields = (
  18. (Aggregate, ['family']),
  19. (Prefix, ['family', 'status']),
  20. (IPAddress, ['family', 'status', 'role']),
  21. (VLAN, ['status']),
  22. (Service, ['protocol']),
  23. )
  24. #
  25. # VRFs
  26. #
  27. class VRFViewSet(CustomFieldModelViewSet):
  28. queryset = VRF.objects.select_related('tenant')
  29. serializer_class = serializers.VRFSerializer
  30. write_serializer_class = serializers.WritableVRFSerializer
  31. filter_class = filters.VRFFilter
  32. #
  33. # RIRs
  34. #
  35. class RIRViewSet(ModelViewSet):
  36. queryset = RIR.objects.all()
  37. serializer_class = serializers.RIRSerializer
  38. filter_class = filters.RIRFilter
  39. #
  40. # Aggregates
  41. #
  42. class AggregateViewSet(CustomFieldModelViewSet):
  43. queryset = Aggregate.objects.select_related('rir')
  44. serializer_class = serializers.AggregateSerializer
  45. write_serializer_class = serializers.WritableAggregateSerializer
  46. filter_class = filters.AggregateFilter
  47. #
  48. # Roles
  49. #
  50. class RoleViewSet(ModelViewSet):
  51. queryset = Role.objects.all()
  52. serializer_class = serializers.RoleSerializer
  53. filter_class = filters.RoleFilter
  54. #
  55. # Prefixes
  56. #
  57. class PrefixViewSet(CustomFieldModelViewSet):
  58. queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
  59. serializer_class = serializers.PrefixSerializer
  60. write_serializer_class = serializers.WritablePrefixSerializer
  61. filter_class = filters.PrefixFilter
  62. @detail_route(url_path='available-prefixes', methods=['get', 'post'])
  63. def available_prefixes(self, request, pk=None):
  64. """
  65. A convenience method for returning available child prefixes within a parent.
  66. """
  67. prefix = get_object_or_404(Prefix, pk=pk)
  68. available_prefixes = prefix.get_available_prefixes()
  69. if request.method == 'POST':
  70. # Permissions check
  71. if not request.user.has_perm('ipam.add_prefix'):
  72. raise PermissionDenied()
  73. requested_prefixes = request.data if isinstance(request.data, list) else [request.data]
  74. # Allocate prefixes to the requested objects based on availability within the parent
  75. for requested_prefix in requested_prefixes:
  76. # Find the first available prefix equal to or larger than the requested size
  77. for available_prefix in available_prefixes.iter_cidrs():
  78. if requested_prefix['prefix_length'] >= available_prefix.prefixlen:
  79. allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])
  80. requested_prefix['prefix'] = allocated_prefix
  81. requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None
  82. break
  83. else:
  84. return Response(
  85. {
  86. "detail": "Insufficient space is available to accommodate the requested prefix size(s)"
  87. },
  88. status=status.HTTP_400_BAD_REQUEST
  89. )
  90. # Remove the allocated prefix from the list of available prefixes
  91. available_prefixes.remove(allocated_prefix)
  92. # Initialize the serializer with a list or a single object depending on what was requested
  93. if isinstance(request.data, list):
  94. serializer = serializers.WritablePrefixSerializer(data=requested_prefixes, many=True)
  95. else:
  96. serializer = serializers.WritablePrefixSerializer(data=requested_prefixes[0])
  97. # Create the new Prefix(es)
  98. if serializer.is_valid():
  99. serializer.save()
  100. return Response(serializer.data, status=status.HTTP_201_CREATED)
  101. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  102. else:
  103. serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={
  104. 'request': request,
  105. 'vrf': prefix.vrf,
  106. })
  107. return Response(serializer.data)
  108. @detail_route(url_path='available-ips', methods=['get', 'post'])
  109. def available_ips(self, request, pk=None):
  110. """
  111. A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
  112. returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
  113. however results will not be paginated.
  114. """
  115. prefix = get_object_or_404(Prefix, pk=pk)
  116. # Create the next available IP within the prefix
  117. if request.method == 'POST':
  118. # Permissions check
  119. if not request.user.has_perm('ipam.add_ipaddress'):
  120. raise PermissionDenied()
  121. # Determine if the requested number of IPs is available
  122. requested_count = len(request.data) if isinstance(request.data, list) else 1
  123. available_ips = list(prefix.get_available_ips())
  124. if len(available_ips) < requested_count:
  125. return Response(
  126. {
  127. "detail": "An insufficient number of IP addresses are available within the prefix {} ({} "
  128. "requested, {} available)".format(prefix, requested_count, len(available_ips))
  129. },
  130. status=status.HTTP_400_BAD_REQUEST
  131. )
  132. # Deserializing multiple IP addresses
  133. if isinstance(request.data, list):
  134. request_data = list(request.data) # Need a mutable copy
  135. for obj in request_data:
  136. obj['address'] = available_ips.pop(0)
  137. obj['vrf'] = prefix.vrf.pk if prefix.vrf else None
  138. serializer = serializers.WritableIPAddressSerializer(data=request_data, many=True)
  139. # Deserializing a single IP address
  140. else:
  141. request_data = request.data.copy() # Need a mutable copy
  142. request_data['address'] = available_ips.pop(0)
  143. request_data['vrf'] = prefix.vrf.pk if prefix.vrf else None
  144. serializer = serializers.WritableIPAddressSerializer(data=request_data)
  145. # Create the new IP address(es)
  146. if serializer.is_valid():
  147. serializer.save()
  148. return Response(serializer.data, status=status.HTTP_201_CREATED)
  149. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  150. # Determine the maximum number of IPs to return
  151. else:
  152. try:
  153. limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))
  154. except ValueError:
  155. limit = settings.PAGINATE_COUNT
  156. if settings.MAX_PAGE_SIZE:
  157. limit = min(limit, settings.MAX_PAGE_SIZE)
  158. # Calculate available IPs within the prefix
  159. ip_list = []
  160. for index, ip in enumerate(prefix.get_available_ips(), start=1):
  161. ip_list.append(ip)
  162. if index == limit:
  163. break
  164. serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={
  165. 'request': request,
  166. 'prefix': prefix.prefix,
  167. 'vrf': prefix.vrf,
  168. })
  169. return Response(serializer.data)
  170. #
  171. # IP addresses
  172. #
  173. class IPAddressViewSet(CustomFieldModelViewSet):
  174. queryset = IPAddress.objects.select_related(
  175. 'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine'
  176. ).prefetch_related(
  177. 'nat_outside'
  178. )
  179. serializer_class = serializers.IPAddressSerializer
  180. write_serializer_class = serializers.WritableIPAddressSerializer
  181. filter_class = filters.IPAddressFilter
  182. #
  183. # VLAN groups
  184. #
  185. class VLANGroupViewSet(ModelViewSet):
  186. queryset = VLANGroup.objects.select_related('site')
  187. serializer_class = serializers.VLANGroupSerializer
  188. write_serializer_class = serializers.WritableVLANGroupSerializer
  189. filter_class = filters.VLANGroupFilter
  190. #
  191. # VLANs
  192. #
  193. class VLANViewSet(CustomFieldModelViewSet):
  194. queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
  195. serializer_class = serializers.VLANSerializer
  196. write_serializer_class = serializers.WritableVLANSerializer
  197. filter_class = filters.VLANFilter
  198. #
  199. # Services
  200. #
  201. class ServiceViewSet(ModelViewSet):
  202. queryset = Service.objects.select_related('device')
  203. serializer_class = serializers.ServiceSerializer
  204. write_serializer_class = serializers.WritableServiceSerializer
  205. filter_class = filters.ServiceFilter