infrahub-server 1.6.3__py3-none-any.whl → 1.7.0__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/actions/tasks.py +4 -2
- infrahub/api/exceptions.py +2 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +54 -28
- infrahub/computed_attribute/gather.py +3 -4
- infrahub/computed_attribute/tasks.py +23 -6
- infrahub/config.py +8 -0
- infrahub/constants/enums.py +12 -0
- infrahub/core/account.py +4 -4
- infrahub/core/attribute.py +106 -108
- infrahub/core/branch/models.py +44 -71
- infrahub/core/branch/tasks.py +5 -3
- infrahub/core/changelog/diff.py +1 -20
- infrahub/core/changelog/models.py +0 -7
- infrahub/core/constants/__init__.py +17 -0
- infrahub/core/constants/database.py +0 -1
- infrahub/core/constants/schema.py +0 -1
- infrahub/core/convert_object_type/repository_conversion.py +3 -4
- infrahub/core/diff/branch_differ.py +1 -1
- infrahub/core/diff/conflict_transferer.py +1 -1
- infrahub/core/diff/data_check_synchronizer.py +4 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -2
- infrahub/core/diff/enricher/hierarchy.py +1 -1
- infrahub/core/diff/enricher/labels.py +1 -1
- infrahub/core/diff/merger/merger.py +28 -2
- infrahub/core/diff/merger/serializer.py +3 -10
- infrahub/core/diff/model/diff.py +1 -1
- infrahub/core/diff/query/merge.py +376 -135
- infrahub/core/diff/repository/repository.py +3 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +3 -3
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/ipam/reconciler.py +8 -6
- infrahub/core/ipam/utilization.py +8 -15
- infrahub/core/manager.py +133 -152
- infrahub/core/merge.py +1 -1
- infrahub/core/metadata/__init__.py +0 -0
- infrahub/core/metadata/interface.py +37 -0
- infrahub/core/metadata/model.py +31 -0
- infrahub/core/metadata/query/__init__.py +0 -0
- infrahub/core/metadata/query/node_metadata.py +301 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -12
- infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
- infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
- infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +1 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +53 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/__init__.py +2 -2
- infrahub/core/migrations/query/attribute_add.py +17 -6
- infrahub/core/migrations/query/attribute_remove.py +19 -5
- infrahub/core/migrations/query/attribute_rename.py +21 -5
- infrahub/core/migrations/query/node_duplicate.py +19 -4
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/attribute_kind_update.py +21 -2
- infrahub/core/migrations/schema/attribute_name_update.py +1 -1
- infrahub/core/migrations/schema/attribute_supports_profile.py +5 -3
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +5 -2
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_kind_update.py +1 -1
- infrahub/core/migrations/schema/node_remove.py +24 -2
- infrahub/core/migrations/schema/tasks.py +4 -1
- infrahub/core/migrations/shared.py +13 -6
- infrahub/core/models.py +6 -6
- infrahub/core/node/__init__.py +157 -58
- infrahub/core/node/base.py +9 -5
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/delete_validator.py +1 -1
- infrahub/core/node/standard.py +100 -14
- infrahub/core/order.py +30 -0
- infrahub/core/property.py +0 -1
- infrahub/core/protocols.py +1 -0
- infrahub/core/protocols_base.py +10 -2
- infrahub/core/query/__init__.py +5 -3
- infrahub/core/query/attribute.py +164 -49
- infrahub/core/query/branch.py +58 -70
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +7 -7
- infrahub/core/query/ipam.py +104 -43
- infrahub/core/query/node.py +1072 -281
- infrahub/core/query/relationship.py +531 -325
- infrahub/core/query/resource_manager.py +107 -18
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- 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 +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_schema.py +2 -2
- 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 +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -10
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +3 -25
- 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 +1 -1
- 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/determiner.py +3 -3
- 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 +4 -4
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
- infrahub/dependencies/registry.py +2 -0
- infrahub/display_labels/tasks.py +12 -3
- infrahub/git/integrator.py +18 -18
- infrahub/git/tasks.py +1 -1
- infrahub/git/utils.py +1 -1
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/field_extractor.py +1 -1
- infrahub/graphql/initialization.py +11 -0
- infrahub/graphql/loaders/account.py +134 -0
- infrahub/graphql/loaders/node.py +5 -12
- infrahub/graphql/loaders/peers.py +5 -7
- infrahub/graphql/manager.py +175 -21
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/mutations/account.py +6 -6
- infrahub/graphql/mutations/attribute.py +0 -2
- infrahub/graphql/mutations/branch.py +9 -5
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +4 -6
- infrahub/graphql/mutations/main.py +9 -4
- infrahub/graphql/mutations/profile.py +16 -22
- infrahub/graphql/mutations/proposed_change.py +4 -4
- infrahub/graphql/mutations/relationship.py +40 -10
- infrahub/graphql/mutations/repository.py +14 -12
- infrahub/graphql/mutations/schema.py +2 -2
- infrahub/graphql/order.py +14 -0
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/resource_manager.py +25 -24
- infrahub/graphql/resolvers/account_metadata.py +84 -0
- infrahub/graphql/resolvers/ipam.py +6 -8
- infrahub/graphql/resolvers/many_relationship.py +77 -35
- infrahub/graphql/resolvers/resolver.py +59 -14
- infrahub/graphql/resolvers/single_relationship.py +87 -23
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/__init__.py +0 -1
- infrahub/graphql/types/attribute.py +10 -5
- infrahub/graphql/types/branch.py +40 -53
- infrahub/graphql/types/enums.py +3 -0
- infrahub/graphql/types/metadata.py +28 -0
- infrahub/graphql/types/node.py +22 -2
- infrahub/graphql/types/relationship.py +10 -2
- infrahub/graphql/types/standard_node.py +12 -7
- infrahub/hfid/tasks.py +12 -3
- 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/gather.py +56 -0
- infrahub/profiles/mandatory_fields_checker.py +116 -0
- infrahub/profiles/models.py +66 -0
- infrahub/profiles/node_applier.py +154 -13
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/action_checker.py +1 -1
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/services/__init__.py +1 -1
- infrahub/services/adapters/cache/nats.py +1 -1
- infrahub/services/adapters/cache/redis.py +7 -0
- infrahub/tasks/artifact.py +1 -0
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +1 -0
- infrahub/trigger/setup.py +3 -3
- infrahub/trigger/tasks.py +3 -0
- infrahub/validators/tasks.py +1 -0
- infrahub/webhook/gather.py +1 -1
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +23 -7
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- 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 +12 -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 +37 -5
- infrahub_sdk/node/relationship.py +18 -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.6.3.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +249 -228
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- infrahub_testcontainers/models.py +3 -3
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub/graphql/models.py +0 -6
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/core/query/branch.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
|
-
from infrahub import config
|
|
6
5
|
from infrahub.core.branch.enums import BranchStatus
|
|
7
6
|
from infrahub.core.constants import GLOBAL_BRANCH_NAME
|
|
8
7
|
from infrahub.core.query import Query, QueryType
|
|
@@ -18,7 +17,7 @@ class DeleteBranchRelationshipsQuery(Query):
|
|
|
18
17
|
|
|
19
18
|
type: QueryType = QueryType.WRITE
|
|
20
19
|
|
|
21
|
-
def __init__(self, branch_name: str, **kwargs: Any):
|
|
20
|
+
def __init__(self, branch_name: str, **kwargs: Any) -> None:
|
|
22
21
|
self.branch_name = branch_name
|
|
23
22
|
super().__init__(**kwargs)
|
|
24
23
|
|
|
@@ -70,89 +69,78 @@ CALL (vertex_id) {
|
|
|
70
69
|
self.add_to_query(query)
|
|
71
70
|
|
|
72
71
|
|
|
73
|
-
class
|
|
74
|
-
|
|
72
|
+
class RebaseBranchQuery(Query):
|
|
73
|
+
"""Rebase a branch onto the default branch by updating edge timestamps
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
WHERE r.branch = $branch_name
|
|
83
|
-
RETURN DISTINCT r
|
|
84
|
-
"""
|
|
85
|
-
self.add_to_query(query=query)
|
|
86
|
-
self.params["branch_name"] = self.branch.name
|
|
87
|
-
self.return_labels = ["r"]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class RebaseBranchUpdateRelationshipQuery(Query):
|
|
91
|
-
name: str = "rebase_branch_update"
|
|
75
|
+
For every edge on this branch
|
|
76
|
+
if it has a from time before $at and no to time, update it to $at
|
|
77
|
+
if it has a to time before $at, delete the edge
|
|
78
|
+
if it has a to time after $at, update the from time to $at
|
|
79
|
+
Then delete any orphaned vertices
|
|
80
|
+
"""
|
|
92
81
|
|
|
82
|
+
name: str = "rebase_branch"
|
|
93
83
|
type: QueryType = QueryType.WRITE
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
self.ids = ids
|
|
97
|
-
super().__init__(**kwargs)
|
|
84
|
+
insert_return: bool = False
|
|
85
|
+
raise_error_if_empty: bool = False
|
|
98
86
|
|
|
99
87
|
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
100
|
-
|
|
101
|
-
MATCH ()-[r]->()
|
|
102
|
-
WHERE %(id_func)s(r) IN $ids
|
|
103
|
-
SET r.from = $at
|
|
104
|
-
SET r.conflict = NULL
|
|
105
|
-
""" % {
|
|
106
|
-
"id_func": db.get_id_function_name(),
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
self.add_to_query(query=query)
|
|
110
|
-
|
|
88
|
+
self.params["branch_name"] = self.branch.name
|
|
111
89
|
self.params["at"] = self.at.to_string()
|
|
112
|
-
self.params["ids"] = [db.to_database_id(id) for id in self.ids]
|
|
113
|
-
self.return_labels = [f"{db.get_id_function_name()}(r)"]
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class RebaseBranchDeleteRelationshipQuery(Query):
|
|
117
|
-
name: str = "rebase_branch_delete"
|
|
118
90
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
91
|
+
query = """
|
|
92
|
+
// --------------
|
|
93
|
+
// Get all edges on this branch with their source and destination vertices
|
|
94
|
+
// --------------
|
|
95
|
+
MATCH (s)-[r]-(d)
|
|
96
|
+
WHERE r.branch = $branch_name
|
|
97
|
+
WITH DISTINCT r, s, d
|
|
98
|
+
WITH r, s, d,
|
|
99
|
+
CASE
|
|
100
|
+
// No `to` and `from` <= at: update
|
|
101
|
+
WHEN r.to IS NULL AND r.from <= $at THEN TRUE
|
|
102
|
+
// Has `to` and `to` < at: delete
|
|
103
|
+
WHEN r.to IS NOT NULL AND r.to < $at THEN FALSE
|
|
104
|
+
// Has `to` and `to` >= at: update
|
|
105
|
+
ELSE TRUE
|
|
106
|
+
END AS do_update
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
query = """
|
|
135
|
-
MATCH p = (s)-[r]-(d)
|
|
136
|
-
WHERE %(id_func)s(r) IN $ids
|
|
137
|
-
DELETE r
|
|
138
|
-
WITH *
|
|
139
|
-
UNWIND nodes(p) AS n
|
|
140
|
-
MATCH (n)
|
|
141
|
-
WHERE NOT exists((n)--())
|
|
142
|
-
DELETE n
|
|
143
|
-
"""
|
|
144
|
-
query %= {
|
|
145
|
-
"id_func": db.get_id_function_name(),
|
|
146
|
-
}
|
|
108
|
+
// --------------
|
|
109
|
+
// Process updates: set from = at for relationships we're keeping
|
|
110
|
+
// --------------
|
|
111
|
+
CALL (r, do_update) {
|
|
112
|
+
WITH r, do_update
|
|
113
|
+
WHERE do_update = TRUE
|
|
114
|
+
SET r.from = $at
|
|
115
|
+
}
|
|
147
116
|
|
|
117
|
+
// --------------
|
|
118
|
+
// Delete the edges
|
|
119
|
+
// --------------
|
|
120
|
+
WITH r, s, d, do_update
|
|
121
|
+
WHERE do_update = FALSE
|
|
122
|
+
CALL (r, s, d) {
|
|
123
|
+
DELETE r
|
|
124
|
+
}
|
|
125
|
+
// --------------
|
|
126
|
+
// Clean up any orpahned nodes edges
|
|
127
|
+
// --------------
|
|
128
|
+
WITH DISTINCT s, d
|
|
129
|
+
UNWIND [s, d] AS n
|
|
130
|
+
WITH DISTINCT n
|
|
131
|
+
CALL (n) {
|
|
132
|
+
MATCH (n)
|
|
133
|
+
WHERE NOT exists((n)--())
|
|
134
|
+
DELETE n
|
|
135
|
+
}
|
|
136
|
+
"""
|
|
148
137
|
self.add_to_query(query=query)
|
|
149
138
|
|
|
150
|
-
self.params["ids"] = [db.to_database_id(id) for id in self.ids]
|
|
151
|
-
|
|
152
139
|
|
|
153
140
|
class BranchNodeGetListQuery(StandardNodeGetListQuery):
|
|
154
141
|
def __init__(self, exclude_global: bool = False, **kwargs: Any) -> None:
|
|
155
142
|
self.raw_filter = f"n.status <> '{BranchStatus.DELETING.value}'"
|
|
143
|
+
|
|
156
144
|
if exclude_global:
|
|
157
145
|
self.raw_filter += f" AND n.name <> '{GLOBAL_BRANCH_NAME}'"
|
|
158
146
|
|
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
|
|
@@ -77,7 +77,7 @@ class DiffCountChanges(Query):
|
|
|
77
77
|
AND diff_rel.branch in $branch_names
|
|
78
78
|
AND (
|
|
79
79
|
(diff_rel.from >= $from_time AND diff_rel.from < $to_time)
|
|
80
|
-
OR (diff_rel.to >= $
|
|
80
|
+
OR (diff_rel.to >= $from_time AND diff_rel.to < $to_time)
|
|
81
81
|
)
|
|
82
82
|
AND (p.branch_support = "aware" OR q.branch_support = "aware")
|
|
83
83
|
WITH diff_rel.branch AS branch_name, count(*) AS num_changes
|
|
@@ -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
|
|
@@ -335,7 +335,7 @@ CALL (p, q, diff_rel, row_from_time) {
|
|
|
335
335
|
AND type(r_node) IN ["HAS_ATTRIBUTE", "IS_RELATED"]
|
|
336
336
|
AND any(l in labels(node) WHERE l in ["Attribute", "Relationship"])
|
|
337
337
|
AND node.branch_support IN [$branch_aware, $branch_agnostic]
|
|
338
|
-
AND type(r_prop) IN ["
|
|
338
|
+
AND type(r_prop) IN ["IS_PROTECTED", "HAS_SOURCE", "HAS_OWNER", "HAS_VALUE", "IS_RELATED"]
|
|
339
339
|
AND any(l in labels(prop) WHERE l in ["Boolean", "Node", "AttributeValue"])
|
|
340
340
|
AND (top_diff_rel.to IS NULL OR top_diff_rel.to >= r_node.from)
|
|
341
341
|
AND (r_node.to IS NULL OR r_node.to >= r_prop.from)
|
|
@@ -532,7 +532,7 @@ CALL (root, r_root, p, diff_rel, q) {
|
|
|
532
532
|
)
|
|
533
533
|
WHERE %(id_func)s(mid_r_root) = %(id_func)s(r_root)
|
|
534
534
|
AND %(id_func)s(mid_diff_rel) = %(id_func)s(diff_rel)
|
|
535
|
-
AND type(r_prop) IN ["
|
|
535
|
+
AND type(r_prop) IN ["IS_PROTECTED", "HAS_SOURCE", "HAS_OWNER", "HAS_VALUE", "IS_RELATED"]
|
|
536
536
|
AND any(l in labels(prop) WHERE l in ["Boolean", "Node", "AttributeValue"])
|
|
537
537
|
AND r_prop.from < $to_time AND r_prop.branch = mid_diff_rel.branch
|
|
538
538
|
AND (mid_diff_rel.to IS NULL OR mid_diff_rel.to >= r_prop.from)
|
|
@@ -609,7 +609,7 @@ class DiffPropertyPathsQuery(DiffCalculationQuery):
|
|
|
609
609
|
MATCH diff_rel_path = (root:Root)<-[r_root:IS_PART_OF]-(n:Node)-[r_node]-(p)-[diff_rel {branch: $branch_name}]->(q)
|
|
610
610
|
WHERE p.branch_support = $branch_aware
|
|
611
611
|
AND any(l in labels(p) WHERE l in ["Attribute", "Relationship"])
|
|
612
|
-
AND type(diff_rel) IN ["
|
|
612
|
+
AND type(diff_rel) IN ["IS_PROTECTED", "HAS_SOURCE", "HAS_OWNER", "HAS_VALUE"]
|
|
613
613
|
AND any(l in labels(q) WHERE l in ["Boolean", "Node", "AttributeValue"])
|
|
614
614
|
AND type(r_node) IN ["HAS_ATTRIBUTE", "IS_RELATED"]
|
|
615
615
|
// node ID and field name filtering first pass
|
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])
|