Преглед на файлове

Introduce IPAddressVar and IPAddressWithMaskVar

Jeremy Stretch преди 6 години
родител
ревизия
f41564b578
променени са 4 файла, в които са добавени 69 реда и са изтрити 17 реда
  1. 13 2
      docs/additional-features/custom-scripts.md
  2. 32 11
      netbox/extras/scripts.py
  3. 2 2
      netbox/ipam/fields.py
  4. 22 2
      netbox/ipam/formfields.py

+ 13 - 2
docs/additional-features/custom-scripts.md

@@ -124,7 +124,7 @@ Arbitrary text of any length. Renders as multi-line text input field.
 
 
 Stored a numeric integer. Options include:
 Stored a numeric integer. Options include:
 
 
-* `min_value:` - Minimum value
+* `min_value` - Minimum value
 * `max_value` - Maximum value
 * `max_value` - Maximum value
 
 
 ### BooleanVar
 ### BooleanVar
@@ -158,9 +158,20 @@ A NetBox object. The list of available objects is defined by the queryset parame
 
 
 An uploaded file. Note that uploaded files are present in memory only for the duration of the script's execution: They will not be save for future use.
 An uploaded file. Note that uploaded files are present in memory only for the duration of the script's execution: They will not be save for future use.
 
 
+### IPAddressVar
+
+An IPv4 or IPv6 address, without a mask. Returns a `netaddr.IPAddress` object.
+
+### IPAddressWithMaskVar
+
+An IPv4 or IPv6 address with a mask. Returns a `netaddr.IPNetwork` object which includes the mask.
+
 ### IPNetworkVar
 ### IPNetworkVar
 
 
-An IPv4 or IPv6 network with a mask.
+An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two attributes are available to validate the provided mask:
+
+* `min_prefix_length` - Minimum length of the mask (default: none)
+* `max_prefix_length` - Maximum length of the mask (default: none)
 
 
 ### Default Options
 ### Default Options
 
 

+ 32 - 11
netbox/extras/scripts.py

@@ -14,10 +14,10 @@ from django.db import transaction
 from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
 from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
 from mptt.models import MPTTModel
 from mptt.models import MPTTModel
 
 
-from ipam.formfields import IPFormField
-from utilities.exceptions import AbortTransaction
-from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator
+from ipam.formfields import IPAddressFormField, IPNetworkFormField
+from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator
 from .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING
 from .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING
+from utilities.exceptions import AbortTransaction
 from .forms import ScriptForm
 from .forms import ScriptForm
 from .signals import purge_changelog
 from .signals import purge_changelog
 
 
@@ -27,6 +27,8 @@ __all__ = [
     'ChoiceVar',
     'ChoiceVar',
     'FileVar',
     'FileVar',
     'IntegerVar',
     'IntegerVar',
+    'IPAddressVar',
+    'IPAddressWithMaskVar',
     'IPNetworkVar',
     'IPNetworkVar',
     'MultiObjectVar',
     'MultiObjectVar',
     'ObjectVar',
     'ObjectVar',
@@ -48,15 +50,19 @@ class ScriptVariable:
 
 
     def __init__(self, label='', description='', default=None, required=True):
     def __init__(self, label='', description='', default=None, required=True):
 
 
-        # Default field attributes
-        self.field_attrs = {
-            'help_text': description,
-            'required': required
-        }
+        # Initialize field attributes
+        if not hasattr(self, 'field_attrs'):
+            self.field_attrs = {}
+        if description:
+            self.field_attrs['help_text'] = description
         if label:
         if label:
             self.field_attrs['label'] = label
             self.field_attrs['label'] = label
         if default:
         if default:
             self.field_attrs['initial'] = default
             self.field_attrs['initial'] = default
+        if required:
+            self.field_attrs['required'] = True
+        if 'validators' not in self.field_attrs:
+            self.field_attrs['validators'] = []
 
 
     def as_field(self):
     def as_field(self):
         """
         """
@@ -196,17 +202,32 @@ class FileVar(ScriptVariable):
     form_field = forms.FileField
     form_field = forms.FileField
 
 
 
 
+class IPAddressVar(ScriptVariable):
+    """
+    An IPv4 or IPv6 address without a mask.
+    """
+    form_field = IPAddressFormField
+
+
+class IPAddressWithMaskVar(ScriptVariable):
+    """
+    An IPv4 or IPv6 address with a mask.
+    """
+    form_field = IPNetworkFormField
+
+
 class IPNetworkVar(ScriptVariable):
 class IPNetworkVar(ScriptVariable):
     """
     """
     An IPv4 or IPv6 prefix.
     An IPv4 or IPv6 prefix.
     """
     """
-    form_field = IPFormField
+    form_field = IPNetworkFormField
+    field_attrs = {
+        'validators': [prefix_validator]
+    }
 
 
     def __init__(self, min_prefix_length=None, max_prefix_length=None, *args, **kwargs):
     def __init__(self, min_prefix_length=None, max_prefix_length=None, *args, **kwargs):
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
 
 
-        self.field_attrs['validators'] = list()
-
         # Optional minimum/maximum prefix lengths
         # Optional minimum/maximum prefix lengths
         if min_prefix_length is not None:
         if min_prefix_length is not None:
             self.field_attrs['validators'].append(
             self.field_attrs['validators'].append(

+ 2 - 2
netbox/ipam/fields.py

@@ -3,7 +3,7 @@ from django.db import models
 from netaddr import AddrFormatError, IPNetwork
 from netaddr import AddrFormatError, IPNetwork
 
 
 from . import lookups, validators
 from . import lookups, validators
-from .formfields import IPFormField
+from .formfields import IPNetworkFormField
 
 
 
 
 class BaseIPField(models.Field):
 class BaseIPField(models.Field):
@@ -33,7 +33,7 @@ class BaseIPField(models.Field):
         return str(self.to_python(value))
         return str(self.to_python(value))
 
 
     def form_class(self):
     def form_class(self):
-        return IPFormField
+        return IPNetworkFormField
 
 
     def formfield(self, **kwargs):
     def formfield(self, **kwargs):
         defaults = {'form_class': self.form_class()}
         defaults = {'form_class': self.form_class()}

+ 22 - 2
netbox/ipam/formfields.py

@@ -1,13 +1,33 @@
 from django import forms
 from django import forms
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
-from netaddr import IPNetwork, AddrFormatError
+from netaddr import IPAddress, IPNetwork, AddrFormatError
 
 
 
 
 #
 #
 # Form fields
 # Form fields
 #
 #
 
 
-class IPFormField(forms.Field):
+class IPAddressFormField(forms.Field):
+    default_error_messages = {
+        'invalid': "Enter a valid IPv4 or IPv6 address (without a mask).",
+    }
+
+    def to_python(self, value):
+        if not value:
+            return None
+
+        if isinstance(value, IPAddress):
+            return value
+
+        try:
+            return IPAddress(value)
+        except ValueError:
+            raise ValidationError('This field requires an IP address without a mask.')
+        except AddrFormatError:
+            raise ValidationError("Please specify a valid IPv4 or IPv6 address.")
+
+
+class IPNetworkFormField(forms.Field):
     default_error_messages = {
     default_error_messages = {
         'invalid': "Enter a valid IPv4 or IPv6 address (with CIDR mask).",
         'invalid': "Enter a valid IPv4 or IPv6 address (with CIDR mask).",
     }
     }