Move L2VPN and L2VPNTermination models from ipam to vpn

This commit is contained in:
Jeremy Stretch 2023-11-22 15:10:23 -05:00
parent 6678880db5
commit 8e9e1e9e64
28 changed files with 163 additions and 48 deletions

View File

@ -5,7 +5,7 @@ from django.utils.translation import gettext as _
from extras.filtersets import LocalConfigContextFilterSet from extras.filtersets import LocalConfigContextFilterSet
from extras.models import ConfigTemplate from extras.models import ConfigTemplate
from ipam.filtersets import PrimaryIPFilterSet from ipam.filtersets import PrimaryIPFilterSet
from ipam.models import ASN, L2VPN, IPAddress, VRF from ipam.models import ASN, IPAddress, VRF
from netbox.filtersets import ( from netbox.filtersets import (
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet, BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet,
) )
@ -17,6 +17,7 @@ from utilities.filters import (
TreeNodeMultipleChoiceFilter, TreeNodeMultipleChoiceFilter,
) )
from virtualization.models import Cluster from virtualization.models import Cluster
from vpn.models import L2VPN
from wireless.choices import WirelessRoleChoices, WirelessChannelChoices from wireless.choices import WirelessRoleChoices, WirelessChannelChoices
from .choices import * from .choices import *
from .constants import * from .constants import *

View File

@ -7,12 +7,13 @@ from dcim.constants import *
from dcim.models import * from dcim.models import *
from extras.forms import LocalConfigContextFilterForm from extras.forms import LocalConfigContextFilterForm
from extras.models import ConfigTemplate from extras.models import ConfigTemplate
from ipam.models import ASN, L2VPN, VRF from ipam.models import ASN, VRF
from netbox.forms import NetBoxModelFilterSetForm from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.widgets import APISelectMultiple, NumberWithOptions from utilities.forms.widgets import APISelectMultiple, NumberWithOptions
from vpn.models import L2VPN
from wireless.choices import * from wireless.choices import *
__all__ = ( __all__ = (

View File

@ -730,7 +730,7 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
related_query_name='interface' related_query_name='interface'
) )
l2vpn_terminations = GenericRelation( l2vpn_terminations = GenericRelation(
to='ipam.L2VPNTermination', to='vpn.L2VPNTermination',
content_type_field='assigned_object_type', content_type_field='assigned_object_type',
object_id_field='assigned_object_id', object_id_field='assigned_object_id',
related_query_name='interface', related_query_name='interface',

View File

@ -2,8 +2,8 @@ from drf_spectacular.utils import extend_schema_serializer
from rest_framework import serializers from rest_framework import serializers
from ipam import models from ipam import models
from ipam.models.l2vpn import L2VPNTermination, L2VPN
from netbox.api.serializers import WritableNestedSerializer from netbox.api.serializers import WritableNestedSerializer
from vpn.models import L2VPN, L2VPNTermination
from .field_serializers import IPAddressField from .field_serializers import IPAddressField
__all__ = [ __all__ = [

View File

@ -12,6 +12,7 @@ from netbox.constants import NESTED_SERIALIZER_PREFIX
from tenancy.api.nested_serializers import NestedTenantSerializer from tenancy.api.nested_serializers import NestedTenantSerializer
from utilities.api import get_serializer_for_model from utilities.api import get_serializer_for_model
from virtualization.api.nested_serializers import NestedVirtualMachineSerializer from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
from vpn.models import L2VPN, L2VPNTermination
from .nested_serializers import * from .nested_serializers import *
from .field_serializers import IPAddressField, IPNetworkField from .field_serializers import IPAddressField, IPNetworkField

View File

@ -14,7 +14,6 @@ from circuits.models import Provider
from dcim.models import Site from dcim.models import Site
from ipam import filtersets from ipam import filtersets
from ipam.models import * from ipam.models import *
from ipam.models import L2VPN, L2VPNTermination
from ipam.utils import get_next_available_prefix from ipam.utils import get_next_available_prefix
from netbox.api.viewsets import NetBoxModelViewSet from netbox.api.viewsets import NetBoxModelViewSet
from netbox.api.viewsets.mixins import ObjectValidationMixin from netbox.api.viewsets.mixins import ObjectValidationMixin
@ -22,6 +21,7 @@ from netbox.config import get_config
from netbox.constants import ADVISORY_LOCK_KEYS from netbox.constants import ADVISORY_LOCK_KEYS
from utilities.api import get_serializer_for_model from utilities.api import get_serializer_for_model
from utilities.utils import count_related from utilities.utils import count_related
from vpn.models import L2VPN, L2VPNTermination
from . import serializers from . import serializers

View File

@ -15,6 +15,7 @@ from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter, ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter,
) )
from virtualization.models import VirtualMachine, VMInterface from virtualization.models import VirtualMachine, VMInterface
from vpn.models import L2VPN, L2VPNTermination
from .choices import * from .choices import *
from .models import * from .models import *

View File

@ -15,6 +15,7 @@ from utilities.forms.fields import (
) )
from utilities.forms.widgets import BulkEditNullBooleanSelect from utilities.forms.widgets import BulkEditNullBooleanSelect
from virtualization.models import Cluster, ClusterGroup from virtualization.models import Cluster, ClusterGroup
from vpn.models import L2VPN
__all__ = ( __all__ = (
'AggregateBulkEditForm', 'AggregateBulkEditForm',

View File

@ -13,6 +13,7 @@ from utilities.forms.fields import (
CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField
) )
from virtualization.models import VirtualMachine, VMInterface from virtualization.models import VirtualMachine, VMInterface
from vpn.models import L2VPN, L2VPNTermination
__all__ = ( __all__ = (
'AggregateImportForm', 'AggregateImportForm',

View File

@ -13,6 +13,7 @@ from utilities.forms.fields import (
ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField, ContentTypeMultipleChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField,
) )
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
from vpn.models import L2VPN, L2VPNTermination
__all__ = ( __all__ = (
'AggregateFilterForm', 'AggregateFilterForm',

View File

@ -18,6 +18,7 @@ from utilities.forms.fields import (
) )
from utilities.forms.widgets import DatePicker from utilities.forms.widgets import DatePicker
from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
from vpn.models import L2VPN, L2VPNTermination
__all__ = ( __all__ = (
'AggregateForm', 'AggregateForm',

View File

@ -1,8 +1,9 @@
import graphene import graphene
from ipam import models
from utilities.graphql_optimizer import gql_query_optimizer
from ipam import models
from netbox.graphql.fields import ObjectField, ObjectListField from netbox.graphql.fields import ObjectField, ObjectListField
from utilities.graphql_optimizer import gql_query_optimizer
from vpn.models import L2VPN, L2VPNTermination
from .types import * from .types import *
@ -42,13 +43,13 @@ class IPAMQuery(graphene.ObjectType):
l2vpn_list = ObjectListField(L2VPNType) l2vpn_list = ObjectListField(L2VPNType)
def resolve_l2vpn_list(root, info, **kwargs): def resolve_l2vpn_list(root, info, **kwargs):
return gql_query_optimizer(models.L2VPN.objects.all(), info) return gql_query_optimizer(L2VPN.objects.all(), info)
l2vpn_termination = ObjectField(L2VPNTerminationType) l2vpn_termination = ObjectField(L2VPNTerminationType)
l2vpn_termination_list = ObjectListField(L2VPNTerminationType) l2vpn_termination_list = ObjectListField(L2VPNTerminationType)
def resolve_l2vpn_termination_list(root, info, **kwargs): def resolve_l2vpn_termination_list(root, info, **kwargs):
return gql_query_optimizer(models.L2VPNTermination.objects.all(), info) return gql_query_optimizer(L2VPNTermination.objects.all(), info)
prefix = ObjectField(PrefixType) prefix = ObjectField(PrefixType)
prefix_list = ObjectListField(PrefixType) prefix_list = ObjectListField(PrefixType)

View File

@ -4,6 +4,7 @@ from extras.graphql.mixins import ContactsMixin
from ipam import filtersets, models from ipam import filtersets, models
from netbox.graphql.scalars import BigInt from netbox.graphql.scalars import BigInt
from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType
from vpn.models import L2VPN, L2VPNTermination
__all__ = ( __all__ = (
'ASNType', 'ASNType',
@ -192,7 +193,7 @@ class VRFType(NetBoxObjectType):
class L2VPNType(ContactsMixin, NetBoxObjectType): class L2VPNType(ContactsMixin, NetBoxObjectType):
class Meta: class Meta:
model = models.L2VPN model = L2VPN
fields = '__all__' fields = '__all__'
filtersets_class = filtersets.L2VPNFilterSet filtersets_class = filtersets.L2VPNFilterSet
@ -201,6 +202,6 @@ class L2VPNTerminationType(NetBoxObjectType):
assigned_object = graphene.Field('ipam.graphql.gfk_mixins.L2VPNAssignmentType') assigned_object = graphene.Field('ipam.graphql.gfk_mixins.L2VPNAssignmentType')
class Meta: class Meta:
model = models.L2VPNTermination model = L2VPNTermination
exclude = ('assigned_object_type', 'assigned_object_id') exclude = ('assigned_object_type', 'assigned_object_id')
filtersets_class = filtersets.L2VPNTerminationFilterSet filtersets_class = filtersets.L2VPNTerminationFilterSet

View File

@ -0,0 +1,47 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ipam', '0067_ipaddress_index_host'),
]
operations = [
migrations.RemoveConstraint(
model_name='l2vpntermination',
name='ipam_l2vpntermination_assigned_object',
),
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.RemoveField(
model_name='l2vpntermination',
name='assigned_object_type',
),
migrations.RemoveField(
model_name='l2vpntermination',
name='l2vpn',
),
migrations.RemoveField(
model_name='l2vpntermination',
name='tags',
),
migrations.DeleteModel(
name='L2VPN',
),
migrations.DeleteModel(
name='L2VPNTermination',
),
],
database_operations=[
migrations.AlterModelTable(
name='L2VPN',
table='vpn_l2vpn',
),
migrations.AlterModelTable(
name='L2VPNTermination',
table='vpn_l2vpntermination',
),
],
),
]

View File

@ -3,27 +3,5 @@ from .asns import *
from .fhrp import * from .fhrp import *
from .vrfs import * from .vrfs import *
from .ip import * from .ip import *
from .l2vpn import *
from .services import * from .services import *
from .vlans import * from .vlans import *
__all__ = (
'ASN',
'ASNRange',
'Aggregate',
'IPAddress',
'IPRange',
'FHRPGroup',
'FHRPGroupAssignment',
'L2VPN',
'L2VPNTermination',
'Prefix',
'RIR',
'Role',
'RouteTarget',
'Service',
'ServiceTemplate',
'VLAN',
'VLANGroup',
'VRF',
)

View File

@ -183,9 +183,8 @@ class VLAN(PrimaryModel):
null=True, null=True,
help_text=_("The primary function of this VLAN") help_text=_("The primary function of this VLAN")
) )
l2vpn_terminations = GenericRelation( l2vpn_terminations = GenericRelation(
to='ipam.L2VPNTermination', to='vpn.L2VPNTermination',
content_type_field='assigned_object_type', content_type_field='assigned_object_type',
object_id_field='assigned_object_id', object_id_field='assigned_object_id',
related_query_name='vlan' related_query_name='vlan'

View File

@ -1,5 +1,6 @@
from . import models
from netbox.search import SearchIndex, register_search from netbox.search import SearchIndex, register_search
from vpn.models import L2VPN
from . import models
@register_search @register_search
@ -71,7 +72,7 @@ class IPRangeIndex(SearchIndex):
@register_search @register_search
class L2VPNIndex(SearchIndex): class L2VPNIndex(SearchIndex):
model = models.L2VPN model = L2VPN
fields = ( fields = (
('name', 100), ('name', 100),
('slug', 110), ('slug', 110),

View File

@ -1,9 +1,9 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import django_tables2 as tables import django_tables2 as tables
from ipam.models import L2VPN, L2VPNTermination
from netbox.tables import NetBoxTable, columns from netbox.tables import NetBoxTable, columns
from tenancy.tables import TenancyColumnsMixin from tenancy.tables import TenancyColumnsMixin
from vpn.models import L2VPN, L2VPNTermination
__all__ = ( __all__ = (
'L2VPNTable', 'L2VPNTable',

View File

@ -9,6 +9,7 @@ from ipam.choices import *
from ipam.models import * from ipam.models import *
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.testing import APITestCase, APIViewTestCases, create_test_device, disable_warnings from utilities.testing import APITestCase, APIViewTestCases, create_test_device, disable_warnings
from vpn.models import L2VPN, L2VPNTermination
class AppTest(APITestCase): class AppTest(APITestCase):

View File

@ -10,6 +10,7 @@ from ipam.models import *
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from vpn.models import L2VPN, L2VPNTermination
class ASNRangeTestCase(TestCase, ChangeLoggedFilterSetTests): class ASNRangeTestCase(TestCase, ChangeLoggedFilterSetTests):

View File

@ -3,8 +3,9 @@ from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from dcim.models import Interface, Device, DeviceRole, DeviceType, Manufacturer, Site from dcim.models import Interface, Device, DeviceRole, DeviceType, Manufacturer, Site
from ipam.choices import IPAddressRoleChoices, PrefixStatusChoices from ipam.choices import *
from ipam.models import Aggregate, IPAddress, IPRange, Prefix, RIR, VLAN, VLANGroup, VRF, L2VPN, L2VPNTermination from ipam.models import *
from vpn.models import L2VPN, L2VPNTermination
class TestAggregate(TestCase): class TestAggregate(TestCase):

View File

@ -10,6 +10,7 @@ from ipam.choices import *
from ipam.models import * from ipam.models import *
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.testing import ViewTestCases, create_test_device, create_tags from utilities.testing import ViewTestCases, create_test_device, create_tags
from vpn.models import L2VPN, L2VPNTermination
class ASNRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase): class ASNRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):

View File

@ -1,5 +1,5 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import F, Prefetch from django.db.models import Prefetch
from django.db.models.expressions import RawSQL from django.db.models.expressions import RawSQL
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
@ -15,6 +15,7 @@ from utilities.utils import count_related
from utilities.views import ViewTab, register_model_view from utilities.views import ViewTab, register_model_view
from virtualization.filtersets import VMInterfaceFilterSet from virtualization.filtersets import VMInterfaceFilterSet
from virtualization.models import VMInterface from virtualization.models import VMInterface
from vpn.models import L2VPN, L2VPNTermination
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .choices import PrefixStatusChoices from .choices import PrefixStatusChoices
from .constants import * from .constants import *

View File

@ -4,13 +4,14 @@ from django.utils.translation import gettext_lazy as _
from dcim.models import Device, DeviceRole, Platform, Region, Site, SiteGroup from dcim.models import Device, DeviceRole, Platform, Region, Site, SiteGroup
from extras.forms import LocalConfigContextFilterForm from extras.forms import LocalConfigContextFilterForm
from extras.models import ConfigTemplate from extras.models import ConfigTemplate
from ipam.models import L2VPN, VRF from ipam.models import VRF
from netbox.forms import NetBoxModelFilterSetForm from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES
from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField
from virtualization.choices import * from virtualization.choices import *
from virtualization.models import * from virtualization.models import *
from vpn.models import L2VPN
__all__ = ( __all__ = (
'ClusterFilterForm', 'ClusterFilterForm',

View File

@ -358,7 +358,7 @@ class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
related_query_name='vminterface', related_query_name='vminterface',
) )
l2vpn_terminations = GenericRelation( l2vpn_terminations = GenericRelation(
to='ipam.L2VPNTermination', to='vpn.L2VPNTermination',
content_type_field='assigned_object_type', content_type_field='assigned_object_type',
object_id_field='assigned_object_id', object_id_field='assigned_object_id',
related_query_name='vminterface', related_query_name='vminterface',

View File

@ -0,0 +1,73 @@
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
import utilities.json
class Migration(migrations.Migration):
dependencies = [
('extras', '0099_cachedvalue_ordering'),
('contenttypes', '0002_remove_content_type_name'),
('tenancy', '0012_contactassignment_custom_fields'),
('ipam', '0068_move_l2vpn'),
('vpn', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name='L2VPN',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
('description', models.CharField(blank=True, max_length=200)),
('comments', models.TextField(blank=True)),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
('type', models.CharField(max_length=50)),
('identifier', models.BigIntegerField(blank=True, null=True)),
('export_targets', models.ManyToManyField(blank=True, related_name='exporting_l2vpns', to='ipam.routetarget')),
('import_targets', models.ManyToManyField(blank=True, related_name='importing_l2vpns', to='ipam.routetarget')),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='l2vpns', to='tenancy.tenant')),
],
options={
'verbose_name': 'L2VPN',
'verbose_name_plural': 'L2VPNs',
'ordering': ('name', 'identifier'),
},
),
migrations.CreateModel(
name='L2VPNTermination',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
('assigned_object_id', models.PositiveBigIntegerField()),
('assigned_object_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(models.Q(('app_label', 'dcim'), ('model', 'interface')), models.Q(('app_label', 'ipam'), ('model', 'vlan')), models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), _connector='OR')), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')),
('l2vpn', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='vpn.l2vpn')),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
],
options={
'verbose_name': 'L2VPN termination',
'verbose_name_plural': 'L2VPN terminations',
'ordering': ('l2vpn',),
},
),
],
# Tables have been renamed from ipam
database_operations=[],
),
migrations.AddConstraint(
model_name='l2vpntermination',
constraint=models.UniqueConstraint(
fields=('assigned_object_type', 'assigned_object_id'),
name='vpn_l2vpntermination_assigned_object'
),
),
]

View File

@ -1,2 +1,3 @@
from .crypto import * from .crypto import *
from .l2vpn import *
from .tunnels import * from .tunnels import *

View File

@ -69,7 +69,7 @@ class L2VPN(ContactsMixin, PrimaryModel):
return f'{self.name}' return f'{self.name}'
def get_absolute_url(self): def get_absolute_url(self):
return reverse('ipam:l2vpn', args=[self.pk]) return reverse('vpn:l2vpn', args=[self.pk])
@cached_property @cached_property
def can_add_termination(self): def can_add_termination(self):
@ -81,7 +81,7 @@ class L2VPN(ContactsMixin, PrimaryModel):
class L2VPNTermination(NetBoxModel): class L2VPNTermination(NetBoxModel):
l2vpn = models.ForeignKey( l2vpn = models.ForeignKey(
to='ipam.L2VPN', to='vpn.L2VPN',
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='terminations' related_name='terminations'
) )
@ -99,7 +99,7 @@ class L2VPNTermination(NetBoxModel):
clone_fields = ('l2vpn',) clone_fields = ('l2vpn',)
prerequisite_models = ( prerequisite_models = (
'ipam.L2VPN', 'vpn.L2VPN',
) )
class Meta: class Meta:
@ -107,7 +107,7 @@ class L2VPNTermination(NetBoxModel):
constraints = ( constraints = (
models.UniqueConstraint( models.UniqueConstraint(
fields=('assigned_object_type', 'assigned_object_id'), fields=('assigned_object_type', 'assigned_object_id'),
name='ipam_l2vpntermination_assigned_object' name='vpn_l2vpntermination_assigned_object'
), ),
) )
verbose_name = _('L2VPN termination') verbose_name = _('L2VPN termination')
@ -119,7 +119,7 @@ class L2VPNTermination(NetBoxModel):
return super().__str__() return super().__str__()
def get_absolute_url(self): def get_absolute_url(self):
return reverse('ipam:l2vpntermination', args=[self.pk]) return reverse('vpn:l2vpntermination', args=[self.pk])
def clean(self): def clean(self):
# Only check is assigned_object is set. Required otherwise we have an Integrity Error thrown. # Only check is assigned_object is set. Required otherwise we have an Integrity Error thrown.