Closes #13086: Virtual circuits (#17933)

* WIP

* Add API tests

* Add remaining tests

* Add model docs

* Show virtual circuit connections on interfaces

* Misc cleanup per PR feedback

* Renumber migration

* Support nested terminations for virtual circuit bulk import
This commit is contained in:
Jeremy Stretch
2024-11-19 10:58:39 -05:00
committed by GitHub
parent 7376314821
commit d2168b107f
36 changed files with 2164 additions and 15 deletions
+11 -1
View File
@@ -4,7 +4,7 @@ from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from circuits.models import CircuitTermination
from circuits.models import CircuitTermination, VirtualCircuit, VirtualCircuitTermination
from extras.filtersets import LocalConfigContextFilterSet
from extras.models import ConfigTemplate
from ipam.filtersets import PrimaryIPFilterSet
@@ -1842,6 +1842,16 @@ class InterfaceFilterSet(
queryset=WirelessLink.objects.all(),
label=_('Wireless link')
)
virtual_circuit_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_circuit_termination__virtual_circuit',
queryset=VirtualCircuit.objects.all(),
label=_('Virtual circuit (ID)'),
)
virtual_circuit_termination_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_circuit_termination',
queryset=VirtualCircuitTermination.objects.all(),
label=_('Virtual circuit termination (ID)'),
)
class Meta:
model = Interface
+8
View File
@@ -998,6 +998,14 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
def l2vpn_termination(self):
return self.l2vpn_terminations.first()
@cached_property
def connected_endpoints(self):
# If this is a virtual interface, return the remote endpoint of the connected
# virtual circuit, if any.
if self.is_virtual and hasattr(self, 'virtual_circuit_termination'):
return self.virtual_circuit_termination.peer_terminations
return super().connected_endpoints
#
# Pass-through ports
+8
View File
@@ -649,6 +649,14 @@ class InterfaceTable(BaseInterfaceTable, ModularDeviceComponentTable, PathEndpoi
url_name='dcim:interface_list'
)
# Override PathEndpointTable.connection to accommodate virtual circuits
connection = columns.TemplateColumn(
accessor='_path__destinations',
template_code=INTERFACE_LINKTERMINATION,
verbose_name=_('Connection'),
orderable=False
)
class Meta(DeviceComponentTable.Meta):
model = models.Interface
fields = (
+14
View File
@@ -10,6 +10,20 @@ LINKTERMINATION = """
{% endfor %}
"""
INTERFACE_LINKTERMINATION = """
{% load i18n %}
{% if record.is_virtual and record.virtual_circuit_termination %}
{% for termination in record.connected_endpoints %}
<a href="{{ termination.interface.parent_object.get_absolute_url }}">{{ termination.interface.parent_object }}</a>
<i class="mdi mdi-chevron-right"></i>
<a href="{{ termination.interface.get_absolute_url }}">{{ termination.interface }}</a>
{% trans "via" %}
<a href="{{ termination.parent_object.get_absolute_url }}">{{ termination.parent_object }}</a>
{% if not forloop.last %}<br />{% endif %}
{% endfor %}
{% else %}""" + LINKTERMINATION + """{% endif %}
"""
CABLE_LENGTH = """
{% load helpers %}
{% if record.length %}{{ record.length|floatformat:"-2" }} {{ record.length_unit }}{% endif %}