We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API Definitions for available-ips and available-prefixes are incorrect
<!--
    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.

    Please indicate the nature of the change by placing an X in one of the
    boxes below.
-->
### Change Type
[ ] Addition
[x] Correction
[ ] Deprecation
[ ] Cleanup (formatting, typos, etc.)

### Area
[ ] Installation instructions
[ ] Configuration parameters
[ ] Functionality/features
[x] REST API
[ ] Administration/development
[ ] Other

<!-- Describe the proposed change(s). -->
### Proposed Changes
I'm writing a ipam project with go-netbox(https://github.com/kobayashi/go-netbox for v2.8), which generate the client from swagger. And I'm using the prefix/available-ips api to create unused ip address. I notice that the api documents in http://netbox/api/docs/ has difference with actually code:
* The request model should be IPAddress rather than WritablePrefix
  _ref: https://github.com/netbox-community/netbox/blob/v2.8.3/netbox/ipam/api/views.py#L211_
* The return code should be 201 rather than 200
  _ref: https://github.com/netbox-community/netbox/blob/v2.8.3/netbox/ipam/api/views.py#L218_

These mismatches make go-netbox client unable to work correctly. I would appreciate if this can be fixed.

--- 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(
78         methods=['get', 'post'],
79         responses={
80             200: serializers.AvailablePrefixSerializer(many=True),
81         }
82     )
83     @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
84     @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])
85     def available_prefixes(self, request, pk=None):
86         """
87         A convenience method for returning available child prefixes within a parent.
88 
89         The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
90         invoked in parallel, which results in a race condition where multiple insertions can occur.
91         """
92         prefix = get_object_or_404(Prefix, pk=pk)
93         available_prefixes = prefix.get_available_prefixes()
94 
95         if request.method == 'POST':
96 
97             # Permissions check
98             if not request.user.has_perm('ipam.add_prefix'):
99                 raise PermissionDenied()
100 
101             # Validate Requested Prefixes' length
102             serializer = serializers.PrefixLengthSerializer(
103                 data=request.data if isinstance(request.data, list) else [request.data],
104                 many=True,
105                 context={
106                     'request': request,
107                     'prefix': prefix,
108                 }
109             )
110             if not serializer.is_valid():
111                 return Response(
112                     serializer.errors,
113                     status=status.HTTP_400_BAD_REQUEST
114                 )
115 
116             requested_prefixes = serializer.validated_data
117             # Allocate prefixes to the requested objects based on availability within the parent
118             for i, requested_prefix in enumerate(requested_prefixes):
119 
120                 # Find the first available prefix equal to or larger than the requested size
121                 for available_prefix in available_prefixes.iter_cidrs():
122                     if requested_prefix['prefix_length'] >= available_prefix.prefixlen:
123                         allocated_prefix = '{}/{}'.format(available_prefix.network, requested_prefix['prefix_length'])
124                         requested_prefix['prefix'] = allocated_prefix
125                         requested_prefix['vrf'] = prefix.vrf.pk if prefix.vrf else None
126                         break
127                 else:
128                     return Response(
129                         {
130                             "detail": "Insufficient space is available to accommodate the requested prefix size(s)"
131                         },
132                         status=status.HTTP_204_NO_CONTENT
133                     )
134 
135                 # Remove the allocated prefix from the list of available prefixes
136                 available_prefixes.remove(allocated_prefix)
137 
138             # Initialize the serializer with a list or a single object depending on what was requested
139             context = {'request': request}
140             if isinstance(request.data, list):
141                 serializer = serializers.PrefixSerializer(data=requested_prefixes, many=True, context=context)
142             else:
143                 serializer = serializers.PrefixSerializer(data=requested_prefixes[0], context=context)
144 
145             # Create the new Prefix(es)
146             if serializer.is_valid():
147                 serializer.save()
148                 return Response(serializer.data, status=status.HTTP_201_CREATED)
149 
150             return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
151 
152         else:
153 
154             serializer = serializers.AvailablePrefixSerializer(available_prefixes.iter_cidrs(), many=True, context={
155                 'request': request,
156                 'vrf': prefix.vrf,
157             })
158 
159             return Response(serializer.data)
160 
161     @swagger_auto_schema(
162         methods=['get', 'post'],
163         responses={
164             200: serializers.AvailableIPSerializer(many=True),
165         }
166     )
167     @action(detail=True, url_path='available-ips', methods=['get', 'post'])
168     @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])
169     def available_ips(self, request, pk=None):
170         """
171         A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
172         returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
173         however results will not be paginated.
174 
175         The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
176         invoked in parallel, which results in a race condition where multiple insertions can occur.
177         """
178         prefix = get_object_or_404(Prefix, pk=pk)
179 
180         # Create the next available IP within the prefix
181         if request.method == 'POST':
182 
183             # Permissions check
184             if not request.user.has_perm('ipam.add_ipaddress'):
185                 raise PermissionDenied()
186 
187             # Normalize to a list of objects
188             requested_ips = request.data if isinstance(request.data, list) else [request.data]
189 
190             # Determine if the requested number of IPs is available
191             available_ips = prefix.get_available_ips()
192             if available_ips.size < len(requested_ips):
193                 return Response(
194                     {
195                         "detail": "An insufficient number of IP addresses are available within the prefix {} ({} "
196                                   "requested, {} available)".format(prefix, len(requested_ips), len(available_ips))
197                     },
198                     status=status.HTTP_204_NO_CONTENT
199                 )
200 
201             # Assign addresses from the list of available IPs and copy VRF assignment from the parent prefix
202             available_ips = iter(available_ips)
203             prefix_length = prefix.prefix.prefixlen
204             for requested_ip in requested_ips:
205                 requested_ip['address'] = '{}/{}'.format(next(available_ips), prefix_length)
206                 requested_ip['vrf'] = prefix.vrf.pk if prefix.vrf else None
207 
208             # Initialize the serializer with a list or a single object depending on what was requested
209             context = {'request': request}
210             if isinstance(request.data, list):
211                 serializer = serializers.IPAddressSerializer(data=requested_ips, many=True, context=context)
212             else:
213                 serializer = serializers.IPAddressSerializer(data=requested_ips[0], context=context)
214 
215             # Create the new IP address(es)
216             if serializer.is_valid():
217                 serializer.save()
218                 return Response(serializer.data, status=status.HTTP_201_CREATED)
219 
220             return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
221 
222         # Determine the maximum number of IPs to return
223         else:
224             try:
225                 limit = int(request.query_params.get('limit', settings.PAGINATE_COUNT))
226             except ValueError:
227                 limit = settings.PAGINATE_COUNT
228             if settings.MAX_PAGE_SIZE:
229                 limit = min(limit, settings.MAX_PAGE_SIZE)
230 
231             # Calculate available IPs within the prefix
232             ip_list = []
233             for index, ip in enumerate(prefix.get_available_ips(), start=1):
234                 ip_list.append(ip)
235                 if index == limit:
236                     break
237             serializer = serializers.AvailableIPSerializer(ip_list, many=True, context={
238                 'request': request,
239                 'prefix': prefix.prefix,
240                 'vrf': prefix.vrf,
241             })
242 
243             return Response(serializer.data)
244 
245 
246 #
247 # IP addresses
248 #
249 
250 class IPAddressViewSet(CustomFieldModelViewSet):
251     queryset = IPAddress.objects.prefetch_related(
252         'vrf__tenant', 'tenant', 'nat_inside', 'interface__device__device_type', 'interface__virtual_machine',
253         'nat_outside', 'tags',
254     )
255     serializer_class = serializers.IPAddressSerializer
256     filterset_class = filters.IPAddressFilterSet
257 
258 
259 #
260 # VLAN groups
261 #
262 
263 class VLANGroupViewSet(ModelViewSet):
264     queryset = VLANGroup.objects.prefetch_related('site').annotate(
265         vlan_count=Count('vlans')
266     )
267     serializer_class = serializers.VLANGroupSerializer
268     filterset_class = filters.VLANGroupFilterSet
269 
270 
271 #
272 # VLANs
273 #
274 
275 class VLANViewSet(CustomFieldModelViewSet):
276     queryset = VLAN.objects.prefetch_related(
277         'site', 'group', 'tenant', 'role', 'tags'
278     ).annotate(
279         prefix_count=get_subquery(Prefix, 'vlan')
280     )
281     serializer_class = serializers.VLANSerializer
282     filterset_class = filters.VLANFilterSet
283 
284 
285 #
286 # Services
287 #
288 
289 class ServiceViewSet(ModelViewSet):
290     queryset = Service.objects.prefetch_related('device').prefetch_related('tags')
291     serializer_class = serializers.ServiceSerializer
292     filterset_class = filters.ServiceFilterSet
293
```
--- 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,12 +74,8 @@
     serializer_class = serializers.PrefixSerializer
     filterset_class = filters.PrefixFilterSet
 
-    @swagger_auto_schema(
-        methods=['get', 'post'],
-        responses={
-            200: serializers.AvailablePrefixSerializer(many=True),
-        }
-    )
+    @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'])
     @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])
     def available_prefixes(self, request, pk=None):
@@ -158,12 +154,9 @@
 
             return Response(serializer.data)
 
-    @swagger_auto_schema(
-        methods=['get', 'post'],
-        responses={
-            200: serializers.AvailableIPSerializer(many=True),
-        }
-    )
+    @swagger_auto_schema(method='get', responses={200: serializers.AvailableIPSerializer(many=True)})
+    @swagger_auto_schema(method='post', responses={201: serializers.AvailableIPSerializer(many=True)},
+                         request_body=serializers.AvailableIPSerializer(many=False))
     @action(detail=True, url_path='available-ips', methods=['get', 'post'])
     @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])
     def available_ips(self, request, pk=None):

{"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,12 +74,8 @@\n     serializer_class = serializers.PrefixSerializer\n     filterset_class = filters.PrefixFilterSet\n \n-    @swagger_auto_schema(\n-        methods=['get', 'post'],\n-        responses={\n-            200: serializers.AvailablePrefixSerializer(many=True),\n-        }\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@@ -158,12 +154,9 @@\n \n             return Response(serializer.data)\n \n-    @swagger_auto_schema(\n-        methods=['get', 'post'],\n-        responses={\n-            200: serializers.AvailableIPSerializer(many=True),\n-        }\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'])\n     @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])\n     def available_ips(self, request, pk=None):\n", "issue": "API Definitions for available-ips and available-prefixes are incorrect\n<!--\r\n    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.\r\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[ ] Addition\r\n[x] Correction\r\n[ ] Deprecation\r\n[ ] Cleanup (formatting, typos, etc.)\r\n\r\n### Area\r\n[ ] Installation instructions\r\n[ ] Configuration parameters\r\n[ ] Functionality/features\r\n[x] REST API\r\n[ ] Administration/development\r\n[ ] Other\r\n\r\n<!-- Describe the proposed change(s). -->\r\n### Proposed Changes\r\nI'm writing a ipam project with go-netbox(https://github.com/kobayashi/go-netbox for v2.8), which generate the client from swagger. And I'm using the prefix/available-ips api to create unused ip address. I notice that the api documents in http://netbox/api/docs/ has difference with actually code:\r\n* The request model should be IPAddress rather than WritablePrefix\r\n  _ref: https://github.com/netbox-community/netbox/blob/v2.8.3/netbox/ipam/api/views.py#L211_\r\n* The return code should be 201 rather than 200\r\n  _ref: https://github.com/netbox-community/netbox/blob/v2.8.3/netbox/ipam/api/views.py#L218_\r\n\r\nThese mismatches make go-netbox client unable to work correctly. I would appreciate if this can be fixed.\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(\n        methods=['get', 'post'],\n        responses={\n            200: serializers.AvailablePrefixSerializer(many=True),\n        }\n    )\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            # Permissions check\n            if not request.user.has_perm('ipam.add_prefix'):\n                raise PermissionDenied()\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(\n        methods=['get', 'post'],\n        responses={\n            200: serializers.AvailableIPSerializer(many=True),\n        }\n    )\n    @action(detail=True, url_path='available-ips', methods=['get', 'post'])\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            # Permissions check\n            if not request.user.has_perm('ipam.add_ipaddress'):\n                raise PermissionDenied()\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    @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            # Permissions check\n            if not request.user.has_perm('ipam.add_prefix'):\n                raise PermissionDenied()\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'])\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            # Permissions check\n            if not request.user.has_perm('ipam.add_ipaddress'):\n                raise PermissionDenied()\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"}]}