Introduced CableTermination abstract model to ptovide Cable access from termination points

This commit is contained in:
Jeremy Stretch 2018-10-25 15:21:16 -04:00
parent 266429101b
commit f134a6ec63
5 changed files with 68 additions and 19 deletions

View File

@ -16,7 +16,6 @@ from taggit.managers import TaggableManager
from timezone_field import TimeZoneField from timezone_field import TimeZoneField
from circuits.models import Circuit from circuits.models import Circuit
from extras.constants import OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange
from utilities.fields import ColorField, NullableCharField from utilities.fields import ColorField, NullableCharField
from utilities.managers import NaturalOrderByManager from utilities.managers import NaturalOrderByManager
@ -24,7 +23,7 @@ from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object from utilities.utils import serialize_object
from .constants import * from .constants import *
from .fields import ASNField, MACAddressField from .fields import ASNField, MACAddressField
from .querysets import InterfaceQuerySet from .querysets import CableQuerySet, InterfaceQuerySet
class ComponentTemplateModel(models.Model): class ComponentTemplateModel(models.Model):
@ -66,6 +65,22 @@ class ComponentModel(models.Model):
).save() ).save()
class CableTermination(models.Model):
class Meta:
abstract = True
def get_connected_cable(self):
"""
Return the connected cable if one exists; else None. Assign the far end of the connection on the Cable instance.
"""
cable = Cable.objects.get_for_termination(self)
if cable is None:
return None
cable.far_end = cable.termination_b if cable.termination_a == self else cable.termination_a
return cable
# #
# Regions # Regions
# #
@ -1603,7 +1618,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
# Console ports # Console ports
# #
class ConsolePort(ComponentModel): class ConsolePort(CableTermination, ComponentModel):
""" """
A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts. A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
""" """
@ -1665,7 +1680,7 @@ class ConsoleServerPortManager(models.Manager):
}).order_by('device', 'name_padded') }).order_by('device', 'name_padded')
class ConsoleServerPort(ComponentModel): class ConsoleServerPort(CableTermination, ComponentModel):
""" """
A physical port within a Device (typically a designated console server) which provides access to ConsolePorts. A physical port within a Device (typically a designated console server) which provides access to ConsolePorts.
""" """
@ -1706,7 +1721,7 @@ class ConsoleServerPort(ComponentModel):
# Power ports # Power ports
# #
class PowerPort(ComponentModel): class PowerPort(CableTermination, ComponentModel):
""" """
A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets. A physical power supply (intake) port within a Device. PowerPorts connect to PowerOutlets.
""" """
@ -1768,7 +1783,7 @@ class PowerOutletManager(models.Manager):
}).order_by('device', 'name_padded') }).order_by('device', 'name_padded')
class PowerOutlet(ComponentModel): class PowerOutlet(CableTermination, ComponentModel):
""" """
A physical power outlet (output) within a Device which provides power to a PowerPort. A physical power outlet (output) within a Device which provides power to a PowerPort.
""" """
@ -1809,7 +1824,7 @@ class PowerOutlet(ComponentModel):
# Interfaces # Interfaces
# #
class Interface(ComponentModel): class Interface(CableTermination, ComponentModel):
""" """
A network interface within a Device or VirtualMachine. A physical Interface can connect to exactly one other A network interface within a Device or VirtualMachine. A physical Interface can connect to exactly one other
Interface. Interface.
@ -2035,7 +2050,7 @@ class Interface(ComponentModel):
# Pass-through ports # Pass-through ports
# #
class FrontPort(ComponentModel): class FrontPort(CableTermination, ComponentModel):
""" """
A pass-through port on the front of a Device. A pass-through port on the front of a Device.
""" """
@ -2089,7 +2104,7 @@ class FrontPort(ComponentModel):
) )
class RearPort(ComponentModel): class RearPort(CableTermination, ComponentModel):
""" """
A pass-through port on the rear of a Device. A pass-through port on the rear of a Device.
""" """
@ -2352,12 +2367,19 @@ class Cable(ChangeLoggedModel):
blank=True blank=True
) )
objects = CableQuerySet.as_manager()
class Meta: class Meta:
unique_together = ( unique_together = (
('termination_a_type', 'termination_a_id'), ('termination_a_type', 'termination_a_id'),
('termination_b_type', 'termination_b_id'), ('termination_b_type', 'termination_b_id'),
) )
def __str__(self):
if self.label:
return '{} (#{})'.format(self.label, self.pk)
return '#{}'.format(self.pk)
def get_path_endpoints(self): def get_path_endpoints(self):
""" """
Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
@ -2387,20 +2409,13 @@ class Cable(ChangeLoggedModel):
return termination return termination
# Find the cable (if any) attached to the peer port # Find the cable (if any) attached to the peer port
port_type = ContentType.objects.get_for_model(peer_port) next_cable, far_end = peer_port.get_connection()
next_cable = Cable.objects.filter(
Q(termination_a_type=port_type, termination_a_id=peer_port.pk) |
Q(termination_b_type=port_type, termination_b_id=peer_port.pk)
).first()
# If no cable exists, return None # If no cable exists, return None
if next_cable is None: if next_cable is None:
return None return None
# Return the far side termination of the cable # Return the far side termination of the cable
if next_cable.termination_a == peer_port: return trace_cable(far_end, position)
return trace_cable(next_cable.termination_b, position)
else:
return trace_cable(next_cable.termination_a, position)
return trace_cable(self.termination_a), trace_cable(self.termination_b) return trace_cable(self.termination_a), trace_cable(self.termination_b)

View File

@ -1,4 +1,5 @@
from django.db.models import QuerySet from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, QuerySet
from django.db.models.expressions import RawSQL from django.db.models.expressions import RawSQL
from .constants import IFACE_ORDERING_NAME, IFACE_ORDERING_POSITION, NONCONNECTABLE_IFACE_TYPES from .constants import IFACE_ORDERING_NAME, IFACE_ORDERING_POSITION, NONCONNECTABLE_IFACE_TYPES
@ -68,3 +69,16 @@ class InterfaceQuerySet(QuerySet):
wireless). wireless).
""" """
return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES) return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES)
class CableQuerySet(QuerySet):
def get_for_termination(self, termination):
"""
Return the Cable (or None) connected to a given termination point.
"""
content_type = ContentType.objects.get_for_model(termination)
return self.filter(
Q(termination_a_type=content_type, termination_a_id=termination.pk) |
Q(termination_b_type=content_type, termination_b_id=termination.pk)
).first()

View File

@ -706,6 +706,7 @@
<th>Type</th> <th>Type</th>
<th>Rear Port</th> <th>Rear Port</th>
<th>Position</th> <th>Position</th>
<th>Connected Cable</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -758,6 +759,7 @@
<th>Name</th> <th>Name</th>
<th>Type</th> <th>Type</th>
<th>Positions</th> <th>Positions</th>
<th>Connected Cable</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>

View File

@ -10,6 +10,15 @@
<td>{{ frontport.get_type_display }}</td> <td>{{ frontport.get_type_display }}</td>
<td>{{ frontport.rear_port }}</td> <td>{{ frontport.rear_port }}</td>
<td>{{ frontport.rear_port_position }}</td> <td>{{ frontport.rear_port_position }}</td>
{% with cable=frontport.get_connected_cable %}
<td>
{% if cable %}
<a href="#">{{ cable }}</a> to <a href="{{ cable.far_end.device.get_absolute_url }}">{{ cable.far_end.device }}</a> <a href="{{ cable.far_end.get_absolute_url }}">{{ cable.far_end }}</a>
{% else %}
<span class="text-muted">Not connected</span>
{% endif %}
</td>
{% endwith %}
<td class="text-right"> <td class="text-right">
{% if perms.dcim.change_frontport %} {% if perms.dcim.change_frontport %}
<a href="{% url 'dcim:frontport_edit' pk=frontport.pk %}?return_url={{ device.get_absolute_url }}" title="Edit port" class="btn btn-info btn-xs"> <a href="{% url 'dcim:frontport_edit' pk=frontport.pk %}?return_url={{ device.get_absolute_url }}" title="Edit port" class="btn btn-info btn-xs">

View File

@ -9,6 +9,15 @@
</td> </td>
<td>{{ rearport.get_type_display }}</td> <td>{{ rearport.get_type_display }}</td>
<td>{{ rearport.positions }}</td> <td>{{ rearport.positions }}</td>
{% with cable=rearport.get_connected_cable %}
<td>
{% if cable %}
<a href="#">{{ cable }}</a> to <a href="{{ cable.far_end.device.get_absolute_url }}">{{ cable.far_end.device }}</a> <a href="{{ cable.far_end.get_absolute_url }}">{{ cable.far_end }}</a>
{% else %}
<span class="text-muted">Not connected</span>
{% endif %}
</td>
{% endwith %}
<td class="text-right"> <td class="text-right">
{% if perms.dcim.change_rearport %} {% if perms.dcim.change_rearport %}
<a href="{% url 'dcim:rearport_edit' pk=rearport.pk %}?return_url={{ device.get_absolute_url }}" title="Edit port" class="btn btn-info btn-xs"> <a href="{% url 'dcim:rearport_edit' pk=rearport.pk %}?return_url={{ device.get_absolute_url }}" title="Edit port" class="btn btn-info btn-xs">