views.py 11 KB

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