11import ipaddress
2+ from uuid import uuid4
23
34from infrahub .core import registry
45from infrahub .core .branch import Branch
56from infrahub .core .constants import InfrahubKind , SchemaPathType
7+ from infrahub .core .diff .coordinator import DiffCoordinator
8+ from infrahub .core .diff .merger .merger import DiffMerger
69from infrahub .core .initialization import create_branch , get_default_ipnamespace
710from infrahub .core .manager import NodeManager
811from infrahub .core .migrations .schema .node_kind_update import NodeKindUpdateMigration
912from infrahub .core .node import Node
1013from infrahub .core .path import SchemaPath
1114from infrahub .core .query .ipam import IPPrefixReconcileQuery
1215from infrahub .core .schema .schema_branch import SchemaBranch
16+ from infrahub .core .timestamp import Timestamp
1317from infrahub .database import InfrahubDatabase
18+ from infrahub .dependencies .registry import get_component_registry
19+
20+
21+ def randomized_branch_name (branch_name : str ) -> str :
22+ return f"{ branch_name } _{ uuid4 ().hex [:8 ]} "
1423
1524
1625async def test_ipprefix_reconcile_query_simple (db : InfrahubDatabase , default_branch : Branch , ip_dataset_01 ):
@@ -241,7 +250,7 @@ async def test_ipprefix_reconcile_query_get_deleted_node_by_uuid(
241250async def test_ipprefix_reconcile_query_deleted_children_ignored_on_branch (
242251 db : InfrahubDatabase , ip_dataset_01 : dict [str , Node ]
243252):
244- branch = await create_branch (db = db , branch_name = "branch2" )
253+ branch = await create_branch (db = db , branch_name = randomized_branch_name ( "branch2" ) )
245254
246255 ns1_id = ip_dataset_01 ["ns1" ].id
247256 net140_branch = await NodeManager .get_one (db = db , branch = branch , id = ip_dataset_01 ["net140" ].id )
@@ -276,7 +285,7 @@ async def test_ipprefix_reconcile_query_deleted_children_ignored_on_branch(
276285async def test_ipprefix_reconcile_query_deleted_parent_ignored_on_branch (
277286 db : InfrahubDatabase , ip_dataset_01 : dict [str , Node ]
278287):
279- branch = await create_branch (db = db , branch_name = "branch2" )
288+ branch = await create_branch (db = db , branch_name = randomized_branch_name ( "branch2" ) )
280289
281290 ns1_id = ip_dataset_01 ["ns1" ].id
282291 net140_branch = await NodeManager .get_one (db = db , branch = branch , id = ip_dataset_01 ["net140" ].id )
@@ -309,7 +318,7 @@ async def test_branch_updates_respected(db: InfrahubDatabase, default_branch: Br
309318 prefix_schema = registry .schema .get_node_schema (name = "IpamIPPrefix" , branch = default_branch )
310319 address_schema = registry .schema .get_node_schema (name = "IpamIPAddress" , branch = default_branch )
311320
312- branch2 = await create_branch (branch_name = "branch2" , db = db )
321+ branch2 = await create_branch (branch_name = randomized_branch_name ( "branch2" ) , db = db )
313322
314323 ns1_id = ip_dataset_01 ["ns1" ].id
315324 net140 = ip_dataset_01 ["net140" ]
@@ -343,6 +352,16 @@ async def test_branch_updates_respected(db: InfrahubDatabase, default_branch: Br
343352 new_address_branch .id ,
344353 }
345354 assert set (query .get_calculated_children_uuids ()) == expected_children
355+ query = await IPPrefixReconcileQuery .init (
356+ db = db , branch = branch2 , ip_value = ipaddress .ip_interface ("10.10.0.1" ), namespace = ns1_id
357+ )
358+ await query .execute (db = db )
359+
360+ assert query .get_ip_node_uuid () == new_address_branch .id
361+ assert query .get_current_parent_uuid () is None
362+ assert query .get_current_children_uuids () == []
363+ assert query .get_calculated_parent_uuid () == new_parent_branch .id
364+ assert query .get_calculated_children_uuids () == []
346365
347366 await branch2 .rebase (db = db )
348367
@@ -364,6 +383,16 @@ async def test_branch_updates_respected(db: InfrahubDatabase, default_branch: Br
364383 new_address_main .id ,
365384 }
366385 assert set (query .get_calculated_children_uuids ()) == expected_children_after_rebase
386+ query = await IPPrefixReconcileQuery .init (
387+ db = db , branch = branch2 , ip_value = ipaddress .ip_interface ("10.10.0.2" ), namespace = ns1_id
388+ )
389+ await query .execute (db = db )
390+
391+ assert query .get_ip_node_uuid () == new_address_main .id
392+ assert query .get_current_parent_uuid () is None
393+ assert query .get_current_children_uuids () == []
394+ assert query .get_calculated_parent_uuid () == new_parent_branch .id
395+ assert query .get_calculated_children_uuids () == []
367396
368397
369398async def test_reconcile_parent_child_identification (
@@ -615,7 +644,7 @@ async def test_reconcile_query_on_migrated_kind_node(db: InfrahubDatabase, defau
615644 prefix_140 = ip_dataset_01 ["net140" ]
616645 namespace = ip_dataset_01 ["ns1" ]
617646
618- branch = await create_branch (db = db , branch_name = "migrated-branch" )
647+ branch = await create_branch (db = db , branch_name = randomized_branch_name ( "migrated-branch" ) )
619648
620649 # update IpamIPPrefix schema name
621650 prefix_schema = registry .schema .get_node_schema (name = "IpamIPPrefix" , branch = default_branch , duplicate = True )
@@ -657,3 +686,40 @@ async def test_reconcile_query_on_migrated_kind_node(db: InfrahubDatabase, defau
657686 ip_dataset_01 ["net145" ].id ,
658687 ip_dataset_01 ["address10" ].id ,
659688 }
689+
690+
691+ async def test_reconcile_query_for_address_with_prefix_added_on_branch_and_merged (
692+ db : InfrahubDatabase , default_branch : Branch , ip_dataset_01
693+ ):
694+ """
695+ Test for bug that could cause an IP address to be its own parent after an update on a branch was merged
696+ """
697+ default_ipnamespace = await get_default_ipnamespace (db = db )
698+ registry .default_ipnamespace = default_ipnamespace .id
699+ address_10 = ip_dataset_01 ["address10" ]
700+ namespace = ip_dataset_01 ["ns1" ]
701+
702+ branch = await create_branch (db = db , branch_name = randomized_branch_name ("address-parent" ))
703+
704+ prefix_schema = registry .schema .get_node_schema (name = "IpamIPPrefix" , branch = branch )
705+ new_prefix = await Node .init (db = db , branch = branch , schema = prefix_schema )
706+ await new_prefix .new (db = db , prefix = "10.10.0.0/28" , ip_namespace = namespace , ip_addresses = [address_10 .id ])
707+ await new_prefix .save (db = db )
708+
709+ component_registry = get_component_registry ()
710+ diff_coordinator = await component_registry .get_component (DiffCoordinator , db = db , branch = branch )
711+ diff_merger = await component_registry .get_component (DiffMerger , db = db , branch = branch )
712+ await diff_coordinator .update_branch_diff (base_branch = default_branch , diff_branch = branch )
713+ await diff_merger .merge_graph (at = Timestamp ())
714+ # get branch to make sure branched_from is refreshed
715+ branch = await Branch .get_by_name (db = db , name = branch .name )
716+
717+ ip_interface = ipaddress .ip_interface (address_10 .address .value )
718+ query = await IPPrefixReconcileQuery .init (db = db , branch = branch , ip_value = ip_interface , namespace = namespace )
719+ await query .execute (db = db )
720+
721+ assert query .get_ip_node_uuid () == address_10 .id
722+ assert query .get_current_parent_uuid () == new_prefix .id
723+ assert query .get_current_children_uuids () == []
724+ assert query .get_calculated_parent_uuid () == new_prefix .id
725+ assert query .get_calculated_children_uuids () == []
0 commit comments