mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Attributed all model ValidationErrors to specific fields (where appropriate)
This commit is contained in:
@ -401,8 +401,11 @@ class Rack(CreatedUpdatedModel, CustomFieldModel):
|
||||
if top_device:
|
||||
min_height = top_device.position + top_device.device_type.u_height - 1
|
||||
if self.u_height < min_height:
|
||||
raise ValidationError("Rack must be at least {}U tall with currently installed devices."
|
||||
.format(min_height))
|
||||
raise ValidationError({
|
||||
'u_height': "Rack must be at least {}U tall to house currently installed devices.".format(
|
||||
min_height
|
||||
)
|
||||
})
|
||||
|
||||
def to_csv(self):
|
||||
return ','.join([
|
||||
@ -596,27 +599,39 @@ class DeviceType(models.Model):
|
||||
u_available = d.rack.get_available_units(u_height=self.u_height, rack_face=face_required,
|
||||
exclude=[d.pk])
|
||||
if d.position not in u_available:
|
||||
raise ValidationError("Device {} in rack {} does not have sufficient space to accommodate a height "
|
||||
"of {}U".format(d, d.rack, self.u_height))
|
||||
raise ValidationError({
|
||||
'u_height': "Device {} in rack {} does not have sufficient space to accommodate a height of "
|
||||
"{}U".format(d, d.rack, self.u_height)
|
||||
})
|
||||
|
||||
if not self.is_console_server and self.cs_port_templates.count():
|
||||
raise ValidationError("Must delete all console server port templates associated with this device before "
|
||||
"declassifying it as a console server.")
|
||||
raise ValidationError({
|
||||
'is_console_server': "Must delete all console server port templates associated with this device before "
|
||||
"declassifying it as a console server."
|
||||
})
|
||||
|
||||
if not self.is_pdu and self.power_outlet_templates.count():
|
||||
raise ValidationError("Must delete all power outlet templates associated with this device before "
|
||||
"declassifying it as a PDU.")
|
||||
raise ValidationError({
|
||||
'is_pdu': "Must delete all power outlet templates associated with this device before declassifying it "
|
||||
"as a PDU."
|
||||
})
|
||||
|
||||
if not self.is_network_device and self.interface_templates.filter(mgmt_only=False).count():
|
||||
raise ValidationError("Must delete all non-management-only interface templates associated with this device "
|
||||
"before declassifying it as a network device.")
|
||||
raise ValidationError({
|
||||
'is_network_device': "Must delete all non-management-only interface templates associated with this "
|
||||
"device before declassifying it as a network device."
|
||||
})
|
||||
|
||||
if self.subdevice_role != SUBDEVICE_ROLE_PARENT and self.device_bay_templates.count():
|
||||
raise ValidationError("Must delete all device bay templates associated with this device before "
|
||||
"declassifying it as a parent device.")
|
||||
raise ValidationError({
|
||||
'subdevice_role': "Must delete all device bay templates associated with this device before "
|
||||
"declassifying it as a parent device."
|
||||
})
|
||||
|
||||
if self.u_height and self.subdevice_role == SUBDEVICE_ROLE_CHILD:
|
||||
raise ValidationError("Child device types must be 0U.")
|
||||
raise ValidationError({
|
||||
'u_height': "Child device types must be 0U."
|
||||
})
|
||||
|
||||
@property
|
||||
def is_parent_device(self):
|
||||
@ -824,17 +839,25 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Validate device type assignment
|
||||
if not hasattr(self, 'device_type'):
|
||||
raise ValidationError("Must specify device type.")
|
||||
|
||||
# Child devices cannot be assigned to a rack face/unit
|
||||
if self.device_type.is_child_device and (self.face is not None or self.position):
|
||||
raise ValidationError("Child device types cannot be assigned a rack face or position.")
|
||||
|
||||
# Validate position/face combination
|
||||
if self.position and self.face is None:
|
||||
raise ValidationError("Must specify rack face with rack position.")
|
||||
raise ValidationError({
|
||||
'face': "Must specify rack face when defining rack position."
|
||||
})
|
||||
|
||||
if self.device_type:
|
||||
|
||||
# Child devices cannot be assigned to a rack face/unit
|
||||
if self.device_type.is_child_device and self.face is not None:
|
||||
raise ValidationError({
|
||||
'face': "Child device types cannot be assigned to a rack face. This is an attribute of the parent "
|
||||
"device."
|
||||
})
|
||||
if self.device_type.is_child_device and self.position:
|
||||
raise ValidationError({
|
||||
'position': "Child device types cannot be assigned to a rack position. This is an attribute of the "
|
||||
"parent device."
|
||||
})
|
||||
|
||||
# Validate rack space
|
||||
rack_face = self.face if not self.device_type.is_full_depth else None
|
||||
@ -843,8 +866,10 @@ class Device(CreatedUpdatedModel, CustomFieldModel):
|
||||
available_units = self.rack.get_available_units(u_height=self.device_type.u_height, rack_face=rack_face,
|
||||
exclude=exclude_list)
|
||||
if self.position and self.position not in available_units:
|
||||
raise ValidationError("U{} is already occupied or does not have sufficient space to accommodate a(n) "
|
||||
"{} ({}U).".format(self.position, self.device_type, self.device_type.u_height))
|
||||
raise ValidationError({
|
||||
'position': "U{} is already occupied or does not have sufficient space to accommodate a(n) {} "
|
||||
"({}U).".format(self.position, self.device_type, self.device_type.u_height)
|
||||
})
|
||||
except Rack.DoesNotExist:
|
||||
pass
|
||||
|
||||
@ -1094,9 +1119,10 @@ class Interface(models.Model):
|
||||
def clean(self):
|
||||
|
||||
if self.form_factor == IFACE_FF_VIRTUAL and self.is_connected:
|
||||
raise ValidationError({'form_factor': "Virtual interfaces cannot be connected to another interface or "
|
||||
"circuit. Disconnect the interface or choose a physical form "
|
||||
"factor."})
|
||||
raise ValidationError({
|
||||
'form_factor': "Virtual interfaces cannot be connected to another interface or circuit. Disconnect the "
|
||||
"interface or choose a physical form factor."
|
||||
})
|
||||
|
||||
@property
|
||||
def is_physical(self):
|
||||
@ -1147,7 +1173,9 @@ class InterfaceConnection(models.Model):
|
||||
|
||||
def clean(self):
|
||||
if self.interface_a == self.interface_b:
|
||||
raise ValidationError("Cannot connect an interface to itself")
|
||||
raise ValidationError({
|
||||
'interface_b': "Cannot connect an interface to itself."
|
||||
})
|
||||
|
||||
# Used for connections export
|
||||
def to_csv(self):
|
||||
@ -1180,8 +1208,9 @@ class DeviceBay(models.Model):
|
||||
|
||||
# Validate that the parent Device can have DeviceBays
|
||||
if not self.device.device_type.is_parent_device:
|
||||
raise ValidationError("This type of device ({}) does not support device bays."
|
||||
.format(self.device.device_type))
|
||||
raise ValidationError("This type of device ({}) does not support device bays.".format(
|
||||
self.device.device_type
|
||||
))
|
||||
|
||||
# Cannot install a device into itself, obviously
|
||||
if self.device == self.installed_device:
|
||||
|
@ -172,16 +172,6 @@ class PrefixForm(BootstrapMixin, CustomFieldForm):
|
||||
else:
|
||||
self.fields['vlan'].choices = []
|
||||
|
||||
def clean_prefix(self):
|
||||
prefix = self.cleaned_data['prefix']
|
||||
if prefix.version == 4 and prefix.prefixlen == 32:
|
||||
raise forms.ValidationError("Cannot create host addresses (/32) as prefixes. These should be IPv4 "
|
||||
"addresses instead.")
|
||||
elif prefix.version == 6 and prefix.prefixlen == 128:
|
||||
raise forms.ValidationError("Cannot create host addresses (/128) as prefixes. These should be IPv6 "
|
||||
"addresses instead.")
|
||||
return prefix
|
||||
|
||||
|
||||
class PrefixFromCSVForm(forms.ModelForm):
|
||||
vrf = forms.ModelChoiceField(queryset=VRF.objects.all(), required=False, to_field_name='rd',
|
||||
|
@ -139,16 +139,22 @@ class Aggregate(CreatedUpdatedModel, CustomFieldModel):
|
||||
if self.pk:
|
||||
covering_aggregates = covering_aggregates.exclude(pk=self.pk)
|
||||
if covering_aggregates:
|
||||
raise ValidationError("{} is already covered by an existing aggregate ({})"
|
||||
.format(self.prefix, covering_aggregates[0]))
|
||||
raise ValidationError({
|
||||
'prefix': "Aggregates cannot overlap. {} is already covered by an existing aggregate ({}).".format(
|
||||
self.prefix, covering_aggregates[0]
|
||||
)
|
||||
})
|
||||
|
||||
# Ensure that the aggregate being added does not cover an existing aggregate
|
||||
covered_aggregates = Aggregate.objects.filter(prefix__net_contained=str(self.prefix))
|
||||
if self.pk:
|
||||
covered_aggregates = covered_aggregates.exclude(pk=self.pk)
|
||||
if covered_aggregates:
|
||||
raise ValidationError("{} overlaps with an existing aggregate ({})"
|
||||
.format(self.prefix, covered_aggregates[0]))
|
||||
raise ValidationError({
|
||||
'prefix': "Aggregates cannot overlap. {} covers an existing aggregate ({}).".format(
|
||||
self.prefix, covered_aggregates[0]
|
||||
)
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.prefix:
|
||||
@ -268,14 +274,17 @@ class Prefix(CreatedUpdatedModel, CustomFieldModel):
|
||||
return reverse('ipam:prefix', args=[self.pk])
|
||||
|
||||
def clean(self):
|
||||
|
||||
# Disallow host masks
|
||||
if self.prefix:
|
||||
if self.prefix.version == 4 and self.prefix.prefixlen == 32:
|
||||
raise ValidationError("Cannot create host addresses (/32) as prefixes. These should be IPv4 addresses "
|
||||
"instead.")
|
||||
raise ValidationError({
|
||||
'prefix': "Cannot create host addresses (/32) as prefixes. Create an IPv4 address instead."
|
||||
})
|
||||
elif self.prefix.version == 6 and self.prefix.prefixlen == 128:
|
||||
raise ValidationError("Cannot create host addresses (/128) as prefixes. These should be IPv6 addresses "
|
||||
"instead.")
|
||||
raise ValidationError({
|
||||
'prefix': "Cannot create host addresses (/128) as prefixes. Create an IPv6 address instead."
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.prefix:
|
||||
@ -369,13 +378,16 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
|
||||
duplicate_ips = IPAddress.objects.filter(vrf=self.vrf, address__net_host=str(self.address.ip))\
|
||||
.exclude(pk=self.pk)
|
||||
if duplicate_ips:
|
||||
raise ValidationError("Duplicate IP address found in VRF {}: {}".format(self.vrf,
|
||||
duplicate_ips.first()))
|
||||
raise ValidationError({
|
||||
'address': "Duplicate IP address found in VRF {}: {}".format(self.vrf, duplicate_ips.first())
|
||||
})
|
||||
elif not self.vrf and settings.ENFORCE_GLOBAL_UNIQUE:
|
||||
duplicate_ips = IPAddress.objects.filter(vrf=None, address__net_host=str(self.address.ip))\
|
||||
.exclude(pk=self.pk)
|
||||
if duplicate_ips:
|
||||
raise ValidationError("Duplicate IP address found in global table: {}".format(duplicate_ips.first()))
|
||||
raise ValidationError({
|
||||
'address': "Duplicate IP address found in global table: {}".format(duplicate_ips.first())
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.address:
|
||||
@ -478,7 +490,9 @@ class VLAN(CreatedUpdatedModel, CustomFieldModel):
|
||||
|
||||
# Validate VLAN group
|
||||
if self.group and self.group.site != self.site:
|
||||
raise ValidationError("VLAN group must belong to the assigned site ({}).".format(self.site))
|
||||
raise ValidationError({
|
||||
'group': "VLAN group must belong to the assigned site ({}).".format(self.site)
|
||||
})
|
||||
|
||||
def to_csv(self):
|
||||
return ','.join([
|
||||
|
@ -57,14 +57,14 @@ class SecretForm(forms.ModelForm, BootstrapMixin):
|
||||
fields = ['role', 'name', 'plaintext', 'plaintext2']
|
||||
|
||||
def clean(self):
|
||||
|
||||
if self.cleaned_data['plaintext']:
|
||||
validate_rsa_key(self.cleaned_data['private_key'])
|
||||
|
||||
def clean_plaintext2(self):
|
||||
plaintext = self.cleaned_data['plaintext']
|
||||
plaintext2 = self.cleaned_data['plaintext2']
|
||||
if plaintext != plaintext2:
|
||||
raise forms.ValidationError("The two given plaintext values do not match. Please check your input.")
|
||||
if self.cleaned_data['plaintext'] != self.cleaned_data['plaintext2']:
|
||||
raise forms.ValidationError({
|
||||
'plaintext2': "The two given plaintext values do not match. Please check your input."
|
||||
})
|
||||
|
||||
|
||||
class SecretFromCSVForm(forms.ModelForm):
|
||||
|
@ -81,24 +81,34 @@ class UserKey(CreatedUpdatedModel):
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
|
||||
# Validate the public key format and length.
|
||||
if self.public_key:
|
||||
|
||||
# Validate the public key format
|
||||
try:
|
||||
pubkey = RSA.importKey(self.public_key)
|
||||
except ValueError:
|
||||
raise ValidationError("Invalid RSA key format.")
|
||||
raise ValidationError({
|
||||
'public_key': "Invalid RSA key format."
|
||||
})
|
||||
except:
|
||||
raise ValidationError("Something went wrong while trying to save your key. Please ensure that you're "
|
||||
"uploading a valid RSA public key in PEM format (no SSH/PGP).")
|
||||
# key.size() returns 1 less than the key modulus
|
||||
pubkey_length = pubkey.size() + 1
|
||||
|
||||
# Validate the public key length
|
||||
pubkey_length = pubkey.size() + 1 # key.size() returns 1 less than the key modulus
|
||||
if pubkey_length < settings.SECRETS_MIN_PUBKEY_SIZE:
|
||||
raise ValidationError("Insufficient key length. Keys must be at least {} bits long."
|
||||
.format(settings.SECRETS_MIN_PUBKEY_SIZE))
|
||||
raise ValidationError({
|
||||
'public_key': "Insufficient key length. Keys must be at least {} bits long.".format(
|
||||
settings.SECRETS_MIN_PUBKEY_SIZE
|
||||
)
|
||||
})
|
||||
# We can't use keys bigger than our master_key_cipher field can hold
|
||||
if pubkey_length > 4096:
|
||||
raise ValidationError("Public key size ({}) is too large. Maximum key size is 4096 bits."
|
||||
.format(pubkey_length))
|
||||
raise ValidationError({
|
||||
'public_key': "Public key size ({}) is too large. Maximum key size is 4096 bits.".format(
|
||||
pubkey_length
|
||||
)
|
||||
})
|
||||
|
||||
super(UserKey, self).clean()
|
||||
|
||||
|
Reference in New Issue
Block a user