We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Race condition when POSTing to /api/ipam/prefixes/<id>/available-ips/
### Environment
* Python version: 3.5.3
* NetBox version:  2.4.6

### Steps to Reproduce
Create a normal prefix, then run this code against the API (replace `$token`, `$prefixid` and `$host`):

`for i in $(seq 16); do curl -X POST -so/dev/null -H "Authorization: Token $token" -H "Accept: application/json; indent=4" https://$host/api/ipam/prefixes/$prefixid/available-ips/ & done`

This code requests 16 IP addresses simultaneously from Netbox triggering the race condition bug.

### Expected Behavior
Netbox assigns IP addresses from .1 up to .16: [Example screenshot](http://xor.meo.ws/8b820a1a/ab06/4ecc/b226/260e6076c512.png)

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

Path: `netbox/utilities/constants.py`
Content:
```
1 COLOR_CHOICES = (
2     ('aa1409', 'Dark red'),
3     ('f44336', 'Red'),
4     ('e91e63', 'Pink'),
5     ('ffe4e1', 'Rose'),
6     ('ff66ff', 'Fuschia'),
7     ('9c27b0', 'Purple'),
8     ('673ab7', 'Dark purple'),
9     ('3f51b5', 'Indigo'),
10     ('2196f3', 'Blue'),
11     ('03a9f4', 'Light blue'),
12     ('00bcd4', 'Cyan'),
13     ('009688', 'Teal'),
14     ('00ffff', 'Aqua'),
15     ('2f6a31', 'Dark green'),
16     ('4caf50', 'Green'),
17     ('8bc34a', 'Light green'),
18     ('cddc39', 'Lime'),
19     ('ffeb3b', 'Yellow'),
20     ('ffc107', 'Amber'),
21     ('ff9800', 'Orange'),
22     ('ff5722', 'Dark orange'),
23     ('795548', 'Brown'),
24     ('c0c0c0', 'Light grey'),
25     ('9e9e9e', 'Grey'),
26     ('607d8b', 'Dark grey'),
27     ('111111', 'Black'),
28     ('ffffff', 'White'),
29 )
30
```
--- 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
@@ -1,6 +1,7 @@
 from django.conf import settings
 from django.db.models import Count
 from django.shortcuts import get_object_or_404
+from django_pglocks import advisory_lock
 from rest_framework import status
 from rest_framework.decorators import action
 from rest_framework.exceptions import PermissionDenied
@@ -10,6 +11,7 @@
 from ipam import filters
 from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from utilities.api import FieldChoicesViewSet, ModelViewSet
+from utilities.constants import ADVISORY_LOCK_KEYS
 from utilities.utils import get_subquery
 from . import serializers
 
@@ -86,9 +88,13 @@
     filterset_class = filters.PrefixFilterSet
 
     @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
+    @advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])
     def available_prefixes(self, request, pk=None):
         """
         A convenience method for returning available child prefixes within a parent.
+
+        The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
+        invoked in parallel, which results in a race condition where multiple insertions can occur.
         """
         prefix = get_object_or_404(Prefix, pk=pk)
         available_prefixes = prefix.get_available_prefixes()
@@ -180,11 +186,15 @@
             return Response(serializer.data)
 
     @action(detail=True, url_path='available-ips', methods=['get', 'post'])
+    @advisory_lock(ADVISORY_LOCK_KEYS['available-ips'])
     def available_ips(self, request, pk=None):
         """
         A convenience method for returning available IP addresses within a prefix. By default, the number of IPs
         returned will be equivalent to PAGINATE_COUNT. An arbitrary limit (up to MAX_PAGE_SIZE, if set) may be passed,
         however results will not be paginated.
+
+        The advisory lock decorator uses a PostgreSQL advisory lock to prevent this API from being
+        invoked in parallel, which results in a race condition where multiple insertions can occur.
         """
         prefix = get_object_or_404(Prefix, pk=pk)
 
diff --git a/netbox/utilities/constants.py b/netbox/utilities/constants.py
--- a/netbox/utilities/constants.py
+++ b/netbox/utilities/constants.py
@@ -27,3 +27,14 @@
     ('111111', 'Black'),
     ('ffffff', 'White'),
 )
+
+# Keys for PostgreSQL advisory locks. These are arbitrary bigints used by
+# the advisory_lock contextmanager. When a lock is acquired,
+# one of these keys will be used to identify said lock.
+#
+# When adding a new key, pick something arbitrary and unique so
+# that it is easily searchable in query logs.
+ADVISORY_LOCK_KEYS = {
+    'available-prefixes': 100100,
+    'available-ips': 100200,
+}

{"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@@ -1,6 +1,7 @@\n from django.conf import settings\n from django.db.models import Count\n from django.shortcuts import get_object_or_404\n+from django_pglocks import advisory_lock\n from rest_framework import status\n from rest_framework.decorators import action\n from rest_framework.exceptions import PermissionDenied\n@@ -10,6 +11,7 @@\n from ipam import filters\n from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF\n from utilities.api import FieldChoicesViewSet, ModelViewSet\n+from utilities.constants import ADVISORY_LOCK_KEYS\n from utilities.utils import get_subquery\n from . import serializers\n \n@@ -86,9 +88,13 @@\n     filterset_class = filters.PrefixFilterSet\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@@ -180,11 +186,15 @@\n             return Response(serializer.data)\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 \ndiff --git a/netbox/utilities/constants.py b/netbox/utilities/constants.py\n--- a/netbox/utilities/constants.py\n+++ b/netbox/utilities/constants.py\n@@ -27,3 +27,14 @@\n     ('111111', 'Black'),\n     ('ffffff', 'White'),\n )\n+\n+# Keys for PostgreSQL advisory locks. These are arbitrary bigints used by\n+# the advisory_lock contextmanager. When a lock is acquired,\n+# one of these keys will be used to identify said lock.\n+#\n+# When adding a new key, pick something arbitrary and unique so\n+# that it is easily searchable in query logs.\n+ADVISORY_LOCK_KEYS = {\n+    'available-prefixes': 100100,\n+    'available-ips': 100200,\n+}\n", "issue": "Race condition when POSTing to /api/ipam/prefixes/<id>/available-ips/\n### Environment\r\n* Python version: 3.5.3\r\n* NetBox version:  2.4.6\r\n\r\n### Steps to Reproduce\r\nCreate a normal prefix, then run this code against the API (replace `$token`, `$prefixid` and `$host`):\r\n\r\n`for i in $(seq 16); do curl -X POST -so/dev/null -H \"Authorization: Token $token\" -H \"Accept: application/json; indent=4\" https://$host/api/ipam/prefixes/$prefixid/available-ips/ & done`\r\n\r\nThis code requests 16 IP addresses simultaneously from Netbox triggering the race condition bug.\r\n\r\n### Expected Behavior\r\nNetbox assigns IP addresses from .1 up to .16: [Example screenshot](http://xor.meo.ws/8b820a1a/ab06/4ecc/b226/260e6076c512.png)\r\n\r\n### Observed Behavior\r\nNetbox assigned IP addresses from .1 up to .5, assigning some of the IPs multiple times: [Example screenshot](http://xor.meo.ws/b993ccfc/632f/4372/b7b3/c0f2398e9ddb.png)\n", "before_files": [{"content": "from django.conf import settings\nfrom django.db.models import Count\nfrom django.shortcuts import get_object_or_404\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 FieldChoicesViewSet, ModelViewSet\nfrom utilities.utils import get_subquery\nfrom . import serializers\n\n\n#\n# Field choices\n#\n\nclass IPAMFieldChoicesViewSet(FieldChoicesViewSet):\n    fields = (\n        (serializers.AggregateSerializer, ['family']),\n        (serializers.PrefixSerializer, ['family', 'status']),\n        (serializers.IPAddressSerializer, ['family', 'status', 'role']),\n        (serializers.VLANSerializer, ['status']),\n        (serializers.ServiceSerializer, ['protocol']),\n    )\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    @action(detail=True, url_path='available-prefixes', methods=['get', 'post'])\n    def available_prefixes(self, request, pk=None):\n        \"\"\"\n        A convenience method for returning available child prefixes within a parent.\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            # Normalize to a list of objects\n            requested_prefixes = request.data if isinstance(request.data, list) else [request.data]\n\n            # Allocate prefixes to the requested objects based on availability within the parent\n            for i, requested_prefix in enumerate(requested_prefixes):\n\n                # Validate requested prefix size\n                prefix_length = requested_prefix.get('prefix_length')\n                if prefix_length is None:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: prefix_length field missing\".format(i)\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\n                try:\n                    prefix_length = int(prefix_length)\n                except ValueError:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: Invalid prefix length ({})\".format(i, prefix_length),\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\n                if prefix.family == 4 and prefix_length > 32:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: Invalid prefix length ({}) for IPv4\".format(i, prefix_length),\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\n                elif prefix.family == 6 and prefix_length > 128:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: Invalid prefix length ({}) for IPv6\".format(i, prefix_length),\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\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    @action(detail=True, url_path='available-ips', methods=['get', 'post'])\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        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, 'role')\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"}, {"content": "COLOR_CHOICES = (\n    ('aa1409', 'Dark red'),\n    ('f44336', 'Red'),\n    ('e91e63', 'Pink'),\n    ('ffe4e1', 'Rose'),\n    ('ff66ff', 'Fuschia'),\n    ('9c27b0', 'Purple'),\n    ('673ab7', 'Dark purple'),\n    ('3f51b5', 'Indigo'),\n    ('2196f3', 'Blue'),\n    ('03a9f4', 'Light blue'),\n    ('00bcd4', 'Cyan'),\n    ('009688', 'Teal'),\n    ('00ffff', 'Aqua'),\n    ('2f6a31', 'Dark green'),\n    ('4caf50', 'Green'),\n    ('8bc34a', 'Light green'),\n    ('cddc39', 'Lime'),\n    ('ffeb3b', 'Yellow'),\n    ('ffc107', 'Amber'),\n    ('ff9800', 'Orange'),\n    ('ff5722', 'Dark orange'),\n    ('795548', 'Brown'),\n    ('c0c0c0', 'Light grey'),\n    ('9e9e9e', 'Grey'),\n    ('607d8b', 'Dark grey'),\n    ('111111', 'Black'),\n    ('ffffff', 'White'),\n)\n", "path": "netbox/utilities/constants.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 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 FieldChoicesViewSet, ModelViewSet\nfrom utilities.constants import ADVISORY_LOCK_KEYS\nfrom utilities.utils import get_subquery\nfrom . import serializers\n\n\n#\n# Field choices\n#\n\nclass IPAMFieldChoicesViewSet(FieldChoicesViewSet):\n    fields = (\n        (serializers.AggregateSerializer, ['family']),\n        (serializers.PrefixSerializer, ['family', 'status']),\n        (serializers.IPAddressSerializer, ['family', 'status', 'role']),\n        (serializers.VLANSerializer, ['status']),\n        (serializers.ServiceSerializer, ['protocol']),\n    )\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    @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            # Normalize to a list of objects\n            requested_prefixes = request.data if isinstance(request.data, list) else [request.data]\n\n            # Allocate prefixes to the requested objects based on availability within the parent\n            for i, requested_prefix in enumerate(requested_prefixes):\n\n                # Validate requested prefix size\n                prefix_length = requested_prefix.get('prefix_length')\n                if prefix_length is None:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: prefix_length field missing\".format(i)\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\n                try:\n                    prefix_length = int(prefix_length)\n                except ValueError:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: Invalid prefix length ({})\".format(i, prefix_length),\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\n                if prefix.family == 4 and prefix_length > 32:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: Invalid prefix length ({}) for IPv4\".format(i, prefix_length),\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\n                elif prefix.family == 6 and prefix_length > 128:\n                    return Response(\n                        {\n                            \"detail\": \"Item {}: Invalid prefix length ({}) for IPv6\".format(i, prefix_length),\n                        },\n                        status=status.HTTP_400_BAD_REQUEST\n                    )\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    @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, 'role')\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"}, {"content": "COLOR_CHOICES = (\n    ('aa1409', 'Dark red'),\n    ('f44336', 'Red'),\n    ('e91e63', 'Pink'),\n    ('ffe4e1', 'Rose'),\n    ('ff66ff', 'Fuschia'),\n    ('9c27b0', 'Purple'),\n    ('673ab7', 'Dark purple'),\n    ('3f51b5', 'Indigo'),\n    ('2196f3', 'Blue'),\n    ('03a9f4', 'Light blue'),\n    ('00bcd4', 'Cyan'),\n    ('009688', 'Teal'),\n    ('00ffff', 'Aqua'),\n    ('2f6a31', 'Dark green'),\n    ('4caf50', 'Green'),\n    ('8bc34a', 'Light green'),\n    ('cddc39', 'Lime'),\n    ('ffeb3b', 'Yellow'),\n    ('ffc107', 'Amber'),\n    ('ff9800', 'Orange'),\n    ('ff5722', 'Dark orange'),\n    ('795548', 'Brown'),\n    ('c0c0c0', 'Light grey'),\n    ('9e9e9e', 'Grey'),\n    ('607d8b', 'Dark grey'),\n    ('111111', 'Black'),\n    ('ffffff', 'White'),\n)\n\n# Keys for PostgreSQL advisory locks. These are arbitrary bigints used by\n# the advisory_lock contextmanager. When a lock is acquired,\n# one of these keys will be used to identify said lock.\n#\n# When adding a new key, pick something arbitrary and unique so\n# that it is easily searchable in query logs.\nADVISORY_LOCK_KEYS = {\n    'available-prefixes': 100100,\n    'available-ips': 100200,\n}\n", "path": "netbox/utilities/constants.py"}]}