diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index aa1d2834a..d4ab43100 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -352,15 +352,17 @@ class AvailableIPSerializer(serializers.Serializer): family = serializers.IntegerField(read_only=True) address = serializers.CharField(read_only=True) vrf = NestedVRFSerializer(read_only=True) + mask_length = serializers.IntegerField(read_only=True, allow_null=True) def to_representation(self, instance): if self.context.get('vrf'): vrf = NestedVRFSerializer(self.context['vrf'], context={'request': self.context['request']}).data else: vrf = None + mask_length = self.context.get('mask_length') or self.context['parent'].mask_length return OrderedDict([ ('family', self.context['parent'].family), - ('address', f"{instance}/{self.context['parent'].mask_length}"), + ('address', f"{instance}/{mask_length}"), ('vrf', vrf), ]) diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 0d098db4b..7fe0f5e7d 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -294,7 +294,8 @@ class AvailableIPAddressesView(ObjectValidationMixin, APIView): # Assign addresses from the list of available IPs and copy VRF assignment from the parent available_ips = iter(available_ips) for requested_ip in requested_ips: - requested_ip['address'] = f'{next(available_ips)}/{parent.mask_length}' + mask_length = requested_ip.get('mask_length') or parent.mask_length + requested_ip['address'] = f'{next(available_ips)}/{mask_length}' requested_ip['vrf'] = parent.vrf.pk if parent.vrf else None # Initialize the serializer with a list or a single object depending on what was requested diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 478c7f29b..be6818998 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -379,8 +379,8 @@ class PrefixTest(APIViewTestCases.APIViewTestCase): url = reverse('ipam-api:prefix-available-ips', kwargs={'pk': prefix.pk}) self.add_permissions('ipam.view_prefix', 'ipam.add_ipaddress') - # Create all four available IPs with individual requests - for i in range(1, 5): + # Create three available IPs with individual requests + for i in range(1, 4): data = { 'description': 'Test IP {}'.format(i) } @@ -388,6 +388,18 @@ class PrefixTest(APIViewTestCases.APIViewTestCase): self.assertHttpStatus(response, status.HTTP_201_CREATED) self.assertEqual(response.data['vrf']['id'], vrf.pk) self.assertEqual(response.data['description'], data['description']) + self.assertEqual(IPNetwork(response.data['address']).prefixlen, 30) + + # Create one more IP with a specified mask length + data = { + 'description': 'Test IP 4', + 'mask_length': 32 + } + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + self.assertEqual(response.data['vrf']['id'], vrf.pk) + self.assertEqual(response.data['description'], data['description']) + self.assertEqual(IPNetwork(response.data['address']).prefixlen, 32) # Try to create one more IP response = self.client.post(url, {}, **self.header)