views.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. from django.conf import settings
  2. from django.db.models import Count
  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 utilities.utils import get_subquery
  13. from . import serializers
  14. #
  15. # Field choices
  16. #
  17. class IPAMFieldChoicesViewSet(FieldChoicesViewSet):
  18. fields = (
  19. (Aggregate, ['family']),
  20. (Prefix, ['family', 'status']),
  21. (IPAddress, ['family', 'status', 'role']),
  22. (VLAN, ['status']),
  23. (Service, ['protocol']),
  24. )
  25. #
  26. # VRFs
  27. #
  28. class VRFViewSet(CustomFieldModelViewSet):
  29. queryset = VRF.objects.select_related('tenant').prefetch_related('tags').annotate(
  30. ipaddress_count=get_subquery(IPAddress, 'vrf'),
  31. prefix_count=get_subquery(Prefix, 'vrf')
  32. )
  33. serializer_class = serializers.VRFSerializer
  34. filterset_class = filters.VRFFilter
  35. #
  36. # RIRs
  37. #
  38. class RIRViewSet(ModelViewSet):
  39. queryset = RIR.objects.annotate(
  40. aggregate_count=Count('aggregates')
  41. )
  42. serializer_class = serializers.RIRSerializer
  43. filterset_class = filters.RIRFilter
  44. #
  45. # Aggregates
  46. #
  47. class AggregateViewSet(CustomFieldModelViewSet):
  48. queryset = Aggregate.objects.select_related('rir').prefetch_related('tags')
  49. serializer_class = serializers.AggregateSerializer
  50. filterset_class = filters.AggregateFilter
  51. #
  52. # Roles
  53. #
  54. class RoleViewSet(ModelViewSet):
  55. queryset = Role.objects.annotate(
  56. prefix_count=get_subquery(Prefix, 'role'),
  57. vlan_count=get_subquery(VLAN, 'role')
  58. )
  59. serializer_class = serializers.RoleSerializer
  60. filterset_class = filters.RoleFilter
  61. #
  62. # Prefixes
  63. #
  64. class PrefixViewSet(CustomFieldModelViewSet):
  65. queryset = Prefix.objects.select_related(
  66. 'site', 'vrf__tenant', 'tenant', 'vlan', 'role'
  67. ).prefetch_related(
  68. 'tags'
  69. )
  70. serializer_class = serializers.PrefixSerializer
  71. filterset_class = filters.PrefixFilter
  72. @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
  73. def available_prefixes(self, request, pk=None):
  74. """
  75. A convenience method for returning available child prefixes within a parent.
  76. """
  77. prefix = get_object_or_404(Prefix, pk=pk)
  78. available_prefixes = prefix.get_available_prefixes()
  79. if request.method == 'POST':
  80. # Permissions check
  81. if not request.user.has_perm('ipam.add_prefix'):
  82. raise PermissionDenied()
  83. # Normalize to a list of objects
  84. requested_prefixes = request.data if isinstance(request.data, list) else [request.data]
  85. # Allocate prefixes to the requested objects based on availability within the parent
  86. for i, requested_prefix in enumerate(requested_prefixes):
  87. # Validate requested prefix size
  88. prefix_length = requested_prefix.get('prefix_length')
  89. if prefix_length is None:
  90. return Response(
  91. {
  92. "detail": "Item {}: prefix_length field missing".format(i)
  93. },
  94. status=status.HTTP_400_BAD_REQUEST
  95. )
  96. try:
  97. prefix_length = int(prefix_length)
  98. except ValueError:
  99. return Response(
  100. {
  101. "detail": "Item {}: Invalid prefix length ({})".format(i, prefix_length),
  102. },
  103. status=status.HTTP_400_BAD_REQUEST
  104. )
  105. if prefix.family == 4 and prefix_length > 32:
  106. return Response(
  107. {
  108. "detail": "Item {}: Invalid prefix length ({}) for IPv4".format(i, prefix_length),
  109. },
  110. status=status.HTTP_400_BAD_REQUEST
  111. )
  112. elif prefix.family == 6 and prefix_length > 128:
  113. return Response(
  114. {
  115. "detail": "Item {}: Invalid prefix length ({}) for IPv6".format(i, prefix_length),
  116. },
  117. status=status.HTTP_400_BAD_REQUEST
  118. )
  119. # Find the first available prefix equal to or larger than the requested size
  120. for available_prefix in available_prefixes.iter_cidrs():
  121. if requested_prefix['prefix_length'] >= available_prefix.prefixlen:
  122. allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])
  123. requested_prefix['prefix'] = allocated_prefix
  124. requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None
  125. break
  126. else:
  127. return Response(
  128. {
  129. "detail": "Insufficient space is available to accommodate the requested prefix size(s)"
  130. },
  131. status=status.HTTP_204_NO_CONTENT
  132. )
  133. # Remove the allocated prefix from the list of available prefixes
  134. available_prefixes.remove(allocated_prefix)
  135. # Initialize the serializer with a list or a single object depending on what was requested
  136. context = {'request': request}
  137. if isinstance(request.data, list):
  138. serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)
  139. else:
  140. serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)
  141. # Create the new Prefix(es)
  142. if serializer.is_valid():
  143. serializer.save()
  144. return Response(serializer.data, status=status.HTTP_201_CREATED)
  145. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  146. else:
  147. serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={
  148. 'request': request,
  149. 'vrf': prefix.vrf,
  150. })
  151. return Response(serializer.data)
  152. @action(detail=True, url_path='available-ips', methods=['get', 'post'])
  153. def available_ips(self, request, pk=None):
  154. """
  155. A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
  156. returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
  157. however results will not be paginated.
  158. """
  159. prefix = get_object_or_404(Prefix, pk=pk)
  160. # Create the next available IP within the prefix
  161. if request.method == 'POST':
  162. # Permissions check
  163. if not request.user.has_perm('ipam.add_ipaddress'):
  164. raise PermissionDenied()
  165. # Normalize to a list of objects
  166. requested_ips = request.data if isinstance(request.data, list) else [request.data]
  167. # Determine if the requested number of IPs is available
  168. available_ips = prefix.get_available_ips()
  169. if available_ips.size < len(requested_ips):
  170. return Response(
  171. {
  172. "detail": "An insufficient number of IP addresses are available within the prefix {} ({} "
  173. "requested, {} available)".format(prefix, len(requested_ips), len(available_ips))
  174. },
  175. status=status.HTTP_204_NO_CONTENT
  176. )
  177. # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix
  178. available_ips = iter(available_ips)
  179. prefix_length = prefix.prefix.prefixlen
  180. for requested_ip in requested_ips:
  181. requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length)
  182. requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None
  183. # Initialize the serializer with a list or a single object depending on what was requested
  184. context = {'request': request}
  185. if isinstance(request.data, list):
  186. serializer = serializers.IPAddressSerializer(data=requested_ips, many=True, context=context)
  187. else:
  188. serializer = serializers.IPAddressSerializer(data=requested_ips[0], context=context)
  189. # Create the new IP address(es)
  190. if serializer.is_valid():
  191. serializer.save()
  192. return Response(serializer.data, status=status.HTTP_201_CREATED)
  193. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  194. # Determine the maximum number of IPs to return
  195. else:
  196. try:
  197. limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))
  198. except ValueError:
  199. limit = settings.PAGINATE_COUNT
  200. if settings.MAX_PAGE_SIZE:
  201. limit = min(limit, settings.MAX_PAGE_SIZE)
  202. # Calculate available IPs within the prefix
  203. ip_list = []
  204. for index, ip in enumerate(prefix.get_available_ips(), start=1):
  205. ip_list.append(ip)
  206. if index == limit:
  207. break
  208. serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={
  209. 'request': request,
  210. 'prefix': prefix.prefix,
  211. 'vrf': prefix.vrf,
  212. })
  213. return Response(serializer.data)
  214. #
  215. # IP addresses
  216. #
  217. class IPAddressViewSet(CustomFieldModelViewSet):
  218. queryset = IPAddress.objects.select_related(
  219. 'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine'
  220. ).prefetch_related(
  221. 'nat_outside', 'tags',
  222. )
  223. serializer_class = serializers.IPAddressSerializer
  224. filterset_class = filters.IPAddressFilter
  225. #
  226. # VLAN groups
  227. #
  228. class VLANGroupViewSet(ModelViewSet):
  229. queryset = VLANGroup.objects.select_related('site').annotate(
  230. vlan_count=Count('vlans')
  231. )
  232. serializer_class = serializers.VLANGroupSerializer
  233. filterset_class = filters.VLANGroupFilter
  234. #
  235. # VLANs
  236. #
  237. class VLANViewSet(CustomFieldModelViewSet):
  238. queryset = VLAN.objects.select_related(
  239. 'site', 'group', 'tenant', 'role'
  240. ).prefetch_related(
  241. 'tags'
  242. ).annotate(
  243. prefix_count=get_subquery(Prefix, 'role')
  244. )
  245. serializer_class = serializers.VLANSerializer
  246. filterset_class = filters.VLANFilter
  247. #
  248. # Services
  249. #
  250. class ServiceViewSet(ModelViewSet):
  251. queryset = Service.objects.select_related('device').prefetch_related('tags')
  252. serializer_class = serializers.ServiceSerializer
  253. filterset_class = filters.ServiceFilter