| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- from django.apps import apps
- from django.contrib.contenttypes.fields import GenericForeignKey
- from django.core.exceptions import ValidationError
- 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',
- )
- class RenderConfigMixin(models.Model):
- config_template = models.ForeignKey(
- to='extras.ConfigTemplate',
- on_delete=models.PROTECT,
- related_name='%(class)ss',
- blank=True,
- null=True
- )
- class Meta:
- abstract = True
- def get_config_template(self):
- """
- Return the appropriate ConfigTemplate (if any) for this Device.
- """
- if self.config_template:
- return self.config_template
- if self.role and self.role.config_template:
- return self.role.config_template
- if self.platform and self.platform.config_template:
- return self.platform.config_template
- return None
- class CachedScopeMixin(models.Model):
- """
- Mixin for adding a GenericForeignKey scope to a model that can point to a Region, SiteGroup, Site, or Location.
- Includes cached fields for each to allow efficient filtering. Appropriate validation must be done in the clean()
- method as this does not have any as validation is generally model-specific.
- """
- scope_type = models.ForeignKey(
- to='contenttypes.ContentType',
- on_delete=models.PROTECT,
- related_name='+',
- blank=True,
- null=True
- )
- scope_id = models.PositiveBigIntegerField(
- blank=True,
- null=True
- )
- scope = GenericForeignKey(
- ct_field='scope_type',
- fk_field='scope_id'
- )
- _location = models.ForeignKey(
- to='dcim.Location',
- on_delete=models.CASCADE,
- blank=True,
- null=True
- )
- _site = models.ForeignKey(
- to='dcim.Site',
- on_delete=models.CASCADE,
- blank=True,
- null=True
- )
- _region = models.ForeignKey(
- to='dcim.Region',
- on_delete=models.CASCADE,
- blank=True,
- null=True
- )
- _site_group = models.ForeignKey(
- to='dcim.SiteGroup',
- on_delete=models.CASCADE,
- blank=True,
- null=True
- )
- class Meta:
- abstract = True
- def clean(self):
- if self.scope_type and not (self.scope or self.scope_id):
- scope_type = self.scope_type.model_class()
- raise ValidationError(
- _("Please select a {scope_type}.").format(scope_type=scope_type._meta.model_name)
- )
- super().clean()
- def save(self, *args, **kwargs):
- # Cache objects associated with the terminating object (for filtering)
- self.cache_related_objects()
- super().save(*args, **kwargs)
- def cache_related_objects(self):
- self._region = self._site_group = self._site = self._location = None
- if self.scope_type:
- scope_type = self.scope_type.model_class()
- if scope_type == apps.get_model('dcim', 'region'):
- self._region = self.scope
- elif scope_type == apps.get_model('dcim', 'sitegroup'):
- self._site_group = self.scope
- elif scope_type == apps.get_model('dcim', 'site'):
- self._region = self.scope.region
- self._site_group = self.scope.group
- self._site = self.scope
- elif scope_type == apps.get_model('dcim', 'location'):
- self._region = self.scope.site.region
- self._site_group = self.scope.site.group
- 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.")})
|