diff --git a/docs/additional-features/custom-scripts.md b/docs/additional-features/custom-scripts.md index 25cd223cf..850988766 100644 --- a/docs/additional-features/custom-scripts.md +++ b/docs/additional-features/custom-scripts.md @@ -160,11 +160,14 @@ An uploaded file. Note that uploaded files are present in memory only for the du ### IPAddressVar -An IPv4 or IPv6 address (CIDR mask not allowed). +An IPv4 or IPv6 address. Returns an instance of `netaddr.IPNetwork`. Options include: + +* `min_prefix_length` - Minimum length of the mask (default: none) +* `max_prefix_length` - Maximum length of the mask (default: none) ### IPNetworkVar -An IPv4 or IPv6 network with a mask. Options include: +Similar to `IPAddressVar` but will only accept networks (i.e. host-bits cannot be set). Options include: * `min_prefix_length` - Minimum length of the mask (default: none) * `max_prefix_length` - Maximum length of the mask (default: none) diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 4e88edc23..56b7a4560 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -14,7 +14,8 @@ from django.db import transaction from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField from mptt.models import MPTTModel -from ipam.formfields import IPAddressFormField, IPNetworkFormField +from ipam.fields import prefix_validator +from ipam.formfields import IPFormField from utilities.exceptions import AbortTransaction from utilities.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator from .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING @@ -201,14 +202,7 @@ class IPAddressVar(ScriptVariable): """ An IPv4 or IPv6 address. """ - form_field = IPAddressFormField - - -class IPNetworkVar(ScriptVariable): - """ - An IPv4 or IPv6 prefix. - """ - form_field = IPNetworkFormField + form_field = IPFormField def __init__(self, min_prefix_length=None, max_prefix_length=None, *args, **kwargs): super().__init__(*args, **kwargs) @@ -226,6 +220,19 @@ class IPNetworkVar(ScriptVariable): ) +class IPNetworkVar(IPAddressVar): + """ + An IPv4 or IPv6 prefix. + """ + form_field = IPFormField + + def __init__(self, min_prefix_length=None, max_prefix_length=None, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Reject prefixes with any host-bits set + self.field_attrs['validators'].append(prefix_validator) + + # # Scripts # diff --git a/netbox/extras/tests/test_scripts.py b/netbox/extras/tests/test_scripts.py index 64118fa1f..649844fc9 100644 --- a/netbox/extras/tests/test_scripts.py +++ b/netbox/extras/tests/test_scripts.py @@ -1,6 +1,6 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase -from netaddr import IPAddress, IPNetwork +from netaddr import IPNetwork from dcim.models import DeviceRole from extras.scripts import * @@ -198,17 +198,11 @@ class ScriptVariablesTest(TestCase): self.assertFalse(form.is_valid()) self.assertIn('var1', form.errors) - # Subnet masks are not accepted - data = {'var1': '192.0.2.0/24'} - form = TestScript().as_form(data, None) - self.assertFalse(form.is_valid()) - self.assertIn('var1', form.errors) - # Validate valid data - data = {'var1': '192.0.2.0'} + data = {'var1': '192.0.2.1/24'} form = TestScript().as_form(data, None) self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['var1'], IPAddress(data['var1'])) + self.assertEqual(form.cleaned_data['var1'], IPNetwork(data['var1'])) def test_ipnetworkvar(self): diff --git a/netbox/ipam/fields.py b/netbox/ipam/fields.py index da750a09c..845820432 100644 --- a/netbox/ipam/fields.py +++ b/netbox/ipam/fields.py @@ -3,7 +3,7 @@ from django.db import models from netaddr import AddrFormatError, IPNetwork, IPAddress from . import lookups -from .formfields import IPNetworkFormField +from .formfields import IPFormField def prefix_validator(prefix): @@ -40,7 +40,7 @@ class BaseIPField(models.Field): return str(self.to_python(value)) def form_class(self): - return IPNetworkFormField + return IPFormField def formfield(self, **kwargs): defaults = {'form_class': self.form_class()} diff --git a/netbox/ipam/formfields.py b/netbox/ipam/formfields.py index d76351f33..7c03408b9 100644 --- a/netbox/ipam/formfields.py +++ b/netbox/ipam/formfields.py @@ -7,29 +7,7 @@ from netaddr import IPAddress, IPNetwork, AddrFormatError # Form fields # -class IPAddressFormField(forms.Field): - default_error_messages = { - 'invalid': "Enter a valid IPv4 or IPv6 address (without CIDR mask).", - } - - def to_python(self, value): - if not value: - return None - - if isinstance(value, IPAddress): - return value - - # Prevent CIDR masks; an IPAddress doesn't have one - if len(value.split('/')) != 1: - raise ValidationError('CIDR mask (e.g. /24) is not allowed.') - - try: - return IPAddress(value) - except AddrFormatError: - raise ValidationError("Please specify a valid IPv4 or IPv6 address.") - - -class IPNetworkFormField(forms.Field): +class IPFormField(forms.Field): default_error_messages = { 'invalid': "Enter a valid IPv4 or IPv6 address (with CIDR mask).", }