infrahub-server 1.7.0b0__py3-none-any.whl → 1.7.1__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.
- infrahub/api/exceptions.py +2 -2
- infrahub/api/schema.py +5 -0
- infrahub/cli/db.py +54 -24
- infrahub/core/account.py +12 -9
- infrahub/core/branch/models.py +11 -117
- infrahub/core/branch/tasks.py +7 -3
- infrahub/core/diff/branch_differ.py +1 -1
- infrahub/core/diff/conflict_transferer.py +1 -1
- infrahub/core/diff/data_check_synchronizer.py +1 -1
- infrahub/core/diff/enricher/cardinality_one.py +1 -1
- infrahub/core/diff/enricher/hierarchy.py +1 -1
- infrahub/core/diff/enricher/labels.py +1 -1
- infrahub/core/diff/merger/merger.py +6 -2
- infrahub/core/diff/repository/repository.py +3 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +1 -1
- infrahub/core/initialization.py +2 -1
- infrahub/core/ipam/reconciler.py +8 -6
- infrahub/core/ipam/utilization.py +8 -15
- infrahub/core/manager.py +1 -26
- infrahub/core/merge.py +1 -1
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +4 -4
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +3 -2
- infrahub/core/migrations/graph/m015_diff_format_update.py +3 -2
- infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +3 -2
- infrahub/core/migrations/graph/m017_add_core_profile.py +6 -4
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +3 -4
- infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -3
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +3 -4
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +4 -5
- infrahub/core/migrations/graph/m028_delete_diffs.py +3 -2
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +3 -2
- infrahub/core/migrations/graph/m031_check_number_attributes.py +4 -3
- infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py +3 -2
- infrahub/core/migrations/graph/m034_find_orphaned_schema_fields.py +3 -2
- infrahub/core/migrations/graph/m035_orphan_relationships.py +3 -3
- infrahub/core/migrations/graph/m036_drop_attr_value_index.py +3 -2
- infrahub/core/migrations/graph/m037_index_attr_vals.py +3 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +4 -5
- infrahub/core/migrations/graph/m039_ipam_reconcile.py +3 -2
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +4 -3
- infrahub/core/migrations/graph/m042_profile_attrs_in_db.py +5 -4
- infrahub/core/migrations/graph/m043_create_hfid_display_label_in_db.py +12 -5
- infrahub/core/migrations/graph/m044_backfill_hfid_display_label_in_db.py +15 -4
- infrahub/core/migrations/graph/m045_backfill_hfid_display_label_in_db_profile_template.py +10 -4
- infrahub/core/migrations/graph/m046_fill_agnostic_hfid_display_labels.py +6 -5
- infrahub/core/migrations/graph/m047_backfill_or_null_display_label.py +19 -5
- infrahub/core/migrations/graph/m048_undelete_rel_props.py +6 -4
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +19 -4
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +3 -3
- infrahub/core/migrations/graph/m051_subtract_branched_from_microsecond.py +39 -0
- infrahub/core/migrations/query/__init__.py +2 -2
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/runner.py +6 -3
- infrahub/core/migrations/schema/attribute_kind_update.py +8 -11
- infrahub/core/migrations/schema/attribute_name_update.py +1 -1
- infrahub/core/migrations/schema/attribute_supports_profile.py +5 -10
- infrahub/core/migrations/schema/models.py +8 -0
- infrahub/core/migrations/schema/node_attribute_add.py +11 -14
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_kind_update.py +1 -1
- infrahub/core/migrations/schema/tasks.py +7 -1
- infrahub/core/migrations/shared.py +37 -30
- infrahub/core/node/__init__.py +3 -2
- infrahub/core/node/base.py +9 -5
- infrahub/core/node/delete_validator.py +1 -1
- infrahub/core/order.py +30 -0
- infrahub/core/protocols.py +1 -0
- infrahub/core/protocols_base.py +4 -0
- infrahub/core/query/__init__.py +8 -5
- infrahub/core/query/attribute.py +3 -3
- infrahub/core/query/branch.py +1 -1
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +3 -3
- infrahub/core/query/ipam.py +104 -43
- infrahub/core/query/node.py +454 -101
- infrahub/core/query/relationship.py +83 -26
- infrahub/core/query/resource_manager.py +107 -18
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/constraints/peer_parent.py +1 -1
- infrahub/core/relationship/constraints/peer_relatives.py +1 -1
- infrahub/core/relationship/constraints/profiles_kind.py +1 -1
- infrahub/core/relationship/constraints/profiles_removal.py +1 -1
- infrahub/core/relationship/model.py +8 -2
- infrahub/core/schema/attribute_parameters.py +28 -1
- infrahub/core/schema/attribute_schema.py +9 -15
- infrahub/core/schema/basenode_schema.py +3 -0
- infrahub/core/schema/definitions/core/__init__.py +8 -2
- infrahub/core/schema/definitions/core/account.py +10 -10
- infrahub/core/schema/definitions/core/artifact.py +14 -8
- infrahub/core/schema/definitions/core/check.py +10 -4
- infrahub/core/schema/definitions/core/generator.py +26 -6
- infrahub/core/schema/definitions/core/graphql_query.py +1 -1
- infrahub/core/schema/definitions/core/group.py +9 -2
- infrahub/core/schema/definitions/core/ipam.py +80 -10
- infrahub/core/schema/definitions/core/menu.py +41 -7
- infrahub/core/schema/definitions/core/permission.py +16 -2
- infrahub/core/schema/definitions/core/profile.py +16 -2
- infrahub/core/schema/definitions/core/propose_change.py +24 -4
- infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
- infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
- infrahub/core/schema/definitions/core/repository.py +10 -0
- infrahub/core/schema/definitions/core/resource_pool.py +8 -1
- infrahub/core/schema/definitions/core/template.py +19 -2
- infrahub/core/schema/definitions/core/transform.py +11 -5
- infrahub/core/schema/definitions/core/webhook.py +27 -9
- infrahub/core/schema/manager.py +50 -38
- infrahub/core/schema/schema_branch.py +68 -2
- infrahub/core/utils.py +3 -3
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +6 -3
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/min_max.py +1 -1
- infrahub/core/validators/attribute/number_pool.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/node/attribute.py +1 -1
- infrahub/core/validators/node/relationship.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/database/__init__.py +1 -1
- infrahub/git/utils.py +1 -1
- infrahub/graphql/app.py +2 -2
- infrahub/graphql/field_extractor.py +1 -1
- infrahub/graphql/manager.py +17 -3
- infrahub/graphql/mutations/account.py +1 -1
- infrahub/graphql/order.py +14 -0
- infrahub/graphql/queries/diff/tree.py +5 -5
- infrahub/graphql/queries/resource_manager.py +25 -24
- infrahub/graphql/resolvers/ipam.py +3 -3
- infrahub/graphql/resolvers/resolver.py +44 -3
- infrahub/graphql/types/standard_node.py +8 -4
- infrahub/lock.py +7 -0
- infrahub/menu/repository.py +1 -1
- infrahub/patch/queries/base.py +1 -1
- infrahub/pools/number.py +1 -8
- infrahub/profiles/node_applier.py +1 -1
- infrahub/profiles/queries/get_profile_data.py +1 -1
- infrahub/proposed_change/action_checker.py +1 -1
- infrahub/services/__init__.py +1 -1
- infrahub/services/adapters/cache/nats.py +1 -1
- infrahub/services/adapters/cache/redis.py +7 -0
- infrahub/webhook/gather.py +1 -1
- infrahub/webhook/tasks.py +22 -6
- infrahub_sdk/analyzer.py +2 -2
- infrahub_sdk/branch.py +12 -39
- infrahub_sdk/checks.py +4 -4
- infrahub_sdk/client.py +36 -0
- infrahub_sdk/ctl/cli_commands.py +2 -1
- infrahub_sdk/ctl/graphql.py +15 -4
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/enums.py +6 -0
- infrahub_sdk/graphql/renderers.py +21 -0
- infrahub_sdk/graphql/utils.py +85 -0
- infrahub_sdk/node/attribute.py +12 -2
- infrahub_sdk/node/constants.py +11 -0
- infrahub_sdk/node/metadata.py +69 -0
- infrahub_sdk/node/node.py +65 -14
- infrahub_sdk/node/property.py +3 -0
- infrahub_sdk/node/related_node.py +24 -1
- infrahub_sdk/node/relationship.py +10 -1
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/schema/repository.py +1 -2
- infrahub_sdk/transforms.py +2 -2
- infrahub_sdk/types.py +18 -2
- {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/METADATA +6 -6
- {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/RECORD +176 -172
- {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/entry_points.txt +0 -1
- infrahub_testcontainers/models.py +3 -3
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub/graphql/models.py +0 -36
- {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/WHEEL +0 -0
- {infrahub_server-1.7.0b0.dist-info → infrahub_server-1.7.1.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/core/order.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, model_validator
|
|
6
|
+
|
|
7
|
+
from infrahub.constants.enums import OrderDirection # noqa: TC001
|
|
8
|
+
from infrahub.exceptions import ValidationError
|
|
9
|
+
|
|
10
|
+
# Metadata field name constants
|
|
11
|
+
METADATA_CREATED_AT = "created_at"
|
|
12
|
+
METADATA_CREATED_BY = "created_by"
|
|
13
|
+
METADATA_UPDATED_AT = "updated_at"
|
|
14
|
+
METADATA_UPDATED_BY = "updated_by"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NodeMetaOrder(BaseModel):
|
|
18
|
+
created_at: OrderDirection | None = None
|
|
19
|
+
updated_at: OrderDirection | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OrderModel(BaseModel):
|
|
23
|
+
disable: bool | None = None
|
|
24
|
+
node_metadata: NodeMetaOrder | None = None
|
|
25
|
+
|
|
26
|
+
@model_validator(mode="after")
|
|
27
|
+
def validate_metadata(self) -> Self:
|
|
28
|
+
if self.node_metadata and self.node_metadata.created_at and self.node_metadata.updated_at:
|
|
29
|
+
raise ValidationError("Cannot order by both created_at and updated_at simultaneously.")
|
|
30
|
+
return self
|
infrahub/core/protocols.py
CHANGED
infrahub/core/protocols_base.py
CHANGED
|
@@ -106,3 +106,7 @@ class CoreNode(Protocol):
|
|
|
106
106
|
) -> dict: ...
|
|
107
107
|
async def render_display_label(self, db: InfrahubDatabase | None = None) -> str: ...
|
|
108
108
|
async def from_graphql(self, data: dict, db: InfrahubDatabase) -> bool: ...
|
|
109
|
+
def _get_created_at(self) -> Timestamp | None: ...
|
|
110
|
+
def _get_created_by(self) -> str | None: ...
|
|
111
|
+
def _get_updated_at(self) -> Timestamp | None: ...
|
|
112
|
+
def _get_updated_by(self) -> str | None: ...
|
infrahub/core/query/__init__.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
3
|
from collections import defaultdict
|
|
5
4
|
from dataclasses import dataclass, field
|
|
6
5
|
from enum import Enum
|
|
@@ -160,7 +159,7 @@ def cleanup_return_labels(labels: list[str]) -> list[str]:
|
|
|
160
159
|
|
|
161
160
|
|
|
162
161
|
class QueryResult:
|
|
163
|
-
def __init__(self, data: list[Neo4jNode | Neo4jRelationship | list[Neo4jNode]], labels: list[str]):
|
|
162
|
+
def __init__(self, data: list[Neo4jNode | Neo4jRelationship | list[Neo4jNode]], labels: list[str]) -> None:
|
|
164
163
|
self.data = data
|
|
165
164
|
self.labels = labels
|
|
166
165
|
self.branch_score: int = 0
|
|
@@ -336,7 +335,7 @@ class QueryStat:
|
|
|
336
335
|
return cls(**data)
|
|
337
336
|
|
|
338
337
|
|
|
339
|
-
class Query
|
|
338
|
+
class Query:
|
|
340
339
|
name: str = "base-query"
|
|
341
340
|
type: QueryType
|
|
342
341
|
|
|
@@ -353,7 +352,7 @@ class Query(ABC):
|
|
|
353
352
|
order_by: list[str] | None = None,
|
|
354
353
|
branch_agnostic: bool = False,
|
|
355
354
|
user_id: str = SYSTEM_USER_ID,
|
|
356
|
-
):
|
|
355
|
+
) -> None:
|
|
357
356
|
if branch:
|
|
358
357
|
self.branch = branch
|
|
359
358
|
|
|
@@ -405,8 +404,12 @@ class Query(ABC):
|
|
|
405
404
|
|
|
406
405
|
return query
|
|
407
406
|
|
|
408
|
-
@abstractmethod
|
|
409
407
|
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
|
|
408
|
+
# Avoid using this method for new queries and look at migrating older queries. The
|
|
409
|
+
# problem here is that we loose so much information with the `**kwargs` we should instead
|
|
410
|
+
# populate this information via the constructor and anything done within the existing query_init methods
|
|
411
|
+
# could either be handled within __init__ or via dedicated methods within each Query class where appropriate,
|
|
412
|
+
# i.e. things might need to happend in a certain order or we just want to separate the logic better.
|
|
410
413
|
raise NotImplementedError
|
|
411
414
|
|
|
412
415
|
def get_context(self) -> dict[str, str]:
|
infrahub/core/query/attribute.py
CHANGED
|
@@ -31,7 +31,7 @@ class AttributeQuery(Query):
|
|
|
31
31
|
at: Timestamp | str | None = None,
|
|
32
32
|
branch: Branch | None = None,
|
|
33
33
|
**kwargs: Any,
|
|
34
|
-
):
|
|
34
|
+
) -> None:
|
|
35
35
|
self.attr = attr
|
|
36
36
|
self.attr_id = attr_id or attr.db_id
|
|
37
37
|
|
|
@@ -171,7 +171,7 @@ class AttributeUpdateNodePropertyQuery(AttributeQuery):
|
|
|
171
171
|
prop_name: str,
|
|
172
172
|
prop_id: str | None = None,
|
|
173
173
|
**kwargs: Any,
|
|
174
|
-
):
|
|
174
|
+
) -> None:
|
|
175
175
|
self.prop_name = prop_name
|
|
176
176
|
self.prop_id = prop_id
|
|
177
177
|
|
|
@@ -246,7 +246,7 @@ class AttributeClearNodePropertyQuery(AttributeQuery):
|
|
|
246
246
|
self,
|
|
247
247
|
prop_name: str,
|
|
248
248
|
**kwargs: Any,
|
|
249
|
-
):
|
|
249
|
+
) -> None:
|
|
250
250
|
self.prop_name = prop_name
|
|
251
251
|
|
|
252
252
|
super().__init__(**kwargs)
|
infrahub/core/query/branch.py
CHANGED
|
@@ -17,7 +17,7 @@ class DeleteBranchRelationshipsQuery(Query):
|
|
|
17
17
|
|
|
18
18
|
type: QueryType = QueryType.WRITE
|
|
19
19
|
|
|
20
|
-
def __init__(self, branch_name: str, **kwargs: Any):
|
|
20
|
+
def __init__(self, branch_name: str, **kwargs: Any) -> None:
|
|
21
21
|
self.branch_name = branch_name
|
|
22
22
|
super().__init__(**kwargs)
|
|
23
23
|
|
infrahub/core/query/delete.py
CHANGED
|
@@ -11,7 +11,7 @@ class DeleteAfterTimeQuery(Query):
|
|
|
11
11
|
insert_return: bool = False
|
|
12
12
|
type: QueryType = QueryType.WRITE
|
|
13
13
|
|
|
14
|
-
def __init__(self, timestamp: Timestamp, **kwargs: Any):
|
|
14
|
+
def __init__(self, timestamp: Timestamp, **kwargs: Any) -> None:
|
|
15
15
|
self.timestamp = timestamp
|
|
16
16
|
super().__init__(**kwargs)
|
|
17
17
|
|
infrahub/core/query/diff.py
CHANGED
|
@@ -26,7 +26,7 @@ class DiffQuery(Query):
|
|
|
26
26
|
diff_from: Timestamp | str = None,
|
|
27
27
|
diff_to: Timestamp | str = None,
|
|
28
28
|
**kwargs,
|
|
29
|
-
):
|
|
29
|
+
) -> None:
|
|
30
30
|
"""A diff is always in the context of a branch"""
|
|
31
31
|
|
|
32
32
|
if not diff_from and branch.is_default:
|
|
@@ -59,7 +59,7 @@ class DiffCountChanges(Query):
|
|
|
59
59
|
diff_from: Timestamp,
|
|
60
60
|
diff_to: Timestamp,
|
|
61
61
|
**kwargs,
|
|
62
|
-
):
|
|
62
|
+
) -> None:
|
|
63
63
|
self.branch_names = branch_names
|
|
64
64
|
self.diff_from = diff_from
|
|
65
65
|
self.diff_to = diff_to
|
|
@@ -122,7 +122,7 @@ class DiffCalculationQuery(DiffQuery):
|
|
|
122
122
|
current_node_field_specifiers: NodeFieldSpecifierMap | None = None,
|
|
123
123
|
new_node_field_specifiers: NodeFieldSpecifierMap | None = None,
|
|
124
124
|
**kwargs: Any,
|
|
125
|
-
):
|
|
125
|
+
) -> None:
|
|
126
126
|
self.base_branch = base_branch
|
|
127
127
|
self.diff_branch_from_time = diff_branch_from_time
|
|
128
128
|
self.current_node_field_specifiers = current_node_field_specifiers
|
infrahub/core/query/ipam.py
CHANGED
|
@@ -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
|
|
706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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])
|