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

184 lines
5.3 KiB
Python

from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel
from netbox.models.features import CustomFieldsMixin, CustomLinksMixin, TagsMixin
from vpn.choices import *
__all__ = (
'Tunnel',
'TunnelGroup',
'TunnelTermination',
)
class TunnelGroup(OrganizationalModel):
"""
An administrative grouping of Tunnels. This can be used to correlate peer-to-peer tunnels which form a mesh,
for example.
"""
class Meta:
ordering = ('name',)
verbose_name = _('tunnel group')
verbose_name_plural = _('tunnel groups')
def get_absolute_url(self):
return reverse('vpn:tunnelgroup', args=[self.pk])
class Tunnel(PrimaryModel):
name = models.CharField(
verbose_name=_('name'),
max_length=100,
unique=True
)
status = models.CharField(
verbose_name=_('status'),
max_length=50,
choices=TunnelStatusChoices,
default=TunnelStatusChoices.STATUS_ACTIVE
)
group = models.ForeignKey(
to='vpn.TunnelGroup',
on_delete=models.PROTECT,
related_name='tunnels',
blank=True,
null=True
)
encapsulation = models.CharField(
verbose_name=_('encapsulation'),
max_length=50,
choices=TunnelEncapsulationChoices
)
ipsec_profile = models.ForeignKey(
to='vpn.IPSecProfile',
on_delete=models.PROTECT,
related_name='tunnels',
blank=True,
null=True
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
on_delete=models.PROTECT,
related_name='tunnels',
blank=True,
null=True
)
tunnel_id = models.PositiveBigIntegerField(
verbose_name=_('tunnel ID'),
blank=True,
null=True
)
clone_fields = (
'status', 'encapsulation', 'ipsec_profile', 'tenant',
)
class Meta:
ordering = ('name',)
constraints = (
models.UniqueConstraint(
fields=('group', 'name'),
name='%(app_label)s_%(class)s_group_name'
),
models.UniqueConstraint(
fields=('name',),
name='%(app_label)s_%(class)s_name',
condition=Q(group__isnull=True)
),
)
verbose_name = _('tunnel')
verbose_name_plural = _('tunnels')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('vpn:tunnel', args=[self.pk])
def get_status_color(self):
return TunnelStatusChoices.colors.get(self.status)
class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLoggedModel):
tunnel = models.ForeignKey(
to='vpn.Tunnel',
on_delete=models.CASCADE,
related_name='terminations'
)
role = models.CharField(
verbose_name=_('role'),
max_length=50,
choices=TunnelTerminationRoleChoices,
default=TunnelTerminationRoleChoices.ROLE_PEER
)
termination_type = models.ForeignKey(
to='contenttypes.ContentType',
on_delete=models.PROTECT,
related_name='+'
)
termination_id = models.PositiveBigIntegerField(
blank=True,
null=True
)
termination = GenericForeignKey(
ct_field='termination_type',
fk_field='termination_id'
)
outside_ip = models.ForeignKey(
to='ipam.IPAddress',
on_delete=models.PROTECT,
related_name='tunnel_terminations',
blank=True,
null=True
)
prerequisite_models = (
'vpn.Tunnel',
)
class Meta:
ordering = ('tunnel', 'role', 'pk')
indexes = (
models.Index(fields=('termination_type', 'termination_id')),
)
constraints = (
models.UniqueConstraint(
fields=('termination_type', 'termination_id'),
name='%(app_label)s_%(class)s_termination',
violation_error_message=_("An object may be terminated to only one tunnel at a time.")
),
)
verbose_name = _('tunnel termination')
verbose_name_plural = _('tunnel terminations')
def __str__(self):
return f'{self.tunnel}: Termination {self.pk}'
def get_absolute_url(self):
return reverse('vpn:tunneltermination', args=[self.pk])
def get_role_color(self):
return TunnelTerminationRoleChoices.colors.get(self.role)
def clean(self):
super().clean()
# Check that the selected termination object is not already attached to a Tunnel
if getattr(self.termination, 'tunnel_termination', None) and self.termination.tunnel_termination.pk != self.pk:
raise ValidationError({
'termination': _("{name} is already attached to a tunnel ({tunnel}).").format(
name=self.termination.name,
tunnel=self.termination.tunnel_termination.tunnel
)
})
def to_objectchange(self, action):
objectchange = super().to_objectchange(action)
objectchange.related_object = self.tunnel
return objectchange