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

Capture path end-to-end status in CablePath

Jeremy Stretch 5 лет назад
Родитель
Сommit
c974c5687c

+ 8 - 1
netbox/dcim/api/serializers.py

@@ -30,7 +30,7 @@ from .nested_serializers import *
 class ConnectedEndpointSerializer(ValidatedModelSerializer):
 class ConnectedEndpointSerializer(ValidatedModelSerializer):
     connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
     connected_endpoint_type = serializers.SerializerMethodField(read_only=True)
     connected_endpoint = 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):
     def get_connected_endpoint_type(self, obj):
         if obj.path is not None:
         if obj.path is not None:
@@ -49,6 +49,13 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer):
             return serializer(obj.path.destination, context=context).data
             return serializer(obj.path.destination, context=context).data
         return None
         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
 # Regions/sites

+ 1 - 2
netbox/dcim/migrations/0120_cablepath.py

@@ -1,5 +1,3 @@
-# Generated by Django 3.1 on 2020-09-30 18:09
-
 import dcim.fields
 import dcim.fields
 from django.db import migrations, models
 from django.db import migrations, models
 import django.db.models.deletion
 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)),
                 ('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')),
                 ('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')),
                 ('origin_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype')),
+                ('is_connected', models.BooleanField(default=False)),
             ],
             ],
         ),
         ),
     ]
     ]

+ 5 - 0
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_type_id = instance.termination_b_type_id
         instance._orig_termination_b_id = instance.termination_b_id
         instance._orig_termination_b_id = instance.termination_b_id
 
 
+        instance._orig_status = instance.status
+
         return instance
         return instance
 
 
     def __str__(self):
     def __str__(self):
@@ -1188,6 +1190,9 @@ class CablePath(models.Model):
         fk_field='destination_id'
         fk_field='destination_id'
     )
     )
     path = PathField()
     path = PathField()
+    is_connected = models.BooleanField(
+        default=False
+    )
 
 
     objects = CablePathManager()
     objects = CablePathManager()
 
 

+ 11 - 6
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.db import transaction
 from django.dispatch import receiver
 from django.dispatch import receiver
 
 
+from .choices import CableStatusChoices
 from .models import Cable, CablePath, Device, PathEndpoint, VirtualChassis
 from .models import Cable, CablePath, Device, PathEndpoint, VirtualChassis
 from .utils import object_to_path_node, trace_path
 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.
     Create CablePaths for all paths originating from the specified node.
     """
     """
-    path, destination = trace_path(node)
+    path, destination, is_connected = trace_path(node)
     if path:
     if path:
-        cp = CablePath(origin=node, path=path, destination=destination)
+        cp = CablePath(origin=node, path=path, destination=destination, is_connected=is_connected)
         cp.save()
         cp.save()
 
 
 
 
@@ -80,10 +81,14 @@ def update_connected_endpoints(instance, created, **kwargs):
                 create_cablepath(termination)
                 create_cablepath(termination)
             else:
             else:
                 rebuild_paths(termination)
                 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)
 @receiver(pre_delete, sender=Cable)

+ 6 - 2
netbox/dcim/utils.py

@@ -1,5 +1,6 @@
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 
 
+from .choices import CableStatusChoices
 from .exceptions import CableTraceSplit
 from .exceptions import CableTraceSplit
 from .models import FrontPort, RearPort
 from .models import FrontPort, RearPort
 
 
@@ -22,11 +23,14 @@ def trace_path(node):
     destination = None
     destination = None
     path = []
     path = []
     position_stack = []
     position_stack = []
+    is_connected = True
 
 
     if node.cable is None:
     if node.cable is None:
-        return [], None
+        return [], None, False
 
 
     while node.cable is not None:
     while node.cable is not None:
+        if node.cable.status != CableStatusChoices.STATUS_CONNECTED:
+            is_connected = False
 
 
         # Follow the cable to its far-end termination
         # Follow the cable to its far-end termination
         path.append(object_to_path_node(node.cable))
         path.append(object_to_path_node(node.cable))
@@ -59,4 +63,4 @@ def trace_path(node):
             destination = peer_termination
             destination = peer_termination
             break
             break
 
 
-    return path, destination
+    return path, destination, is_connected