mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-25 08:46:10 -06:00
13132 add gettext_lazy to models
This commit is contained in:
parent
2eb2703219
commit
54c610130c
@ -7,7 +7,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
@ -52,6 +52,7 @@ class ComponentModel(NetBoxModel):
|
|||||||
related_name='%(class)ss'
|
related_name='%(class)ss'
|
||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
|
verbose_name=_('name'),
|
||||||
max_length=64
|
max_length=64
|
||||||
)
|
)
|
||||||
_name = NaturalOrderingField(
|
_name = NaturalOrderingField(
|
||||||
@ -60,11 +61,13 @@ class ComponentModel(NetBoxModel):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
|
verbose_name=_('label'),
|
||||||
max_length=64,
|
max_length=64,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("Physical label")
|
help_text=_('Physical label')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
|
verbose_name=_('description'),
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
@ -101,7 +104,7 @@ class ComponentModel(NetBoxModel):
|
|||||||
# Check list of Modules that allow device field to be changed
|
# Check list of Modules that allow device field to be changed
|
||||||
if (type(self) not in [InventoryItem]) and (self.pk is not None) and (self._original_device != self.device_id):
|
if (type(self) not in [InventoryItem]) and (self.pk is not None) and (self._original_device != self.device_id):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"device": "Components cannot be moved to a different device."
|
"device": _("Components cannot be moved to a different device.")
|
||||||
})
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -140,13 +143,15 @@ class CabledObjectModel(models.Model):
|
|||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
cable_end = models.CharField(
|
cable_end = models.CharField(
|
||||||
|
verbose_name=_('cable end'),
|
||||||
max_length=1,
|
max_length=1,
|
||||||
blank=True,
|
blank=True,
|
||||||
choices=CableEndChoices
|
choices=CableEndChoices
|
||||||
)
|
)
|
||||||
mark_connected = models.BooleanField(
|
mark_connected = models.BooleanField(
|
||||||
|
verbose_name=_('mark connected'),
|
||||||
default=False,
|
default=False,
|
||||||
help_text=_("Treat as if a cable is connected")
|
help_text=_('Treat as if a cable is connected')
|
||||||
)
|
)
|
||||||
|
|
||||||
cable_terminations = GenericRelation(
|
cable_terminations = GenericRelation(
|
||||||
@ -164,15 +169,15 @@ class CabledObjectModel(models.Model):
|
|||||||
|
|
||||||
if self.cable and not self.cable_end:
|
if self.cable and not self.cable_end:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"cable_end": "Must specify cable end (A or B) when attaching a cable."
|
"cable_end": _("Must specify cable end (A or B) when attaching a cable.")
|
||||||
})
|
})
|
||||||
if self.cable_end and not self.cable:
|
if self.cable_end and not self.cable:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"cable_end": "Cable end must not be set without a cable."
|
"cable_end": _("Cable end must not be set without a cable.")
|
||||||
})
|
})
|
||||||
if self.mark_connected and self.cable:
|
if self.mark_connected and self.cable:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"mark_connected": "Cannot mark as connected with a cable attached."
|
"mark_connected": _("Cannot mark as connected with a cable attached.")
|
||||||
})
|
})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -195,7 +200,7 @@ class CabledObjectModel(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def parent_object(self):
|
def parent_object(self):
|
||||||
raise NotImplementedError(f"{self.__class__.__name__} models must declare a parent_object property")
|
raise NotImplementedError(_("{class_name} models must declare a parent_object property").format(class_name=self.__class__.__name__))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def opposite_cable_end(self):
|
def opposite_cable_end(self):
|
||||||
@ -275,12 +280,14 @@ class ConsolePort(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki
|
|||||||
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
|
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
|
||||||
"""
|
"""
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_('Physical port type')
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
speed = models.PositiveIntegerField(
|
speed = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('speed'),
|
||||||
choices=ConsolePortSpeedChoices,
|
choices=ConsolePortSpeedChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -298,12 +305,14 @@ class ConsoleServerPort(ModularComponentModel, CabledObjectModel, PathEndpoint,
|
|||||||
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
|
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
|
||||||
"""
|
"""
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_('Physical port type')
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
speed = models.PositiveIntegerField(
|
speed = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('speed'),
|
||||||
choices=ConsolePortSpeedChoices,
|
choices=ConsolePortSpeedChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -325,22 +334,25 @@ class PowerPort(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracking
|
|||||||
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
|
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
|
||||||
"""
|
"""
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_('Physical port type')
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
maximum_draw = models.PositiveIntegerField(
|
maximum_draw = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('maximum draw'),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text=_("Maximum power draw (watts)")
|
help_text=_("Maximum power draw (watts)")
|
||||||
)
|
)
|
||||||
allocated_draw = models.PositiveIntegerField(
|
allocated_draw = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('allocated draw'),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text=_("Allocated power draw (watts)")
|
help_text=_('Allocated power draw (watts)')
|
||||||
)
|
)
|
||||||
|
|
||||||
clone_fields = ('device', 'module', 'maximum_draw', 'allocated_draw')
|
clone_fields = ('device', 'module', 'maximum_draw', 'allocated_draw')
|
||||||
@ -354,7 +366,7 @@ class PowerPort(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracking
|
|||||||
if self.maximum_draw is not None and self.allocated_draw is not None:
|
if self.maximum_draw is not None and self.allocated_draw is not None:
|
||||||
if self.allocated_draw > self.maximum_draw:
|
if self.allocated_draw > self.maximum_draw:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'allocated_draw': f"Allocated draw cannot exceed the maximum draw ({self.maximum_draw}W)."
|
'allocated_draw': _("Allocated draw cannot exceed the maximum draw ({maximum_draw}W).").format(maximum_draw=self.maximum_draw)
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_downstream_powerports(self, leg=None):
|
def get_downstream_powerports(self, leg=None):
|
||||||
@ -434,6 +446,7 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki
|
|||||||
A physical power outlet (output) within a Device which provides power to a PowerPort.
|
A physical power outlet (output) within a Device which provides power to a PowerPort.
|
||||||
"""
|
"""
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -447,10 +460,11 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki
|
|||||||
related_name='poweroutlets'
|
related_name='poweroutlets'
|
||||||
)
|
)
|
||||||
feed_leg = models.CharField(
|
feed_leg = models.CharField(
|
||||||
|
verbose_name=_('feed leg'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletFeedLegChoices,
|
choices=PowerOutletFeedLegChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("Phase (for three-phase feeds)")
|
help_text=_('Phase (for three-phase feeds)')
|
||||||
)
|
)
|
||||||
|
|
||||||
clone_fields = ('device', 'module', 'type', 'power_port', 'feed_leg')
|
clone_fields = ('device', 'module', 'type', 'power_port', 'feed_leg')
|
||||||
@ -463,7 +477,7 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki
|
|||||||
|
|
||||||
# Validate power port assignment
|
# Validate power port assignment
|
||||||
if self.power_port and self.power_port.device != self.device:
|
if self.power_port and self.power_port.device != self.device:
|
||||||
raise ValidationError(f"Parent power port ({self.power_port}) must belong to the same device")
|
raise ValidationError(_("Parent power port ({power_port}) must belong to the same device").format(power_port=self.power_port))
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -475,12 +489,13 @@ class BaseInterface(models.Model):
|
|||||||
Abstract base class for fields shared by dcim.Interface and virtualization.VMInterface.
|
Abstract base class for fields shared by dcim.Interface and virtualization.VMInterface.
|
||||||
"""
|
"""
|
||||||
enabled = models.BooleanField(
|
enabled = models.BooleanField(
|
||||||
|
verbose_name=_('enabled'),
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
mac_address = MACAddressField(
|
mac_address = MACAddressField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='MAC Address'
|
verbose_name=_('MAC Address')
|
||||||
)
|
)
|
||||||
mtu = models.PositiveIntegerField(
|
mtu = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -489,13 +504,14 @@ class BaseInterface(models.Model):
|
|||||||
MinValueValidator(INTERFACE_MTU_MIN),
|
MinValueValidator(INTERFACE_MTU_MIN),
|
||||||
MaxValueValidator(INTERFACE_MTU_MAX)
|
MaxValueValidator(INTERFACE_MTU_MAX)
|
||||||
],
|
],
|
||||||
verbose_name='MTU'
|
verbose_name=_('MTU')
|
||||||
)
|
)
|
||||||
mode = models.CharField(
|
mode = models.CharField(
|
||||||
|
verbose_name=_('mode'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfaceModeChoices,
|
choices=InterfaceModeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("IEEE 802.1Q tagging strategy")
|
help_text=_('IEEE 802.1Q tagging strategy')
|
||||||
)
|
)
|
||||||
parent = models.ForeignKey(
|
parent = models.ForeignKey(
|
||||||
to='self',
|
to='self',
|
||||||
@ -503,7 +519,7 @@ class BaseInterface(models.Model):
|
|||||||
related_name='child_interfaces',
|
related_name='child_interfaces',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Parent interface'
|
verbose_name=_('Parent interface')
|
||||||
)
|
)
|
||||||
bridge = models.ForeignKey(
|
bridge = models.ForeignKey(
|
||||||
to='self',
|
to='self',
|
||||||
@ -511,7 +527,7 @@ class BaseInterface(models.Model):
|
|||||||
related_name='bridge_interfaces',
|
related_name='bridge_interfaces',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Bridge interface'
|
verbose_name=_('Bridge interface')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -559,23 +575,25 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
related_name='member_interfaces',
|
related_name='member_interfaces',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Parent LAG'
|
verbose_name=_('Parent LAG')
|
||||||
)
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfaceTypeChoices
|
choices=InterfaceTypeChoices
|
||||||
)
|
)
|
||||||
mgmt_only = models.BooleanField(
|
mgmt_only = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name='Management only',
|
verbose_name=_('Management only'),
|
||||||
help_text=_('This interface is used only for out-of-band management')
|
help_text=_('This interface is used only for out-of-band management')
|
||||||
)
|
)
|
||||||
speed = models.PositiveIntegerField(
|
speed = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Speed (Kbps)'
|
verbose_name=_('Speed (Kbps)')
|
||||||
)
|
)
|
||||||
duplex = models.CharField(
|
duplex = models.CharField(
|
||||||
|
verbose_name=_('duplex'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
@ -584,27 +602,27 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
wwn = WWNField(
|
wwn = WWNField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='WWN',
|
verbose_name=_('WWN'),
|
||||||
help_text=_('64-bit World Wide Name')
|
help_text=_('64-bit World Wide Name')
|
||||||
)
|
)
|
||||||
rf_role = models.CharField(
|
rf_role = models.CharField(
|
||||||
max_length=30,
|
max_length=30,
|
||||||
choices=WirelessRoleChoices,
|
choices=WirelessRoleChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Wireless role'
|
verbose_name=_('Wireless role')
|
||||||
)
|
)
|
||||||
rf_channel = models.CharField(
|
rf_channel = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=WirelessChannelChoices,
|
choices=WirelessChannelChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Wireless channel'
|
verbose_name=_('Wireless channel')
|
||||||
)
|
)
|
||||||
rf_channel_frequency = models.DecimalField(
|
rf_channel_frequency = models.DecimalField(
|
||||||
max_digits=7,
|
max_digits=7,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Channel frequency (MHz)',
|
verbose_name=_('Channel frequency (MHz)'),
|
||||||
help_text=_("Populated by selected channel (if set)")
|
help_text=_("Populated by selected channel (if set)")
|
||||||
)
|
)
|
||||||
rf_channel_width = models.DecimalField(
|
rf_channel_width = models.DecimalField(
|
||||||
@ -612,26 +630,26 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
decimal_places=3,
|
decimal_places=3,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Channel width (MHz)',
|
verbose_name=('Channel width (MHz)'),
|
||||||
help_text=_("Populated by selected channel (if set)")
|
help_text=_("Populated by selected channel (if set)")
|
||||||
)
|
)
|
||||||
tx_power = models.PositiveSmallIntegerField(
|
tx_power = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=(MaxValueValidator(127),),
|
validators=(MaxValueValidator(127),),
|
||||||
verbose_name='Transmit power (dBm)'
|
verbose_name=_('Transmit power (dBm)')
|
||||||
)
|
)
|
||||||
poe_mode = models.CharField(
|
poe_mode = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfacePoEModeChoices,
|
choices=InterfacePoEModeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='PoE mode'
|
verbose_name=_('PoE mode')
|
||||||
)
|
)
|
||||||
poe_type = models.CharField(
|
poe_type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=InterfacePoETypeChoices,
|
choices=InterfacePoETypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='PoE type'
|
verbose_name=_('PoE type')
|
||||||
)
|
)
|
||||||
wireless_link = models.ForeignKey(
|
wireless_link = models.ForeignKey(
|
||||||
to='wireless.WirelessLink',
|
to='wireless.WirelessLink',
|
||||||
@ -644,7 +662,7 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
to='wireless.WirelessLAN',
|
to='wireless.WirelessLAN',
|
||||||
related_name='interfaces',
|
related_name='interfaces',
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Wireless LANs'
|
verbose_name=_('Wireless LANs')
|
||||||
)
|
)
|
||||||
untagged_vlan = models.ForeignKey(
|
untagged_vlan = models.ForeignKey(
|
||||||
to='ipam.VLAN',
|
to='ipam.VLAN',
|
||||||
@ -652,13 +670,13 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
related_name='interfaces_as_untagged',
|
related_name='interfaces_as_untagged',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Untagged VLAN'
|
verbose_name=_('Untagged VLAN')
|
||||||
)
|
)
|
||||||
tagged_vlans = models.ManyToManyField(
|
tagged_vlans = models.ManyToManyField(
|
||||||
to='ipam.VLAN',
|
to='ipam.VLAN',
|
||||||
related_name='interfaces_as_tagged',
|
related_name='interfaces_as_tagged',
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Tagged VLANs'
|
verbose_name=_('Tagged VLANs')
|
||||||
)
|
)
|
||||||
vrf = models.ForeignKey(
|
vrf = models.ForeignKey(
|
||||||
to='ipam.VRF',
|
to='ipam.VRF',
|
||||||
@ -666,7 +684,7 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
related_name='interfaces',
|
related_name='interfaces',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='VRF'
|
verbose_name=_('VRF')
|
||||||
)
|
)
|
||||||
ip_addresses = GenericRelation(
|
ip_addresses = GenericRelation(
|
||||||
to='ipam.IPAddress',
|
to='ipam.IPAddress',
|
||||||
@ -704,77 +722,84 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
# Virtual Interfaces cannot have a Cable attached
|
# Virtual Interfaces cannot have a Cable attached
|
||||||
if self.is_virtual and self.cable:
|
if self.is_virtual and self.cable:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'type': f"{self.get_type_display()} interfaces cannot have a cable attached."
|
'type': _("{display_type} interfaces cannot have a cable attached.").format(display_type=self.get_type_display())
|
||||||
})
|
})
|
||||||
|
|
||||||
# Virtual Interfaces cannot be marked as connected
|
# Virtual Interfaces cannot be marked as connected
|
||||||
if self.is_virtual and self.mark_connected:
|
if self.is_virtual and self.mark_connected:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'mark_connected': f"{self.get_type_display()} interfaces cannot be marked as connected."
|
'mark_connected': _("{display_type} interfaces cannot be marked as connected.".format(display_type=self.get_type_display()))
|
||||||
})
|
})
|
||||||
|
|
||||||
# Parent validation
|
# Parent validation
|
||||||
|
|
||||||
# An interface cannot be its own parent
|
# An interface cannot be its own parent
|
||||||
if self.pk and self.parent_id == self.pk:
|
if self.pk and self.parent_id == self.pk:
|
||||||
raise ValidationError({'parent': "An interface cannot be its own parent."})
|
raise ValidationError({'parent': _("An interface cannot be its own parent.")})
|
||||||
|
|
||||||
# A physical interface cannot have a parent interface
|
# A physical interface cannot have a parent interface
|
||||||
if self.type != InterfaceTypeChoices.TYPE_VIRTUAL and self.parent is not None:
|
if self.type != InterfaceTypeChoices.TYPE_VIRTUAL and self.parent is not None:
|
||||||
raise ValidationError({'parent': "Only virtual interfaces may be assigned to a parent interface."})
|
raise ValidationError({'parent': _("Only virtual interfaces may be assigned to a parent interface.")})
|
||||||
|
|
||||||
# An interface's parent must belong to the same device or virtual chassis
|
# An interface's parent must belong to the same device or virtual chassis
|
||||||
if self.parent and self.parent.device != self.device:
|
if self.parent and self.parent.device != self.device:
|
||||||
if self.device.virtual_chassis is None:
|
if self.device.virtual_chassis is None:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'parent': f"The selected parent interface ({self.parent}) belongs to a different device "
|
'parent': _("The selected parent interface ({selected_parent}) belongs to a different device ({parent_device})").format(
|
||||||
f"({self.parent.device})."
|
selected_parent=self.parent, parent_device=self.parent.device)
|
||||||
})
|
})
|
||||||
elif self.parent.device.virtual_chassis != self.parent.virtual_chassis:
|
elif self.parent.device.virtual_chassis != self.parent.virtual_chassis:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'parent': f"The selected parent interface ({self.parent}) belongs to {self.parent.device}, which "
|
'parent': _("""
|
||||||
f"is not part of virtual chassis {self.device.virtual_chassis}."
|
The selected parent interface ({parent}) belongs to {parent_device}, which
|
||||||
|
is not part of virtual chassis {virtual_chassis}.
|
||||||
|
""").format(parent=self.parent, parent_device=self.parent_device, virtual_chassis=self.device.virtual_chassis)
|
||||||
})
|
})
|
||||||
|
|
||||||
# Bridge validation
|
# Bridge validation
|
||||||
|
|
||||||
# An interface cannot be bridged to itself
|
# An interface cannot be bridged to itself
|
||||||
if self.pk and self.bridge_id == self.pk:
|
if self.pk and self.bridge_id == self.pk:
|
||||||
raise ValidationError({'bridge': "An interface cannot be bridged to itself."})
|
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
|
||||||
|
|
||||||
# A bridged interface belong to the same device or virtual chassis
|
# A bridged interface belong to the same device or virtual chassis
|
||||||
if self.bridge and self.bridge.device != self.device:
|
if self.bridge and self.bridge.device != self.device:
|
||||||
if self.device.virtual_chassis is None:
|
if self.device.virtual_chassis is None:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'bridge': f"The selected bridge interface ({self.bridge}) belongs to a different device "
|
'bridge': _("""
|
||||||
f"({self.bridge.device})."
|
The selected bridge interface ({bridge}) belongs to a different device
|
||||||
|
({device}).""").format(bridge=self.bridge, device=self.bridge.device)
|
||||||
})
|
})
|
||||||
elif self.bridge.device.virtual_chassis != self.device.virtual_chassis:
|
elif self.bridge.device.virtual_chassis != self.device.virtual_chassis:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'bridge': f"The selected bridge interface ({self.bridge}) belongs to {self.bridge.device}, which "
|
'bridge': _("""
|
||||||
f"is not part of virtual chassis {self.device.virtual_chassis}."
|
The selected bridge interface ({bridge}) belongs to {device}, which "
|
||||||
|
is not part of virtual chassis {virtual_chassis}.
|
||||||
|
""").format(bridge=self.bridge, device=self.bridge.device, virtual_chassis=self.device.virtual_chassis)
|
||||||
})
|
})
|
||||||
|
|
||||||
# LAG validation
|
# LAG validation
|
||||||
|
|
||||||
# A virtual interface cannot have a parent LAG
|
# A virtual interface cannot have a parent LAG
|
||||||
if self.type == InterfaceTypeChoices.TYPE_VIRTUAL and self.lag is not None:
|
if self.type == InterfaceTypeChoices.TYPE_VIRTUAL and self.lag is not None:
|
||||||
raise ValidationError({'lag': "Virtual interfaces cannot have a parent LAG interface."})
|
raise ValidationError({'lag': _("Virtual interfaces cannot have a parent LAG interface.")})
|
||||||
|
|
||||||
# A LAG interface cannot be its own parent
|
# A LAG interface cannot be its own parent
|
||||||
if self.pk and self.lag_id == self.pk:
|
if self.pk and self.lag_id == self.pk:
|
||||||
raise ValidationError({'lag': "A LAG interface cannot be its own parent."})
|
raise ValidationError({'lag': _("A LAG interface cannot be its own parent.")})
|
||||||
|
|
||||||
# An interface's LAG must belong to the same device or virtual chassis
|
# An interface's LAG must belong to the same device or virtual chassis
|
||||||
if self.lag and self.lag.device != self.device:
|
if self.lag and self.lag.device != self.device:
|
||||||
if self.device.virtual_chassis is None:
|
if self.device.virtual_chassis is None:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'lag': f"The selected LAG interface ({self.lag}) belongs to a different device ({self.lag.device})."
|
'lag': _("The selected LAG interface ({lag}) belongs to a different device ({device}).").format(lag=self.lag, device=self.lag.device)
|
||||||
})
|
})
|
||||||
elif self.lag.device.virtual_chassis != self.device.virtual_chassis:
|
elif self.lag.device.virtual_chassis != self.device.virtual_chassis:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'lag': f"The selected LAG interface ({self.lag}) belongs to {self.lag.device}, which is not part "
|
'lag': _("""
|
||||||
f"of virtual chassis {self.device.virtual_chassis}."
|
The selected LAG interface ({lag}) belongs to {device}, which is not part
|
||||||
|
of virtual chassis {virtual_chassis}.
|
||||||
|
""".format(lag=self.lag, device=self.lag.device, virtual_chassis=self.device.virtual_chassis))
|
||||||
})
|
})
|
||||||
|
|
||||||
# PoE validation
|
# PoE validation
|
||||||
@ -782,52 +807,54 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
|||||||
# Only physical interfaces may have a PoE mode/type assigned
|
# Only physical interfaces may have a PoE mode/type assigned
|
||||||
if self.poe_mode and self.is_virtual:
|
if self.poe_mode and self.is_virtual:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'poe_mode': "Virtual interfaces cannot have a PoE mode."
|
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
|
||||||
})
|
})
|
||||||
if self.poe_type and self.is_virtual:
|
if self.poe_type and self.is_virtual:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'poe_type': "Virtual interfaces cannot have a PoE type."
|
'poe_type': _("Virtual interfaces cannot have a PoE type.")
|
||||||
})
|
})
|
||||||
|
|
||||||
# An interface with a PoE type set must also specify a mode
|
# An interface with a PoE type set must also specify a mode
|
||||||
if self.poe_type and not self.poe_mode:
|
if self.poe_type and not self.poe_mode:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'poe_type': "Must specify PoE mode when designating a PoE type."
|
'poe_type': _("Must specify PoE mode when designating a PoE type.")
|
||||||
})
|
})
|
||||||
|
|
||||||
# Wireless validation
|
# Wireless validation
|
||||||
|
|
||||||
# RF role & channel may only be set for wireless interfaces
|
# RF role & channel may only be set for wireless interfaces
|
||||||
if self.rf_role and not self.is_wireless:
|
if self.rf_role and not self.is_wireless:
|
||||||
raise ValidationError({'rf_role': "Wireless role may be set only on wireless interfaces."})
|
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})
|
||||||
if self.rf_channel and not self.is_wireless:
|
if self.rf_channel and not self.is_wireless:
|
||||||
raise ValidationError({'rf_channel': "Channel may be set only on wireless interfaces."})
|
raise ValidationError({'rf_channel': _("Channel may be set only on wireless interfaces.")})
|
||||||
|
|
||||||
# Validate channel frequency against interface type and selected channel (if any)
|
# Validate channel frequency against interface type and selected channel (if any)
|
||||||
if self.rf_channel_frequency:
|
if self.rf_channel_frequency:
|
||||||
if not self.is_wireless:
|
if not self.is_wireless:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'rf_channel_frequency': "Channel frequency may be set only on wireless interfaces.",
|
'rf_channel_frequency': _("Channel frequency may be set only on wireless interfaces."),
|
||||||
})
|
})
|
||||||
if self.rf_channel and self.rf_channel_frequency != get_channel_attr(self.rf_channel, 'frequency'):
|
if self.rf_channel and self.rf_channel_frequency != get_channel_attr(self.rf_channel, 'frequency'):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'rf_channel_frequency': "Cannot specify custom frequency with channel selected.",
|
'rf_channel_frequency': _("Cannot specify custom frequency with channel selected."),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Validate channel width against interface type and selected channel (if any)
|
# Validate channel width against interface type and selected channel (if any)
|
||||||
if self.rf_channel_width:
|
if self.rf_channel_width:
|
||||||
if not self.is_wireless:
|
if not self.is_wireless:
|
||||||
raise ValidationError({'rf_channel_width': "Channel width may be set only on wireless interfaces."})
|
raise ValidationError({'rf_channel_width': _("Channel width may be set only on wireless interfaces.")})
|
||||||
if self.rf_channel and self.rf_channel_width != get_channel_attr(self.rf_channel, 'width'):
|
if self.rf_channel and self.rf_channel_width != get_channel_attr(self.rf_channel, 'width'):
|
||||||
raise ValidationError({'rf_channel_width': "Cannot specify custom width with channel selected."})
|
raise ValidationError({'rf_channel_width': _("Cannot specify custom width with channel selected.")})
|
||||||
|
|
||||||
# VLAN validation
|
# VLAN validation
|
||||||
|
|
||||||
# Validate untagged VLAN
|
# Validate untagged VLAN
|
||||||
if self.untagged_vlan and self.untagged_vlan.site not in [self.device.site, None]:
|
if self.untagged_vlan and self.untagged_vlan.site not in [self.device.site, None]:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'untagged_vlan': f"The untagged VLAN ({self.untagged_vlan}) must belong to the same site as the "
|
'untagged_vlan': _("""
|
||||||
f"interface's parent device, or it must be global."
|
The untagged VLAN ({untagged_vlan}) must belong to the same site as the
|
||||||
|
interface's parent device, or it must be global.
|
||||||
|
""").format(untagged_vlan=self.untagged_vlan)
|
||||||
})
|
})
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
@ -894,10 +921,12 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
A pass-through port on the front of a Device.
|
A pass-through port on the front of a Device.
|
||||||
"""
|
"""
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
)
|
)
|
||||||
color = ColorField(
|
color = ColorField(
|
||||||
|
verbose_name=_('color'),
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
rear_port = models.ForeignKey(
|
rear_port = models.ForeignKey(
|
||||||
@ -906,6 +935,7 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
related_name='frontports'
|
related_name='frontports'
|
||||||
)
|
)
|
||||||
rear_port_position = models.PositiveSmallIntegerField(
|
rear_port_position = models.PositiveSmallIntegerField(
|
||||||
|
verbose_name=_('rear_port_position'),
|
||||||
default=1,
|
default=1,
|
||||||
validators=[
|
validators=[
|
||||||
MinValueValidator(REARPORT_POSITIONS_MIN),
|
MinValueValidator(REARPORT_POSITIONS_MIN),
|
||||||
@ -939,14 +969,16 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
# Validate rear port assignment
|
# Validate rear port assignment
|
||||||
if self.rear_port.device != self.device:
|
if self.rear_port.device != self.device:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"rear_port": f"Rear port ({self.rear_port}) must belong to the same device"
|
"rear_port": _("Rear port ({rear_port}) must belong to the same device").format(rear_port=self.rear_port)
|
||||||
})
|
})
|
||||||
|
|
||||||
# Validate rear port position assignment
|
# Validate rear port position assignment
|
||||||
if self.rear_port_position > self.rear_port.positions:
|
if self.rear_port_position > self.rear_port.positions:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"rear_port_position": f"Invalid rear port position ({self.rear_port_position}): Rear port "
|
"rear_port_position": _("""
|
||||||
f"{self.rear_port.name} has only {self.rear_port.positions} positions"
|
Invalid rear port position ({rear_port_position}): Rear port
|
||||||
|
{name} has only {positions} positions
|
||||||
|
""").format(rear_port_position=self.rear_port_position, name=self.rear_port.name, positions=self.rear_port.positions)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -955,13 +987,16 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
A pass-through port on the rear of a Device.
|
A pass-through port on the rear of a Device.
|
||||||
"""
|
"""
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
|
verbose_name=_('type'),
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PortTypeChoices
|
choices=PortTypeChoices
|
||||||
)
|
)
|
||||||
color = ColorField(
|
color = ColorField(
|
||||||
|
verbose_name=_('color'),
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
positions = models.PositiveSmallIntegerField(
|
positions = models.PositiveSmallIntegerField(
|
||||||
|
verbose_name=_('positions'),
|
||||||
default=1,
|
default=1,
|
||||||
validators=[
|
validators=[
|
||||||
MinValueValidator(REARPORT_POSITIONS_MIN),
|
MinValueValidator(REARPORT_POSITIONS_MIN),
|
||||||
@ -982,8 +1017,9 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin):
|
|||||||
frontport_count = self.frontports.count()
|
frontport_count = self.frontports.count()
|
||||||
if self.positions < frontport_count:
|
if self.positions < frontport_count:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"positions": f"The number of positions cannot be less than the number of mapped front ports "
|
"positions": _("""
|
||||||
f"({frontport_count})"
|
The number of positions cannot be less than the number of mapped front ports
|
||||||
|
({frontport_count})""").format(frontport_count=frontport_count)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -996,6 +1032,7 @@ class ModuleBay(ComponentModel, TrackingModelMixin):
|
|||||||
An empty space within a Device which can house a child device
|
An empty space within a Device which can house a child device
|
||||||
"""
|
"""
|
||||||
position = models.CharField(
|
position = models.CharField(
|
||||||
|
verbose_name=_('position'),
|
||||||
max_length=30,
|
max_length=30,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_('Identifier to reference when renaming installed components')
|
help_text=_('Identifier to reference when renaming installed components')
|
||||||
@ -1014,7 +1051,7 @@ class DeviceBay(ComponentModel, TrackingModelMixin):
|
|||||||
installed_device = models.OneToOneField(
|
installed_device = models.OneToOneField(
|
||||||
to='dcim.Device',
|
to='dcim.Device',
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
related_name='parent_bay',
|
related_name=_('parent_bay'),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
@ -1029,20 +1066,20 @@ class DeviceBay(ComponentModel, TrackingModelMixin):
|
|||||||
|
|
||||||
# Validate that the parent Device can have DeviceBays
|
# Validate that the parent Device can have DeviceBays
|
||||||
if not self.device.device_type.is_parent_device:
|
if not self.device.device_type.is_parent_device:
|
||||||
raise ValidationError("This type of device ({}) does not support device bays.".format(
|
raise ValidationError(_("This type of device ({}) does not support device bays.").format(
|
||||||
self.device.device_type
|
self.device.device_type
|
||||||
))
|
))
|
||||||
|
|
||||||
# Cannot install a device into itself, obviously
|
# Cannot install a device into itself, obviously
|
||||||
if self.device == self.installed_device:
|
if self.device == self.installed_device:
|
||||||
raise ValidationError("Cannot install a device into itself.")
|
raise ValidationError(_("Cannot install a device into itself."))
|
||||||
|
|
||||||
# Check that the installed device is not already installed elsewhere
|
# Check that the installed device is not already installed elsewhere
|
||||||
if self.installed_device:
|
if self.installed_device:
|
||||||
current_bay = DeviceBay.objects.filter(installed_device=self.installed_device).first()
|
current_bay = DeviceBay.objects.filter(installed_device=self.installed_device).first()
|
||||||
if current_bay and current_bay != self:
|
if current_bay and current_bay != self:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'installed_device': "Cannot install the specified device; device is already installed in {}".format(
|
'installed_device': _("Cannot install the specified device; device is already installed in {}").format(
|
||||||
current_bay
|
current_bay
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1058,6 +1095,7 @@ class InventoryItemRole(OrganizationalModel):
|
|||||||
Inventory items may optionally be assigned a functional role.
|
Inventory items may optionally be assigned a functional role.
|
||||||
"""
|
"""
|
||||||
color = ColorField(
|
color = ColorField(
|
||||||
|
verbose_name=_('color'),
|
||||||
default=ColorChoices.COLOR_GREY
|
default=ColorChoices.COLOR_GREY
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1110,13 +1148,13 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
|||||||
)
|
)
|
||||||
part_id = models.CharField(
|
part_id = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Part ID',
|
verbose_name=_('Part ID'),
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_('Manufacturer-assigned part identifier')
|
help_text=_('Manufacturer-assigned part identifier')
|
||||||
)
|
)
|
||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Serial number',
|
verbose_name=_('Serial number'),
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
asset_tag = models.CharField(
|
asset_tag = models.CharField(
|
||||||
@ -1124,10 +1162,11 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
|||||||
unique=True,
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Asset tag',
|
verbose_name=_('Asset tag'),
|
||||||
help_text=_('A unique tag used to identify this item')
|
help_text=_('A unique tag used to identify this item')
|
||||||
)
|
)
|
||||||
discovered = models.BooleanField(
|
discovered = models.BooleanField(
|
||||||
|
verbose_name=_('discovered'),
|
||||||
default=False,
|
default=False,
|
||||||
help_text=_('This item was automatically discovered')
|
help_text=_('This item was automatically discovered')
|
||||||
)
|
)
|
||||||
@ -1154,7 +1193,7 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
|||||||
# An InventoryItem cannot be its own parent
|
# An InventoryItem cannot be its own parent
|
||||||
if self.pk and self.parent_id == self.pk:
|
if self.pk and self.parent_id == self.pk:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"parent": "Cannot assign self as parent."
|
"parent": _("Cannot assign self as parent.")
|
||||||
})
|
})
|
||||||
|
|
||||||
# Validation for moving InventoryItems
|
# Validation for moving InventoryItems
|
||||||
@ -1162,13 +1201,13 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
|||||||
# Cannot move an InventoryItem to another device if it has a parent
|
# Cannot move an InventoryItem to another device if it has a parent
|
||||||
if self.parent and self.parent.device != self.device:
|
if self.parent and self.parent.device != self.device:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"parent": "Parent inventory item does not belong to the same device."
|
"parent": _("Parent inventory item does not belong to the same device.")
|
||||||
})
|
})
|
||||||
|
|
||||||
# Prevent moving InventoryItems with children
|
# Prevent moving InventoryItems with children
|
||||||
first_child = self.get_children().first()
|
first_child = self.get_children().first()
|
||||||
if first_child and first_child.device != self.device:
|
if first_child and first_child.device != self.device:
|
||||||
raise ValidationError("Cannot move an inventory item with dependent children")
|
raise ValidationError(_("Cannot move an inventory item with dependent children"))
|
||||||
|
|
||||||
# When moving an InventoryItem to another device, remove any associated component
|
# When moving an InventoryItem to another device, remove any associated component
|
||||||
if self.component and self.component.device != self.device:
|
if self.component and self.component.device != self.device:
|
||||||
@ -1176,5 +1215,5 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin):
|
|||||||
else:
|
else:
|
||||||
if self.component and self.component.device != self.device:
|
if self.component and self.component.device != self.device:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
"device": "Cannot assign inventory item to component on another device"
|
"device": _("Cannot assign inventory item to component on another device")
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user