views.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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').prefetch_related('tags')
  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').prefetch_related('tags')
  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').prefetch_related('tags')
  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 i, requested_prefix in enumerate(requested_prefixes):
  74. # Validate requested prefix size
  75. error_msg = None
  76. if 'prefix_length' not in requested_prefix:
  77. error_msg = "Item {}: prefix_length field missing".format(i)
  78. elif not isinstance(requested_prefix['prefix_length'], int):
  79. error_msg = "Item {}: Invalid prefix length ({})".format(
  80. i, requested_prefix['prefix_length']
  81. )
  82. elif prefix.family == 4 and requested_prefix['prefix_length'] > 32:
  83. error_msg = "Item {}: Invalid prefix length ({}) for IPv4".format(
  84. i, requested_prefix['prefix_length']
  85. )
  86. elif prefix.family == 6 and requested_prefix['prefix_length'] > 128:
  87. error_msg = "Item {}: Invalid prefix length ({}) for IPv6".format(
  88. i, requested_prefix['prefix_length']
  89. )
  90. if error_msg:
  91. return Response(
  92. {
  93. "detail": error_msg
  94. },
  95. status=status.HTTP_400_BAD_REQUEST
  96. )
  97. # Find the first available prefix equal to or larger than the requested size
  98. for available_prefix in available_prefixes.iter_cidrs():
  99. if requested_prefix['prefix_length'] >= available_prefix.prefixlen:
  100. allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])
  101. requested_prefix['prefix'] = allocated_prefix
  102. requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None
  103. break
  104. else:
  105. return Response(
  106. {
  107. "detail": "Insufficient space is available to accommodate the requested prefix size(s)"
  108. },
  109. status=status.HTTP_400_BAD_REQUEST
  110. )
  111. # Remove the allocated prefix from the list of available prefixes
  112. available_prefixes.remove(allocated_prefix)
  113. # Initialize the serializer with a list or a single object depending on what was requested
  114. context = {'request': request}
  115. if isinstance(request.data, list):
  116. serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)
  117. else:
  118. serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)
  119. # Create the new Prefix(es)
  120. if serializer.is_valid():
  121. serializer.save()
  122. return Response(serializer.data, status=status.HTTP_201_CREATED)
  123. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  124. else:
  125. serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={
  126. 'request': request,
  127. 'vrf': prefix.vrf,
  128. })
  129. return Response(serializer.data)
  130. @action(detail=True, url_path='available-ips', methods=['get', 'post'])
  131. def available_ips(self, request, pk=None):
  132. """
  133. A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
  134. returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
  135. however results will not be paginated.
  136. """
  137. prefix = get_object_or_404(Prefix, pk=pk)
  138. # Create the next available IP within the prefix
  139. if request.method == 'POST':
  140. # Permissions check
  141. if not request.user.has_perm('ipam.add_ipaddress'):
  142. raise PermissionDenied()
  143. # Normalize to a list of objects
  144. requested_ips = request.data if isinstance(request.data, list) else [request.data]
  145. # Determine if the requested number of IPs is available
  146. available_ips = prefix.get_available_ips()
  147. if available_ips.size < len(requested_ips):
  148. return Response(
  149. {
  150. "detail": "An insufficient number of IP addresses are available within the prefix {} ({} "
  151. "requested, {} available)".format(prefix, len(requested_ips), len(available_ips))
  152. },
  153. status=status.HTTP_400_BAD_REQUEST
  154. )
  155. # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix
  156. available_ips = iter(available_ips)
  157. prefix_length = prefix.prefix.prefixlen
  158. for requested_ip in requested_ips:
  159. requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length)
  160. requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None
  161. # Initialize the serializer with a list or a single object depending on what was requested
  162. context = {'request': request}
  163. if isinstance(request.data, list):
  164. serializer = serializers.IPAddressSerializer(data=requested_ips, many=True, context=context)
  165. else:
  166. serializer = serializers.IPAddressSerializer(data=requested_ips[0], context=context)
  167. # Create the new IP address(es)
  168. if serializer.is_valid():
  169. serializer.save()
  170. return Response(serializer.data, status=status.HTTP_201_CREATED)
  171. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  172. # Determine the maximum number of IPs to return
  173. else:
  174. try:
  175. limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))
  176. except ValueError:
  177. limit = settings.PAGINATE_COUNT
  178. if settings.MAX_PAGE_SIZE:
  179. limit = min(limit, settings.MAX_PAGE_SIZE)
  180. # Calculate available IPs within the prefix
  181. ip_list = []
  182. for index, ip in enumerate(prefix.get_available_ips(), start=1):
  183. ip_list.append(ip)
  184. if index == limit:
  185. break
  186. serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={
  187. 'request': request,
  188. 'prefix': prefix.prefix,
  189. 'vrf': prefix.vrf,
  190. })
  191. return Response(serializer.data)
  192. #
  193. # IP addresses
  194. #
  195. class IPAddressViewSet(CustomFieldModelViewSet):
  196. queryset = IPAddress.objects.select_related(
  197. 'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine'
  198. ).prefetch_related(
  199. 'nat_outside', 'tags',
  200. )
  201. serializer_class = serializers.IPAddressSerializer
  202. filter_class = filters.IPAddressFilter
  203. #
  204. # VLAN groups
  205. #
  206. class VLANGroupViewSet(ModelViewSet):
  207. queryset = VLANGroup.objects.select_related('site')
  208. serializer_class = serializers.VLANGroupSerializer
  209. filter_class = filters.VLANGroupFilter
  210. #
  211. # VLANs
  212. #
  213. class VLANViewSet(CustomFieldModelViewSet):
  214. queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('tags')
  215. serializer_class = serializers.VLANSerializer
  216. filter_class = filters.VLANFilter
  217. #
  218. # Services
  219. #
  220. class ServiceViewSet(ModelViewSet):
  221. queryset = Service.objects.select_related('device').prefetch_related('tags')
  222. serializer_class = serializers.ServiceSerializer
  223. filter_class = filters.ServiceFilter