Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions netbox/dcim/models/device_component_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from dcim.choices import *
from dcim.constants import *
from dcim.models.mixins import InterfaceValidationMixin
from netbox.models import ChangeLoggedModel
from utilities.fields import ColorField, NaturalOrderingField
from utilities.mptt import TreeManager
Expand Down Expand Up @@ -405,7 +406,7 @@ def to_yaml(self):
}


class InterfaceTemplate(ModularComponentTemplateModel):
class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel):
"""
A template for a physical data interface on a new Device.
"""
Expand Down Expand Up @@ -469,8 +470,6 @@ def clean(self):
super().clean()

if self.bridge:
if self.pk and self.bridge_id == self.pk:
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
if self.device_type and self.device_type != self.bridge.device_type:
raise ValidationError({
'bridge': _(
Expand All @@ -484,11 +483,6 @@ def clean(self):
).format(bridge=self.bridge)
})

if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({
'rf_role': "Wireless role may be set only on wireless interfaces."
})

def instantiate(self, **kwargs):
return self.component_model(
name=self.resolve_name(kwargs.get('module')),
Expand Down
36 changes: 10 additions & 26 deletions netbox/dcim/models/device_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from dcim.choices import *
from dcim.constants import *
from dcim.fields import WWNField
from dcim.models.mixins import InterfaceValidationMixin
from netbox.choices import ColorChoices
from netbox.models import OrganizationalModel, NetBoxModel
from utilities.fields import ColorField, NaturalOrderingField
Expand Down Expand Up @@ -676,7 +677,14 @@ def mac_address(self):
return self.primary_mac_address.mac_address


class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEndpoint, TrackingModelMixin):
class Interface(
InterfaceValidationMixin,
ModularComponentModel,
BaseInterface,
CabledObjectModel,
PathEndpoint,
TrackingModelMixin,
):
"""
A network interface within a Device. A physical Interface can connect to exactly one other Interface.
"""
Expand Down Expand Up @@ -893,10 +901,6 @@ def clean(self):

# Bridge validation

# An interface cannot be bridged to itself
if self.pk and self.bridge_id == self.pk:
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})

# A bridged interface belongs to the same device or virtual chassis
if self.bridge and self.bridge.device != self.device:
if self.device.virtual_chassis is None:
Expand Down Expand Up @@ -942,29 +946,9 @@ def clean(self):
)
})

# PoE validation

# Only physical interfaces may have a PoE mode/type assigned
if self.poe_mode and self.is_virtual:
raise ValidationError({
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
})
if self.poe_type and self.is_virtual:
raise ValidationError({
'poe_type': _("Virtual interfaces cannot have a PoE type.")
})

# An interface with a PoE type set must also specify a mode
if self.poe_type and not self.poe_mode:
raise ValidationError({
'poe_type': _("Must specify PoE mode when designating a PoE type.")
})

# Wireless validation

# RF role & channel may only be set for wireless interfaces
if self.rf_role and not self.is_wireless:
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})
# RF channel may only be set for wireless interfaces
if self.rf_channel and not self.is_wireless:
raise ValidationError({'rf_channel': _("Channel may be set only on wireless interfaces.")})

Expand Down
33 changes: 33 additions & 0 deletions netbox/dcim/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from dcim.constants import VIRTUAL_IFACE_TYPES, WIRELESS_IFACE_TYPES

__all__ = (
'CachedScopeMixin',
'InterfaceValidationMixin',
'RenderConfigMixin',
)

Expand Down Expand Up @@ -116,3 +119,33 @@ def cache_related_objects(self):
self._site = self.scope.site
self._location = self.scope
cache_related_objects.alters_data = True


class InterfaceValidationMixin:

def clean(self):
super().clean()

# An interface cannot be bridged to itself
if self.pk and self.bridge_id == self.pk:
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})

# Only physical interfaces may have a PoE mode/type assigned
if self.poe_mode and self.type in VIRTUAL_IFACE_TYPES:
raise ValidationError({
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
})
if self.poe_type and self.type in VIRTUAL_IFACE_TYPES:
raise ValidationError({
'poe_type': _("Virtual interfaces cannot have a PoE type.")
})

# An interface with a PoE type set must also specify a mode
if self.poe_type and not self.poe_mode:
raise ValidationError({
'poe_type': _("Must specify PoE mode when designating a PoE type.")
})

# RF role may be set only for wireless interfaces
if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})