We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Swagger doesn't show required field "prefix_length" in available-prefixes POST request
<!--
    Please indicate the nature of the change by placing an X in one of the
    boxes below.
-->
### Change Type
[X ] Addition
[ ] Correction
[ ] Deprecation
[ ] Cleanup (formatting, typos, etc.)

### Environment
NetBox version: 2.5.13

<!-- Describe the proposed change(s). -->
### Proposed Change

Add `prefix_length` field in Swagger docs, something like:

prefix_length* integer
title: Prefix length
Size of this prefix 

In /ipam/prefixes/{id}/available-prefixes/ POST request

![prefixes](https://user-images.githubusercontent.com/20525775/58911018-397cf980-8717-11e9-9964-d82a805f0778.png)



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `netbox/ipam/api/views.py`
Content:
```
1 from django.conf import settings
2 from django.db.models import Count
3 from django.shortcuts import get_object_or_404
4 from django_pglocks import advisory_lock
5 from drf_yasg.utils import swagger_auto_schema
6 from rest_framework import status
7 from rest_framework.decorators import action
8 from rest_framework.exceptions import PermissionDenied
9 from rest_framework.response import Response
10 
11 from extras.api.views import CustomFieldModelViewSet
12 from ipam import filters
13 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
14 from utilities.api import ModelViewSet
15 from utilities.constants import ADVISORY_LOCK_KEYS
16 from utilities.utils import get_subquery
17 from . import serializers
18 
19 
20 #
21 # VRFs
22 #
23 
24 class VRFViewSet(CustomFieldModelViewSet):
25     queryset = VRF.objects.prefetch_related('tenant').prefetch_related('tags').annotate(
26         ipaddress_count=get_subquery(IPAddress, 'vrf'),
27         prefix_count=get_subquery(Prefix, 'vrf')
28     )
29     serializer_class = serializers.VRFSerializer
30     filterset_class = filters.VRFFilterSet
31 
32 
33 #
34 # RIRs
35 #
36 
37 class RIRViewSet(ModelViewSet):
38     queryset = RIR.objects.annotate(
39         aggregate_count=Count('aggregates')
40     )
41     serializer_class = serializers.RIRSerializer
42     filterset_class = filters.RIRFilterSet
43 
44 
45 #
46 # Aggregates
47 #
48 
49 class AggregateViewSet(CustomFieldModelViewSet):
50     queryset = Aggregate.objects.prefetch_related('rir').prefetch_related('tags')
51     serializer_class = serializers.AggregateSerializer
52     filterset_class = filters.AggregateFilterSet
53 
54 
55 #
56 # Roles
57 #
58 
59 class RoleViewSet(ModelViewSet):
60     queryset = Role.objects.annotate(
61         prefix_count=get_subquery(Prefix, 'role'),
62         vlan_count=get_subquery(VLAN, 'role')
63     )
64     serializer_class = serializers.RoleSerializer
65     filterset_class = filters.RoleFilterSet
66 
67 
68 #
69 # Prefixes
70 #
71 
72 class PrefixViewSet(CustomFieldModelViewSet):
73     queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role', 'tags')
74     serializer_class = serializers.PrefixSerializer
75     filterset_class = filters.PrefixFilterSet
76 
77     @swagger_auto_schema(method='get', responses={200: serializers.AvailablePrefixSerializer(many=True)})
78     @swagger_auto_schema(method='post', responses={201: serializers.AvailablePrefixSerializer(many=True)})
79     @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
80     @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])
81     def available_prefixes(self, request, pk=None):
82         """
83         A convenience method for returning available child prefixes within a parent.
84 
85         The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
86         invoked in parallel, which results in a race condition where multiple insertions can occur.
87         """
88         prefix = get_object_or_404(Prefix, pk=pk)
89         available_prefixes = prefix.get_available_prefixes()
90 
91         if request.method == 'POST':
92 
93             # Validate Requested Prefixes' length
94             serializer = serializers.PrefixLengthSerializer(
95                 data=request.data if isinstance(request.data, list) else [request.data],
96                 many=True,
97                 context={
98                     'request': request,
99                     'prefix': prefix,
100                 }
101             )
102             if not serializer.is_valid():
103                 return Response(
104                     serializer.errors,
105                     status=status.HTTP_400_BAD_REQUEST
106                 )
107 
108             requested_prefixes = serializer.validated_data
109             # Allocate prefixes to the requested objects based on availability within the parent
110             for i, requested_prefix in enumerate(requested_prefixes):
111 
112                 # Find the first available prefix equal to or larger than the requested size
113                 for available_prefix in available_prefixes.iter_cidrs():
114                     if requested_prefix['prefix_length'] >= available_prefix.prefixlen:
115                         allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])
116                         requested_prefix['prefix'] = allocated_prefix
117                         requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None
118                         break
119                 else:
120                     return Response(
121                         {
122                             "detail": "Insufficient space is available to accommodate the requested prefix size(s)"
123                         },
124                         status=status.HTTP_204_NO_CONTENT
125                     )
126 
127                 # Remove the allocated prefix from the list of available prefixes
128                 available_prefixes.remove(allocated_prefix)
129 
130             # Initialize the serializer with a list or a single object depending on what was requested
131             context = {'request': request}
132             if isinstance(request.data, list):
133                 serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)
134             else:
135                 serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)
136 
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 
142             return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
143 
144         else:
145 
146             serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={
147                 'request': request,
148                 'vrf': prefix.vrf,
149             })
150 
151             return Response(serializer.data)
152 
153     @swagger_auto_schema(method='get', responses={200: serializers.AvailableIPSerializer(many=True)})
154     @swagger_auto_schema(method='post', responses={201: serializers.AvailableIPSerializer(many=True)},
155                          request_body=serializers.AvailableIPSerializer(many=False))
156     @action(detail=True, url_path='available-ips', methods=['get', 'post'], queryset=IPAddress.objects.all())
157     @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])
158     def available_ips(self, request, pk=None):
159         """
160         A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
161         returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
162         however results will not be paginated.
163 
164         The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
165         invoked in parallel, which results in a race condition where multiple insertions can occur.
166         """
167         prefix = get_object_or_404(Prefix, pk=pk)
168 
169         # Create the next available IP within the prefix
170         if request.method == 'POST':
171 
172             # Normalize to a list of objects
173             requested_ips = request.data if isinstance(request.data, list) else [request.data]
174 
175             # Determine if the requested number of IPs is available
176             available_ips = prefix.get_available_ips()
177             if available_ips.size < len(requested_ips):
178                 return Response(
179                     {
180                         "detail": "An insufficient number of IP addresses are available within the prefix {} ({} "
181                                   "requested, {} available)".format(prefix, len(requested_ips), len(available_ips))
182                     },
183                     status=status.HTTP_204_NO_CONTENT
184                 )
185 
186             # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix
187             available_ips = iter(available_ips)
188             prefix_length = prefix.prefix.prefixlen
189             for requested_ip in requested_ips:
190                 requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length)
191                 requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None
192 
193             # Initialize the serializer with a list or a single object depending on what was requested
194             context = {'request': request}
195             if isinstance(request.data, list):
196                 serializer = serializers.IPAddressSerializer(data=requested_ips, many=True, context=context)
197             else:
198                 serializer = serializers.IPAddressSerializer(data=requested_ips[0], context=context)
199 
200             # Create the new IP address(es)
201             if serializer.is_valid():
202                 serializer.save()
203                 return Response(serializer.data, status=status.HTTP_201_CREATED)
204 
205             return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
206 
207         # Determine the maximum number of IPs to return
208         else:
209             try:
210                 limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))
211             except ValueError:
212                 limit = settings.PAGINATE_COUNT
213             if settings.MAX_PAGE_SIZE:
214                 limit = min(limit, settings.MAX_PAGE_SIZE)
215 
216             # Calculate available IPs within the prefix
217             ip_list = []
218             for index, ip in enumerate(prefix.get_available_ips(), start=1):
219                 ip_list.append(ip)
220                 if index == limit:
221                     break
222             serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={
223                 'request': request,
224                 'prefix': prefix.prefix,
225                 'vrf': prefix.vrf,
226             })
227 
228             return Response(serializer.data)
229 
230 
231 #
232 # IP addresses
233 #
234 
235 class IPAddressViewSet(CustomFieldModelViewSet):
236     queryset = IPAddress.objects.prefetch_related(
237         'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine',
238         'nat_outside', 'tags',
239     )
240     serializer_class = serializers.IPAddressSerializer
241     filterset_class = filters.IPAddressFilterSet
242 
243 
244 #
245 # VLAN groups
246 #
247 
248 class VLANGroupViewSet(ModelViewSet):
249     queryset = VLANGroup.objects.prefetch_related('site').annotate(
250         vlan_count=Count('vlans')
251     )
252     serializer_class = serializers.VLANGroupSerializer
253     filterset_class = filters.VLANGroupFilterSet
254 
255 
256 #
257 # VLANs
258 #
259 
260 class VLANViewSet(CustomFieldModelViewSet):
261     queryset = VLAN.objects.prefetch_related(
262         'site', 'group', 'tenant', 'role', 'tags'
263     ).annotate(
264         prefix_count=get_subquery(Prefix, 'vlan')
265     )
266     serializer_class = serializers.VLANSerializer
267     filterset_class = filters.VLANFilterSet
268 
269 
270 #
271 # Services
272 #
273 
274 class ServiceViewSet(ModelViewSet):
275     queryset = Service.objects.prefetch_related('device').prefetch_related('tags')
276     serializer_class = serializers.ServiceSerializer
277     filterset_class = filters.ServiceFilterSet
278
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py
--- a/netbox/ipam/api/views.py
+++ b/netbox/ipam/api/views.py
@@ -74,6 +74,11 @@
     serializer_class = serializers.PrefixSerializer
     filterset_class = filters.PrefixFilterSet
 
+    def get_serializer_class(self):
+        if self.action == "available_prefixes" and self.request.method == "POST":
+            return serializers.PrefixLengthSerializer
+        return super().get_serializer_class()
+
     @swagger_auto_schema(method='get', responses={200: serializers.AvailablePrefixSerializer(many=True)})
     @swagger_auto_schema(method='post', responses={201: serializers.AvailablePrefixSerializer(many=True)})
     @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])

{"golden_diff": "diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py\n--- a/netbox/ipam/api/views.py\n+++ b/netbox/ipam/api/views.py\n@@ -74,6 +74,11 @@\n     serializer_class = serializers.PrefixSerializer\n     filterset_class = filters.PrefixFilterSet\n \n+    def get_serializer_class(self):\n+        if self.action == \"available_prefixes\" and self.request.method == \"POST\":\n+            return serializers.PrefixLengthSerializer\n+        return super().get_serializer_class()\n+\n     @swagger_auto_schema(method='get', responses={200: serializers.AvailablePrefixSerializer(many=True)})\n     @swagger_auto_schema(method='post', responses={201: serializers.AvailablePrefixSerializer(many=True)})\n     @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])\n", "issue": "Swagger doesn't show required field \"prefix_length\" in available-prefixes POST request\n<!--\r\n    Please indicate the nature of the change by placing an X in one of the\r\n    boxes below.\r\n-->\r\n### Change Type\r\n[X ] Addition\r\n[ ] Correction\r\n[ ] Deprecation\r\n[ ] Cleanup (formatting, typos, etc.)\r\n\r\n### Environment\r\nNetBox version: 2.5.13\r\n\r\n<!-- Describe the proposed change(s). -->\r\n### Proposed Change\r\n\r\nAdd `prefix_length` field in Swagger docs, something like:\r\n\r\nprefix_length* integer\r\ntitle: Prefix length\r\nSize of this prefix \r\n\r\nIn /ipam/prefixes/{id}/available-prefixes/ POST request\r\n\r\n![prefixes](https://user-images.githubusercontent.com/20525775/58911018-397cf980-8717-11e9-9964-d82a805f0778.png)\r\n\r\n\n", "before_files": [{"content": "from django.conf import settings\nfrom django.db.models import Count\nfrom django.shortcuts import get_object_or_404\nfrom django_pglocks import advisory_lock\nfrom drf_yasg.utils import swagger_auto_schema\nfrom rest_framework import status\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import PermissionDenied\nfrom rest_framework.response import Response\n\nfrom extras.api.views import CustomFieldModelViewSet\nfrom ipam import filters\nfrom ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF\nfrom utilities.api import ModelViewSet\nfrom utilities.constants import ADVISORY_LOCK_KEYS\nfrom utilities.utils import get_subquery\nfrom . import serializers\n\n\n#\n# VRFs\n#\n\nclass VRFViewSet(CustomFieldModelViewSet):\n    queryset = VRF.objects.prefetch_related('tenant').prefetch_related('tags').annotate(\n        ipaddress_count=get_subquery(IPAddress, 'vrf'),\n        prefix_count=get_subquery(Prefix, 'vrf')\n    )\n    serializer_class = serializers.VRFSerializer\n    filterset_class = filters.VRFFilterSet\n\n\n#\n# RIRs\n#\n\nclass RIRViewSet(ModelViewSet):\n    queryset = RIR.objects.annotate(\n        aggregate_count=Count('aggregates')\n    )\n    serializer_class = serializers.RIRSerializer\n    filterset_class = filters.RIRFilterSet\n\n\n#\n# Aggregates\n#\n\nclass AggregateViewSet(CustomFieldModelViewSet):\n    queryset = Aggregate.objects.prefetch_related('rir').prefetch_related('tags')\n    serializer_class = serializers.AggregateSerializer\n    filterset_class = filters.AggregateFilterSet\n\n\n#\n# Roles\n#\n\nclass RoleViewSet(ModelViewSet):\n    queryset = Role.objects.annotate(\n        prefix_count=get_subquery(Prefix, 'role'),\n        vlan_count=get_subquery(VLAN, 'role')\n    )\n    serializer_class = serializers.RoleSerializer\n    filterset_class = filters.RoleFilterSet\n\n\n#\n# Prefixes\n#\n\nclass PrefixViewSet(CustomFieldModelViewSet):\n    queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role', 'tags')\n    serializer_class = serializers.PrefixSerializer\n    filterset_class = filters.PrefixFilterSet\n\n    @swagger_auto_schema(method='get', responses={200: serializers.AvailablePrefixSerializer(many=True)})\n    @swagger_auto_schema(method='post', responses={201: serializers.AvailablePrefixSerializer(many=True)})\n    @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])\n    @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])\n    def available_prefixes(self, request, pk=None):\n        \"\"\"\n        A convenience method for returning available child prefixes within a parent.\n\n        The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being\n        invoked in parallel, which results in a race condition where multiple insertions can occur.\n        \"\"\"\n        prefix = get_object_or_404(Prefix, pk=pk)\n        available_prefixes = prefix.get_available_prefixes()\n\n        if request.method == 'POST':\n\n            # Validate Requested Prefixes' length\n            serializer = serializers.PrefixLengthSerializer(\n                data=request.data if isinstance(request.data, list) else [request.data],\n                many=True,\n                context={\n                    'request': request,\n                    'prefix': prefix,\n                }\n            )\n            if not serializer.is_valid():\n                return Response(\n                    serializer.errors,\n                    status=status.HTTP_400_BAD_REQUEST\n                )\n\n            requested_prefixes = serializer.validated_data\n            # Allocate prefixes to the requested objects based on availability within the parent\n            for i, requested_prefix in enumerate(requested_prefixes):\n\n                # Find the first available prefix equal to or larger than the requested size\n                for available_prefix in available_prefixes.iter_cidrs():\n                    if requested_prefix['prefix_length'] >= available_prefix.prefixlen:\n                        allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])\n                        requested_prefix['prefix'] = allocated_prefix\n                        requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None\n                        break\n                else:\n                    return Response(\n                        {\n                            \"detail\": \"Insufficient space is available to accommodate the requested prefix size(s)\"\n                        },\n                        status=status.HTTP_204_NO_CONTENT\n                    )\n\n                # Remove the allocated prefix from the list of available prefixes\n                available_prefixes.remove(allocated_prefix)\n\n            # Initialize the serializer with a list or a single object depending on what was requested\n            context = {'request': request}\n            if isinstance(request.data, list):\n                serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)\n            else:\n                serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)\n\n            # Create the new Prefix(es)\n            if serializer.is_valid():\n                serializer.save()\n                return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n        else:\n\n            serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={\n                'request': request,\n                'vrf': prefix.vrf,\n            })\n\n            return Response(serializer.data)\n\n    @swagger_auto_schema(method='get', responses={200: serializers.AvailableIPSerializer(many=True)})\n    @swagger_auto_schema(method='post', responses={201: serializers.AvailableIPSerializer(many=True)},\n                         request_body=serializers.AvailableIPSerializer(many=False))\n    @action(detail=True, url_path='available-ips', methods=['get', 'post'], queryset=IPAddress.objects.all())\n    @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])\n    def available_ips(self, request, pk=None):\n        \"\"\"\n        A convenience method for returning available IP addresses within a prefix. By default, the number of IPs\n        returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,\n        however results will not be paginated.\n\n        The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being\n        invoked in parallel, which results in a race condition where multiple insertions can occur.\n        \"\"\"\n        prefix = get_object_or_404(Prefix, pk=pk)\n\n        # Create the next available IP within the prefix\n        if request.method == 'POST':\n\n            # Normalize to a list of objects\n            requested_ips = request.data if isinstance(request.data, list) else [request.data]\n\n            # Determine if the requested number of IPs is available\n            available_ips = prefix.get_available_ips()\n            if available_ips.size < len(requested_ips):\n                return Response(\n                    {\n                        \"detail\": \"An insufficient number of IP addresses are available within the prefix {} ({} \"\n                                  \"requested, {} available)\".format(prefix, len(requested_ips), len(available_ips))\n                    },\n                    status=status.HTTP_204_NO_CONTENT\n                )\n\n            # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix\n            available_ips = iter(available_ips)\n            prefix_length = prefix.prefix.prefixlen\n            for requested_ip in requested_ips:\n                requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length)\n                requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None\n\n            # Initialize the serializer with a list or a single object depending on what was requested\n            context = {'request': request}\n            if isinstance(request.data, list):\n                serializer = serializers.IPAddressSerializer(data=requested_ips, many=True, context=context)\n            else:\n                serializer = serializers.IPAddressSerializer(data=requested_ips[0], context=context)\n\n            # Create the new IP address(es)\n            if serializer.is_valid():\n                serializer.save()\n                return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n        # Determine the maximum number of IPs to return\n        else:\n            try:\n                limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))\n            except ValueError:\n                limit = settings.PAGINATE_COUNT\n            if settings.MAX_PAGE_SIZE:\n                limit = min(limit, settings.MAX_PAGE_SIZE)\n\n            # Calculate available IPs within the prefix\n            ip_list = []\n            for index, ip in enumerate(prefix.get_available_ips(), start=1):\n                ip_list.append(ip)\n                if index == limit:\n                    break\n            serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={\n                'request': request,\n                'prefix': prefix.prefix,\n                'vrf': prefix.vrf,\n            })\n\n            return Response(serializer.data)\n\n\n#\n# IP addresses\n#\n\nclass IPAddressViewSet(CustomFieldModelViewSet):\n    queryset = IPAddress.objects.prefetch_related(\n        'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine',\n        'nat_outside', 'tags',\n    )\n    serializer_class = serializers.IPAddressSerializer\n    filterset_class = filters.IPAddressFilterSet\n\n\n#\n# VLAN groups\n#\n\nclass VLANGroupViewSet(ModelViewSet):\n    queryset = VLANGroup.objects.prefetch_related('site').annotate(\n        vlan_count=Count('vlans')\n    )\n    serializer_class = serializers.VLANGroupSerializer\n    filterset_class = filters.VLANGroupFilterSet\n\n\n#\n# VLANs\n#\n\nclass VLANViewSet(CustomFieldModelViewSet):\n    queryset = VLAN.objects.prefetch_related(\n        'site', 'group', 'tenant', 'role', 'tags'\n    ).annotate(\n        prefix_count=get_subquery(Prefix, 'vlan')\n    )\n    serializer_class = serializers.VLANSerializer\n    filterset_class = filters.VLANFilterSet\n\n\n#\n# Services\n#\n\nclass ServiceViewSet(ModelViewSet):\n    queryset = Service.objects.prefetch_related('device').prefetch_related('tags')\n    serializer_class = serializers.ServiceSerializer\n    filterset_class = filters.ServiceFilterSet\n", "path": "netbox/ipam/api/views.py"}], "after_files": [{"content": "from django.conf import settings\nfrom django.db.models import Count\nfrom django.shortcuts import get_object_or_404\nfrom django_pglocks import advisory_lock\nfrom drf_yasg.utils import swagger_auto_schema\nfrom rest_framework import status\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import PermissionDenied\nfrom rest_framework.response import Response\n\nfrom extras.api.views import CustomFieldModelViewSet\nfrom ipam import filters\nfrom ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF\nfrom utilities.api import ModelViewSet\nfrom utilities.constants import ADVISORY_LOCK_KEYS\nfrom utilities.utils import get_subquery\nfrom . import serializers\n\n\n#\n# VRFs\n#\n\nclass VRFViewSet(CustomFieldModelViewSet):\n    queryset = VRF.objects.prefetch_related('tenant').prefetch_related('tags').annotate(\n        ipaddress_count=get_subquery(IPAddress, 'vrf'),\n        prefix_count=get_subquery(Prefix, 'vrf')\n    )\n    serializer_class = serializers.VRFSerializer\n    filterset_class = filters.VRFFilterSet\n\n\n#\n# RIRs\n#\n\nclass RIRViewSet(ModelViewSet):\n    queryset = RIR.objects.annotate(\n        aggregate_count=Count('aggregates')\n    )\n    serializer_class = serializers.RIRSerializer\n    filterset_class = filters.RIRFilterSet\n\n\n#\n# Aggregates\n#\n\nclass AggregateViewSet(CustomFieldModelViewSet):\n    queryset = Aggregate.objects.prefetch_related('rir').prefetch_related('tags')\n    serializer_class = serializers.AggregateSerializer\n    filterset_class = filters.AggregateFilterSet\n\n\n#\n# Roles\n#\n\nclass RoleViewSet(ModelViewSet):\n    queryset = Role.objects.annotate(\n        prefix_count=get_subquery(Prefix, 'role'),\n        vlan_count=get_subquery(VLAN, 'role')\n    )\n    serializer_class = serializers.RoleSerializer\n    filterset_class = filters.RoleFilterSet\n\n\n#\n# Prefixes\n#\n\nclass PrefixViewSet(CustomFieldModelViewSet):\n    queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role', 'tags')\n    serializer_class = serializers.PrefixSerializer\n    filterset_class = filters.PrefixFilterSet\n\n    def get_serializer_class(self):\n        if self.action == \"available_prefixes\" and self.request.method == \"POST\":\n            return serializers.PrefixLengthSerializer\n        return super().get_serializer_class()\n\n    @swagger_auto_schema(method='get', responses={200: serializers.AvailablePrefixSerializer(many=True)})\n    @swagger_auto_schema(method='post', responses={201: serializers.AvailablePrefixSerializer(many=True)})\n    @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])\n    @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])\n    def available_prefixes(self, request, pk=None):\n        \"\"\"\n        A convenience method for returning available child prefixes within a parent.\n\n        The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being\n        invoked in parallel, which results in a race condition where multiple insertions can occur.\n        \"\"\"\n        prefix = get_object_or_404(Prefix, pk=pk)\n        available_prefixes = prefix.get_available_prefixes()\n\n        if request.method == 'POST':\n\n            # Validate Requested Prefixes' length\n            serializer = serializers.PrefixLengthSerializer(\n                data=request.data if isinstance(request.data, list) else [request.data],\n                many=True,\n                context={\n                    'request': request,\n                    'prefix': prefix,\n                }\n            )\n            if not serializer.is_valid():\n                return Response(\n                    serializer.errors,\n                    status=status.HTTP_400_BAD_REQUEST\n                )\n\n            requested_prefixes = serializer.validated_data\n            # Allocate prefixes to the requested objects based on availability within the parent\n            for i, requested_prefix in enumerate(requested_prefixes):\n\n                # Find the first available prefix equal to or larger than the requested size\n                for available_prefix in available_prefixes.iter_cidrs():\n                    if requested_prefix['prefix_length'] >= available_prefix.prefixlen:\n                        allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])\n                        requested_prefix['prefix'] = allocated_prefix\n                        requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None\n                        break\n                else:\n                    return Response(\n                        {\n                            \"detail\": \"Insufficient space is available to accommodate the requested prefix size(s)\"\n                        },\n                        status=status.HTTP_204_NO_CONTENT\n                    )\n\n                # Remove the allocated prefix from the list of available prefixes\n                available_prefixes.remove(allocated_prefix)\n\n            # Initialize the serializer with a list or a single object depending on what was requested\n            context = {'request': request}\n            if isinstance(request.data, list):\n                serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)\n            else:\n                serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)\n\n            # Create the new Prefix(es)\n            if serializer.is_valid():\n                serializer.save()\n                return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n        else:\n\n            serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={\n                'request': request,\n                'vrf': prefix.vrf,\n            })\n\n            return Response(serializer.data)\n\n    @swagger_auto_schema(method='get', responses={200: serializers.AvailableIPSerializer(many=True)})\n    @swagger_auto_schema(method='post', responses={201: serializers.AvailableIPSerializer(many=True)},\n                         request_body=serializers.AvailableIPSerializer(many=False))\n    @action(detail=True, url_path='available-ips', methods=['get', 'post'], queryset=IPAddress.objects.all())\n    @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])\n    def available_ips(self, request, pk=None):\n        \"\"\"\n        A convenience method for returning available IP addresses within a prefix. By default, the number of IPs\n        returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,\n        however results will not be paginated.\n\n        The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being\n        invoked in parallel, which results in a race condition where multiple insertions can occur.\n        \"\"\"\n        prefix = get_object_or_404(Prefix, pk=pk)\n\n        # Create the next available IP within the prefix\n        if request.method == 'POST':\n\n            # Normalize to a list of objects\n            requested_ips = request.data if isinstance(request.data, list) else [request.data]\n\n            # Determine if the requested number of IPs is available\n            available_ips = prefix.get_available_ips()\n            if available_ips.size < len(requested_ips):\n                return Response(\n                    {\n                        \"detail\": \"An insufficient number of IP addresses are available within the prefix {} ({} \"\n                                  \"requested, {} available)\".format(prefix, len(requested_ips), len(available_ips))\n                    },\n                    status=status.HTTP_204_NO_CONTENT\n                )\n\n            # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix\n            available_ips = iter(available_ips)\n            prefix_length = prefix.prefix.prefixlen\n            for requested_ip in requested_ips:\n                requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length)\n                requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None\n\n            # Initialize the serializer with a list or a single object depending on what was requested\n            context = {'request': request}\n            if isinstance(request.data, list):\n                serializer = serializers.IPAddressSerializer(data=requested_ips, many=True, context=context)\n            else:\n                serializer = serializers.IPAddressSerializer(data=requested_ips[0], context=context)\n\n            # Create the new IP address(es)\n            if serializer.is_valid():\n                serializer.save()\n                return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n        # Determine the maximum number of IPs to return\n        else:\n            try:\n                limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))\n            except ValueError:\n                limit = settings.PAGINATE_COUNT\n            if settings.MAX_PAGE_SIZE:\n                limit = min(limit, settings.MAX_PAGE_SIZE)\n\n            # Calculate available IPs within the prefix\n            ip_list = []\n            for index, ip in enumerate(prefix.get_available_ips(), start=1):\n                ip_list.append(ip)\n                if index == limit:\n                    break\n            serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={\n                'request': request,\n                'prefix': prefix.prefix,\n                'vrf': prefix.vrf,\n            })\n\n            return Response(serializer.data)\n\n\n#\n# IP addresses\n#\n\nclass IPAddressViewSet(CustomFieldModelViewSet):\n    queryset = IPAddress.objects.prefetch_related(\n        'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine',\n        'nat_outside', 'tags',\n    )\n    serializer_class = serializers.IPAddressSerializer\n    filterset_class = filters.IPAddressFilterSet\n\n\n#\n# VLAN groups\n#\n\nclass VLANGroupViewSet(ModelViewSet):\n    queryset = VLANGroup.objects.prefetch_related('site').annotate(\n        vlan_count=Count('vlans')\n    )\n    serializer_class = serializers.VLANGroupSerializer\n    filterset_class = filters.VLANGroupFilterSet\n\n\n#\n# VLANs\n#\n\nclass VLANViewSet(CustomFieldModelViewSet):\n    queryset = VLAN.objects.prefetch_related(\n        'site', 'group', 'tenant', 'role', 'tags'\n    ).annotate(\n        prefix_count=get_subquery(Prefix, 'vlan')\n    )\n    serializer_class = serializers.VLANSerializer\n    filterset_class = filters.VLANFilterSet\n\n\n#\n# Services\n#\n\nclass ServiceViewSet(ModelViewSet):\n    queryset = Service.objects.prefetch_related('device').prefetch_related('tags')\n    serializer_class = serializers.ServiceSerializer\n    filterset_class = filters.ServiceFilterSet\n", "path": "netbox/ipam/api/views.py"}]}