diff --git a/netbox/ipam/api/field_serializers.py b/netbox/ipam/api/field_serializers.py new file mode 100644 index 000000000..d44d8b7d4 --- /dev/null +++ b/netbox/ipam/api/field_serializers.py @@ -0,0 +1,32 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from ipam import models +from netaddr import AddrFormatError, IPNetwork + +__all__ = [ + 'IPAddressField', +] + + +# +# IP address field +# + +class IPAddressField(serializers.CharField): + """IPAddressField with mask""" + + default_error_messages = { + 'invalid': _('Enter a valid IPv4 or IPv6 address with optional mask.'), + } + + def to_internal_value(self, data): + try: + return IPNetwork(data) + except AddrFormatError: + raise serializers.ValidationError("Invalid IP address format: {}".format(data)) + except (TypeError, ValueError) as e: + raise serializers.ValidationError(e) + + def to_representation(self, value): + return str(value) diff --git a/netbox/ipam/api/nested_serializers.py b/netbox/ipam/api/nested_serializers.py index 57bb58b5e..9e150e2cb 100644 --- a/netbox/ipam/api/nested_serializers.py +++ b/netbox/ipam/api/nested_serializers.py @@ -4,6 +4,7 @@ from rest_framework import serializers from ipam import models from ipam.models.l2vpn import L2VPNTermination, L2VPN from netbox.api.serializers import WritableNestedSerializer +from .field_serializers import IPAddressField __all__ = [ 'NestedAggregateSerializer', @@ -182,6 +183,8 @@ class NestedPrefixSerializer(WritableNestedSerializer): class NestedIPRangeSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail') family = serializers.IntegerField(read_only=True) + start_address = IPAddressField() + end_address = IPAddressField() class Meta: model = models.IPRange @@ -195,6 +198,7 @@ class NestedIPRangeSerializer(WritableNestedSerializer): class NestedIPAddressSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail') family = serializers.IntegerField(read_only=True) + address = IPAddressField() class Meta: model = models.IPAddress diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 6339ca60c..064452667 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -13,6 +13,7 @@ from tenancy.api.nested_serializers import NestedTenantSerializer from utilities.api import get_serializer_for_model from virtualization.api.nested_serializers import NestedVirtualMachineSerializer from .nested_serializers import * +from .field_serializers import IPAddressField # @@ -369,6 +370,8 @@ class AvailablePrefixSerializer(serializers.Serializer): class IPRangeSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:iprange-detail') family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) + start_address = IPAddressField() + end_address = IPAddressField() vrf = NestedVRFSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) status = ChoiceField(choices=IPRangeStatusChoices, required=False) @@ -391,6 +394,7 @@ class IPRangeSerializer(NetBoxModelSerializer): class IPAddressSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='ipam-api:ipaddress-detail') family = ChoiceField(choices=IPAddressFamilyChoices, read_only=True) + address = IPAddressField() vrf = NestedVRFSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) status = ChoiceField(choices=IPAddressStatusChoices, required=False)