Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12278 add ipaddressfield serializer for OpenAPI spectacular typing #12285

Merged
merged 10 commits into from
Apr 21, 2023
38 changes: 38 additions & 0 deletions netbox/ipam/api/field_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers

from ipam import models
from ipam.validators import validate_ipaddress_with_mask
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 __init__(self, **kwargs):
super().__init__(**kwargs)
validator = validate_ipaddress_with_mask
self.validators.append(validator)
arthanson marked this conversation as resolved.
Show resolved Hide resolved

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)
4 changes: 4 additions & 0 deletions netbox/ipam/api/nested_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ipam import models
from ipam.models.l2vpn import L2VPNTermination, L2VPN
from netbox.api.serializers import WritableNestedSerializer
from .field_serializers import IPAddressField

__all__ = [
'NestedAggregateSerializer',
Expand Down Expand Up @@ -182,6 +183,8 @@ class Meta:
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
Expand All @@ -195,6 +198,7 @@ class Meta:
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
Expand Down
4 changes: 4 additions & 0 deletions netbox/ipam/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from utilities.api import get_serializer_for_model
from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
from .nested_serializers import *
from .field_serializers import IPAddressField


#
Expand Down Expand Up @@ -369,6 +370,8 @@ def to_representation(self, instance):
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)
Expand All @@ -391,6 +394,7 @@ class Meta:
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)
Expand Down
11 changes: 11 additions & 0 deletions netbox/ipam/validators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.core.exceptions import ValidationError
from django.core.validators import BaseValidator, RegexValidator
from netaddr import AddrFormatError, IPNetwork


def prefix_validator(prefix):
Expand Down Expand Up @@ -28,3 +29,13 @@ def compare(self, a, b):
message='Only alphanumeric characters, asterisks, hyphens, periods, and underscores are allowed in DNS names',
code='invalid'
)


def validate_ipaddress_with_mask(address):
if address:
try:
IPNetwork(address)
except AddrFormatError:
raise ValidationError("Invalid IP address format: {}".format(address))
except (TypeError, ValueError) as e:
raise ValidationError(e)