infrahub-server 1.3.5__py3-none-any.whl → 1.4.0b0__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/internal.py +5 -0
- infrahub/artifacts/tasks.py +17 -22
- infrahub/branch/merge_mutation_checker.py +38 -0
- infrahub/cli/__init__.py +2 -2
- infrahub/cli/context.py +7 -3
- infrahub/cli/db.py +5 -16
- infrahub/cli/upgrade.py +7 -29
- infrahub/computed_attribute/tasks.py +36 -46
- infrahub/config.py +53 -2
- infrahub/constants/environment.py +1 -0
- infrahub/core/attribute.py +9 -7
- infrahub/core/branch/tasks.py +43 -41
- infrahub/core/constants/__init__.py +20 -6
- infrahub/core/constants/infrahubkind.py +2 -0
- infrahub/core/diff/coordinator.py +3 -1
- infrahub/core/diff/repository/repository.py +0 -8
- infrahub/core/diff/tasks.py +11 -8
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/index.py +1 -2
- infrahub/core/graph/schema.py +50 -29
- infrahub/core/initialization.py +62 -33
- infrahub/core/ipam/tasks.py +4 -3
- infrahub/core/merge.py +8 -10
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m035_drop_attr_value_index.py +45 -0
- infrahub/core/migrations/query/attribute_add.py +27 -2
- infrahub/core/migrations/schema/tasks.py +6 -5
- infrahub/core/node/proposed_change.py +43 -0
- infrahub/core/protocols.py +12 -0
- infrahub/core/query/attribute.py +32 -14
- infrahub/core/query/diff.py +11 -0
- infrahub/core/query/ipam.py +13 -7
- infrahub/core/query/node.py +51 -10
- infrahub/core/query/resource_manager.py +3 -3
- infrahub/core/schema/basenode_schema.py +8 -0
- infrahub/core/schema/definitions/core/__init__.py +10 -1
- infrahub/core/schema/definitions/core/ipam.py +28 -2
- infrahub/core/schema/definitions/core/propose_change.py +15 -0
- infrahub/core/schema/definitions/core/webhook.py +3 -0
- infrahub/core/schema/generic_schema.py +10 -0
- infrahub/core/schema/manager.py +10 -1
- infrahub/core/schema/node_schema.py +22 -17
- infrahub/core/schema/profile_schema.py +8 -0
- infrahub/core/schema/schema_branch.py +9 -5
- infrahub/core/schema/template_schema.py +8 -0
- infrahub/core/validators/checks_runner.py +5 -5
- infrahub/core/validators/tasks.py +6 -7
- infrahub/core/validators/uniqueness/checker.py +4 -2
- infrahub/core/validators/uniqueness/model.py +1 -0
- infrahub/core/validators/uniqueness/query.py +57 -7
- infrahub/database/__init__.py +2 -1
- infrahub/events/__init__.py +18 -0
- infrahub/events/constants.py +7 -0
- infrahub/events/generator.py +29 -2
- infrahub/events/proposed_change_action.py +181 -0
- infrahub/generators/tasks.py +24 -20
- infrahub/git/base.py +4 -7
- infrahub/git/integrator.py +21 -12
- infrahub/git/repository.py +15 -30
- infrahub/git/tasks.py +121 -106
- infrahub/graphql/field_extractor.py +69 -0
- infrahub/graphql/manager.py +15 -11
- infrahub/graphql/mutations/account.py +2 -2
- infrahub/graphql/mutations/action.py +8 -2
- infrahub/graphql/mutations/artifact_definition.py +4 -1
- infrahub/graphql/mutations/branch.py +10 -5
- infrahub/graphql/mutations/graphql_query.py +2 -1
- infrahub/graphql/mutations/main.py +14 -8
- infrahub/graphql/mutations/menu.py +2 -1
- infrahub/graphql/mutations/proposed_change.py +225 -8
- infrahub/graphql/mutations/relationship.py +5 -0
- infrahub/graphql/mutations/repository.py +2 -1
- infrahub/graphql/mutations/tasks.py +7 -9
- infrahub/graphql/mutations/webhook.py +4 -1
- infrahub/graphql/parser.py +15 -6
- infrahub/graphql/queries/__init__.py +10 -1
- infrahub/graphql/queries/account.py +3 -3
- infrahub/graphql/queries/branch.py +2 -2
- infrahub/graphql/queries/diff/tree.py +3 -3
- infrahub/graphql/queries/event.py +13 -3
- infrahub/graphql/queries/ipam.py +23 -1
- infrahub/graphql/queries/proposed_change.py +84 -0
- infrahub/graphql/queries/relationship.py +2 -2
- infrahub/graphql/queries/resource_manager.py +3 -3
- infrahub/graphql/queries/search.py +3 -2
- infrahub/graphql/queries/status.py +3 -2
- infrahub/graphql/queries/task.py +2 -2
- infrahub/graphql/resolvers/ipam.py +440 -0
- infrahub/graphql/resolvers/many_relationship.py +4 -3
- infrahub/graphql/resolvers/resolver.py +5 -5
- infrahub/graphql/resolvers/single_relationship.py +3 -2
- infrahub/graphql/schema.py +25 -5
- infrahub/graphql/types/__init__.py +2 -2
- infrahub/graphql/types/attribute.py +3 -3
- infrahub/graphql/types/event.py +60 -0
- infrahub/groups/tasks.py +6 -6
- infrahub/lock.py +3 -2
- infrahub/menu/generator.py +8 -0
- infrahub/message_bus/operations/__init__.py +9 -12
- infrahub/message_bus/operations/git/file.py +6 -5
- infrahub/message_bus/operations/git/repository.py +12 -20
- infrahub/message_bus/operations/refresh/registry.py +15 -9
- infrahub/message_bus/operations/send/echo.py +7 -4
- infrahub/message_bus/types.py +1 -0
- infrahub/permissions/globals.py +1 -4
- infrahub/permissions/manager.py +8 -5
- infrahub/pools/prefix.py +7 -5
- infrahub/prefect_server/app.py +31 -0
- infrahub/prefect_server/bootstrap.py +18 -0
- infrahub/proposed_change/action_checker.py +206 -0
- infrahub/proposed_change/approval_revoker.py +40 -0
- infrahub/proposed_change/branch_diff.py +3 -1
- infrahub/proposed_change/checker.py +45 -0
- infrahub/proposed_change/constants.py +32 -2
- infrahub/proposed_change/tasks.py +182 -150
- infrahub/py.typed +0 -0
- infrahub/server.py +29 -17
- infrahub/services/__init__.py +13 -28
- infrahub/services/adapters/cache/__init__.py +4 -0
- infrahub/services/adapters/cache/nats.py +2 -0
- infrahub/services/adapters/cache/redis.py +3 -0
- infrahub/services/adapters/message_bus/__init__.py +0 -2
- infrahub/services/adapters/message_bus/local.py +1 -2
- infrahub/services/adapters/message_bus/nats.py +6 -8
- infrahub/services/adapters/message_bus/rabbitmq.py +7 -9
- infrahub/services/adapters/workflow/__init__.py +1 -0
- infrahub/services/adapters/workflow/local.py +1 -8
- infrahub/services/component.py +2 -1
- infrahub/task_manager/event.py +52 -0
- infrahub/task_manager/models.py +9 -0
- infrahub/tasks/artifact.py +6 -7
- infrahub/tasks/check.py +4 -7
- infrahub/telemetry/tasks.py +15 -18
- infrahub/transformations/tasks.py +10 -6
- infrahub/trigger/tasks.py +4 -3
- infrahub/types.py +4 -0
- infrahub/validators/events.py +7 -7
- infrahub/validators/tasks.py +6 -7
- infrahub/webhook/models.py +45 -45
- infrahub/webhook/tasks.py +25 -24
- infrahub/workers/dependencies.py +143 -0
- infrahub/workers/infrahub_async.py +19 -43
- infrahub/workflows/catalogue.py +16 -2
- infrahub/workflows/initialization.py +5 -4
- infrahub/workflows/models.py +2 -0
- infrahub_sdk/client.py +6 -6
- infrahub_sdk/ctl/repository.py +51 -0
- infrahub_sdk/ctl/schema.py +9 -9
- infrahub_sdk/protocols.py +40 -6
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/METADATA +5 -4
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/RECORD +158 -144
- infrahub_testcontainers/container.py +17 -0
- infrahub_testcontainers/docker-compose-cluster.test.yml +56 -1
- infrahub_testcontainers/docker-compose.test.yml +56 -1
- infrahub_testcontainers/helpers.py +4 -1
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.3.5.dist-info → infrahub_server-1.4.0b0.dist-info}/entry_points.txt +0 -0
infrahub/core/query/attribute.py
CHANGED
|
@@ -5,9 +5,16 @@ from typing import TYPE_CHECKING, Any
|
|
|
5
5
|
from infrahub.core.constants import AttributeDBNodeType
|
|
6
6
|
from infrahub.core.constants.relationship_label import RELATIONSHIP_TO_NODE_LABEL, RELATIONSHIP_TO_VALUE_LABEL
|
|
7
7
|
from infrahub.core.constants.schema import FlagProperty, NodeProperty
|
|
8
|
+
from infrahub.core.graph.schema import (
|
|
9
|
+
GraphAttributeIPHostNode,
|
|
10
|
+
GraphAttributeIPNetworkNode,
|
|
11
|
+
GraphAttributeValueIndexedNode,
|
|
12
|
+
GraphAttributeValueNode,
|
|
13
|
+
)
|
|
8
14
|
from infrahub.core.query import Query, QueryNode, QueryRel, QueryType
|
|
9
15
|
from infrahub.core.timestamp import Timestamp
|
|
10
16
|
from infrahub.core.utils import build_regex_attrs
|
|
17
|
+
from infrahub.types import is_large_attribute_type
|
|
11
18
|
|
|
12
19
|
if TYPE_CHECKING:
|
|
13
20
|
from infrahub.core.attribute import BaseAttribute
|
|
@@ -56,12 +63,14 @@ class AttributeUpdateValueQuery(AttributeQuery):
|
|
|
56
63
|
|
|
57
64
|
prop_list = [f"{key}: ${key}" for key in content.keys()]
|
|
58
65
|
|
|
59
|
-
labels = [
|
|
66
|
+
labels = [GraphAttributeValueNode.get_default_label()]
|
|
60
67
|
node_type = self.attr.get_db_node_type()
|
|
61
|
-
if
|
|
62
|
-
labels.append(
|
|
63
|
-
|
|
64
|
-
labels.append(
|
|
68
|
+
if AttributeDBNodeType.INDEXED in node_type:
|
|
69
|
+
labels.append(GraphAttributeValueIndexedNode.get_default_label())
|
|
70
|
+
if AttributeDBNodeType.IPHOST in node_type:
|
|
71
|
+
labels.append(GraphAttributeIPHostNode.get_default_label())
|
|
72
|
+
if AttributeDBNodeType.IPNETWORK in node_type:
|
|
73
|
+
labels.append(GraphAttributeIPNetworkNode.get_default_label())
|
|
65
74
|
|
|
66
75
|
query = """
|
|
67
76
|
MATCH (a:Attribute { uuid: $attr_uuid })
|
|
@@ -198,6 +207,9 @@ async def default_attribute_query_filter(
|
|
|
198
207
|
support_profiles: bool = False,
|
|
199
208
|
) -> tuple[list[QueryElement], dict[str, Any], list[str]]:
|
|
200
209
|
"""Generate Query String Snippet to filter the right node."""
|
|
210
|
+
attribute_value_label = GraphAttributeValueNode.get_default_label()
|
|
211
|
+
if attribute_kind and not is_large_attribute_type(attribute_kind):
|
|
212
|
+
attribute_value_label = GraphAttributeValueIndexedNode.get_default_label()
|
|
201
213
|
|
|
202
214
|
query_filter: list[QueryElement] = []
|
|
203
215
|
query_params: dict[str, Any] = {}
|
|
@@ -226,33 +238,35 @@ async def default_attribute_query_filter(
|
|
|
226
238
|
query_filter.append(QueryRel(labels=[RELATIONSHIP_TO_VALUE_LABEL]))
|
|
227
239
|
|
|
228
240
|
if filter_value is None:
|
|
229
|
-
query_filter.append(QueryNode(name="av", labels=[
|
|
241
|
+
query_filter.append(QueryNode(name="av", labels=[attribute_value_label]))
|
|
230
242
|
else:
|
|
231
243
|
if partial_match:
|
|
232
|
-
query_filter.append(QueryNode(name="av", labels=[
|
|
244
|
+
query_filter.append(QueryNode(name="av", labels=[attribute_value_label]))
|
|
233
245
|
query_where.append(
|
|
234
246
|
f"toLower(toString(av.{filter_name})) CONTAINS toLower(toString(${param_prefix}_{filter_name}))"
|
|
235
247
|
)
|
|
236
248
|
elif attribute_kind and attribute_kind == "List" and not isinstance(filter_value, list):
|
|
237
|
-
query_filter.append(QueryNode(name="av", labels=[
|
|
249
|
+
query_filter.append(QueryNode(name="av", labels=[attribute_value_label]))
|
|
238
250
|
filter_value = build_regex_attrs(values=[filter_value])
|
|
239
251
|
query_where.append(f"toString(av.{filter_name}) =~ ${param_prefix}_{filter_name}")
|
|
240
252
|
elif filter_name == "isnull":
|
|
241
|
-
query_filter.append(QueryNode(name="av", labels=[
|
|
253
|
+
query_filter.append(QueryNode(name="av", labels=[attribute_value_label]))
|
|
242
254
|
elif support_profiles:
|
|
243
|
-
query_filter.append(QueryNode(name="av", labels=[
|
|
255
|
+
query_filter.append(QueryNode(name="av", labels=[attribute_value_label]))
|
|
244
256
|
query_where.append(f"(av.{filter_name} = ${param_prefix}_{filter_name} OR av.is_default)")
|
|
245
257
|
else:
|
|
246
258
|
query_filter.append(
|
|
247
259
|
QueryNode(
|
|
248
|
-
name="av",
|
|
260
|
+
name="av",
|
|
261
|
+
labels=[attribute_value_label],
|
|
262
|
+
params={filter_name: f"${param_prefix}_{filter_name}"},
|
|
249
263
|
)
|
|
250
264
|
)
|
|
251
265
|
query_params[f"{param_prefix}_{filter_name}"] = filter_value
|
|
252
266
|
|
|
253
267
|
elif filter_name == "values" and isinstance(filter_value, list):
|
|
254
268
|
query_filter.extend(
|
|
255
|
-
(QueryRel(labels=[RELATIONSHIP_TO_VALUE_LABEL]), QueryNode(name="av", labels=[
|
|
269
|
+
(QueryRel(labels=[RELATIONSHIP_TO_VALUE_LABEL]), QueryNode(name="av", labels=[attribute_value_label]))
|
|
256
270
|
)
|
|
257
271
|
if attribute_kind and attribute_kind == "List":
|
|
258
272
|
query_params[f"{param_prefix}_{filter_name}"] = build_regex_attrs(values=filter_value)
|
|
@@ -267,10 +281,14 @@ async def default_attribute_query_filter(
|
|
|
267
281
|
query_filter.append(QueryRel(labels=[RELATIONSHIP_TO_VALUE_LABEL]))
|
|
268
282
|
|
|
269
283
|
if filter_value is None:
|
|
270
|
-
query_filter.append(QueryNode(name="av", labels=[
|
|
284
|
+
query_filter.append(QueryNode(name="av", labels=[GraphAttributeValueNode.get_default_label()]))
|
|
271
285
|
else:
|
|
272
286
|
query_filter.append(
|
|
273
|
-
QueryNode(
|
|
287
|
+
QueryNode(
|
|
288
|
+
name="av",
|
|
289
|
+
labels=[GraphAttributeValueNode.get_default_label()],
|
|
290
|
+
params={filter_name: f"${param_prefix}_{filter_name}"},
|
|
291
|
+
)
|
|
274
292
|
)
|
|
275
293
|
query_params[f"{param_prefix}_{filter_name}"] = filter_value
|
|
276
294
|
|
infrahub/core/query/diff.py
CHANGED
|
@@ -100,6 +100,17 @@ class DiffCountChanges(Query):
|
|
|
100
100
|
return branch_count_map
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
async def get_num_changes_in_time_range_by_branch(
|
|
104
|
+
branch_names: list[str],
|
|
105
|
+
from_time: Timestamp,
|
|
106
|
+
to_time: Timestamp,
|
|
107
|
+
db: InfrahubDatabase,
|
|
108
|
+
) -> dict[str, int]:
|
|
109
|
+
query = await DiffCountChanges.init(db=db, branch_names=branch_names, diff_from=from_time, diff_to=to_time)
|
|
110
|
+
await query.execute(db=db)
|
|
111
|
+
return query.get_num_changes_by_branch()
|
|
112
|
+
|
|
113
|
+
|
|
103
114
|
class DiffCalculationQuery(DiffQuery):
|
|
104
115
|
type = QueryType.READ
|
|
105
116
|
insert_limit = False
|
infrahub/core/query/ipam.py
CHANGED
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import TYPE_CHECKING, Iterable
|
|
6
6
|
|
|
7
7
|
from infrahub.core.constants import InfrahubKind
|
|
8
|
+
from infrahub.core.graph.schema import GraphAttributeIPHostNode, GraphAttributeIPNetworkNode
|
|
8
9
|
from infrahub.core.ipam.constants import AllIPTypes, IPAddressType, IPNetworkType
|
|
9
10
|
from infrahub.core.query import QueryType
|
|
10
11
|
from infrahub.core.registry import registry
|
|
@@ -21,8 +22,8 @@ if TYPE_CHECKING:
|
|
|
21
22
|
from infrahub.database import InfrahubDatabase
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
PREFIX_ATTRIBUTE_LABEL =
|
|
25
|
-
ADDRESS_ATTRIBUTE_LABEL =
|
|
25
|
+
PREFIX_ATTRIBUTE_LABEL = GraphAttributeIPNetworkNode.get_default_label()
|
|
26
|
+
ADDRESS_ATTRIBUTE_LABEL = GraphAttributeIPHostNode.get_default_label()
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
@dataclass
|
|
@@ -280,14 +281,13 @@ class IPPrefixUtilization(Query):
|
|
|
280
281
|
MATCH path = (
|
|
281
282
|
(pfx)-[r_1:IS_RELATED]-(rl:Relationship)-[r_2:IS_RELATED]-(child:Node)
|
|
282
283
|
-[r_attr:HAS_ATTRIBUTE]->(attr:Attribute)
|
|
283
|
-
-[r_attr_val:HAS_VALUE]->(av:
|
|
284
|
+
-[r_attr_val:HAS_VALUE]->(av:{PREFIX_ATTRIBUTE_LABEL}|{ADDRESS_ATTRIBUTE_LABEL})
|
|
284
285
|
)
|
|
285
286
|
WHERE %(id_func)s(r_1) = %(id_func)s(r_rel1)
|
|
286
287
|
AND %(id_func)s(r_2) = %(id_func)s(r_rel2)
|
|
287
288
|
AND ({rel_filter("r_attr")})
|
|
288
289
|
AND ({rel_filter("r_attr_val")})
|
|
289
290
|
AND attr.name IN ["prefix", "address"]
|
|
290
|
-
AND any(l in labels(av) WHERE l in ["{PREFIX_ATTRIBUTE_LABEL}", "{ADDRESS_ATTRIBUTE_LABEL}"])
|
|
291
291
|
WITH
|
|
292
292
|
path,
|
|
293
293
|
pfx,
|
|
@@ -478,6 +478,7 @@ class IPPrefixReconcileQuery(Query):
|
|
|
478
478
|
-[pr2:IS_RELATED {status: "active"}]-(maybe_new_parent:%(ip_prefix_kind)s)
|
|
479
479
|
-[har:HAS_ATTRIBUTE]->(:Attribute {name: "prefix"})
|
|
480
480
|
-[hvr:HAS_VALUE]->(av:%(ip_prefix_attribute_kind)s)
|
|
481
|
+
USING INDEX av:%(ip_prefix_attribute_kind)s(binary_address)
|
|
481
482
|
WHERE all(r IN relationships(parent_path) WHERE (%(branch_filter)s))
|
|
482
483
|
AND av.version = $ip_version
|
|
483
484
|
AND av.binary_address IN $possible_prefix_list
|
|
@@ -514,15 +515,16 @@ class IPPrefixReconcileQuery(Query):
|
|
|
514
515
|
OPTIONAL MATCH child_path = (
|
|
515
516
|
(ip_namespace)-[r1:IS_RELATED]
|
|
516
517
|
-(ns_rel:Relationship)-[r2:IS_RELATED]
|
|
517
|
-
-(maybe_new_child
|
|
518
|
+
-(maybe_new_child:%(ip_prefix_kind)s|%(ip_address_kind)s)-[har:HAS_ATTRIBUTE]
|
|
518
519
|
->(a:Attribute)-[hvr:HAS_VALUE]
|
|
519
|
-
->(av
|
|
520
|
+
->(av:%(ip_prefix_attribute_kind)s|%(ip_address_attribute_kind)s)
|
|
520
521
|
)
|
|
522
|
+
USING INDEX av:%(ip_prefix_attribute_kind)s(binary_address)
|
|
523
|
+
USING INDEX av:%(ip_address_attribute_kind)s(binary_address)
|
|
521
524
|
WHERE $is_prefix // only prefix nodes can have children
|
|
522
525
|
AND ns_rel.name IN ["ip_namespace__ip_prefix", "ip_namespace__ip_address"]
|
|
523
526
|
AND any(child_kind IN [$ip_prefix_kind, $ip_address_kind] WHERE child_kind IN labels(maybe_new_child))
|
|
524
527
|
AND a.name in ["prefix", "address"]
|
|
525
|
-
AND any(attr_kind IN [$ip_prefix_attribute_kind, $ip_address_attribute_kind] WHERE attr_kind IN labels(av))
|
|
526
528
|
AND (ip_node IS NULL OR maybe_new_child.uuid <> ip_node.uuid)
|
|
527
529
|
AND (
|
|
528
530
|
($ip_prefix_kind IN labels(maybe_new_child) AND av.prefixlen > $prefixlen)
|
|
@@ -580,6 +582,10 @@ class IPPrefixReconcileQuery(Query):
|
|
|
580
582
|
collect(new_child) as new_children
|
|
581
583
|
""" % {
|
|
582
584
|
"branch_filter": branch_filter,
|
|
585
|
+
"ip_prefix_kind": InfrahubKind.IPPREFIX,
|
|
586
|
+
"ip_address_kind": InfrahubKind.IPADDRESS,
|
|
587
|
+
"ip_prefix_attribute_kind": PREFIX_ATTRIBUTE_LABEL,
|
|
588
|
+
"ip_address_attribute_kind": ADDRESS_ATTRIBUTE_LABEL,
|
|
583
589
|
}
|
|
584
590
|
self.add_to_query(get_new_children_query)
|
|
585
591
|
self.return_labels = ["ip_node", "current_parent", "current_children", "new_parent", "new_children"]
|
infrahub/core/query/node.py
CHANGED
|
@@ -140,15 +140,19 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
140
140
|
attributes: list[AttributeCreateData] = []
|
|
141
141
|
attributes_iphost: list[AttributeCreateData] = []
|
|
142
142
|
attributes_ipnetwork: list[AttributeCreateData] = []
|
|
143
|
+
attributes_indexed: list[AttributeCreateData] = []
|
|
143
144
|
|
|
144
145
|
for attr_name in self.node._attributes:
|
|
145
146
|
attr: BaseAttribute = getattr(self.node, attr_name)
|
|
146
147
|
attr_data = attr.get_create_data()
|
|
148
|
+
node_type = attr.get_db_node_type()
|
|
147
149
|
|
|
148
|
-
if
|
|
150
|
+
if AttributeDBNodeType.IPHOST in node_type:
|
|
149
151
|
attributes_iphost.append(attr_data)
|
|
150
|
-
elif
|
|
152
|
+
elif AttributeDBNodeType.IPNETWORK in node_type:
|
|
151
153
|
attributes_ipnetwork.append(attr_data)
|
|
154
|
+
elif AttributeDBNodeType.INDEXED in node_type:
|
|
155
|
+
attributes_indexed.append(attr_data)
|
|
152
156
|
else:
|
|
153
157
|
attributes.append(attr_data)
|
|
154
158
|
|
|
@@ -167,6 +171,7 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
167
171
|
relationships.append(rel_create_data)
|
|
168
172
|
|
|
169
173
|
self.params["attrs"] = [attr.model_dump() for attr in attributes]
|
|
174
|
+
self.params["attrs_indexed"] = [attr.model_dump() for attr in attributes_indexed]
|
|
170
175
|
self.params["attrs_iphost"] = [attr.model_dump() for attr in attributes_iphost]
|
|
171
176
|
self.params["attrs_ipnetwork"] = [attr.model_dump() for attr in attributes_ipnetwork]
|
|
172
177
|
self.params["rels_bidir"] = [
|
|
@@ -209,24 +214,59 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
209
214
|
"binary_address": "attr.content.binary_address",
|
|
210
215
|
"version": "attr.content.version",
|
|
211
216
|
"prefixlen": "attr.content.prefixlen",
|
|
212
|
-
# "num_addresses": "attr.content.num_addresses",
|
|
213
217
|
}
|
|
214
218
|
ipnetwork_prop_list = [f"{key}: {value}" for key, value in ipnetwork_prop.items()]
|
|
215
219
|
|
|
216
|
-
|
|
220
|
+
attrs_nonindexed_query = """
|
|
217
221
|
WITH distinct n
|
|
218
222
|
UNWIND $attrs AS attr
|
|
223
|
+
// Try to find a matching vertex
|
|
224
|
+
OPTIONAL MATCH (existing_av:AttributeValue {value: attr.content.value, is_default: attr.content.is_default})
|
|
225
|
+
WHERE NOT existing_av:AttributeValueIndexed
|
|
226
|
+
CALL (attr, existing_av) {
|
|
227
|
+
// If none found, create a new one
|
|
228
|
+
WITH existing_av
|
|
229
|
+
WHERE existing_av IS NULL
|
|
230
|
+
CREATE (:AttributeValue {value: attr.content.value, is_default: attr.content.is_default})
|
|
231
|
+
}
|
|
232
|
+
CALL (attr) {
|
|
233
|
+
MATCH (av:AttributeValue {value: attr.content.value, is_default: attr.content.is_default})
|
|
234
|
+
WHERE NOT av:AttributeValueIndexed
|
|
235
|
+
RETURN av
|
|
236
|
+
LIMIT 1
|
|
237
|
+
}
|
|
238
|
+
CALL (n, attr, av) {
|
|
239
|
+
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
240
|
+
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
241
|
+
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
242
|
+
MERGE (ip:Boolean { value: attr.is_protected })
|
|
243
|
+
MERGE (iv:Boolean { value: attr.is_visible })
|
|
244
|
+
WITH a, ip, iv
|
|
245
|
+
LIMIT 1
|
|
246
|
+
CREATE (a)-[:IS_PROTECTED { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(ip)
|
|
247
|
+
CREATE (a)-[:IS_VISIBLE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(iv)
|
|
248
|
+
FOREACH ( prop IN attr.source_prop |
|
|
249
|
+
MERGE (peer:Node { uuid: prop.peer_id })
|
|
250
|
+
CREATE (a)-[:HAS_SOURCE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
|
|
251
|
+
)
|
|
252
|
+
FOREACH ( prop IN attr.owner_prop |
|
|
253
|
+
MERGE (peer:Node { uuid: prop.peer_id })
|
|
254
|
+
CREATE (a)-[:HAS_OWNER { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(peer)
|
|
255
|
+
)
|
|
256
|
+
}"""
|
|
257
|
+
|
|
258
|
+
attrs_indexed_query = """
|
|
259
|
+
WITH distinct n
|
|
260
|
+
UNWIND $attrs_indexed AS attr
|
|
219
261
|
CALL (n, attr) {
|
|
220
262
|
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
221
263
|
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
222
|
-
MERGE (av:AttributeValue { value: attr.content.value, is_default: attr.content.is_default })
|
|
264
|
+
MERGE (av:AttributeValue:AttributeValueIndexed { value: attr.content.value, is_default: attr.content.is_default })
|
|
223
265
|
WITH av, a
|
|
224
266
|
LIMIT 1
|
|
225
267
|
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
226
268
|
MERGE (ip:Boolean { value: attr.is_protected })
|
|
227
269
|
MERGE (iv:Boolean { value: attr.is_visible })
|
|
228
|
-
WITH a, ip, iv
|
|
229
|
-
LIMIT 1
|
|
230
270
|
CREATE (a)-[:IS_PROTECTED { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(ip)
|
|
231
271
|
CREATE (a)-[:IS_VISIBLE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(iv)
|
|
232
272
|
FOREACH ( prop IN attr.source_prop |
|
|
@@ -245,7 +285,7 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
245
285
|
CALL (n, attr) {
|
|
246
286
|
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
247
287
|
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
248
|
-
MERGE (av:AttributeValue:AttributeIPHost { %(iphost_prop)s })
|
|
288
|
+
MERGE (av:AttributeValue:AttributeValueIndexed:AttributeIPHost { %(iphost_prop)s })
|
|
249
289
|
WITH attr, av, a
|
|
250
290
|
LIMIT 1
|
|
251
291
|
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
@@ -272,7 +312,7 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
272
312
|
CALL (n, attr) {
|
|
273
313
|
CREATE (a:Attribute { uuid: attr.uuid, name: attr.name, branch_support: attr.branch_support })
|
|
274
314
|
CREATE (n)-[:HAS_ATTRIBUTE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(a)
|
|
275
|
-
MERGE (av:AttributeValue:AttributeIPNetwork { %(ipnetwork_prop)s })
|
|
315
|
+
MERGE (av:AttributeValue:AttributeValueIndexed:AttributeIPNetwork { %(ipnetwork_prop)s })
|
|
276
316
|
WITH attr, av, a
|
|
277
317
|
LIMIT 1
|
|
278
318
|
CREATE (a)-[:HAS_VALUE { branch: attr.branch, branch_level: attr.branch_level, status: attr.status, from: $at }]->(av)
|
|
@@ -409,7 +449,8 @@ class NodeCreateAllQuery(NodeQuery):
|
|
|
409
449
|
MATCH (root:Root)
|
|
410
450
|
CREATE (n:Node:%(labels)s $node_prop )
|
|
411
451
|
CREATE (n)-[r:IS_PART_OF $node_branch_prop ]->(root)
|
|
412
|
-
{
|
|
452
|
+
{attrs_nonindexed_query if self.params["attrs"] else ""}
|
|
453
|
+
{attrs_indexed_query if self.params["attrs_indexed"] else ""}
|
|
413
454
|
{attrs_iphost_query if self.params["attrs_iphost"] else ""}
|
|
414
455
|
{attrs_ipnetwork_query if self.params["attrs_ipnetwork"] else ""}
|
|
415
456
|
{rels_bidir_query if self.params["rels_bidir"] else ""}
|
|
@@ -140,7 +140,7 @@ class NumberPoolGetAllocated(Query):
|
|
|
140
140
|
self.params.update(branch_params)
|
|
141
141
|
|
|
142
142
|
query = """
|
|
143
|
-
MATCH (n:%(node)s)-[ha:HAS_ATTRIBUTE]-(a:Attribute {name: $node_attribute})-[hv:HAS_VALUE]-(av:
|
|
143
|
+
MATCH (n:%(node)s)-[ha:HAS_ATTRIBUTE]-(a:Attribute {name: $node_attribute})-[hv:HAS_VALUE]-(av:AttributeValueIndexed)
|
|
144
144
|
MATCH (a)-[hs:HAS_SOURCE]-(pool:%(number_pool_kind)s)
|
|
145
145
|
WHERE
|
|
146
146
|
pool.uuid = $pool_id
|
|
@@ -306,7 +306,7 @@ class NumberPoolGetUsed(Query):
|
|
|
306
306
|
self.params["attribute_name"] = self.pool.node_attribute.value
|
|
307
307
|
|
|
308
308
|
query = """
|
|
309
|
-
MATCH (pool:%(number_pool)s { uuid: $pool_id })-[res:IS_RESERVED]->(av:
|
|
309
|
+
MATCH (pool:%(number_pool)s { uuid: $pool_id })-[res:IS_RESERVED]->(av:AttributeValueIndexed)
|
|
310
310
|
WHERE toInteger(av.value) >= $start_range and toInteger(av.value) <= $end_range
|
|
311
311
|
CALL (pool, res, av) {
|
|
312
312
|
MATCH (pool)-[res]->(av)<-[hv:HAS_VALUE]-(attr:Attribute)<-[ha:HAS_ATTRIBUTE]-(n:%(node)s)
|
|
@@ -371,7 +371,7 @@ class NumberPoolSetReserved(Query):
|
|
|
371
371
|
|
|
372
372
|
query = """
|
|
373
373
|
MATCH (pool:%(number_pool)s { uuid: $pool_id })
|
|
374
|
-
MERGE (value:AttributeValue { value: $reserved, is_default: false })
|
|
374
|
+
MERGE (value:AttributeValue:AttributeValueIndexed { value: $reserved, is_default: false })
|
|
375
375
|
WITH value, pool
|
|
376
376
|
LIMIT 1
|
|
377
377
|
CREATE (pool)-[rel:IS_RESERVED $rel_prop]->(value)
|
|
@@ -55,6 +55,14 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
55
55
|
def is_profile_schema(self) -> bool:
|
|
56
56
|
return False
|
|
57
57
|
|
|
58
|
+
@property
|
|
59
|
+
def is_ip_prefix(self) -> bool:
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def is_ip_address(self) -> bool:
|
|
64
|
+
return False
|
|
65
|
+
|
|
58
66
|
@property
|
|
59
67
|
def kind(self) -> str:
|
|
60
68
|
if self.namespace == "Attribute":
|
|
@@ -35,7 +35,14 @@ from .group import (
|
|
|
35
35
|
core_repository_group,
|
|
36
36
|
core_standard_group,
|
|
37
37
|
)
|
|
38
|
-
from .ipam import
|
|
38
|
+
from .ipam import (
|
|
39
|
+
builtin_ip_address,
|
|
40
|
+
builtin_ip_prefix,
|
|
41
|
+
builtin_ipam,
|
|
42
|
+
core_ipam_namespace,
|
|
43
|
+
internal_ipam_ip_prefix_available,
|
|
44
|
+
internal_ipam_ip_range_available,
|
|
45
|
+
)
|
|
39
46
|
from .lineage import lineage_owner, lineage_source
|
|
40
47
|
from .menu import generic_menu_item, menu_item
|
|
41
48
|
from .permission import (
|
|
@@ -172,6 +179,8 @@ core_models_mixed: dict[str, list] = {
|
|
|
172
179
|
core_object_permission,
|
|
173
180
|
core_account_role,
|
|
174
181
|
core_account_group,
|
|
182
|
+
internal_ipam_ip_prefix_available,
|
|
183
|
+
internal_ipam_ip_range_available,
|
|
175
184
|
],
|
|
176
185
|
}
|
|
177
186
|
|
|
@@ -59,7 +59,7 @@ builtin_ip_prefix = GenericSchema(
|
|
|
59
59
|
name="IPPrefix",
|
|
60
60
|
label="IP Prefix",
|
|
61
61
|
namespace="Builtin",
|
|
62
|
-
description="
|
|
62
|
+
description="IPv4 or IPv6 prefix also referred as network",
|
|
63
63
|
include_in_menu=False,
|
|
64
64
|
default_filter="prefix__value",
|
|
65
65
|
order_by=["prefix__version", "prefix__binary_address", "prefix__prefixlen"],
|
|
@@ -142,7 +142,7 @@ builtin_ip_address = GenericSchema(
|
|
|
142
142
|
name="IPAddress",
|
|
143
143
|
label="IP Address",
|
|
144
144
|
namespace="Builtin",
|
|
145
|
-
description="
|
|
145
|
+
description="IPv4 or IPv6 address",
|
|
146
146
|
include_in_menu=False,
|
|
147
147
|
default_filter="address__value",
|
|
148
148
|
order_by=["address__version", "address__binary_address"],
|
|
@@ -176,6 +176,32 @@ builtin_ip_address = GenericSchema(
|
|
|
176
176
|
],
|
|
177
177
|
)
|
|
178
178
|
|
|
179
|
+
internal_ipam_ip_range_available = NodeSchema(
|
|
180
|
+
name="IPRangeAvailable",
|
|
181
|
+
label="Available IP Range",
|
|
182
|
+
namespace="Internal",
|
|
183
|
+
description="Range of IPv4 or IPv6 addresses which has not been allocated yet",
|
|
184
|
+
include_in_menu=False,
|
|
185
|
+
display_labels=["address__value", "last_address__value"],
|
|
186
|
+
branch=BranchSupportType.AWARE,
|
|
187
|
+
inherit_from=[InfrahubKind.IPADDRESS],
|
|
188
|
+
generate_profile=False,
|
|
189
|
+
attributes=[Attr(name="last_address", kind="IPHost", branch=BranchSupportType.AWARE, order_weight=2000)],
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
internal_ipam_ip_prefix_available = NodeSchema(
|
|
193
|
+
name="IPPrefixAvailable",
|
|
194
|
+
label="Available IP Prefix",
|
|
195
|
+
namespace="Internal",
|
|
196
|
+
description="IPv4 or IPv6 prefix also referred as network which has not been allocated yet",
|
|
197
|
+
include_in_menu=False,
|
|
198
|
+
display_labels=["prefix__value"],
|
|
199
|
+
branch=BranchSupportType.AWARE,
|
|
200
|
+
inherit_from=[InfrahubKind.IPPREFIX],
|
|
201
|
+
generate_profile=False,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
179
205
|
core_ipam_namespace = NodeSchema(
|
|
180
206
|
name="Namespace",
|
|
181
207
|
namespace="Ipam",
|
|
@@ -38,6 +38,9 @@ core_proposed_change = NodeSchema(
|
|
|
38
38
|
default_value=ProposedChangeState.OPEN.value,
|
|
39
39
|
optional=True,
|
|
40
40
|
),
|
|
41
|
+
Attr(name="is_draft", kind="Boolean", optional=False, default_value=False),
|
|
42
|
+
# Ideally we should support some "runtime-attribute" that could not even be stored in the database.
|
|
43
|
+
Attr(name="total_comments", kind="Number", optional=True, read_only=True),
|
|
41
44
|
],
|
|
42
45
|
relationships=[
|
|
43
46
|
Rel(
|
|
@@ -48,6 +51,17 @@ core_proposed_change = NodeSchema(
|
|
|
48
51
|
kind=RelKind.ATTRIBUTE,
|
|
49
52
|
branch=BranchSupportType.AGNOSTIC,
|
|
50
53
|
identifier="coreaccount__proposedchange_approved_by",
|
|
54
|
+
read_only=True,
|
|
55
|
+
),
|
|
56
|
+
Rel(
|
|
57
|
+
name="rejected_by",
|
|
58
|
+
peer=InfrahubKind.GENERICACCOUNT,
|
|
59
|
+
optional=True,
|
|
60
|
+
cardinality=Cardinality.MANY,
|
|
61
|
+
kind=RelKind.ATTRIBUTE,
|
|
62
|
+
branch=BranchSupportType.AGNOSTIC,
|
|
63
|
+
identifier="coreaccount__proposedchange_rejected_by",
|
|
64
|
+
read_only=True,
|
|
51
65
|
),
|
|
52
66
|
Rel(
|
|
53
67
|
name="reviewers",
|
|
@@ -66,6 +80,7 @@ core_proposed_change = NodeSchema(
|
|
|
66
80
|
kind=RelKind.ATTRIBUTE,
|
|
67
81
|
branch=BranchSupportType.AGNOSTIC,
|
|
68
82
|
identifier="coreaccount__proposedchange_created_by",
|
|
83
|
+
read_only=True,
|
|
69
84
|
),
|
|
70
85
|
Rel(
|
|
71
86
|
name="comments",
|
|
@@ -120,6 +120,9 @@ core_custom_webhook = NodeSchema(
|
|
|
120
120
|
branch=BranchSupportType.AGNOSTIC,
|
|
121
121
|
generate_profile=False,
|
|
122
122
|
inherit_from=[InfrahubKind.WEBHOOK, InfrahubKind.TASKTARGET],
|
|
123
|
+
attributes=[
|
|
124
|
+
Attr(name="shared_key", kind="Password", unique=False, optional=True, order_weight=4000),
|
|
125
|
+
],
|
|
123
126
|
relationships=[
|
|
124
127
|
Rel(
|
|
125
128
|
name="transformation",
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
+
from infrahub.core.constants import InfrahubKind
|
|
6
|
+
|
|
5
7
|
from .generated.genericnode_schema import GeneratedGenericSchema
|
|
6
8
|
|
|
7
9
|
if TYPE_CHECKING:
|
|
@@ -28,6 +30,14 @@ class GenericSchema(GeneratedGenericSchema):
|
|
|
28
30
|
def is_template_schema(self) -> bool:
|
|
29
31
|
return False
|
|
30
32
|
|
|
33
|
+
@property
|
|
34
|
+
def is_ip_prefix(self) -> bool:
|
|
35
|
+
return self.kind == InfrahubKind.IPPREFIX
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def is_ip_address(self) -> bool:
|
|
39
|
+
return self.kind == InfrahubKind.IPADDRESS
|
|
40
|
+
|
|
31
41
|
def get_hierarchy_schema(self, db: InfrahubDatabase, branch: Branch | str | None = None) -> GenericSchema: # noqa: ARG002
|
|
32
42
|
if self.hierarchical:
|
|
33
43
|
return self
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -93,6 +93,15 @@ class SchemaManager(NodeManager):
|
|
|
93
93
|
|
|
94
94
|
raise ValueError("The selected node is not of type NodeSchema")
|
|
95
95
|
|
|
96
|
+
def get_generic_schema(
|
|
97
|
+
self, name: str, branch: Branch | str | None = None, duplicate: bool = True
|
|
98
|
+
) -> GenericSchema:
|
|
99
|
+
schema = self.get(name=name, branch=branch, duplicate=duplicate)
|
|
100
|
+
if isinstance(schema, GenericSchema):
|
|
101
|
+
return schema
|
|
102
|
+
|
|
103
|
+
raise ValueError("The selected node is not of type GenericSchema")
|
|
104
|
+
|
|
96
105
|
def get_profile_schema(
|
|
97
106
|
self, name: str, branch: Branch | str | None = None, duplicate: bool = True
|
|
98
107
|
) -> ProfileSchema:
|
|
@@ -122,7 +131,7 @@ class SchemaManager(NodeManager):
|
|
|
122
131
|
|
|
123
132
|
return self._branches[branch_name].get_all(duplicate=duplicate)
|
|
124
133
|
|
|
125
|
-
async def get_full_safe(self, branch: Branch | str | None = None) -> dict[str,
|
|
134
|
+
async def get_full_safe(self, branch: Branch | str | None = None) -> dict[str, MainSchemaTypes]:
|
|
126
135
|
await lock.registry.local_schema_wait()
|
|
127
136
|
|
|
128
137
|
return self.get_full(branch=branch)
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from infrahub.core.constants import AllowOverrideType, InfrahubKind
|
|
5
|
+
from infrahub.core.constants import AllowOverrideType, InfrahubKind, RelationshipKind
|
|
6
6
|
|
|
7
7
|
from .generated.node_schema import GeneratedNodeSchema
|
|
8
8
|
from .generic_schema import GenericSchema
|
|
@@ -29,6 +29,16 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
29
29
|
def is_template_schema(self) -> bool:
|
|
30
30
|
return False
|
|
31
31
|
|
|
32
|
+
@property
|
|
33
|
+
def is_ip_prefix(self) -> bool:
|
|
34
|
+
"""Return whether a node is a derivative of built-in IP prefixes."""
|
|
35
|
+
return InfrahubKind.IPPREFIX in self.inherit_from
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def is_ip_address(self) -> bool:
|
|
39
|
+
"""Return whether a node is a derivative of built-in IP addreses."""
|
|
40
|
+
return InfrahubKind.IPADDRESS in self.inherit_from
|
|
41
|
+
|
|
32
42
|
def validate_inheritance(self, interface: GenericSchema) -> None:
|
|
33
43
|
"""Perform checks specific to inheritance from Generics.
|
|
34
44
|
|
|
@@ -60,14 +70,17 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
60
70
|
)
|
|
61
71
|
|
|
62
72
|
for relationship in self.relationships:
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
if relationship.name in interface.relationship_names and not relationship.inherited:
|
|
74
|
+
interface_relationship = interface.get_relationship(relationship.name)
|
|
75
|
+
if interface_relationship.allow_override == AllowOverrideType.NONE:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} cannot be overriden"
|
|
78
|
+
)
|
|
79
|
+
if relationship.kind != RelationshipKind.HIERARCHY and relationship.peer != interface_relationship.peer:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"{self.kind}'s relationship {relationship.name} inherited from {interface.kind} must have the same peer "
|
|
82
|
+
f"({interface_relationship.peer} != {relationship.peer})"
|
|
83
|
+
)
|
|
71
84
|
|
|
72
85
|
def inherit_from_interface(self, interface: GenericSchema) -> None:
|
|
73
86
|
existing_inherited_attributes: dict[str, int] = {
|
|
@@ -137,11 +150,3 @@ class NodeSchema(GeneratedNodeSchema):
|
|
|
137
150
|
if self.namespace not in ["Schema", "Internal"] and InfrahubKind.GENERICGROUP not in self.inherit_from:
|
|
138
151
|
labels.append(InfrahubKind.NODE)
|
|
139
152
|
return labels
|
|
140
|
-
|
|
141
|
-
def is_ip_prefix(self) -> bool:
|
|
142
|
-
"""Return whether a node is a derivative of built-in IP prefixes."""
|
|
143
|
-
return InfrahubKind.IPPREFIX in self.inherit_from
|
|
144
|
-
|
|
145
|
-
def is_ip_address(self) -> bool:
|
|
146
|
-
"""Return whether a node is a derivative of built-in IP addreses."""
|
|
147
|
-
return InfrahubKind.IPADDRESS in self.inherit_from
|
|
@@ -28,6 +28,14 @@ class ProfileSchema(BaseNodeSchema):
|
|
|
28
28
|
def is_template_schema(self) -> bool:
|
|
29
29
|
return False
|
|
30
30
|
|
|
31
|
+
@property
|
|
32
|
+
def is_ip_prefix(self) -> bool:
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def is_ip_address(self) -> bool:
|
|
37
|
+
return False
|
|
38
|
+
|
|
31
39
|
def get_labels(self) -> list[str]:
|
|
32
40
|
"""Return the labels for this object, composed of the kind
|
|
33
41
|
and the list of Generic this object is inheriting from."""
|