فهرست منبع

Introduced CableTermination abstract model to ptovide Cable access from termination points

Jeremy Stretch 7 سال پیش
والد
کامیت
f134a6ec63

+ 33 - 18
netbox/dcim/models.py

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

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

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

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

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

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

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