diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index cc8e6df1f..8078f8819 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -30,7 +30,7 @@ from .nested_serializers import * class ConnectedEndpointSerializer(ValidatedModelSerializer): connected_endpoint_type = serializers.SerializerMethodField(read_only=True) connected_endpoint = serializers.SerializerMethodField(read_only=True) - connection_status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, read_only=True) + connection_status = serializers.SerializerMethodField(read_only=True) def get_connected_endpoint_type(self, obj): if obj.path is not None: @@ -49,6 +49,13 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer): return serializer(obj.path.destination, context=context).data return None + # TODO: Tweak the representation for this field + @swagger_serializer_method(serializer_or_field=serializers.BooleanField) + def get_connection_status(self, obj): + if obj.path is not None: + return obj.path.is_connected + return None + # # Regions/sites diff --git a/netbox/dcim/migrations/0120_cablepath.py b/netbox/dcim/migrations/0120_cablepath.py index 4e36c31d0..2cb8376b7 100644 --- a/netbox/dcim/migrations/0120_cablepath.py +++ b/netbox/dcim/migrations/0120_cablepath.py @@ -1,5 +1,3 @@ -# Generated by Django 3.1 on 2020-09-30 18:09 - import dcim.fields from django.db import migrations, models import django.db.models.deletion @@ -22,6 +20,7 @@ class Migration(migrations.Migration): ('path', dcim.fields.PathField(base_field=models.CharField(max_length=40), size=None)), ('destination_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')), ('origin_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')), + ('is_connected', models.BooleanField(default=False)), ], ), ] diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 162dcb831..3b13b1f73 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -992,6 +992,8 @@ class Cable(ChangeLoggedModel, CustomFieldModel): instance._orig_termination_b_type_id = instance.termination_b_type_id instance._orig_termination_b_id = instance.termination_b_id + instance._orig_status = instance.status + return instance def __str__(self): @@ -1188,6 +1190,9 @@ class CablePath(models.Model): fk_field='destination_id' ) path = PathField() + is_connected = models.BooleanField( + default=False + ) objects = CablePathManager() diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 46a2cf1d3..0c5da6160 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -5,6 +5,7 @@ from django.db.models.signals import post_save, pre_delete from django.db import transaction from django.dispatch import receiver +from .choices import CableStatusChoices from .models import Cable, CablePath, Device, PathEndpoint, VirtualChassis from .utils import object_to_path_node, trace_path @@ -13,9 +14,9 @@ def create_cablepath(node): """ Create CablePaths for all paths originating from the specified node. """ - path, destination = trace_path(node) + path, destination, is_connected = trace_path(node) if path: - cp = CablePath(origin=node, path=path, destination=destination) + cp = CablePath(origin=node, path=path, destination=destination, is_connected=is_connected) cp.save() @@ -80,10 +81,14 @@ def update_connected_endpoints(instance, created, **kwargs): create_cablepath(termination) else: rebuild_paths(termination) - else: - # We currently don't support modifying either termination of an existing Cable. This - # may change in the future. - pass + 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 != CableStatusChoices.STATUS_CONNECTED: + CablePath.objects.filter(path__contains=object_to_path_node(instance)).update(is_connected=False) + else: + rebuild_paths(instance) @receiver(pre_delete, sender=Cable) diff --git a/netbox/dcim/utils.py b/netbox/dcim/utils.py index 59ca59bfc..f97a1e8f0 100644 --- a/netbox/dcim/utils.py +++ b/netbox/dcim/utils.py @@ -1,5 +1,6 @@ from django.contrib.contenttypes.models import ContentType +from .choices import CableStatusChoices from .exceptions import CableTraceSplit from .models import FrontPort, RearPort @@ -22,11 +23,14 @@ def trace_path(node): destination = None path = [] position_stack = [] + is_connected = True if node.cable is None: - return [], None + return [], None, False while node.cable is not None: + if node.cable.status != CableStatusChoices.STATUS_CONNECTED: + is_connected = False # Follow the cable to its far-end termination path.append(object_to_path_node(node.cable)) @@ -59,4 +63,4 @@ def trace_path(node): destination = peer_termination break - return path, destination + return path, destination, is_connected