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

165 lines
5.2 KiB
Python
Raw Normal View History

2023-08-04 09:43:44 -04:00
from django.contrib.contenttypes.fields import GenericForeignKey
2022-06-27 23:24:50 -05:00
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
2022-08-23 16:17:40 -07:00
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
2022-06-27 23:24:50 -05:00
from core.models import ObjectType
from netbox.models import NetBoxModel, PrimaryModel
2023-08-04 09:43:44 -04:00
from netbox.models.features import ContactsMixin
from vpn.choices import L2VPNTypeChoices
from vpn.constants import L2VPN_ASSIGNMENT_MODELS
2022-06-27 23:24:50 -05:00
__all__ = (
'L2VPN',
'L2VPNTermination',
)
2022-06-27 23:24:50 -05:00
2023-08-04 09:43:44 -04:00
class L2VPN(ContactsMixin, PrimaryModel):
2022-06-27 23:24:50 -05:00
name = models.CharField(
verbose_name=_('name'),
2022-06-27 23:24:50 -05:00
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
)
2022-06-27 23:24:50 -05:00
identifier = models.BigIntegerField(
verbose_name=_('identifier'),
2022-06-27 23:24:50 -05:00
null=True,
blank=True
2022-06-27 23:24:50 -05:00
)
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,
2022-06-27 23:24:50 -05:00
related_name='l2vpns',
blank=True,
null=True
)
clone_fields = ('type',)
2022-06-27 23:24:50 -05:00
class Meta:
2022-07-11 21:51:39 -04:00
ordering = ('name', 'identifier')
verbose_name = _('L2VPN')
verbose_name_plural = _('L2VPNs')
2022-06-27 23:24:50 -05:00
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])
2022-06-27 23:24:50 -05:00
2022-08-23 16:17:40 -07:00
@cached_property
def can_add_termination(self):
if self.type in L2VPNTypeChoices.P2P and self.terminations.count() >= 2:
return False
else:
return True
2022-06-27 23:24:50 -05:00
class L2VPNTermination(NetBoxModel):
l2vpn = models.ForeignKey(
to='vpn.L2VPN',
2022-06-27 23:24:50 -05:00
on_delete=models.CASCADE,
2022-07-06 08:57:15 -05:00
related_name='terminations'
2022-06-27 23:24:50 -05:00
)
assigned_object_type = models.ForeignKey(
to='contenttypes.ContentType',
2022-06-27 23:24:50 -05:00
limit_choices_to=L2VPN_ASSIGNMENT_MODELS,
on_delete=models.PROTECT,
2022-07-06 08:57:15 -05:00
related_name='+'
2022-06-27 23:24:50 -05:00
)
2022-07-06 08:57:15 -05:00
assigned_object_id = models.PositiveBigIntegerField()
2022-06-27 23:24:50 -05:00
assigned_object = GenericForeignKey(
ct_field='assigned_object_type',
fk_field='assigned_object_id'
)
clone_fields = ('l2vpn',)
prerequisite_models = (
'vpn.L2VPN',
)
2022-06-27 23:24:50 -05:00
class Meta:
ordering = ('l2vpn',)
indexes = (
models.Index(fields=('assigned_object_type', 'assigned_object_id')),
)
2022-06-27 23:24:50 -05:00
constraints = (
models.UniqueConstraint(
fields=('assigned_object_type', 'assigned_object_id'),
name='vpn_l2vpntermination_assigned_object'
2022-06-27 23:24:50 -05:00
),
)
verbose_name = _('L2VPN termination')
verbose_name_plural = _('L2VPN terminations')
2022-06-27 23:24:50 -05:00
def __str__(self):
if self.pk is not None:
return f'{self.assigned_object} <> {self.l2vpn}'
2022-07-06 08:57:15 -05:00
return super().__str__()
2022-06-27 23:24:50 -05:00
def get_absolute_url(self):
return reverse('vpn:l2vpntermination', args=[self.pk])
2022-06-27 23:24:50 -05:00
def clean(self):
2022-07-06 08:57:15 -05:00
# Only check is assigned_object is set. Required otherwise we have an Integrity Error thrown.
2022-06-27 23:24:50 -05:00
if self.assigned_object:
obj_id = self.assigned_object.pk
obj_type = ObjectType.objects.get_for_model(self.assigned_object)
2022-06-27 23:24:50 -05:00
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
)
)
2022-06-27 23:24:50 -05:00
# Only check if L2VPN is set and is of type P2P
2022-07-11 21:51:39 -04:00
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