1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00
2024-03-04 08:33:44 -05:00

165 lines
5.2 KiB
Python

from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from core.models import ObjectType
from netbox.models import NetBoxModel, PrimaryModel
from netbox.models.features import ContactsMixin
from vpn.choices import L2VPNTypeChoices
from vpn.constants import L2VPN_ASSIGNMENT_MODELS
__all__ = (
'L2VPN',
'L2VPNTermination',
)
class L2VPN(ContactsMixin, PrimaryModel):
name = models.CharField(
verbose_name=_('name'),
max_length=100,
unique=True
)
slug = models.SlugField(
verbose_name=_('slug'),
max_length=100,
unique=True
)
type = models.CharField(
verbose_name=_('type'),
max_length=50,
choices=L2VPNTypeChoices
)
identifier = models.BigIntegerField(
verbose_name=_('identifier'),
null=True,
blank=True
)
import_targets = models.ManyToManyField(
to='ipam.RouteTarget',
related_name='importing_l2vpns',
blank=True,
)
export_targets = models.ManyToManyField(
to='ipam.RouteTarget',
related_name='exporting_l2vpns',
blank=True
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
on_delete=models.PROTECT,
related_name='l2vpns',
blank=True,
null=True
)
clone_fields = ('type',)
class Meta:
ordering = ('name', 'identifier')
verbose_name = _('L2VPN')
verbose_name_plural = _('L2VPNs')
def __str__(self):
if self.identifier:
return f'{self.name} ({self.identifier})'
return f'{self.name}'
def get_absolute_url(self):
return reverse('vpn:l2vpn', args=[self.pk])
@cached_property
def can_add_termination(self):
if self.type in L2VPNTypeChoices.P2P and self.terminations.count() >= 2:
return False
else:
return True
class L2VPNTermination(NetBoxModel):
l2vpn = models.ForeignKey(
to='vpn.L2VPN',
on_delete=models.CASCADE,
related_name='terminations'
)
assigned_object_type = models.ForeignKey(
to='contenttypes.ContentType',
limit_choices_to=L2VPN_ASSIGNMENT_MODELS,
on_delete=models.PROTECT,
related_name='+'
)
assigned_object_id = models.PositiveBigIntegerField()
assigned_object = GenericForeignKey(
ct_field='assigned_object_type',
fk_field='assigned_object_id'
)
clone_fields = ('l2vpn',)
prerequisite_models = (
'vpn.L2VPN',
)
class Meta:
ordering = ('l2vpn',)
indexes = (
models.Index(fields=('assigned_object_type', 'assigned_object_id')),
)
constraints = (
models.UniqueConstraint(
fields=('assigned_object_type', 'assigned_object_id'),
name='vpn_l2vpntermination_assigned_object'
),
)
verbose_name = _('L2VPN termination')
verbose_name_plural = _('L2VPN terminations')
def __str__(self):
if self.pk is not None:
return f'{self.assigned_object} <> {self.l2vpn}'
return super().__str__()
def get_absolute_url(self):
return reverse('vpn:l2vpntermination', args=[self.pk])
def clean(self):
# Only check is assigned_object is set. Required otherwise we have an Integrity Error thrown.
if self.assigned_object:
obj_id = self.assigned_object.pk
obj_type = ObjectType.objects.get_for_model(self.assigned_object)
if L2VPNTermination.objects.filter(assigned_object_id=obj_id, assigned_object_type=obj_type).\
exclude(pk=self.pk).count() > 0:
raise ValidationError(
_('L2VPN Termination already assigned ({assigned_object})').format(
assigned_object=self.assigned_object
)
)
# Only check if L2VPN is set and is of type P2P
if hasattr(self, 'l2vpn') and self.l2vpn.type in L2VPNTypeChoices.P2P:
terminations_count = L2VPNTermination.objects.filter(l2vpn=self.l2vpn).exclude(pk=self.pk).count()
if terminations_count >= 2:
l2vpn_type = self.l2vpn.get_type_display()
raise ValidationError(
_(
'{l2vpn_type} L2VPNs cannot have more than two terminations; found {terminations_count} '
'already defined.'
).format(l2vpn_type=l2vpn_type, terminations_count=terminations_count))
@property
def assigned_object_parent(self):
obj_type = ObjectType.objects.get_for_model(self.assigned_object)
if obj_type.model == 'vminterface':
return self.assigned_object.virtual_machine
elif obj_type.model == 'interface':
return self.assigned_object.device
elif obj_type.model == 'vminterface':
return self.assigned_object.virtual_machine
return None
@property
def assigned_object_site(self):
return self.assigned_object_parent.site