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

Cache peer termination on CableTerminations

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

+ 49 - 0
netbox/circuits/migrations/0022_cache_cable_peer.py

@@ -0,0 +1,49 @@
+import sys
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+def cache_cable_peers(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    Cable = apps.get_model('dcim', 'Cable')
+    CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
+
+    if 'test' not in sys.argv:
+        print(f"\n    Updating circuit termination cable peers...", flush=True)
+    ct = ContentType.objects.get_for_model(CircuitTermination)
+    for cable in Cable.objects.filter(termination_a_type=ct):
+        CircuitTermination.objects.filter(pk=cable.termination_a_id).update(
+            _cable_peer_type_id=cable.termination_b_type_id,
+            _cable_peer_id=cable.termination_b_id
+        )
+    for cable in Cable.objects.filter(termination_b_type=ct):
+        CircuitTermination.objects.filter(pk=cable.termination_b_id).update(
+            _cable_peer_type_id=cable.termination_a_type_id,
+            _cable_peer_id=cable.termination_a_id
+        )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('circuits', '0021_cablepath'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='circuittermination',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='circuittermination',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.RunPython(
+            code=cache_cable_peers,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 141 - 0
netbox/dcim/migrations/0121_cache_cable_peer.py

@@ -0,0 +1,141 @@
+import sys
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+def cache_cable_peers(apps, schema_editor):
+    ContentType = apps.get_model('contenttypes', 'ContentType')
+    Cable = apps.get_model('dcim', 'Cable')
+    ConsolePort = apps.get_model('dcim', 'ConsolePort')
+    ConsoleServerPort = apps.get_model('dcim', 'ConsoleServerPort')
+    PowerPort = apps.get_model('dcim', 'PowerPort')
+    PowerOutlet = apps.get_model('dcim', 'PowerOutlet')
+    Interface = apps.get_model('dcim', 'Interface')
+    FrontPort = apps.get_model('dcim', 'FrontPort')
+    RearPort = apps.get_model('dcim', 'RearPort')
+    PowerFeed = apps.get_model('dcim', 'PowerFeed')
+
+    models = (
+        ConsolePort,
+        ConsoleServerPort,
+        PowerPort,
+        PowerOutlet,
+        Interface,
+        FrontPort,
+        RearPort,
+        PowerFeed
+    )
+
+    if 'test' not in sys.argv:
+        print("\n", end="")
+
+    for model in models:
+        if 'test' not in sys.argv:
+            print(f"    Updating {model._meta.verbose_name} cable peers...", flush=True)
+        ct = ContentType.objects.get_for_model(model)
+        for cable in Cable.objects.filter(termination_a_type=ct):
+            model.objects.filter(pk=cable.termination_a_id).update(
+                _cable_peer_type_id=cable.termination_b_type_id,
+                _cable_peer_id=cable.termination_b_id
+            )
+        for cable in Cable.objects.filter(termination_b_type=ct):
+            model.objects.filter(pk=cable.termination_b_id).update(
+                _cable_peer_type_id=cable.termination_a_type_id,
+                _cable_peer_id=cable.termination_a_id
+            )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('dcim', '0120_cablepath'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='consoleport',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='consoleport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='consoleserverport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='frontport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='interface',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='powerfeed',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='poweroutlet',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='powerport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='_cable_peer_id',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='rearport',
+            name='_cable_peer_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.contenttype'),
+        ),
+        migrations.RunPython(
+            code=cache_cable_peers,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 18 - 7
netbox/dcim/models/device_components.py

@@ -1,4 +1,5 @@
-from django.contrib.contenttypes.fields import GenericRelation
+from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
+from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
@@ -99,6 +100,21 @@ class CableTermination(models.Model):
         blank=True,
         null=True
     )
+    _cable_peer_type = models.ForeignKey(
+        to=ContentType,
+        on_delete=models.SET_NULL,
+        related_name='+',
+        blank=True,
+        null=True
+    )
+    _cable_peer_id = models.PositiveIntegerField(
+        blank=True,
+        null=True
+    )
+    _cable_peer = GenericForeignKey(
+        ct_field='_cable_peer_type',
+        fk_field='_cable_peer_id'
+    )
 
     # Generic relations to Cable. These ensure that an attached Cable is deleted if the terminated object is deleted.
     _cabled_as_a = GenericRelation(
@@ -116,12 +132,7 @@ class CableTermination(models.Model):
         abstract = True
 
     def get_cable_peer(self):
-        if self.cable is None:
-            return None
-        if self._cabled_as_a.exists():
-            return self.cable.termination_b
-        if self._cabled_as_b.exists():
-            return self.cable.termination_a
+        return self._cable_peer
 
 
 class PathEndpoint(models.Model):

+ 4 - 0
netbox/dcim/signals.py

@@ -67,10 +67,12 @@ def update_connected_endpoints(instance, created, **kwargs):
     if instance.termination_a.cable != instance:
         logger.debug(f"Updating termination A for cable {instance}")
         instance.termination_a.cable = instance
+        instance.termination_a._cable_peer = instance.termination_b
         instance.termination_a.save()
     if instance.termination_b.cable != instance:
         logger.debug(f"Updating termination B for cable {instance}")
         instance.termination_b.cable = instance
+        instance.termination_b._cable_peer = instance.termination_a
         instance.termination_b.save()
 
     # Create/update cable paths
@@ -101,10 +103,12 @@ def nullify_connected_endpoints(instance, **kwargs):
     if instance.termination_a is not None:
         logger.debug(f"Nullifying termination A for cable {instance}")
         instance.termination_a.cable = None
+        instance.termination_a._cable_peer = None
         instance.termination_a.save()
     if instance.termination_b is not None:
         logger.debug(f"Nullifying termination B for cable {instance}")
         instance.termination_b.cable = None
+        instance.termination_b._cable_peer = None
         instance.termination_b.save()
 
     # Delete and retrace any dependent cable paths

+ 5 - 1
netbox/dcim/tests/test_models.py

@@ -398,9 +398,11 @@ class CableTestCase(TestCase):
         When a new Cable is created, it must be cached on either termination point.
         """
         interface1 = Interface.objects.get(pk=self.interface1.pk)
-        self.assertEqual(self.cable.termination_a, interface1)
         interface2 = Interface.objects.get(pk=self.interface2.pk)
+        self.assertEqual(self.cable.termination_a, interface1)
+        self.assertEqual(interface1._cable_peer, interface2)
         self.assertEqual(self.cable.termination_b, interface2)
+        self.assertEqual(interface2._cable_peer, interface1)
 
     def test_cable_deletion(self):
         """
@@ -412,8 +414,10 @@ class CableTestCase(TestCase):
         self.assertNotEqual(str(self.cable), '#None')
         interface1 = Interface.objects.get(pk=self.interface1.pk)
         self.assertIsNone(interface1.cable)
+        self.assertIsNone(interface1._cable_peer)
         interface2 = Interface.objects.get(pk=self.interface2.pk)
         self.assertIsNone(interface2.cable)
+        self.assertIsNone(interface2._cable_peer)
 
     def test_cabletermination_deletion(self):
         """