mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-12 03:19:36 -06:00
Switch to using triggers
Still outstanding: * IPAddress and IPRange triggers * Triggers for VRF changes on Prefix * Triggers for changing to "container" on Prefix * Rework logic for saving on all models
This commit is contained in:
parent
905656f13e
commit
bdde4b7e94
@ -6,7 +6,6 @@ import time
|
||||
from django.db import migrations, models
|
||||
|
||||
from ipam.choices import PrefixStatusChoices
|
||||
from ipam.models import Prefix
|
||||
|
||||
|
||||
def draw_progress(count, total, length=20):
|
||||
|
||||
@ -20,7 +20,7 @@ class Migration(migrations.Migration):
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name='ipam_prefix_delete',
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func="\n-- Update Child Prefix's with Prefix's PARENT\nUPDATE ipam_prefix SET parent_id=OLD.parent_id WHERE parent_id=OLD.id;\nRETURN OLD;\n",
|
||||
func="\n-- Update Child Prefix's with Prefix's PARENT\nUPDATE ipam_prefix SET parent_id=OLD.parent_id WHERE parent_id=OLD.id;\nRETURN OLD;\n", # noqa: E501
|
||||
hash='899e1943cb201118be7ef02f36f49747224774f2',
|
||||
operation='DELETE',
|
||||
pgid='pgtrigger_ipam_prefix_delete_e7810',
|
||||
@ -34,7 +34,7 @@ class Migration(migrations.Migration):
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name='ipam_prefix_insert',
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func="\nUPDATE ipam_prefix\nSET parent_id=NEW.id \nWHERE \n prefix << NEW.prefix\n AND\n (\n (vrf_id = NEW.vrf_id OR (vrf_id IS NULL AND NEW.vrf_id IS NULL))\n OR\n (\n NEW.vrf_id IS NULL\n AND\n NEW.status = 'container'\n AND\n NOT EXISTS(\n SELECT 1 FROM ipam_prefix p WHERE p.prefix >> ipam_prefix.prefix AND p.vrf_id = ipam_prefix.vrf_id\n )\n )\n )\n AND id != NEW.id\n AND NOT EXISTS (\n SELECT 1 FROM ipam_prefix p\n WHERE\n p.prefix >> ipam_prefix.prefix\n AND p.prefix << NEW.prefix\n AND (\n (p.vrf_id = ipam_prefix.vrf_id OR (p.vrf_id IS NULL AND ipam_prefix.vrf_id IS NULL))\n OR\n (p.vrf_id IS NULL AND p.status = 'container')\n )\n AND p.id != NEW.id\n )\n;\nRETURN NEW;\n",
|
||||
func="\nUPDATE ipam_prefix\nSET parent_id=NEW.id \nWHERE \n prefix << NEW.prefix\n AND\n (\n (vrf_id = NEW.vrf_id OR (vrf_id IS NULL AND NEW.vrf_id IS NULL))\n OR\n (\n NEW.vrf_id IS NULL\n AND\n NEW.status = 'container'\n AND\n NOT EXISTS(\n SELECT 1 FROM ipam_prefix p WHERE p.prefix >> ipam_prefix.prefix AND p.vrf_id = ipam_prefix.vrf_id\n )\n )\n )\n AND id != NEW.id\n AND NOT EXISTS (\n SELECT 1 FROM ipam_prefix p\n WHERE\n p.prefix >> ipam_prefix.prefix\n AND p.prefix << NEW.prefix\n AND (\n (p.vrf_id = ipam_prefix.vrf_id OR (p.vrf_id IS NULL AND ipam_prefix.vrf_id IS NULL))\n OR\n (p.vrf_id IS NULL AND p.status = 'container')\n )\n AND p.id != NEW.id\n )\n;\nRETURN NEW;\n", # noqa: E501
|
||||
hash='0e05bbe61861227a9eb710b6c94bae9e0cc7119e',
|
||||
operation='INSERT',
|
||||
pgid='pgtrigger_ipam_prefix_insert_46c72',
|
||||
|
||||
25
netbox/ipam/migrations/0085_alter_prefix_parent.py
Normal file
25
netbox/ipam/migrations/0085_alter_prefix_parent.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 5.2.5 on 2025-11-25 03:53
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0084_prefix_ipam_prefix_delete_prefix_ipam_prefix_insert'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='prefix',
|
||||
name='parent',
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
related_name='children',
|
||||
to='ipam.prefix',
|
||||
),
|
||||
),
|
||||
]
|
||||
65
netbox/ipam/migrations/0086_update_trigger.py
Normal file
65
netbox/ipam/migrations/0086_update_trigger.py
Normal file
@ -0,0 +1,65 @@
|
||||
# Generated by Django 5.2.5 on 2025-11-25 06:00
|
||||
|
||||
import pgtrigger.compiler
|
||||
import pgtrigger.migrations
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0085_alter_prefix_parent'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
pgtrigger.migrations.RemoveTrigger(
|
||||
model_name='prefix',
|
||||
name='ipam_prefix_delete',
|
||||
),
|
||||
pgtrigger.migrations.RemoveTrigger(
|
||||
model_name='prefix',
|
||||
name='ipam_prefix_insert',
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name='prefix',
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name='ipam_prefix_delete',
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func="\n-- Update Child Prefix's with Prefix's PARENT This is a safe assumption based on the fact that the parent would be the\n-- next direct parent for anything else that could contain this prefix\nUPDATE ipam_prefix SET parent_id=OLD.parent_id WHERE parent_id=OLD.id;\nRETURN OLD;\n", # noqa: E501
|
||||
hash='ee3f890009c05a3617428158e7b6f3d77317885d',
|
||||
operation='DELETE',
|
||||
pgid='pgtrigger_ipam_prefix_delete_e7810',
|
||||
table='ipam_prefix',
|
||||
when='BEFORE',
|
||||
),
|
||||
),
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name='prefix',
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name='ipam_prefix_insert',
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func="\n-- Update the prefix with the new parent if the parent is the most appropriate prefix\nUPDATE ipam_prefix\nSET parent_id=NEW.id\nWHERE\n prefix << NEW.prefix\n AND\n (\n (vrf_id = NEW.vrf_id OR (vrf_id IS NULL AND NEW.vrf_id IS NULL))\n OR\n (\n NEW.vrf_id IS NULL\n AND\n NEW.status = 'container'\n AND\n NOT EXISTS(\n SELECT 1 FROM ipam_prefix p WHERE p.prefix >> ipam_prefix.prefix AND p.vrf_id = ipam_prefix.vrf_id\n )\n )\n )\n AND id != NEW.id\n AND NOT EXISTS (\n SELECT 1 FROM ipam_prefix p\n WHERE\n p.prefix >> ipam_prefix.prefix\n AND p.prefix << NEW.prefix\n AND (\n (p.vrf_id = ipam_prefix.vrf_id OR (p.vrf_id IS NULL AND ipam_prefix.vrf_id IS NULL))\n OR\n (p.vrf_id IS NULL AND p.status = 'container')\n )\n AND p.id != NEW.id\n )\n;\nRETURN NEW;\n", # noqa: E501
|
||||
hash='1d71498f09e767183d3b0d29c06c9ac9e2cc000a',
|
||||
operation='INSERT',
|
||||
pgid='pgtrigger_ipam_prefix_insert_46c72',
|
||||
table='ipam_prefix',
|
||||
when='AFTER',
|
||||
),
|
||||
),
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name='prefix',
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name='ipam_prefix_update',
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func="\n-- When a prefix changes, reassign any IPAddresses that no longer\n-- fall within the new prefix range to the parent prefix (or set null if no parent exists)\nUPDATE ipam_prefix\nSET parent_id = OLD.parent_id\nWHERE\n parent_id = NEW.id\n -- IP address no longer contained within the updated prefix\n AND NOT (prefix << NEW.prefix);\n\n-- Update the prefix with the new parent if the parent is the most appropriate prefix\nUPDATE ipam_prefix\nSET parent_id=NEW.id\nWHERE\n prefix << NEW.prefix\n AND\n (\n (vrf_id = NEW.vrf_id OR (vrf_id IS NULL AND NEW.vrf_id IS NULL))\n OR\n (\n NEW.vrf_id IS NULL\n AND\n NEW.status = 'container'\n AND\n NOT EXISTS(\n SELECT 1 FROM ipam_prefix p WHERE p.prefix >> ipam_prefix.prefix AND p.vrf_id = ipam_prefix.vrf_id\n )\n )\n )\n AND id != NEW.id\n AND NOT EXISTS (\n SELECT 1 FROM ipam_prefix p\n WHERE\n p.prefix >> ipam_prefix.prefix\n AND p.prefix << NEW.prefix\n AND (\n (p.vrf_id = ipam_prefix.vrf_id OR (p.vrf_id IS NULL AND ipam_prefix.vrf_id IS NULL))\n OR\n (p.vrf_id IS NULL AND p.status = 'container')\n )\n AND p.id != NEW.id\n )\n;\nRETURN NEW;\n", # noqa: E501
|
||||
hash='747230a84703df5a4aa3d32e7f45b5a32525b799',
|
||||
operation='UPDATE',
|
||||
pgid='pgtrigger_ipam_prefix_update_e5fca',
|
||||
table='ipam_prefix',
|
||||
when='AFTER',
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -18,7 +18,8 @@ from ipam.fields import IPNetworkField, IPAddressField
|
||||
from ipam.lookups import Host
|
||||
from ipam.managers import IPAddressManager
|
||||
from ipam.querysets import PrefixQuerySet
|
||||
from ipam.triggers import ipam_prefix_delete_adjust_prefix_parent, ipam_prefix_insert_adjust_prefix_parent
|
||||
from ipam.triggers import ipam_prefix_delete_adjust_prefix_parent, ipam_prefix_insert_adjust_prefix_parent, \
|
||||
ipam_prefix_update_adjust_prefix_parent
|
||||
from ipam.validators import DNSValidator
|
||||
from netbox.config import get_config
|
||||
from netbox.models import OrganizationalModel, PrimaryModel
|
||||
@ -196,7 +197,7 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||
"""
|
||||
aggregate = models.ForeignKey(
|
||||
to='ipam.Aggregate',
|
||||
on_delete=models.SET_NULL,
|
||||
on_delete=models.SET_NULL, # This is handled by triggers
|
||||
related_name='prefixes',
|
||||
blank=True,
|
||||
null=True,
|
||||
@ -204,7 +205,7 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||
)
|
||||
parent = models.ForeignKey(
|
||||
to='ipam.Prefix',
|
||||
on_delete=models.SET_NULL,
|
||||
on_delete=models.DO_NOTHING,
|
||||
related_name='children',
|
||||
blank=True,
|
||||
null=True,
|
||||
@ -302,6 +303,12 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||
when=pgtrigger.After,
|
||||
func=ipam_prefix_insert_adjust_prefix_parent,
|
||||
),
|
||||
pgtrigger.Trigger(
|
||||
name='ipam_prefix_update',
|
||||
operation=pgtrigger.Update,
|
||||
when=pgtrigger.After,
|
||||
func=ipam_prefix_update_adjust_prefix_parent,
|
||||
),
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -533,11 +540,11 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||
prefixes = Prefix.objects.filter(
|
||||
models.Q(
|
||||
vrf=network.vrf,
|
||||
prefix__net_contains=str(network)
|
||||
prefix__net_contains=str(network.prefix)
|
||||
) | models.Q(
|
||||
vrf=None,
|
||||
status=PrefixStatusChoices.STATUS_CONTAINER,
|
||||
prefix__net_contains=str(network),
|
||||
prefix__net_contains=str(network.prefix),
|
||||
)
|
||||
)
|
||||
return prefixes.last()
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
from django.db.models import Q
|
||||
from django.db.models.signals import post_delete, post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from netaddr.ip import IPNetwork
|
||||
|
||||
from dcim.models import Device
|
||||
from virtualization.models import VirtualMachine
|
||||
from .choices import PrefixStatusChoices
|
||||
from .models import IPAddress, Prefix, IPRange
|
||||
from .models import IPAddress, Prefix
|
||||
|
||||
|
||||
def update_parents_children(prefix):
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
from time import sleep
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase, override_settings
|
||||
@ -664,109 +662,105 @@ class TestTriggers(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
prefixes = (
|
||||
# IPv4
|
||||
Prefix(prefix='10.0.0.0/8'),
|
||||
Prefix(prefix='10.0.0.0/16'),
|
||||
Prefix(prefix='10.0.0.0/24'),
|
||||
Prefix(prefix='192.168.0.0/16'),
|
||||
# IPv6
|
||||
Prefix(prefix='2001:db8::/32'),
|
||||
Prefix(prefix='2001:db8::/40'),
|
||||
Prefix(prefix='2001:db8::/48'),
|
||||
)
|
||||
|
||||
for prefix in prefixes:
|
||||
prefix.clean()
|
||||
prefix.save()
|
||||
|
||||
vrfs = (
|
||||
VRF(name='VRF A'),
|
||||
VRF(name='VRF B'),
|
||||
)
|
||||
|
||||
for prefix in prefixes:
|
||||
prefix.clean()
|
||||
prefix.save()
|
||||
|
||||
for vrf in vrfs:
|
||||
vrf.clean()
|
||||
vrf.save()
|
||||
|
||||
cls.prefixes = (
|
||||
# IPv4
|
||||
Prefix(prefix='10.0.0.0/8'),
|
||||
Prefix(prefix='10.0.0.0/16'),
|
||||
Prefix(prefix='10.0.0.0/22'),
|
||||
Prefix(prefix='10.0.0.0/23'),
|
||||
Prefix(prefix='10.0.2.0/23'),
|
||||
Prefix(prefix='10.0.0.0/24'),
|
||||
Prefix(prefix='10.0.1.0/24'),
|
||||
Prefix(prefix='10.0.2.0/24'),
|
||||
Prefix(prefix='10.0.3.0/24'),
|
||||
Prefix(prefix='10.1.0.0/16', status='container'),
|
||||
Prefix(prefix='10.1.0.0/22', vrf=vrfs[0]),
|
||||
Prefix(prefix='10.1.0.0/23', vrf=vrfs[0]),
|
||||
Prefix(prefix='10.1.2.0/23', vrf=vrfs[0]),
|
||||
Prefix(prefix='10.1.0.0/24', vrf=vrfs[0]),
|
||||
Prefix(prefix='10.1.1.0/24', vrf=vrfs[0]),
|
||||
Prefix(prefix='10.1.2.0/24', vrf=vrfs[0]),
|
||||
Prefix(prefix='10.1.3.0/24', vrf=vrfs[0]),
|
||||
)
|
||||
|
||||
for prefix in cls.prefixes:
|
||||
prefix.clean()
|
||||
prefix.save()
|
||||
|
||||
def test_current_hierarchy(self):
|
||||
self.assertIsNone(Prefix.objects.get(prefix='10.0.0.0/8').parent)
|
||||
self.assertIsNone(Prefix.objects.get(prefix='192.168.0.0/16').parent)
|
||||
self.assertIsNone(Prefix.objects.get(prefix='2001:db8::/32').parent)
|
||||
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='10.0.0.0/16').parent)
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='10.0.0.0/24').parent)
|
||||
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='2001:db8::/40').parent)
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='2001:db8::/48').parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/16').parent, Prefix.objects.get(prefix='10.0.0.0/8'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/22').parent, Prefix.objects.get(prefix='10.0.0.0/16'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/23').parent, Prefix.objects.get(prefix='10.0.0.0/22'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.2.0/23').parent, Prefix.objects.get(prefix='10.0.0.0/22'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/24').parent, Prefix.objects.get(prefix='10.0.0.0/23'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.1.0/24').parent, Prefix.objects.get(prefix='10.0.0.0/23'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.2.0/24').parent, Prefix.objects.get(prefix='10.0.2.0/23'))
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.3.0/24').parent, Prefix.objects.get(prefix='10.0.2.0/23'))
|
||||
|
||||
def test_basic_insert(self):
|
||||
pfx = Prefix.objects.create(prefix='2001:db8::/44')
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='2001:db8::/48').parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='2001:db8::/48').parent, pfx)
|
||||
pfx = Prefix.objects.create(prefix='10.0.0.0/21')
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='10.0.0.0/22').parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/22').parent, pfx)
|
||||
|
||||
def test_vrf_insert(self):
|
||||
vrf = VRF.objects.get(name='VRF A')
|
||||
pfx = Prefix.objects.create(prefix='2001:db8::/44', vrf=vrf)
|
||||
parent = Prefix.objects.get(prefix='2001:db8::/40')
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='2001:db8::/48').parent)
|
||||
self.assertNotEqual(Prefix.objects.get(prefix='2001:db8::/48').parent, pfx)
|
||||
self.assertEqual(Prefix.objects.get(prefix='2001:db8::/48').parent, parent)
|
||||
pfx = Prefix.objects.create(prefix='10.1.0.0/21', vrf=vrf)
|
||||
parent = Prefix.objects.get(prefix='10.1.0.0/16')
|
||||
|
||||
prefixes = (
|
||||
Prefix(prefix='10.2.0.0/16', vrf=vrf),
|
||||
Prefix(prefix='10.2.0.0/24', vrf=vrf),
|
||||
)
|
||||
|
||||
for prefix in prefixes:
|
||||
prefix.clean()
|
||||
prefix.save()
|
||||
|
||||
self.assertIsNone(Prefix.objects.get(pk=prefixes[0].pk).parent)
|
||||
self.assertEqual(Prefix.objects.get(pk=prefixes[1].pk).parent, prefixes[0])
|
||||
|
||||
new_pfx = Prefix.objects.create(prefix='10.2.0.0/23', vrf=vrf)
|
||||
|
||||
self.assertIsNone(Prefix.objects.get(pk=prefixes[0].pk).parent)
|
||||
self.assertEqual(new_pfx.parent, prefixes[0])
|
||||
self.assertEqual(Prefix.objects.get(pk=prefixes[1].pk).parent, new_pfx)
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='10.1.0.0/21').parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.0.0/21').parent, parent)
|
||||
self.assertIsNotNone(Prefix.objects.get(prefix='10.1.0.0/22').parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.0.0/22').parent, pfx)
|
||||
|
||||
def test_basic_delete(self):
|
||||
prefixes = (
|
||||
Prefix(prefix='10.2.0.0/16'),
|
||||
Prefix(prefix='10.2.0.0/23'),
|
||||
Prefix(prefix='10.2.0.0/24'),
|
||||
)
|
||||
for prefix in prefixes:
|
||||
prefix.clean()
|
||||
prefix.save()
|
||||
Prefix.objects.get(prefix='10.0.0.0/23').delete()
|
||||
parent = Prefix.objects.get(prefix='10.0.0.0/22')
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.1.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.2.0/24').parent, Prefix.objects.get(prefix='10.0.2.0/23'))
|
||||
|
||||
def test_vrf_delete(self):
|
||||
vrf = VRF.objects.get(name='VRF A')
|
||||
Prefix.objects.get(prefix='10.1.0.0/23').delete()
|
||||
parent = Prefix.objects.get(prefix='10.1.0.0/22')
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.0.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.1.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.2.0/24').parent, Prefix.objects.get(prefix='10.1.2.0/23'))
|
||||
|
||||
prefixes = (
|
||||
Prefix(prefix='10.2.0.0/16', vrf=vrf),
|
||||
Prefix(prefix='10.2.0.0/23', vrf=vrf),
|
||||
Prefix(prefix='10.2.0.0/24', vrf=vrf),
|
||||
)
|
||||
def test_basic_update(self):
|
||||
pfx = Prefix.objects.get(prefix='10.0.0.0/23')
|
||||
parent = Prefix.objects.get(prefix='10.0.0.0/22')
|
||||
pfx.prefix = '10.3.0.0/23'
|
||||
pfx.parent = Prefix.objects.get(prefix='10.0.0.0/8')
|
||||
pfx.clean()
|
||||
pfx.save()
|
||||
|
||||
for prefix in prefixes:
|
||||
prefix.clean()
|
||||
prefix.save()
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.0.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.1.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.0.2.0/24').parent, Prefix.objects.get(prefix='10.0.2.0/23'))
|
||||
|
||||
self.assertIsNone(prefixes[0].parent)
|
||||
self.assertEqual(prefixes[1].parent, prefixes[0])
|
||||
self.assertEqual(prefixes[2].parent, prefixes[1])
|
||||
def test_vrf_update(self):
|
||||
pfx = Prefix.objects.get(prefix='10.1.0.0/23')
|
||||
parent = Prefix.objects.get(prefix='10.1.0.0/22')
|
||||
pfx.prefix = '10.3.0.0/23'
|
||||
pfx.parent = None
|
||||
pfx.clean()
|
||||
pfx.save()
|
||||
|
||||
prefixes[1].delete()
|
||||
prefixes[2].refresh_from_db()
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.0.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.1.0/24').parent, parent)
|
||||
self.assertEqual(Prefix.objects.get(prefix='10.1.2.0/24').parent, Prefix.objects.get(prefix='10.1.2.0/23'))
|
||||
|
||||
self.assertIsNone(prefixes[0].parent)
|
||||
self.assertEqual(prefixes[2].parent, prefixes[0])
|
||||
# TODO: Test VRF Changes
|
||||
|
||||
|
||||
class TestIPAddress(TestCase):
|
||||
|
||||
@ -1,25 +1,13 @@
|
||||
ipam_prefix_delete_adjust_prefix_parent = """
|
||||
-- Update Child Prefix's with Prefix's PARENT
|
||||
-- Update Child Prefix's with Prefix's PARENT This is a safe assumption based on the fact that the parent would be the
|
||||
-- next direct parent for anything else that could contain this prefix
|
||||
UPDATE ipam_prefix SET parent_id=OLD.parent_id WHERE parent_id=OLD.id;
|
||||
RETURN OLD;
|
||||
"""
|
||||
|
||||
|
||||
ipam_prefix_delete_adjust_ipaddress_prefix = """
|
||||
-- Update IP Address with prefix's PARENT
|
||||
UPDATE ipam_ipaddress SET prefix_id=OLD.parent_id WHERE prefix_id=OLD.id;
|
||||
RETURN OLD;
|
||||
"""
|
||||
|
||||
|
||||
ipam_prefix_delete_adjust_iprange_prefix = """
|
||||
-- Update IP Range with prefix's PARENT
|
||||
UPDATE ipam_iprange SET prefix_id=OLD.parent_id WHERE prefix_id=OLD.id;
|
||||
RETURN OLD;
|
||||
"""
|
||||
|
||||
|
||||
ipam_prefix_insert_adjust_prefix_parent = """
|
||||
-- Update the prefix with the new parent if the parent is the most appropriate prefix
|
||||
UPDATE ipam_prefix
|
||||
SET parent_id=NEW.id
|
||||
WHERE
|
||||
@ -56,53 +44,48 @@ RETURN NEW;
|
||||
"""
|
||||
|
||||
|
||||
ipam_prefix_insert_adjust_ipaddress_prefix = """
|
||||
ipam_prefix_update_adjust_prefix_parent = """
|
||||
-- When a prefix changes, reassign any IPAddresses that no longer
|
||||
-- fall within the new prefix range to the parent prefix (or set null if no parent exists)
|
||||
UPDATE ipam_prefix
|
||||
SET prefix_id=NEW.id
|
||||
SET parent_id = OLD.parent_id
|
||||
WHERE
|
||||
NEW.prefix >> ipaddress.address
|
||||
parent_id = NEW.id
|
||||
-- IP address no longer contained within the updated prefix
|
||||
AND NOT (prefix << NEW.prefix);
|
||||
|
||||
-- Update the prefix with the new parent if the parent is the most appropriate prefix
|
||||
UPDATE ipam_prefix
|
||||
SET parent_id=NEW.id
|
||||
WHERE
|
||||
prefix << NEW.prefix
|
||||
AND
|
||||
(
|
||||
(NEW.vrf = ipaddress.vrf_id OR (NEW.vrf_id IS NULL and ipaddress.vrf_id IS NULL))
|
||||
(vrf_id = NEW.vrf_id OR (vrf_id IS NULL AND NEW.vrf_id IS NULL))
|
||||
OR
|
||||
(NEW.vrf_id IS NULL AND NEW.status = 'container')
|
||||
)
|
||||
AND (
|
||||
ipaddress.prefix_id IS NULL
|
||||
OR
|
||||
EXISTS (
|
||||
SELECT 1 from prefix p WHERE
|
||||
p.id = ipaddress.prefix_id
|
||||
AND NEW.prefix << p.prefix
|
||||
)
|
||||
)
|
||||
AND
|
||||
-- Check to ensure current parent PREFIX is not in a VRF
|
||||
NOT EXISTS (
|
||||
SELECT 1 from prefix p WHERE (
|
||||
p.id = ipaddress.prefix_id
|
||||
AND
|
||||
p.vrf_id IS NOT NULL
|
||||
AND
|
||||
ipaddress.vrf_id IS NOT NULL
|
||||
AND
|
||||
(
|
||||
NEW.vrf_id IS NULL AND NEW.status = 'container'
|
||||
)
|
||||
)
|
||||
)
|
||||
NEW.vrf_id IS NULL
|
||||
AND
|
||||
NEW.status = 'container'
|
||||
AND
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM prefix p
|
||||
SELECT 1 FROM ipam_prefix p WHERE p.prefix >> ipam_prefix.prefix AND p.vrf_id = ipam_prefix.vrf_id
|
||||
)
|
||||
)
|
||||
)
|
||||
AND id != NEW.id
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM ipam_prefix p
|
||||
WHERE
|
||||
p.prefix >> ipaddress.address
|
||||
AND p.id != NEW.id
|
||||
p.prefix >> ipam_prefix.prefix
|
||||
AND p.prefix << NEW.prefix
|
||||
AND (
|
||||
(p.vrf_id = ipaddress.vrf_id OR (p.vrf_id IS NULL AND ipaddress.vrf_id IS NULL))
|
||||
(p.vrf_id = ipam_prefix.vrf_id OR (p.vrf_id IS NULL AND ipam_prefix.vrf_id IS NULL))
|
||||
OR
|
||||
(p.vrf_id IS NULL AND NEW.status = 'container')
|
||||
(p.vrf_id IS NULL AND p.status = 'container')
|
||||
)
|
||||
);
|
||||
AND p.id != NEW.id
|
||||
)
|
||||
;
|
||||
RETURN NEW;
|
||||
"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user