Explorar o código

Cache peer termination on CableTerminations

Jeremy Stretch %!s(int64=5) %!d(string=hai) anos
pai
achega
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.exceptions import ObjectDoesNotExist, ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
 from django.db import models
@@ -99,6 +100,21 @@ class CableTermination(models.Model):
         blank=True,
         blank=True,
         null=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.
     # Generic relations to Cable. These ensure that an attached Cable is deleted if the terminated object is deleted.
     _cabled_as_a = GenericRelation(
     _cabled_as_a = GenericRelation(
@@ -116,12 +132,7 @@ class CableTermination(models.Model):
         abstract = True
         abstract = True
 
 
     def get_cable_peer(self):
     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):
 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:
     if instance.termination_a.cable != instance:
         logger.debug(f"Updating termination A for cable {instance}")
         logger.debug(f"Updating termination A for cable {instance}")
         instance.termination_a.cable = instance
         instance.termination_a.cable = instance
+        instance.termination_a._cable_peer = instance.termination_b
         instance.termination_a.save()
         instance.termination_a.save()
     if instance.termination_b.cable != instance:
     if instance.termination_b.cable != instance:
         logger.debug(f"Updating termination B for cable {instance}")
         logger.debug(f"Updating termination B for cable {instance}")
         instance.termination_b.cable = instance
         instance.termination_b.cable = instance
+        instance.termination_b._cable_peer = instance.termination_a
         instance.termination_b.save()
         instance.termination_b.save()
 
 
     # Create/update cable paths
     # Create/update cable paths
@@ -101,10 +103,12 @@ def nullify_connected_endpoints(instance, **kwargs):
     if instance.termination_a is not None:
     if instance.termination_a is not None:
         logger.debug(f"Nullifying termination A for cable {instance}")
         logger.debug(f"Nullifying termination A for cable {instance}")
         instance.termination_a.cable = None
         instance.termination_a.cable = None
+        instance.termination_a._cable_peer = None
         instance.termination_a.save()
         instance.termination_a.save()
     if instance.termination_b is not None:
     if instance.termination_b is not None:
         logger.debug(f"Nullifying termination B for cable {instance}")
         logger.debug(f"Nullifying termination B for cable {instance}")
         instance.termination_b.cable = None
         instance.termination_b.cable = None
+        instance.termination_b._cable_peer = None
         instance.termination_b.save()
         instance.termination_b.save()
 
 
     # Delete and retrace any dependent cable paths
     # 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.
         When a new Cable is created, it must be cached on either termination point.
         """
         """
         interface1 = Interface.objects.get(pk=self.interface1.pk)
         interface1 = Interface.objects.get(pk=self.interface1.pk)
-        self.assertEqual(self.cable.termination_a, interface1)
         interface2 = Interface.objects.get(pk=self.interface2.pk)
         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(self.cable.termination_b, interface2)
+        self.assertEqual(interface2._cable_peer, interface1)
 
 
     def test_cable_deletion(self):
     def test_cable_deletion(self):
         """
         """
@@ -412,8 +414,10 @@ class CableTestCase(TestCase):
         self.assertNotEqual(str(self.cable), '#None')
         self.assertNotEqual(str(self.cable), '#None')
         interface1 = Interface.objects.get(pk=self.interface1.pk)
         interface1 = Interface.objects.get(pk=self.interface1.pk)
         self.assertIsNone(interface1.cable)
         self.assertIsNone(interface1.cable)
+        self.assertIsNone(interface1._cable_peer)
         interface2 = Interface.objects.get(pk=self.interface2.pk)
         interface2 = Interface.objects.get(pk=self.interface2.pk)
         self.assertIsNone(interface2.cable)
         self.assertIsNone(interface2.cable)
+        self.assertIsNone(interface2._cable_peer)
 
 
     def test_cabletermination_deletion(self):
     def test_cabletermination_deletion(self):
         """
         """