Update cable connection forms

This commit is contained in:
jeremystretch 2022-04-29 15:16:35 -04:00
parent 1f4ad444ae
commit 5d37f9f975
18 changed files with 185 additions and 212 deletions

View File

@ -962,14 +962,8 @@ class InventoryItemRoleSerializer(NetBoxModelSerializer):
class CableSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail')
termination_a_type = ContentTypeField(
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
)
termination_b_type = ContentTypeField(
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
)
termination_a = serializers.SerializerMethodField(read_only=True)
termination_b = serializers.SerializerMethodField(read_only=True)
# termination_a = serializers.SerializerMethodField(read_only=True)
# termination_b = serializers.SerializerMethodField(read_only=True)
status = ChoiceField(choices=LinkStatusChoices, required=False)
tenant = NestedTenantSerializer(required=False, allow_null=True)
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
@ -977,9 +971,8 @@ class CableSerializer(NetBoxModelSerializer):
class Meta:
model = Cable
fields = [
'id', 'url', 'display', 'termination_a_type', 'termination_a_ids', 'termination_a', 'termination_b_type',
'termination_b_ids', 'termination_b', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit',
'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'display', 'type', 'status', 'tenant', 'label', 'color',
'length', 'length_unit', 'tags', 'custom_fields', 'created', 'last_updated',
]
def _get_termination(self, obj, side):

View File

@ -17,18 +17,47 @@ __all__ = (
)
class ConnectCableToDeviceForm(TenancyForm, NetBoxModelForm):
"""
Base form for connecting a Cable to a Device component
"""
# Termination A
termination_a_ids = DynamicModelMultipleChoiceField(
class BaseCableConnectionForm(TenancyForm, NetBoxModelForm):
a_terminations = DynamicModelMultipleChoiceField(
queryset=Interface.objects.all(),
label='Name',
disabled_indicator='_occupied'
)
b_terminations = DynamicModelMultipleChoiceField(
queryset=Interface.objects.all(),
label='Name',
disabled_indicator='_occupied'
)
# Termination B
def save(self, commit=True):
instance = super().save(commit=commit)
# Create CableTermination instances
terminations = []
terminations.extend([
CableTermination(cable=instance, cable_end='A', termination=termination)
for termination in self.cleaned_data['a_terminations']
])
terminations.extend([
CableTermination(cable=instance, cable_end='B', termination=termination)
for termination in self.cleaned_data['b_terminations']
])
if commit:
CableTermination.objects.bulk_create(terminations)
else:
instance.terminations = [
*self.cleaned_data['a_terminations'],
*self.cleaned_data['b_terminations'],
]
return instance
class ConnectCableToDeviceForm(BaseCableConnectionForm):
"""
Base form for connecting a Cable to a Device component
"""
termination_b_region = DynamicModelChoiceField(
queryset=Region.objects.all(),
label='Region',
@ -83,17 +112,12 @@ class ConnectCableToDeviceForm(TenancyForm, NetBoxModelForm):
'rack_id': '$termination_b_rack',
}
)
termination_b_ids = DynamicModelMultipleChoiceField(
queryset=Interface.objects.all(),
label='Name',
disabled_indicator='_occupied'
)
class Meta:
model = Cable
fields = [
'termination_a_ids', 'termination_b_region', 'termination_b_sitegroup', 'termination_b_site',
'termination_b_rack', 'termination_b_device', 'termination_b_ids', 'type', 'status', 'tenant_group',
'a_terminations', 'termination_b_region', 'termination_b_sitegroup', 'termination_b_site',
'termination_b_rack', 'termination_b_device', 'b_terminations', 'type', 'status', 'tenant_group',
'tenant', 'label', 'color', 'length', 'length_unit', 'tags',
]
widgets = {
@ -102,17 +126,9 @@ class ConnectCableToDeviceForm(TenancyForm, NetBoxModelForm):
'length_unit': StaticSelect,
}
def clean_termination_a_ids(self):
# Return the PK rather than the object
return [getattr(obj, 'pk') for obj in self.cleaned_data['termination_a_ids']]
def clean_termination_b_ids(self):
# Return the PK rather than the object
return [getattr(obj, 'pk') for obj in self.cleaned_data['termination_b_ids']]
class ConnectCableToConsolePortForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=ConsolePort.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -123,7 +139,7 @@ class ConnectCableToConsolePortForm(ConnectCableToDeviceForm):
class ConnectCableToConsoleServerPortForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=ConsoleServerPort.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -134,7 +150,7 @@ class ConnectCableToConsoleServerPortForm(ConnectCableToDeviceForm):
class ConnectCableToPowerPortForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=PowerPort.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -145,7 +161,7 @@ class ConnectCableToPowerPortForm(ConnectCableToDeviceForm):
class ConnectCableToPowerOutletForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=PowerOutlet.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -156,7 +172,7 @@ class ConnectCableToPowerOutletForm(ConnectCableToDeviceForm):
class ConnectCableToInterfaceForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=Interface.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -168,7 +184,7 @@ class ConnectCableToInterfaceForm(ConnectCableToDeviceForm):
class ConnectCableToFrontPortForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=FrontPort.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -179,7 +195,7 @@ class ConnectCableToFrontPortForm(ConnectCableToDeviceForm):
class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=RearPort.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -189,15 +205,7 @@ class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
)
class ConnectCableToCircuitTerminationForm(TenancyForm, NetBoxModelForm):
# Termination A
termination_a_ids = DynamicModelMultipleChoiceField(
queryset=Interface.objects.all(),
label='Side',
disabled_indicator='_occupied'
)
# Termination B
class ConnectCableToCircuitTerminationForm(BaseCableConnectionForm):
termination_b_provider = DynamicModelChoiceField(
queryset=Provider.objects.all(),
label='Provider',
@ -236,7 +244,7 @@ class ConnectCableToCircuitTerminationForm(TenancyForm, NetBoxModelForm):
'site_id': '$termination_b_site',
}
)
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=CircuitTermination.objects.all(),
label='Side',
disabled_indicator='_occupied',
@ -247,29 +255,13 @@ class ConnectCableToCircuitTerminationForm(TenancyForm, NetBoxModelForm):
class Meta(ConnectCableToDeviceForm.Meta):
fields = [
'termination_a_ids', 'termination_b_provider', 'termination_b_region', 'termination_b_sitegroup',
'termination_b_site', 'termination_b_circuit', 'termination_b_ids', 'type', 'status', 'tenant_group',
'a_terminations', 'termination_b_provider', 'termination_b_region', 'termination_b_sitegroup',
'termination_b_site', 'termination_b_circuit', 'b_terminations', 'type', 'status', 'tenant_group',
'tenant', 'label', 'color', 'length', 'length_unit', 'tags',
]
def clean_termination_a_id(self):
# Return the PK rather than the object
return getattr(self.cleaned_data['termination_a_id'], 'pk', None)
def clean_termination_b_id(self):
# Return the PK rather than the object
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)
class ConnectCableToPowerFeedForm(TenancyForm, NetBoxModelForm):
# Termination A
termination_a_ids = DynamicModelMultipleChoiceField(
queryset=Interface.objects.all(),
label='Name',
disabled_indicator='_occupied'
)
# Termination B
class ConnectCableToPowerFeedForm(BaseCableConnectionForm):
termination_b_region = DynamicModelChoiceField(
queryset=Region.objects.all(),
label='Region',
@ -312,7 +304,7 @@ class ConnectCableToPowerFeedForm(TenancyForm, NetBoxModelForm):
'location_id': '$termination_b_location',
}
)
termination_b_ids = DynamicModelMultipleChoiceField(
b_terminations = DynamicModelMultipleChoiceField(
queryset=PowerFeed.objects.all(),
label='Name',
disabled_indicator='_occupied',
@ -323,15 +315,7 @@ class ConnectCableToPowerFeedForm(TenancyForm, NetBoxModelForm):
class Meta(ConnectCableToDeviceForm.Meta):
fields = [
'termination_a_ids', 'termination_b_region', 'termination_b_sitegroup', 'termination_b_site',
'termination_b_location', 'termination_b_powerpanel', 'termination_b_ids', 'type', 'status', 'tenant_group',
'a_terminations', 'termination_b_region', 'termination_b_sitegroup', 'termination_b_site',
'termination_b_location', 'termination_b_powerpanel', 'b_terminations', 'type', 'status', 'tenant_group',
'tenant', 'label', 'color', 'length', 'length_unit', 'tags',
]
def clean_termination_a_id(self):
# Return the PK rather than the object
return getattr(self.cleaned_data['termination_a_id'], 'pk', None)
def clean_termination_b_id(self):
# Return the PK rather than the object
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)

View File

@ -168,6 +168,16 @@ class Cable(NetBoxModel):
def get_status_color(self):
return LinkStatusChoices.colors.get(self.status)
def get_a_terminations(self):
return [
term.termination for term in CableTermination.objects.filter(cable=self, cable_end='A')
]
def get_b_terminations(self):
return [
term.termination for term in CableTermination.objects.filter(cable=self, cable_end='B')
]
class CableTermination(models.Model):
"""

View File

@ -81,34 +81,27 @@ def update_connected_endpoints(instance, created, raw=False, **kwargs):
# TODO: Update link peer fields
# Cache the Cable on its termination points
for term in instance.termination_a:
if term.cable != instance:
for term in instance.terminations.all():
if term.termination.cable != instance:
logger.debug(f"Updating termination A for cable {instance}: {term}")
term.cable = instance
# term._link_peer = instance.termination_b
term.save()
for term in instance.termination_b:
if term.cable != instance:
logger.debug(f"Updating termination B for cable {instance}")
term.cable = instance
# term._link_peer = instance.termination_a
term.termination.cable = instance
term.save()
# Create/update cable paths
if created:
for termination in [*instance.termination_a, *instance.termination_b]:
if isinstance(termination, PathEndpoint):
create_cablepath(termination)
else:
rebuild_paths(termination)
elif instance.status != instance._orig_status:
# We currently don't support modifying either termination of an existing Cable. (This
# may change in the future.) However, we do need to capture status changes and update
# any CablePaths accordingly.
if instance.status != LinkStatusChoices.STATUS_CONNECTED:
CablePath.objects.filter(path__contains=instance).update(is_active=False)
else:
rebuild_paths(instance)
# # Create/update cable paths
# if created:
# for term in instance.terminations.all():
# if isinstance(term.termination, PathEndpoint):
# create_cablepath(term.termination)
# else:
# rebuild_paths(term.termination)
# elif instance.status != instance._orig_status:
# # We currently don't support modifying either termination of an existing Cable. (This
# # may change in the future.) However, we do need to capture status changes and update
# # any CablePaths accordingly.
# if instance.status != LinkStatusChoices.STATUS_CONNECTED:
# CablePath.objects.filter(path__contains=instance).update(is_active=False)
# else:
# rebuild_paths(instance)
@receiver(post_delete, sender=Cable)

View File

@ -139,9 +139,9 @@ CONSOLEPORT_BUTTONS = """
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'dcim:consoleport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.consoleserverport&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Console Server Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.consoleserverport&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Console Server Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_consoleports' pk=object.pk %}">Rear Port</a></li>
</ul>
</span>
{% else %}
@ -171,9 +171,9 @@ CONSOLESERVERPORT_BUTTONS = """
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'dcim:consoleserverport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.consoleport&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Console Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleserverport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleserverport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleserverport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.consoleport&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Console Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleserverport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:consoleserverport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_consoleserverports' pk=object.pk %}">Rear Port</a></li>
</ul>
</span>
{% else %}
@ -203,8 +203,8 @@ POWERPORT_BUTTONS = """
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'dcim:powerport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.poweroutlet&return_url={% url 'dcim:device_powerports' pk=object.pk %}">Power Outlet</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:powerport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.powerfeed&return_url={% url 'dcim:device_powerports' pk=object.pk %}">Power Feed</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:powerport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.poweroutlet&return_url={% url 'dcim:device_powerports' pk=object.pk %}">Power Outlet</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:powerport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.powerfeed&return_url={% url 'dcim:device_powerports' pk=object.pk %}">Power Feed</a></li>
</ul>
</span>
{% else %}
@ -230,7 +230,7 @@ POWEROUTLET_BUTTONS = """
<a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-transit-connection-variant" aria-hidden="true"></i></a>
<a href="#" class="btn btn-outline-dark btn-sm disabled"><i class="mdi mdi-lan-connect" aria-hidden="true"></i></a>
{% if not record.mark_connected %}
<a href="{% url 'dcim:poweroutlet_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.powerport&return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}" title="Connect" class="btn btn-success btn-sm">
<a href="{% url 'dcim:poweroutlet_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.powerport&return_url={% url 'dcim:device_poweroutlets' pk=object.pk %}" title="Connect" class="btn btn-success btn-sm">
<i class="mdi mdi-ethernet-cable" aria-hidden="true"></i>
</a>
{% else %}
@ -280,10 +280,10 @@ INTERFACE_BUTTONS = """
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.interface&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=circuits.circuittermination&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Circuit Termination</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.interface&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ record.pk }}&termination_b_type=circuits.circuittermination&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Circuit Termination</a></li>
</ul>
</span>
{% else %}
@ -319,12 +319,12 @@ FRONTPORT_BUTTONS = """
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.interface&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.consoleserverport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Console Server Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.consoleport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Console Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=circuits.circuittermination&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Circuit Termination</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.interface&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.consoleserverport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Console Server Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.consoleport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Console Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=circuits.circuittermination&return_url={% url 'dcim:device_frontports' pk=object.pk %}">Circuit Termination</a></li>
</ul>
</span>
{% else %}
@ -356,12 +356,12 @@ REARPORT_BUTTONS = """
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.interface&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.consoleserverport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Console Server Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.consoleport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Console Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ record.pk }}&termination_b_type=circuits.circuitterminations&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Circuit Termination</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.interface&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.consoleserverport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Console Server Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.consoleport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Console Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.frontport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=dcim.rearport&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ record.pk }}&termination_b_type=circuits.circuitterminations&return_url={% url 'dcim:device_rearports' pk=object.pk %}">Circuit Termination</a></li>
</ul>
</span>
{% else %}

View File

@ -2829,24 +2829,14 @@ class CableCreateView(generic.ObjectEditView):
# Always return a new instance
return self.queryset.model()
def alter_object(self, obj, request, url_args, url_kwargs):
termination_a_type = url_kwargs.get('termination_a_type')
termination_a_ids = request.GET.get('termination_a_ids', [])
app_label, model = request.GET.get('termination_b_type').split('.')
self.termination_b_type = ContentType.objects.get(app_label=app_label, model=model)
# Initialize Cable termination attributes
obj.termination_a_type = ContentType.objects.get_for_model(termination_a_type)
obj.termination_a_ids = termination_a_type.objects.filter(pk__in=termination_a_ids)
obj.termination_b_type = self.termination_b_type
return obj
def get(self, request, *args, **kwargs):
obj = self.get_object(**kwargs)
obj = self.alter_object(obj, request, args, kwargs)
initial_data = request.GET
app_label, model = request.GET.get('termination_b_type').split('.')
termination_b_type = ContentType.objects.get(app_label=app_label, model=model)
# TODO
# # Set initial site and rack based on side A termination (if not already set)
# termination_a_site = getattr(obj.termination_a.parent_object, 'site', None)
@ -2857,12 +2847,17 @@ class CableCreateView(generic.ObjectEditView):
form = self.form(instance=obj, initial=initial_data)
# Set the queryset of termination A
form.fields['termination_a_ids'].queryset = kwargs['termination_a_type'].objects.all()
form.fields['a_terminations'].queryset = kwargs['termination_a_type'].objects.all()
# TODO Find a better way to infer the near-end parent object
termination_a = kwargs['termination_a_type'].objects.filter(pk__in=initial_data['a_terminations']).first()
return render(request, self.template_name, {
'obj': obj,
'obj_type': Cable._meta.verbose_name,
'termination_b_type': self.termination_b_type.name,
'termination_a_type': kwargs['termination_a_type']._meta.model_name,
'termination_a': termination_a,
'termination_b_type': termination_b_type.name,
'form': form,
'return_url': self.get_return_url(request, obj),
})

View File

@ -70,10 +70,10 @@
<span class="mdi mdi-ethernet-cable" aria-hidden="true"></span> Connect
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?termination_a_id={{ termination.pk }}&termination_b_type=dcim.interface&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?termination_a_id={{ termination.pk }}&termination_b_type=dcim.frontport&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?termination_a_id={{ termination.pk }}&termination_b_type=dcim.rearport&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?termination_a_id={{ termination.pk }}&termination_b_type=circuits.circuittermination&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Circuit Termination</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?a_terminations={{ termination.pk }}&termination_b_type=dcim.interface&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Interface</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?a_terminations={{ termination.pk }}&termination_b_type=dcim.frontport&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Front Port</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?a_terminations={{ termination.pk }}&termination_b_type=dcim.rearport&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Rear Port</a></li>
<li><a class="dropdown-item" href="{% url 'circuits:circuittermination_connect' %}?a_terminations={{ termination.pk }}&termination_b_type=circuits.circuittermination&termination_b_site={{ termination.site.pk }}&return_url={{ object.get_absolute_url }}">Circuit Termination</a></li>
</ul>
</div>
{% endif %}

View File

@ -63,13 +63,13 @@
<div class="card">
<h5 class="card-header">Termination A</h5>
<div class="card-body">
{% include 'dcim/inc/cable_termination.html' with termination=object.termination_a %}
{% include 'dcim/inc/cable_termination.html' with terminations=object.get_a_terminations %}
</div>
</div>
<div class="card">
<h5 class="card-header">Termination B</h5>
<div class="card-body">
{% include 'dcim/inc/cable_termination.html' with termination=object.termination_b %}
{% include 'dcim/inc/cable_termination.html' with terminations=object.get_b_terminations %}
</div>
</div>
{% plugin_right_page object %}

View File

@ -15,9 +15,8 @@
{% block content-wrapper %}
<div class="tab-content">
{% with termination_a=form.instance.termination_a.0 %}
{% render_errors form %}
<form method="post">
{% render_errors form %}
<form method="post">
{% csrf_token %}
{% for field in form.hidden_fields %}
{{ field }}
@ -27,7 +26,27 @@
<div class="card h-100">
<h5 class="card-header offset-sm-3">A Side</h5>
<div class="card-body">
{% if termination_a.device %}
{% if termination_a_type == 'circuit' %}
{# Circuit termination #}
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Site</label>
<div class="col">
<input class="form-control" value="{{ termination_a.site }}" disabled />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Provider</label>
<div class="col">
<input class="form-control" value="{{ termination_a.circuit.provider }}" disabled />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Circuit</label>
<div class="col">
<input class="form-control" value="{{ termination_a.circuit.cid }}" disabled />
</div>
</div>
{% else %}
{# Device component #}
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Region</label>
@ -71,28 +90,8 @@
<input class="form-control" value="{{ termination_a|meta:"verbose_name"|capfirst }}" disabled />
</div>
</div>
{% else %}
{# Circuit termination #}
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Site</label>
<div class="col">
<input class="form-control" value="{{ termination_a.site }}" disabled />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Provider</label>
<div class="col">
<input class="form-control" value="{{ termination_a.circuit.provider }}" disabled />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-3 col-form-label text-lg-end">Circuit</label>
<div class="col">
<input class="form-control" value="{{ termination_a.circuit.cid }}" disabled />
</div>
</div>
{% endif %}
{% render_field form.termination_a_ids %}
{% render_field form.a_terminations %}
</div>
</div>
</div>
@ -148,7 +147,7 @@
<input class="form-control" value="{{ termination_b_type|capfirst }}" disabled />
</div>
</div>
{% render_field form.termination_b_ids %}
{% render_field form.b_terminations %}
</div>
</div>
</div>
@ -170,6 +169,5 @@
</div>
</div>
</form>
{% endwith %}
</div>
{% endblock %}

View File

@ -113,7 +113,7 @@
<li>
<a
class="dropdown-item"
href="{% url 'dcim:consoleport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.consoleserverport&return_url={{ object.get_absolute_url }}"
href="{% url 'dcim:consoleport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.consoleserverport&return_url={{ object.get_absolute_url }}"
>
Console Server Port
</a>
@ -121,7 +121,7 @@
<li>
<a
class="dropdown-item"
href="{% url 'dcim:consoleport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}"
href="{% url 'dcim:consoleport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}"
>
Front Port
</a>
@ -129,7 +129,7 @@
<li>
<a
class="dropdown-item"
href="{% url 'dcim:consoleport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}"
href="{% url 'dcim:consoleport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}"
>
Rear Port
</a>

View File

@ -115,7 +115,7 @@
<li>
<a
class="dropdown-item"
href="{% url 'dcim:consoleserverport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.consoleport&return_url={{ object.get_absolute_url }}"
href="{% url 'dcim:consoleserverport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.consoleport&return_url={{ object.get_absolute_url }}"
>
Console Port
</a>
@ -123,7 +123,7 @@
<li>
<a
class="dropdown-item"
href="{% url 'dcim:consoleserverport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}"
href="{% url 'dcim:consoleserverport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}"
>
Front Port
</a>
@ -131,7 +131,7 @@
<li>
<a
class="dropdown-item"
href="{% url 'dcim:consoleserverport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}"
href="{% url 'dcim:consoleserverport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}"
>
Rear Port
</a>

View File

@ -105,22 +105,22 @@
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.interface&return_url={{ object.get_absolute_url }}">Interface</a>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.interface&return_url={{ object.get_absolute_url }}">Interface</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.consoleserverport&return_url={{ object.get_absolute_url }}">Console Server Port</a>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.consoleserverport&return_url={{ object.get_absolute_url }}">Console Server Port</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.consoleport&return_url={{ object.get_absolute_url }}">Console Port</a>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.consoleport&return_url={{ object.get_absolute_url }}">Console Port</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}">Front Port</a>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}">Front Port</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}">Rear Port</a>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}">Rear Port</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">Circuit Termination</a>
<a class="dropdown-item" href="{% url 'dcim:frontport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">Circuit Termination</a>
</li>
</ul>
</div>

View File

@ -1,29 +1,29 @@
{% load helpers %}
<table class="table table-hover panel-body attr-table">
{% if termination.0.device %}
{% if terminations.0.device %}
{# Device component #}
<tr>
<td>Device</td>
<td>{{ termination.0.device|linkify }}</td>
<td>{{ terminations.0.device|linkify }}</td>
</tr>
<tr>
<td>Site</td>
<td>{{ termination.0.device.site|linkify }}</td>
<td>{{ terminations.0.device.site|linkify }}</td>
</tr>
{% if termination.0.device.rack %}
{% if terminations.0.device.rack %}
<tr>
<td>Rack</td>
<td>{{ termination.0.device.rack|linkify }}</td>
<td>{{ terminations.0.device.rack|linkify }}</td>
</tr>
{% endif %}
<tr>
<td>Type</td>
<td>{{ termination.0|meta:"verbose_name"|capfirst }}</td>
<td>{{ terminations.0|meta:"verbose_name"|capfirst }}</td>
</tr>
<tr>
<td>Component(s)</td>
<td>Name(s)</td>
<td>
{% for term in termination %}
{% for term in terminations %}
{{ term|linkify }}{% if not forloop.last %},{% endif %}
{% endfor %}
</td>
@ -32,12 +32,12 @@
{# Circuit termination #}
<tr>
<td>Provider</td>
<td>{{ termination.0.circuit.provider|linkify }}</td>
<td>{{ terminations.0.circuit.provider|linkify }}</td>
</tr>
<tr>
<td>Circuit</td>
<td>
{% for term in termination %}
{% for term in terminations %}
{{ term.circuit|linkify }} ({{ term }}){% if not forloop.last %},{% endif %}
{% endfor %}
</td>

View File

@ -251,22 +251,22 @@
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.interface&return_url={{ object.get_absolute_url }}">
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.interface&return_url={{ object.get_absolute_url }}">
Interface
</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}">
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}">
Front Port
</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}">
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}">
Rear Port
</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">
<a class="dropdown-item" href="{% url 'dcim:interface_connect' %}?a_terminations={{ object.pk }}&termination_b_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">
Circuit Termination
</a>
</li>

View File

@ -158,7 +158,7 @@
{% if not object.mark_connected and not object.cable %}
<div class="card-footer">
{% if perms.dcim.add_cable %}
<a href="{% url 'dcim:powerfeed_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.powerport&return_url={{ object.get_absolute_url }}"
<a href="{% url 'dcim:powerfeed_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.powerport&return_url={{ object.get_absolute_url }}"
class="btn btn-primary btn-sm float-end">
<i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> Connect
</a>

View File

@ -111,7 +111,7 @@
<div class="text-muted">
Not Connected
{% if perms.dcim.add_cable %}
<a href="{% url 'dcim:poweroutlet_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.powerport&return_url={{ object.get_absolute_url }}" title="Connect" class="btn btn-primary btn-sm float-end">
<a href="{% url 'dcim:poweroutlet_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.powerport&return_url={{ object.get_absolute_url }}" title="Connect" class="btn btn-primary btn-sm float-end">
<i class="mdi mdi-ethernet-cable" aria-hidden="true"></i> Connect
</a>
{% endif %}

View File

@ -117,10 +117,10 @@
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-link" href="{% url 'dcim:powerport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.poweroutlet&return_url={{ object.get_absolute_url }}">Power Outlet</a>
<a class="dropdown-link" href="{% url 'dcim:powerport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.poweroutlet&return_url={{ object.get_absolute_url }}">Power Outlet</a>
</li>
<li>
<a class="dropdown-link" href="{% url 'dcim:powerport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.powerfeed&return_url={{ object.get_absolute_url }}">Power Feed</a>
<a class="dropdown-link" href="{% url 'dcim:powerport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.powerfeed&return_url={{ object.get_absolute_url }}">Power Feed</a>
</li>
</ul>
</span>

View File

@ -101,16 +101,16 @@
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.interface&return_url={{ object.get_absolute_url }}">Interface</a>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.interface&return_url={{ object.get_absolute_url }}">Interface</a>
</li>
<li>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}">Front Port</a>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.frontport&return_url={{ object.get_absolute_url }}">Front Port</a>
</li>
<li>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}">Rear Port</a>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=dcim.rearport&return_url={{ object.get_absolute_url }}">Rear Port</a>
</li>
<li>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?termination_a_id={{ object.pk }}&termination_b_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">Circuit Termination</a>
<a class="dropdown-link" href="{% url 'dcim:rearport_connect' %}?a_terminations={{ object.pk }}&termination_b_type=circuits.circuittermination&return_url={{ object.get_absolute_url }}">Circuit Termination</a>
</li>
</ul>
</span>