views.py 8.6 KB

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