Просмотр исходного кода

Introduced CableTermination abstract model to ptovide Cable access from termination points

Jeremy Stretch 7 лет назад
Родитель
Сommit
f134a6ec63

+ 33 - 18
netbox/dcim/models.py

@@ -16,7 +16,6 @@ from taggit.managers import TaggableManager
 from timezone_field import TimeZoneField
 
 from circuits.models import Circuit
-from extras.constants import OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE
 from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange
 from utilities.fields import ColorField, NullableCharField
 from utilities.managers import NaturalOrderByManager
@@ -24,7 +23,7 @@ from utilities.models import ChangeLoggedModel
 from utilities.utils import serialize_object
 from .constants import *
 from .fields import ASNField, MACAddressField
-from .querysets import InterfaceQuerySet
+from .querysets import CableQuerySet, InterfaceQuerySet
 
 
 class ComponentTemplateModel(models.Model):
@@ -66,6 +65,22 @@ class ComponentModel(models.Model):
         ).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
 #
@@ -1603,7 +1618,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
 # Console ports
 #
 
-class ConsolePort(ComponentModel):
+class ConsolePort(CableTermination, ComponentModel):
     """
     A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts.
     """
@@ -1665,7 +1680,7 @@ class ConsoleServerPortManager(models.Manager):
         }).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.
     """
@@ -1706,7 +1721,7 @@ class ConsoleServerPort(ComponentModel):
 # Power ports
 #
 
-class PowerPort(ComponentModel):
+class PowerPort(CableTermination, ComponentModel):
     """
     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')
 
 
-class PowerOutlet(ComponentModel):
+class PowerOutlet(CableTermination, ComponentModel):
     """
     A physical power outlet (output) within a Device which provides power to a PowerPort.
     """
@@ -1809,7 +1824,7 @@ class PowerOutlet(ComponentModel):
 # 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
     Interface.
@@ -2035,7 +2050,7 @@ class Interface(ComponentModel):
 # Pass-through ports
 #
 
-class FrontPort(ComponentModel):
+class FrontPort(CableTermination, ComponentModel):
     """
     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.
     """
@@ -2352,12 +2367,19 @@ class Cable(ChangeLoggedModel):
         blank=True
     )
 
+    objects = CableQuerySet.as_manager()
+
     class Meta:
         unique_together = (
             ('termination_a_type', 'termination_a_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):
         """
         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
 
             # Find the cable (if any) attached to the peer port
-            port_type = ContentType.objects.get_for_model(peer_port)
-            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()
+            next_cable, far_end = peer_port.get_connection()
 
             # If no cable exists, return None
             if next_cable is None:
                 return None
 
             # Return the far side termination of the cable
-            if next_cable.termination_a == peer_port:
-                return trace_cable(next_cable.termination_b, position)
-            else:
-                return trace_cable(next_cable.termination_a, position)
+            return trace_cable(far_end, position)
 
         return trace_cable(self.termination_a), trace_cable(self.termination_b)

+ 15 - 1
netbox/dcim/querysets.py

@@ -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 .constants import IFACE_ORDERING_NAME, IFACE_ORDERING_POSITION, NONCONNECTABLE_IFACE_TYPES
@@ -68,3 +69,16 @@ class InterfaceQuerySet(QuerySet):
         wireless).
         """
         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()

+ 2 - 0
netbox/templates/dcim/device.html

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

+ 9 - 0
netbox/templates/dcim/inc/frontport.html

@@ -10,6 +10,15 @@
     <td>{{ frontport.get_type_display }}</td>
     <td>{{ frontport.rear_port }}</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">
         {% 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">

+ 9 - 0
netbox/templates/dcim/inc/rearport.html

@@ -9,6 +9,15 @@
     </td>
     <td>{{ rearport.get_type_display }}</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">
         {% 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">