diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 81f04c1a4..3aefad822 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -240,6 +240,7 @@ PATCH) to maintain backward compatibility. This behavior will be discontinued be ## Bug Fixes * [#3830](https://github.com/digitalocean/netbox/issues/3830) - Ensure deterministic ordering for all models +* [#3930](https://github.com/digitalocean/netbox/issues/3930) - Fix API rendering of the `family` field for aggregates ## Bug Fixes (From Beta) diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 1e1759408..44f67d538 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -9,7 +9,7 @@ from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerial from dcim.models import Interface from extras.api.customfields import CustomFieldModelSerializer from ipam.choices import * -from ipam.models import AF_CHOICES, Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF +from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF from tenancy.api.nested_serializers import NestedTenantSerializer from utilities.api import ( ChoiceField, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer, @@ -49,6 +49,7 @@ class RIRSerializer(ValidatedModelSerializer): class AggregateSerializer(TaggitSerializer, CustomFieldModelSerializer): + family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) rir = NestedRIRSerializer() tags = TagListSerializerField(required=False) @@ -135,7 +136,7 @@ class VLANSerializer(TaggitSerializer, CustomFieldModelSerializer): # class PrefixSerializer(TaggitSerializer, CustomFieldModelSerializer): - family = ChoiceField(choices=AF_CHOICES, read_only=True) + family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) site = NestedSiteSerializer(required=False, allow_null=True) vrf = NestedVRFSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) @@ -197,7 +198,7 @@ class IPAddressInterfaceSerializer(WritableNestedSerializer): class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer): - family = ChoiceField(choices=AF_CHOICES, read_only=True) + family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) vrf = NestedVRFSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) status = ChoiceField(choices=IPAddressStatusChoices, required=False) diff --git a/netbox/ipam/choices.py b/netbox/ipam/choices.py index fab1e42aa..9e211ab6d 100644 --- a/netbox/ipam/choices.py +++ b/netbox/ipam/choices.py @@ -1,6 +1,17 @@ from utilities.choices import ChoiceSet +class IPAddressFamilyChoices(ChoiceSet): + + FAMILY_4 = 4 + FAMILY_6 = 6 + + CHOICES = ( + (FAMILY_4, 'IPv4'), + (FAMILY_6, 'IPv6'), + ) + + # # Prefixes # diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index ed0b8f4a7..885d5d617 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -21,13 +21,6 @@ from .querysets import PrefixQuerySet from .validators import DNSValidator -# IP address families -AF_CHOICES = ( - (4, 'IPv4'), - (6, 'IPv6'), -) - - __all__ = ( 'Aggregate', 'IPAddress', @@ -158,7 +151,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel): the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR. """ family = models.PositiveSmallIntegerField( - choices=AF_CHOICES + choices=IPAddressFamilyChoices ) prefix = IPNetworkField() rir = models.ForeignKey( @@ -299,7 +292,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel): assigned to a VLAN where appropriate. """ family = models.PositiveSmallIntegerField( - choices=AF_CHOICES, + choices=IPAddressFamilyChoices, editable=False ) prefix = IPNetworkField( @@ -570,7 +563,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP. """ family = models.PositiveSmallIntegerField( - choices=AF_CHOICES, + choices=IPAddressFamilyChoices, editable=False ) address = IPAddressField( diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index 450f2ad5f..22fb71501 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -264,6 +264,7 @@ class AggregateTest(APITestCase): url = reverse('ipam-api:aggregate-detail', kwargs={'pk': self.aggregate1.pk}) response = self.client.get(url, **self.header) + self.assertEqual(response.data['family']['value'], 4) self.assertEqual(response.data['prefix'], str(self.aggregate1.prefix)) def test_list_aggregates(self): @@ -470,6 +471,7 @@ class PrefixTest(APITestCase): url = reverse('ipam-api:prefix-detail', kwargs={'pk': self.prefix1.pk}) response = self.client.get(url, **self.header) + self.assertEqual(response.data['family']['value'], 4) self.assertEqual(response.data['prefix'], str(self.prefix1.prefix)) def test_list_prefixes(self): @@ -706,6 +708,7 @@ class IPAddressTest(APITestCase): url = reverse('ipam-api:ipaddress-detail', kwargs={'pk': self.ipaddress1.pk}) response = self.client.get(url, **self.header) + self.assertEqual(response.data['family']['value'], 4) self.assertEqual(response.data['address'], str(self.ipaddress1.address)) def test_list_ipaddresss(self):