diff --git a/netbox/templates/vpn/tunneltermination.html b/netbox/templates/vpn/tunneltermination.html index 458acb412..6f4e83ce0 100644 --- a/netbox/templates/vpn/tunneltermination.html +++ b/netbox/templates/vpn/tunneltermination.html @@ -20,17 +20,17 @@ - {% if object.interface.device %} + {% if object.termination.device %} {% trans "Device" %} - {% elif object.interface.virtual_machine %} + {% elif object.termination.virtual_machine %} {% trans "Virtual Machine" %} {% endif %} - {{ object.interface.parent_object|linkify }} + {{ object.termination.parent_object|linkify }} {% trans "Interface" %} - {{ object.interface|linkify }} + {{ object.termination|linkify }} {% trans "Outside IP" %} diff --git a/netbox/vpn/api/serializers.py b/netbox/vpn/api/serializers.py index 1c80d1f34..881d3d0c0 100644 --- a/netbox/vpn/api/serializers.py +++ b/netbox/vpn/api/serializers.py @@ -58,10 +58,10 @@ class TunnelTerminationSerializer(NetBoxModelSerializer): role = ChoiceField( choices=TunnelTerminationRoleChoices ) - interface_type = ContentTypeField( + termination_type = ContentTypeField( queryset=ContentType.objects.all() ) - interface = serializers.SerializerMethodField( + termination = serializers.SerializerMethodField( read_only=True ) outside_ip = NestedIPAddressSerializer( @@ -72,15 +72,15 @@ class TunnelTerminationSerializer(NetBoxModelSerializer): class Meta: model = TunnelTermination fields = ( - 'id', 'url', 'display', 'tunnel', 'role', 'interface_type', 'interface_id', 'interface', 'outside_ip', + 'id', 'url', 'display', 'tunnel', 'role', 'termination_type', 'termination_id', 'termination', 'outside_ip', 'tags', 'custom_fields', 'created', 'last_updated', ) @extend_schema_field(serializers.JSONField(allow_null=True)) - def get_interface(self, obj): - serializer = get_serializer_for_model(obj.interface, prefix=NESTED_SERIALIZER_PREFIX) + def get_termination(self, obj): + serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX) context = {'request': self.context['request']} - return serializer(obj.interface, context=context).data + return serializer(obj.termination, context=context).data class IKEProposalSerializer(NetBoxModelSerializer): diff --git a/netbox/vpn/forms/bulk_import.py b/netbox/vpn/forms/bulk_import.py index e31df31d1..b912153d2 100644 --- a/netbox/vpn/forms/bulk_import.py +++ b/netbox/vpn/forms/bulk_import.py @@ -78,12 +78,12 @@ class TunnelTerminationImportForm(NetBoxModelImportForm): to_field_name='name', help_text=_('Parent VM of assigned interface') ) - interface = CSVModelChoiceField( - label=_('Interface'), + termination = CSVModelChoiceField( + label=_('Termination'), queryset=Interface.objects.none(), # Can also refer to VMInterface required=False, to_field_name='name', - help_text=_('Assigned interface') + help_text=_('Device or virtual machine interface') ) outside_ip = CSVModelChoiceField( label=_('Outside IP'), @@ -103,21 +103,21 @@ class TunnelTerminationImportForm(NetBoxModelImportForm): if data: - # Limit interface queryset by assigned device/VM + # Limit termination queryset by assigned device/VM if data.get('device'): - self.fields['interface'].queryset = Interface.objects.filter( + self.fields['termination'].queryset = Interface.objects.filter( **{f"device__{self.fields['device'].to_field_name}": data['device']} ) elif data.get('virtual_machine'): - self.fields['interface'].queryset = VMInterface.objects.filter( + self.fields['termination'].queryset = VMInterface.objects.filter( **{f"virtual_machine__{self.fields['virtual_machine'].to_field_name}": data['virtual_machine']} ) def save(self, *args, **kwargs): - # Set interface assignment - if self.cleaned_data.get('interface'): - self.instance.interface = self.cleaned_data['interface'] + # Assign termination object + if self.cleaned_data.get('termination'): + self.instance.termination = self.cleaned_data['termination'] return super().save(*args, **kwargs) diff --git a/netbox/vpn/forms/model_forms.py b/netbox/vpn/forms/model_forms.py index e5cdcd42d..35fa2cad3 100644 --- a/netbox/vpn/forms/model_forms.py +++ b/netbox/vpn/forms/model_forms.py @@ -65,7 +65,7 @@ class TunnelCreateForm(TunnelForm): selector=True, label=_('Device') ) - termination1_interface = DynamicModelChoiceField( + termination1_termination = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, label=_('Interface'), @@ -100,7 +100,7 @@ class TunnelCreateForm(TunnelForm): selector=True, label=_('Device') ) - termination2_interface = DynamicModelChoiceField( + termination2_termination = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, label=_('Interface'), @@ -122,11 +122,11 @@ class TunnelCreateForm(TunnelForm): (_('Security'), ('ipsec_profile',)), (_('Tenancy'), ('tenant_group', 'tenant')), (_('First Termination'), ( - 'termination1_role', 'termination1_type', 'termination1_parent', 'termination1_interface', + 'termination1_role', 'termination1_type', 'termination1_parent', 'termination1_termination', 'termination1_outside_ip', )), (_('Second Termination'), ( - 'termination2_role', 'termination2_type', 'termination2_parent', 'termination2_interface', + 'termination2_role', 'termination2_type', 'termination2_parent', 'termination2_termination', 'termination2_outside_ip', )), ) @@ -137,8 +137,8 @@ class TunnelCreateForm(TunnelForm): if initial and initial.get('termination1_type') == TunnelTerminationTypeChoices.TYPE_VIRUTALMACHINE: self.fields['termination1_parent'].label = _('Virtual Machine') self.fields['termination1_parent'].queryset = VirtualMachine.objects.all() - self.fields['termination1_interface'].queryset = VMInterface.objects.all() - self.fields['termination1_interface'].widget.add_query_params({ + self.fields['termination1_termination'].queryset = VMInterface.objects.all() + self.fields['termination1_termination'].widget.add_query_params({ 'virtual_machine_id': '$termination1_parent', }) self.fields['termination1_outside_ip'].widget.add_query_params({ @@ -148,8 +148,8 @@ class TunnelCreateForm(TunnelForm): if initial and initial.get('termination2_type') == TunnelTerminationTypeChoices.TYPE_VIRUTALMACHINE: self.fields['termination2_parent'].label = _('Virtual Machine') self.fields['termination2_parent'].queryset = VirtualMachine.objects.all() - self.fields['termination2_interface'].queryset = VMInterface.objects.all() - self.fields['termination2_interface'].widget.add_query_params({ + self.fields['termination2_termination'].queryset = VMInterface.objects.all() + self.fields['termination2_termination'].widget.add_query_params({ 'virtual_machine_id': '$termination2_parent', }) self.fields['termination2_outside_ip'].widget.add_query_params({ @@ -162,7 +162,7 @@ class TunnelCreateForm(TunnelForm): # Validate attributes for each termination (if any) for term in ('termination1', 'termination2'): required_parameters = ( - f'{term}_role', f'{term}_parent', f'{term}_interface', + f'{term}_role', f'{term}_parent', f'{term}_termination', ) parameters = ( *required_parameters, @@ -179,20 +179,20 @@ class TunnelCreateForm(TunnelForm): instance = super().save(*args, **kwargs) # Create first termination - if self.cleaned_data['termination1_interface']: + if self.cleaned_data['termination1_termination']: TunnelTermination.objects.create( tunnel=instance, role=self.cleaned_data['termination1_role'], - interface=self.cleaned_data['termination1_interface'], + termination=self.cleaned_data['termination1_termination'], outside_ip=self.cleaned_data['termination1_outside_ip'], ) # Create second termination, if defined - if self.cleaned_data['termination2_interface']: + if self.cleaned_data['termination2_termination']: TunnelTermination.objects.create( tunnel=instance, role=self.cleaned_data['termination2_role'], - interface=self.cleaned_data['termination2_interface'], + termination=self.cleaned_data['termination2_termination'], outside_ip=self.cleaned_data.get('termination1_outside_ip'), ) @@ -213,7 +213,7 @@ class TunnelTerminationForm(NetBoxModelForm): selector=True, label=_('Device') ) - interface = DynamicModelChoiceField( + termination = DynamicModelChoiceField( queryset=Interface.objects.all(), label=_('Interface'), query_params={ @@ -230,13 +230,13 @@ class TunnelTerminationForm(NetBoxModelForm): ) fieldsets = ( - (None, ('tunnel', 'role', 'type', 'parent', 'interface', 'outside_ip', 'tags')), + (None, ('tunnel', 'role', 'type', 'parent', 'termination', 'outside_ip', 'tags')), ) class Meta: model = TunnelTermination fields = [ - 'tunnel', 'role', 'interface', 'outside_ip', 'tags', + 'tunnel', 'role', 'termination', 'outside_ip', 'tags', ] def __init__(self, *args, initial=None, **kwargs): @@ -245,8 +245,8 @@ class TunnelTerminationForm(NetBoxModelForm): if initial and initial.get('type') == TunnelTerminationTypeChoices.TYPE_VIRUTALMACHINE: self.fields['parent'].label = _('Virtual Machine') self.fields['parent'].queryset = VirtualMachine.objects.all() - self.fields['interface'].queryset = VMInterface.objects.all() - self.fields['interface'].widget.add_query_params({ + self.fields['termination'].queryset = VMInterface.objects.all() + self.fields['termination'].widget.add_query_params({ 'virtual_machine_id': '$parent', }) self.fields['outside_ip'].widget.add_query_params({ @@ -254,14 +254,14 @@ class TunnelTerminationForm(NetBoxModelForm): }) if self.instance.pk: - self.fields['parent'].initial = self.instance.interface.parent_object - self.fields['interface'].initial = self.instance.interface + self.fields['parent'].initial = self.instance.termination.parent_object + self.fields['termination'].initial = self.instance.termination def clean(self): super().clean() - # Assign the interface - self.instance.interface = self.cleaned_data.get('interface') + # Set the terminated object + self.instance.termination = self.cleaned_data.get('termination') class IKEProposalForm(NetBoxModelForm): diff --git a/netbox/vpn/migrations/0001_initial.py b/netbox/vpn/migrations/0001_initial.py index ac0658b74..f5d9ae0c1 100644 --- a/netbox/vpn/migrations/0001_initial.py +++ b/netbox/vpn/migrations/0001_initial.py @@ -104,8 +104,8 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), ('role', models.CharField(default='peer', max_length=50)), - ('interface_id', models.PositiveBigIntegerField(blank=True, null=True)), - ('interface_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), + ('termination_id', models.PositiveBigIntegerField(blank=True, null=True)), + ('termination_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype')), ('outside_ip', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tunnel_termination', to='ipam.ipaddress')), ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), ('tunnel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='terminations', to='vpn.tunnel')), @@ -181,6 +181,6 @@ class Migration(migrations.Migration): ), migrations.AddConstraint( model_name='tunneltermination', - constraint=models.UniqueConstraint(fields=('interface_type', 'interface_id'), name='vpn_tunneltermination_interface', violation_error_message='An interface may be terminated to only one tunnel at a time.'), + constraint=models.UniqueConstraint(fields=('termination_type', 'termination_id'), name='vpn_tunneltermination_termination', violation_error_message='An object may be terminated to only one tunnel at a time.'), ), ] diff --git a/netbox/vpn/models/tunnels.py b/netbox/vpn/models/tunnels.py index 377f39706..f7390d0b4 100644 --- a/netbox/vpn/models/tunnels.py +++ b/netbox/vpn/models/tunnels.py @@ -82,18 +82,18 @@ class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLo choices=TunnelTerminationRoleChoices, default=TunnelTerminationRoleChoices.ROLE_PEER ) - interface_type = models.ForeignKey( + termination_type = models.ForeignKey( to='contenttypes.ContentType', on_delete=models.PROTECT, related_name='+' ) - interface_id = models.PositiveBigIntegerField( + termination_id = models.PositiveBigIntegerField( blank=True, null=True ) - interface = GenericForeignKey( - ct_field='interface_type', - fk_field='interface_id' + termination = GenericForeignKey( + ct_field='termination_type', + fk_field='termination_id' ) outside_ip = models.OneToOneField( to='ipam.IPAddress', @@ -111,9 +111,9 @@ class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLo ordering = ('tunnel', 'role', 'pk') constraints = ( models.UniqueConstraint( - fields=('interface_type', 'interface_id'), - name='%(app_label)s_%(class)s_interface', - violation_error_message=_("An interface may be terminated to only one tunnel at a time.") + 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') @@ -131,12 +131,12 @@ class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLo def clean(self): super().clean() - # Check that the selected Interface is not already attached to a Tunnel - if getattr(self.interface, 'tunnel_termination', None) and self.interface.tunnel_termination.pk != self.pk: + # 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({ - 'interface': _("Interface {name} is already attached to a tunnel ({tunnel}).").format( - name=self.interface.name, - tunnel=self.interface.tunnel_termination.tunnel + 'termination': _("{name} is already attached to a tunnel ({tunnel}).").format( + name=self.termination.name, + tunnel=self.termination.tunnel_termination.tunnel ) }) diff --git a/netbox/vpn/tables.py b/netbox/vpn/tables.py index 5e7d187c6..a174c5a43 100644 --- a/netbox/vpn/tables.py +++ b/netbox/vpn/tables.py @@ -59,18 +59,18 @@ class TunnelTerminationTable(TenancyColumnsMixin, NetBoxTable): role = columns.ChoiceFieldColumn( verbose_name=_('Role') ) - interface_parent = tables.Column( - accessor='interface__parent_object', + termination_parent = tables.Column( + accessor='termination__parent_object', linkify=True, orderable=False, verbose_name=_('Host') ) - interface = tables.Column( - verbose_name=_('Interface'), + termination = tables.Column( + verbose_name=_('Termination'), linkify=True ) ip_addresses = tables.ManyToManyColumn( - accessor=tables.A('interface__ip_addresses'), + accessor=tables.A('termination__ip_addresses'), orderable=False, linkify_item=True, verbose_name=_('IP Addresses') @@ -86,10 +86,12 @@ class TunnelTerminationTable(TenancyColumnsMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = TunnelTermination fields = ( - 'pk', 'id', 'tunnel', 'role', 'interface_parent', 'interface', 'ip_addresses', 'outside_ip', 'tags', + 'pk', 'id', 'tunnel', 'role', 'termination_parent', 'termination', 'ip_addresses', 'outside_ip', 'tags', 'created', 'last_updated', ) - default_columns = ('pk', 'id', 'tunnel', 'role', 'interface_parent', 'interface', 'ip_addresses', 'outside_ip') + default_columns = ( + 'pk', 'id', 'tunnel', 'role', 'termination_parent', 'termination', 'ip_addresses', 'outside_ip', + ) class IKEProposalTable(NetBoxTable): diff --git a/netbox/vpn/tests/test_api.py b/netbox/vpn/tests/test_api.py index cdb127ef1..9bfa297ab 100644 --- a/netbox/vpn/tests/test_api.py +++ b/netbox/vpn/tests/test_api.py @@ -96,17 +96,17 @@ class TunnelTerminationTest(APIViewTestCases.APIViewTestCase): TunnelTermination( tunnel=tunnel, role=TunnelTerminationRoleChoices.ROLE_HUB, - interface=interfaces[0] + termination=interfaces[0] ), TunnelTermination( tunnel=tunnel, role=TunnelTerminationRoleChoices.ROLE_HUB, - interface=interfaces[1] + termination=interfaces[1] ), TunnelTermination( tunnel=tunnel, role=TunnelTerminationRoleChoices.ROLE_HUB, - interface=interfaces[2] + termination=interfaces[2] ), ) TunnelTermination.objects.bulk_create(tunnel_terminations) @@ -115,20 +115,20 @@ class TunnelTerminationTest(APIViewTestCases.APIViewTestCase): { 'tunnel': tunnel.pk, 'role': TunnelTerminationRoleChoices.ROLE_PEER, - 'interface_type': 'dcim.interface', - 'interface_id': interfaces[3].pk, + 'termination_type': 'dcim.interface', + 'termination_id': interfaces[3].pk, }, { 'tunnel': tunnel.pk, 'role': TunnelTerminationRoleChoices.ROLE_PEER, - 'interface_type': 'dcim.interface', - 'interface_id': interfaces[4].pk, + 'termination_type': 'dcim.interface', + 'termination_id': interfaces[4].pk, }, { 'tunnel': tunnel.pk, 'role': TunnelTerminationRoleChoices.ROLE_PEER, - 'interface_type': 'dcim.interface', - 'interface_id': interfaces[5].pk, + 'termination_type': 'dcim.interface', + 'termination_id': interfaces[5].pk, }, ] diff --git a/netbox/vpn/tests/test_filtersets.py b/netbox/vpn/tests/test_filtersets.py index 9e53d8cd2..db84cd412 100644 --- a/netbox/vpn/tests/test_filtersets.py +++ b/netbox/vpn/tests/test_filtersets.py @@ -147,19 +147,19 @@ class TunnelTerminationTestCase(TestCase, ChangeLoggedFilterSetTests): TunnelTermination( tunnel=tunnels[0], role=TunnelTerminationRoleChoices.ROLE_HUB, - interface=interfaces[0], + termination=interfaces[0], outside_ip=ip_addresses[0] ), TunnelTermination( tunnel=tunnels[1], role=TunnelTerminationRoleChoices.ROLE_SPOKE, - interface=interfaces[1], + termination=interfaces[1], outside_ip=ip_addresses[1] ), TunnelTermination( tunnel=tunnels[2], role=TunnelTerminationRoleChoices.ROLE_PEER, - interface=interfaces[2], + termination=interfaces[2], outside_ip=ip_addresses[2] ), ) diff --git a/netbox/vpn/tests/test_views.py b/netbox/vpn/tests/test_views.py index c0c22d1ec..433eca467 100644 --- a/netbox/vpn/tests/test_views.py +++ b/netbox/vpn/tests/test_views.py @@ -1,5 +1,3 @@ -from django.contrib.contenttypes.models import ContentType - from dcim.choices import InterfaceTypeChoices from dcim.models import Interface from vpn.choices import * @@ -66,7 +64,7 @@ class TunnelTestCase(ViewTestCases.PrimaryObjectViewTestCase): class TunnelTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = TunnelTermination # TODO: Workaround for conflict between form field and GFK - validation_excluded_fields = ('interface',) + validation_excluded_fields = ('termination',) @classmethod def setUpTestData(cls): @@ -92,17 +90,17 @@ class TunnelTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase): TunnelTermination( tunnel=tunnel, role=TunnelTerminationRoleChoices.ROLE_HUB, - interface=interfaces[0] + termination=interfaces[0] ), TunnelTermination( tunnel=tunnel, role=TunnelTerminationRoleChoices.ROLE_SPOKE, - interface=interfaces[1] + termination=interfaces[1] ), TunnelTermination( tunnel=tunnel, role=TunnelTerminationRoleChoices.ROLE_SPOKE, - interface=interfaces[2] + termination=interfaces[2] ), ) TunnelTermination.objects.bulk_create(tunnel_terminations) @@ -114,12 +112,12 @@ class TunnelTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'role': TunnelTerminationRoleChoices.ROLE_PEER, 'type': TunnelTerminationTypeChoices.TYPE_DEVICE, 'parent': device.pk, - 'interface': interfaces[6].pk, + 'termination': interfaces[6].pk, 'tags': [t.pk for t in tags], } cls.csv_data = ( - "tunnel,role,device,interface", + "tunnel,role,device,termination", "Tunnel 1,peer,Device 1,Interface 4", "Tunnel 1,peer,Device 1,Interface 5", "Tunnel 1,peer,Device 1,Interface 6",