infrahub-server 1.7.0b0__py3-none-any.whl → 1.7.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. infrahub/api/exceptions.py +2 -2
  2. infrahub/cli/db.py +48 -22
  3. infrahub/core/account.py +12 -9
  4. infrahub/core/diff/branch_differ.py +1 -1
  5. infrahub/core/diff/conflict_transferer.py +1 -1
  6. infrahub/core/diff/data_check_synchronizer.py +1 -1
  7. infrahub/core/diff/enricher/cardinality_one.py +1 -1
  8. infrahub/core/diff/enricher/hierarchy.py +1 -1
  9. infrahub/core/diff/enricher/labels.py +1 -1
  10. infrahub/core/diff/merger/merger.py +1 -1
  11. infrahub/core/diff/repository/repository.py +3 -1
  12. infrahub/core/graph/constraints.py +1 -1
  13. infrahub/core/ipam/reconciler.py +8 -6
  14. infrahub/core/ipam/utilization.py +8 -15
  15. infrahub/core/manager.py +1 -26
  16. infrahub/core/merge.py +1 -1
  17. infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
  18. infrahub/core/migrations/graph/m013_convert_git_password_credential.py +4 -4
  19. infrahub/core/migrations/graph/m041_deleted_dup_edges.py +1 -1
  20. infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +16 -1
  21. infrahub/core/migrations/query/__init__.py +2 -2
  22. infrahub/core/migrations/query/schema_attribute_update.py +1 -1
  23. infrahub/core/migrations/schema/attribute_name_update.py +1 -1
  24. infrahub/core/migrations/schema/attribute_supports_profile.py +2 -2
  25. infrahub/core/migrations/schema/node_attribute_add.py +1 -1
  26. infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
  27. infrahub/core/migrations/schema/node_kind_update.py +1 -1
  28. infrahub/core/node/__init__.py +1 -1
  29. infrahub/core/node/base.py +9 -5
  30. infrahub/core/node/delete_validator.py +1 -1
  31. infrahub/core/order.py +30 -0
  32. infrahub/core/protocols.py +1 -0
  33. infrahub/core/protocols_base.py +4 -0
  34. infrahub/core/query/__init__.py +8 -5
  35. infrahub/core/query/attribute.py +3 -3
  36. infrahub/core/query/branch.py +1 -1
  37. infrahub/core/query/delete.py +1 -1
  38. infrahub/core/query/diff.py +3 -3
  39. infrahub/core/query/ipam.py +104 -43
  40. infrahub/core/query/node.py +454 -101
  41. infrahub/core/query/relationship.py +83 -26
  42. infrahub/core/query/resource_manager.py +107 -18
  43. infrahub/core/relationship/constraints/count.py +1 -1
  44. infrahub/core/relationship/constraints/peer_kind.py +1 -1
  45. infrahub/core/relationship/constraints/peer_parent.py +1 -1
  46. infrahub/core/relationship/constraints/peer_relatives.py +1 -1
  47. infrahub/core/relationship/constraints/profiles_kind.py +1 -1
  48. infrahub/core/relationship/constraints/profiles_removal.py +1 -1
  49. infrahub/core/schema/attribute_schema.py +0 -13
  50. infrahub/core/schema/basenode_schema.py +3 -0
  51. infrahub/core/schema/definitions/core/__init__.py +8 -2
  52. infrahub/core/schema/definitions/core/account.py +10 -10
  53. infrahub/core/schema/definitions/core/artifact.py +14 -8
  54. infrahub/core/schema/definitions/core/check.py +10 -4
  55. infrahub/core/schema/definitions/core/generator.py +26 -6
  56. infrahub/core/schema/definitions/core/graphql_query.py +1 -1
  57. infrahub/core/schema/definitions/core/group.py +9 -2
  58. infrahub/core/schema/definitions/core/ipam.py +80 -10
  59. infrahub/core/schema/definitions/core/menu.py +41 -7
  60. infrahub/core/schema/definitions/core/permission.py +16 -2
  61. infrahub/core/schema/definitions/core/profile.py +16 -2
  62. infrahub/core/schema/definitions/core/propose_change.py +24 -4
  63. infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
  64. infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
  65. infrahub/core/schema/definitions/core/repository.py +10 -0
  66. infrahub/core/schema/definitions/core/resource_pool.py +8 -1
  67. infrahub/core/schema/definitions/core/template.py +19 -2
  68. infrahub/core/schema/definitions/core/transform.py +11 -5
  69. infrahub/core/schema/definitions/core/webhook.py +27 -9
  70. infrahub/core/schema/schema_branch.py +68 -2
  71. infrahub/core/utils.py +3 -3
  72. infrahub/core/validators/aggregated_checker.py +1 -1
  73. infrahub/core/validators/attribute/choices.py +1 -1
  74. infrahub/core/validators/attribute/enum.py +1 -1
  75. infrahub/core/validators/attribute/kind.py +1 -1
  76. infrahub/core/validators/attribute/length.py +1 -1
  77. infrahub/core/validators/attribute/min_max.py +1 -1
  78. infrahub/core/validators/attribute/number_pool.py +1 -1
  79. infrahub/core/validators/attribute/optional.py +1 -1
  80. infrahub/core/validators/attribute/regex.py +1 -1
  81. infrahub/core/validators/node/attribute.py +1 -1
  82. infrahub/core/validators/node/relationship.py +1 -1
  83. infrahub/core/validators/relationship/peer.py +1 -1
  84. infrahub/database/__init__.py +1 -1
  85. infrahub/git/utils.py +1 -1
  86. infrahub/graphql/app.py +2 -2
  87. infrahub/graphql/field_extractor.py +1 -1
  88. infrahub/graphql/manager.py +9 -1
  89. infrahub/graphql/mutations/account.py +1 -1
  90. infrahub/graphql/order.py +14 -0
  91. infrahub/graphql/queries/diff/tree.py +5 -5
  92. infrahub/graphql/queries/resource_manager.py +25 -24
  93. infrahub/graphql/resolvers/ipam.py +3 -3
  94. infrahub/graphql/resolvers/resolver.py +44 -3
  95. infrahub/graphql/types/standard_node.py +8 -4
  96. infrahub/menu/repository.py +1 -1
  97. infrahub/patch/queries/base.py +1 -1
  98. infrahub/pools/number.py +1 -8
  99. infrahub/profiles/node_applier.py +1 -1
  100. infrahub/profiles/queries/get_profile_data.py +1 -1
  101. infrahub/proposed_change/action_checker.py +1 -1
  102. infrahub/services/__init__.py +1 -1
  103. infrahub/services/adapters/cache/nats.py +1 -1
  104. infrahub/webhook/gather.py +1 -1
  105. infrahub/webhook/tasks.py +22 -6
  106. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.0rc0.dist-info}/METADATA +1 -1
  107. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.0rc0.dist-info}/RECORD +111 -110
  108. infrahub_testcontainers/models.py +3 -3
  109. infrahub/graphql/models.py +0 -36
  110. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.0rc0.dist-info}/WHEEL +0 -0
  111. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.0rc0.dist-info}/entry_points.txt +0 -0
  112. {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.0rc0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Iterable
7
7
  from infrahub.core.constants import InfrahubKind
8
8
  from infrahub.core.graph.schema import GraphAttributeIPHostNode, GraphAttributeIPNetworkNode
9
9
  from infrahub.core.ipam.constants import AllIPTypes, IPAddressType, IPNetworkType
10
- from infrahub.core.query import QueryType
10
+ from infrahub.core.query import QueryResult, QueryType
11
11
  from infrahub.core.registry import registry
12
12
  from infrahub.core.utils import convert_ip_to_binary_str
13
13
 
@@ -57,7 +57,7 @@ class IPPrefixSubnetFetch(Query):
57
57
  obj: IPNetworkType,
58
58
  namespace: Node | str | None = None,
59
59
  **kwargs,
60
- ):
60
+ ) -> None:
61
61
  self.obj = obj
62
62
  self.namespace_id = _get_namespace_id(namespace)
63
63
 
@@ -146,7 +146,7 @@ class IPPrefixIPAddressFetch(Query):
146
146
  obj: IPNetworkType,
147
147
  namespace: Node | str | None = None,
148
148
  **kwargs,
149
- ):
149
+ ) -> None:
150
150
  self.obj = obj
151
151
  self.namespace_id = _get_namespace_id(namespace)
152
152
 
@@ -242,11 +242,53 @@ async def get_ip_addresses(
242
242
  return query.get_addresses()
243
243
 
244
244
 
245
+ @dataclass(frozen=True)
246
+ class IPPrefixUtilizationResult:
247
+ """Result from IPPrefixUtilization containing prefix child allocation data."""
248
+
249
+ prefix_uuid: str
250
+ """UUID of the parent prefix node."""
251
+
252
+ child_uuid: str
253
+ """UUID of the child node (prefix or address)."""
254
+
255
+ child_kind: str
256
+ """Kind/type of the child node."""
257
+
258
+ child_labels: tuple[str, ...]
259
+ """Labels of the child node (used to determine if IPADDRESS or IPPREFIX)."""
260
+
261
+ ip_value: str
262
+ """IP value (address or prefix) of the child."""
263
+
264
+ prefixlen: int
265
+ """Prefix length of the child IP value."""
266
+
267
+ branch: str
268
+ """Branch name where this allocation exists."""
269
+
270
+ @classmethod
271
+ def from_db(cls, result: QueryResult) -> IPPrefixUtilizationResult:
272
+ """Convert raw QueryResult to typed dataclass."""
273
+ pfx = result.get_node("pfx")
274
+ child = result.get_node("child")
275
+ av = result.get_node("av")
276
+ return cls(
277
+ prefix_uuid=str(pfx.get("uuid")),
278
+ child_uuid=str(child.get("uuid")),
279
+ child_kind=child.get("kind"),
280
+ child_labels=tuple(child.labels),
281
+ ip_value=av.get("value"),
282
+ prefixlen=av.get("prefixlen"),
283
+ branch=str(result.get("branch")),
284
+ )
285
+
286
+
245
287
  class IPPrefixUtilization(Query):
246
288
  name = "ipprefix_utilization_prefix"
247
289
  type = QueryType.READ
248
290
 
249
- def __init__(self, ip_prefixes: list[str], allocated_kinds: list[str], **kwargs):
291
+ def __init__(self, ip_prefixes: list[str], allocated_kinds: list[str], **kwargs) -> None:
250
292
  self.ip_prefixes = ip_prefixes
251
293
  self.allocated_kinds: list[str] = []
252
294
  self.allocated_kinds_rel: list[str] = []
@@ -315,6 +357,55 @@ class IPPrefixUtilization(Query):
315
357
  self.return_labels = ["pfx", "child", "av", "branch_level", "branch"]
316
358
  self.add_to_query(query)
317
359
 
360
+ def get_data(self) -> list[IPPrefixUtilizationResult]:
361
+ """Return results as typed dataclass instances.
362
+
363
+ Returns:
364
+ List of IPPrefixUtilizationResult containing prefix child allocation data.
365
+ """
366
+ return [IPPrefixUtilizationResult.from_db(result) for result in self.get_results()]
367
+
368
+
369
+ @dataclass(frozen=True)
370
+ class IPPrefixReconcileQueryResult:
371
+ """Result from IPPrefixReconcileQuery containing IP reconciliation data."""
372
+
373
+ ip_node_uuid: str | None
374
+ """UUID of the IP node being reconciled, or None if not found."""
375
+
376
+ current_parent_uuid: str | None
377
+ """UUID of the current parent prefix, or None if no parent exists."""
378
+
379
+ calculated_parent_uuid: str | None
380
+ """UUID of the calculated correct parent prefix, or None if should be top-level."""
381
+
382
+ current_children_uuids: tuple[str, ...]
383
+ """UUIDs of current child prefixes/addresses."""
384
+
385
+ calculated_children_uuids: tuple[str, ...]
386
+ """UUIDs of calculated correct child prefixes/addresses."""
387
+
388
+ @classmethod
389
+ def from_db(cls, result: QueryResult) -> IPPrefixReconcileQueryResult:
390
+ """Convert raw QueryResult to typed dataclass."""
391
+
392
+ def get_optional_node_uuid(label: str) -> str | None:
393
+ """Extract UUID from an optional node (may be None from OPTIONAL MATCH)."""
394
+ node = result.get(label)
395
+ return str(node.get("uuid")) if node and node.get("uuid") else None
396
+
397
+ def get_collection_uuids(label: str) -> tuple[str, ...]:
398
+ """Extract UUIDs from a node collection (may contain None from COLLECT)."""
399
+ return tuple(str(n.get("uuid")) for n in result.get_node_collection(label) if n and n.get("uuid"))
400
+
401
+ return cls(
402
+ ip_node_uuid=get_optional_node_uuid("ip_node"),
403
+ current_parent_uuid=get_optional_node_uuid("current_parent"),
404
+ calculated_parent_uuid=get_optional_node_uuid("new_parent"),
405
+ current_children_uuids=get_collection_uuids("current_children"),
406
+ calculated_children_uuids=get_collection_uuids("new_children"),
407
+ )
408
+
318
409
 
319
410
  class IPPrefixReconcileQuery(Query):
320
411
  name = "ip_prefix_reconcile"
@@ -326,7 +417,7 @@ class IPPrefixReconcileQuery(Query):
326
417
  namespace: Node | str | None = None,
327
418
  node_uuid: str | None = None,
328
419
  **kwargs,
329
- ):
420
+ ) -> None:
330
421
  self.ip_value = ip_value
331
422
  self.ip_uuid = node_uuid
332
423
  self.namespace_id = _get_namespace_id(namespace)
@@ -702,44 +793,14 @@ class IPPrefixReconcileQuery(Query):
702
793
  self.order_by = ["ip_node.uuid"]
703
794
  self.return_labels = ["ip_node", "current_parent", "current_children", "new_parent", "new_children"]
704
795
 
705
- def _get_uuid_from_query(self, node_name: str) -> str | None:
706
- results = list(self.get_results())
707
- if not results:
708
- return None
709
- result = results[0]
710
- node = result.get(node_name)
711
- if not node:
712
- return None
713
- node_uuid = node.get("uuid")
714
- if node_uuid:
715
- return str(node_uuid)
716
- return None
796
+ def get_data(self) -> IPPrefixReconcileQueryResult | None:
797
+ """Return single result as typed dataclass instance.
717
798
 
718
- def _get_uuids_from_query_list(self, alias_name: str) -> list[str]:
799
+ Returns:
800
+ IPPrefixReconcileQueryResult containing reconciliation data,
801
+ or None if no results found.
802
+ """
719
803
  results = list(self.get_results())
720
804
  if not results:
721
- return []
722
- result = results[0]
723
- element_uuids = []
724
- for element in result.get(alias_name):
725
- if not element:
726
- continue
727
- element_uuid = element.get("uuid")
728
- if element_uuid:
729
- element_uuids.append(str(element_uuid))
730
- return element_uuids
731
-
732
- def get_ip_node_uuid(self) -> str | None:
733
- return self._get_uuid_from_query("ip_node")
734
-
735
- def get_current_parent_uuid(self) -> str | None:
736
- return self._get_uuid_from_query("current_parent")
737
-
738
- def get_calculated_parent_uuid(self) -> str | None:
739
- return self._get_uuid_from_query("new_parent")
740
-
741
- def get_current_children_uuids(self) -> list[str]:
742
- return self._get_uuids_from_query_list("current_children")
743
-
744
- def get_calculated_children_uuids(self) -> list[str]:
745
- return self._get_uuids_from_query_list("new_children")
805
+ return None
806
+ return IPPrefixReconcileQueryResult.from_db(results[0])