Rename interface to termination on CableTermination

This commit is contained in:
Jeremy Stretch 2023-11-27 13:05:18 -05:00
parent dbed518354
commit dda4071b3a
10 changed files with 84 additions and 84 deletions

View File

@ -20,17 +20,17 @@
</tr>
<tr>
<th scope="row">
{% if object.interface.device %}
{% if object.termination.device %}
{% trans "Device" %}
{% elif object.interface.virtual_machine %}
{% elif object.termination.virtual_machine %}
{% trans "Virtual Machine" %}
{% endif %}
</th>
<td>{{ object.interface.parent_object|linkify }}</td>
<td>{{ object.termination.parent_object|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Interface" %}</th>
<td>{{ object.interface|linkify }}</td>
<td>{{ object.termination|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Outside IP" %}</th>

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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.'),
),
]

View File

@ -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
)
})

View File

@ -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):

View File

@ -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,
},
]

View File

@ -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]
),
)

View File

@ -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",