infrahub-server 1.2.11__py3-none-any.whl → 1.3.0b1__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/constants.py +86 -0
- infrahub/actions/gather.py +114 -0
- infrahub/actions/models.py +241 -0
- infrahub/actions/parsers.py +104 -0
- infrahub/actions/schema.py +382 -0
- infrahub/actions/tasks.py +126 -0
- infrahub/actions/triggers.py +21 -0
- infrahub/cli/db.py +1 -2
- infrahub/core/account.py +24 -47
- infrahub/core/attribute.py +13 -15
- infrahub/core/constants/__init__.py +5 -0
- infrahub/core/constants/infrahubkind.py +9 -0
- infrahub/core/convert_object_type/__init__.py +0 -0
- infrahub/core/convert_object_type/conversion.py +122 -0
- infrahub/core/convert_object_type/schema_mapping.py +56 -0
- infrahub/core/diff/query/all_conflicts.py +1 -5
- infrahub/core/diff/query/artifact.py +10 -20
- infrahub/core/diff/query/diff_get.py +3 -6
- infrahub/core/diff/query/field_summary.py +2 -4
- infrahub/core/diff/query/merge.py +70 -123
- infrahub/core/diff/query/save.py +20 -32
- infrahub/core/diff/query/summary_counts_enricher.py +34 -54
- infrahub/core/manager.py +14 -11
- infrahub/core/migrations/graph/m003_relationship_parent_optional.py +1 -2
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +2 -4
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +11 -22
- infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -6
- infrahub/core/migrations/graph/m021_missing_hierarchy_merge.py +1 -2
- infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +1 -2
- infrahub/core/migrations/query/attribute_add.py +1 -2
- infrahub/core/migrations/query/attribute_rename.py +5 -10
- infrahub/core/migrations/query/delete_element_in_schema.py +19 -17
- infrahub/core/migrations/query/node_duplicate.py +19 -21
- infrahub/core/migrations/query/relationship_duplicate.py +19 -17
- infrahub/core/migrations/schema/node_attribute_remove.py +4 -8
- infrahub/core/migrations/schema/node_remove.py +19 -19
- infrahub/core/models.py +29 -2
- infrahub/core/node/__init__.py +90 -18
- infrahub/core/node/create.py +211 -0
- infrahub/core/node/resource_manager/number_pool.py +31 -5
- infrahub/core/node/standard.py +6 -1
- infrahub/core/protocols.py +56 -0
- infrahub/core/protocols_base.py +3 -0
- infrahub/core/query/__init__.py +2 -2
- infrahub/core/query/diff.py +19 -32
- infrahub/core/query/ipam.py +10 -20
- infrahub/core/query/node.py +28 -46
- infrahub/core/query/relationship.py +53 -32
- infrahub/core/query/resource_manager.py +1 -2
- infrahub/core/query/subquery.py +2 -4
- infrahub/core/relationship/model.py +3 -0
- infrahub/core/schema/__init__.py +2 -1
- infrahub/core/schema/attribute_parameters.py +160 -0
- infrahub/core/schema/attribute_schema.py +111 -8
- infrahub/core/schema/basenode_schema.py +25 -1
- infrahub/core/schema/definitions/core/__init__.py +29 -1
- infrahub/core/schema/definitions/core/group.py +45 -0
- infrahub/core/schema/definitions/internal.py +27 -4
- infrahub/core/schema/generated/attribute_schema.py +16 -3
- infrahub/core/schema/manager.py +3 -0
- infrahub/core/schema/schema_branch.py +67 -7
- infrahub/core/validators/__init__.py +13 -1
- infrahub/core/validators/attribute/choices.py +1 -3
- infrahub/core/validators/attribute/enum.py +1 -3
- infrahub/core/validators/attribute/kind.py +1 -3
- infrahub/core/validators/attribute/length.py +13 -7
- infrahub/core/validators/attribute/min_max.py +118 -0
- infrahub/core/validators/attribute/number_pool.py +106 -0
- infrahub/core/validators/attribute/optional.py +1 -4
- infrahub/core/validators/attribute/regex.py +5 -6
- infrahub/core/validators/attribute/unique.py +1 -3
- infrahub/core/validators/determiner.py +18 -2
- infrahub/core/validators/enum.py +12 -0
- infrahub/core/validators/node/hierarchy.py +3 -6
- infrahub/core/validators/query.py +1 -3
- infrahub/core/validators/relationship/count.py +6 -12
- infrahub/core/validators/relationship/optional.py +2 -4
- infrahub/core/validators/relationship/peer.py +3 -8
- infrahub/core/validators/uniqueness/query.py +5 -9
- infrahub/database/__init__.py +11 -2
- infrahub/events/group_action.py +1 -0
- infrahub/git/base.py +5 -3
- infrahub/git/integrator.py +102 -3
- infrahub/graphql/analyzer.py +139 -18
- infrahub/graphql/manager.py +4 -0
- infrahub/graphql/mutations/action.py +164 -0
- infrahub/graphql/mutations/convert_object_type.py +62 -0
- infrahub/graphql/mutations/main.py +24 -175
- infrahub/graphql/mutations/proposed_change.py +20 -17
- infrahub/graphql/mutations/resource_manager.py +62 -6
- infrahub/graphql/queries/convert_object_type_mapping.py +36 -0
- infrahub/graphql/queries/resource_manager.py +7 -1
- infrahub/graphql/schema.py +6 -0
- infrahub/menu/menu.py +31 -0
- infrahub/message_bus/messages/__init__.py +0 -10
- infrahub/message_bus/operations/__init__.py +0 -8
- infrahub/patch/queries/consolidate_duplicated_nodes.py +3 -6
- infrahub/patch/queries/delete_duplicated_edges.py +5 -10
- infrahub/pools/number.py +5 -3
- infrahub/prefect_server/models.py +1 -19
- infrahub/proposed_change/models.py +68 -3
- infrahub/proposed_change/tasks.py +907 -30
- infrahub/task_manager/models.py +10 -6
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +18 -2
- infrahub/trigger/tasks.py +3 -1
- infrahub/types.py +6 -0
- infrahub/workflows/catalogue.py +76 -0
- infrahub_sdk/client.py +43 -10
- infrahub_sdk/node/__init__.py +39 -0
- infrahub_sdk/node/attribute.py +122 -0
- infrahub_sdk/node/constants.py +21 -0
- infrahub_sdk/{node.py → node/node.py} +50 -749
- infrahub_sdk/node/parsers.py +15 -0
- infrahub_sdk/node/property.py +24 -0
- infrahub_sdk/node/related_node.py +266 -0
- infrahub_sdk/node/relationship.py +302 -0
- infrahub_sdk/protocols.py +112 -0
- infrahub_sdk/protocols_base.py +34 -2
- infrahub_sdk/query_groups.py +13 -2
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/schema/repository.py +16 -0
- infrahub_sdk/spec/object.py +1 -1
- infrahub_sdk/store.py +1 -1
- infrahub_sdk/testing/schemas/car_person.py +1 -0
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/METADATA +4 -4
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/RECORD +134 -122
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/WHEEL +1 -1
- infrahub_testcontainers/container.py +0 -1
- infrahub_testcontainers/docker-compose.test.yml +1 -1
- infrahub_testcontainers/helpers.py +8 -2
- infrahub/message_bus/messages/check_generator_run.py +0 -26
- infrahub/message_bus/messages/finalize_validator_execution.py +0 -15
- infrahub/message_bus/messages/proposed_change/base_with_diff.py +0 -16
- infrahub/message_bus/messages/proposed_change/request_proposedchange_refreshartifacts.py +0 -11
- infrahub/message_bus/messages/request_generatordefinition_check.py +0 -20
- infrahub/message_bus/messages/request_proposedchange_pipeline.py +0 -23
- infrahub/message_bus/operations/check/__init__.py +0 -3
- infrahub/message_bus/operations/check/generator.py +0 -156
- infrahub/message_bus/operations/finalize/__init__.py +0 -3
- infrahub/message_bus/operations/finalize/validator.py +0 -133
- infrahub/message_bus/operations/requests/__init__.py +0 -9
- infrahub/message_bus/operations/requests/generator_definition.py +0 -140
- infrahub/message_bus/operations/requests/proposed_change.py +0 -629
- /infrahub/{message_bus/messages/proposed_change → actions}/__init__.py +0 -0
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.11.dist-info → infrahub_server-1.3.0b1.dist-info}/entry_points.txt +0 -0
infrahub/core/node/__init__.py
CHANGED
|
@@ -22,7 +22,15 @@ from infrahub.core.constants import (
|
|
|
22
22
|
from infrahub.core.constants.schema import SchemaElementPathType
|
|
23
23
|
from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
|
|
24
24
|
from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
|
|
25
|
-
from infrahub.core.schema import
|
|
25
|
+
from infrahub.core.schema import (
|
|
26
|
+
AttributeSchema,
|
|
27
|
+
NodeSchema,
|
|
28
|
+
NonGenericSchemaTypes,
|
|
29
|
+
ProfileSchema,
|
|
30
|
+
RelationshipSchema,
|
|
31
|
+
TemplateSchema,
|
|
32
|
+
)
|
|
33
|
+
from infrahub.core.schema.attribute_parameters import NumberPoolParameters
|
|
26
34
|
from infrahub.core.timestamp import Timestamp
|
|
27
35
|
from infrahub.exceptions import InitializationError, NodeNotFoundError, PoolExhaustedError, ValidationError
|
|
28
36
|
from infrahub.types import ATTRIBUTE_TYPES
|
|
@@ -66,7 +74,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
66
74
|
_meta.default_filter = default_filter
|
|
67
75
|
super().__init_subclass_with_meta__(_meta=_meta, **options)
|
|
68
76
|
|
|
69
|
-
def get_schema(self) ->
|
|
77
|
+
def get_schema(self) -> NonGenericSchemaTypes:
|
|
70
78
|
return self._schema
|
|
71
79
|
|
|
72
80
|
def get_kind(self) -> str:
|
|
@@ -247,6 +255,12 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
247
255
|
within the create code.
|
|
248
256
|
"""
|
|
249
257
|
|
|
258
|
+
number_pool_parameters: NumberPoolParameters | None = None
|
|
259
|
+
if attribute.schema.kind == "NumberPool" and isinstance(attribute.schema.parameters, NumberPoolParameters):
|
|
260
|
+
attribute.from_pool = {"id": attribute.schema.parameters.number_pool_id}
|
|
261
|
+
attribute.is_default = False
|
|
262
|
+
number_pool_parameters = attribute.schema.parameters
|
|
263
|
+
|
|
250
264
|
if not attribute.from_pool:
|
|
251
265
|
return
|
|
252
266
|
|
|
@@ -255,19 +269,25 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
255
269
|
db=db, id=attribute.from_pool["id"], kind=CoreNumberPool
|
|
256
270
|
)
|
|
257
271
|
except NodeNotFoundError:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
272
|
+
if number_pool_parameters:
|
|
273
|
+
number_pool = await self._create_number_pool(
|
|
274
|
+
db=db, attribute=attribute, number_pool_parameters=number_pool_parameters
|
|
261
275
|
)
|
|
262
|
-
|
|
263
|
-
|
|
276
|
+
|
|
277
|
+
else:
|
|
278
|
+
errors.append(
|
|
279
|
+
ValidationError(
|
|
280
|
+
{f"{attribute.name}.from_pool": f"The pool requested {attribute.from_pool} was not found."}
|
|
281
|
+
)
|
|
282
|
+
)
|
|
283
|
+
return
|
|
264
284
|
|
|
265
285
|
if (
|
|
266
286
|
number_pool.node.value in [self._schema.kind] + self._schema.inherit_from
|
|
267
287
|
and number_pool.node_attribute.value == attribute.name
|
|
268
288
|
):
|
|
269
289
|
try:
|
|
270
|
-
next_free = await number_pool.get_resource(db=db, branch=self._branch, node=self)
|
|
290
|
+
next_free = await number_pool.get_resource(db=db, branch=self._branch, node=self, attribute=attribute)
|
|
271
291
|
except PoolExhaustedError:
|
|
272
292
|
errors.append(
|
|
273
293
|
ValidationError({f"{attribute.name}.from_pool": f"The pool {number_pool.node.value} is exhausted."})
|
|
@@ -285,6 +305,35 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
285
305
|
)
|
|
286
306
|
)
|
|
287
307
|
|
|
308
|
+
async def _create_number_pool(
|
|
309
|
+
self, db: InfrahubDatabase, attribute: BaseAttribute, number_pool_parameters: NumberPoolParameters
|
|
310
|
+
) -> CoreNumberPool:
|
|
311
|
+
schema = db.schema.get_node_schema(name="CoreNumberPool", duplicate=False)
|
|
312
|
+
|
|
313
|
+
pool_node = self._schema.kind
|
|
314
|
+
schema_attribute = self._schema.get_attribute(attribute.schema.name)
|
|
315
|
+
if schema_attribute.inherited:
|
|
316
|
+
for generic_name in self._schema.inherit_from:
|
|
317
|
+
generic_node = db.schema.get_generic_schema(name=generic_name, duplicate=False)
|
|
318
|
+
if attribute.schema.name in generic_node.attribute_names:
|
|
319
|
+
pool_node = generic_node.kind
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
number_pool = await Node.init(db=db, schema=schema, branch=self._branch)
|
|
323
|
+
await number_pool.new(
|
|
324
|
+
db=db,
|
|
325
|
+
id=number_pool_parameters.number_pool_id,
|
|
326
|
+
name=f"{pool_node}.{attribute.schema.name} [{number_pool_parameters.number_pool_id}]",
|
|
327
|
+
node=pool_node,
|
|
328
|
+
node_attribute=attribute.schema.name,
|
|
329
|
+
start_range=number_pool_parameters.start_range,
|
|
330
|
+
end_range=number_pool_parameters.end_range,
|
|
331
|
+
)
|
|
332
|
+
await number_pool.save(db=db)
|
|
333
|
+
# Do a lookup of the number pool to get the correct mapped type from the registry
|
|
334
|
+
# without this we don't get access to the .get_resource() method.
|
|
335
|
+
return await registry.manager.get_one_by_id_or_default_filter(db=db, id=number_pool.id, kind=CoreNumberPool)
|
|
336
|
+
|
|
288
337
|
async def handle_object_template(self, fields: dict, db: InfrahubDatabase, errors: list) -> None:
|
|
289
338
|
"""Fill the `fields` parameters with values from an object template if one is in use."""
|
|
290
339
|
object_template_field = fields.get(OBJECT_TEMPLATE_RELATIONSHIP_NAME)
|
|
@@ -369,6 +418,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
369
418
|
self._computed_jinja2_attributes.append(mandatory_attr)
|
|
370
419
|
continue
|
|
371
420
|
|
|
421
|
+
if mandatory_attribute.kind == "NumberPool":
|
|
422
|
+
continue
|
|
423
|
+
|
|
372
424
|
errors.append(
|
|
373
425
|
ValidationError({mandatory_attr: f"{mandatory_attr} is mandatory for {self.get_kind()}"})
|
|
374
426
|
)
|
|
@@ -385,6 +437,21 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
385
437
|
# -------------------------------------------
|
|
386
438
|
# Generate Attribute and Relationship and assign them
|
|
387
439
|
# -------------------------------------------
|
|
440
|
+
errors.extend(await self._process_fields_relationships(fields=fields, db=db))
|
|
441
|
+
errors.extend(await self._process_fields_attributes(fields=fields, db=db))
|
|
442
|
+
|
|
443
|
+
if errors:
|
|
444
|
+
raise ValidationError(errors)
|
|
445
|
+
|
|
446
|
+
# Check if any post processor have been defined
|
|
447
|
+
# A processor can be used for example to assigne a default value
|
|
448
|
+
for name in self._attributes + self._relationships:
|
|
449
|
+
if hasattr(self, f"process_{name}"):
|
|
450
|
+
await getattr(self, f"process_{name}")(db=db)
|
|
451
|
+
|
|
452
|
+
async def _process_fields_relationships(self, fields: dict, db: InfrahubDatabase) -> list[ValidationError]:
|
|
453
|
+
errors: list[ValidationError] = []
|
|
454
|
+
|
|
388
455
|
for rel_schema in self._schema.relationships:
|
|
389
456
|
self._relationships.append(rel_schema.name)
|
|
390
457
|
|
|
@@ -406,6 +473,11 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
406
473
|
except ValidationError as exc:
|
|
407
474
|
errors.append(exc)
|
|
408
475
|
|
|
476
|
+
return errors
|
|
477
|
+
|
|
478
|
+
async def _process_fields_attributes(self, fields: dict, db: InfrahubDatabase) -> list[ValidationError]:
|
|
479
|
+
errors: list[ValidationError] = []
|
|
480
|
+
|
|
409
481
|
for attr_schema in self._schema.attributes:
|
|
410
482
|
self._attributes.append(attr_schema.name)
|
|
411
483
|
if not self._existing and attr_schema.name in self._computed_jinja2_attributes:
|
|
@@ -434,14 +506,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
434
506
|
except ValidationError as exc:
|
|
435
507
|
errors.append(exc)
|
|
436
508
|
|
|
437
|
-
|
|
438
|
-
raise ValidationError(errors)
|
|
439
|
-
|
|
440
|
-
# Check if any post processor have been defined
|
|
441
|
-
# A processor can be used for example to assigne a default value
|
|
442
|
-
for name in self._attributes + self._relationships:
|
|
443
|
-
if hasattr(self, f"process_{name}"):
|
|
444
|
-
await getattr(self, f"process_{name}")(db=db)
|
|
509
|
+
return errors
|
|
445
510
|
|
|
446
511
|
async def _process_macros(self, db: InfrahubDatabase) -> None:
|
|
447
512
|
schema_branch = db.schema.get_schema_branch(self._branch.name)
|
|
@@ -872,9 +937,11 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
872
937
|
if relationship.kind == RelationshipKind.PARENT:
|
|
873
938
|
return relationship.name
|
|
874
939
|
|
|
875
|
-
async def get_object_template(self, db: InfrahubDatabase) ->
|
|
940
|
+
async def get_object_template(self, db: InfrahubDatabase) -> CoreObjectTemplate | None:
|
|
876
941
|
object_template: RelationshipManager = getattr(self, OBJECT_TEMPLATE_RELATIONSHIP_NAME, None)
|
|
877
|
-
return
|
|
942
|
+
return (
|
|
943
|
+
await object_template.get_peer(db=db, peer_type=CoreObjectTemplate) if object_template is not None else None
|
|
944
|
+
)
|
|
878
945
|
|
|
879
946
|
def get_relationships(
|
|
880
947
|
self, kind: RelationshipKind, exclude: Sequence[str] | None = None
|
|
@@ -888,3 +955,8 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
888
955
|
for relationship in self.get_schema().relationships
|
|
889
956
|
if relationship.name not in exclude and relationship.kind == kind
|
|
890
957
|
]
|
|
958
|
+
|
|
959
|
+
def validate_relationships(self) -> None:
|
|
960
|
+
for name in self._relationships:
|
|
961
|
+
relm: RelationshipManager = getattr(self, name)
|
|
962
|
+
relm.validate()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
|
4
|
+
|
|
5
|
+
from infrahub.core import registry
|
|
6
|
+
from infrahub.core.constants import RelationshipCardinality, RelationshipKind
|
|
7
|
+
from infrahub.core.constraint.node.runner import NodeConstraintRunner
|
|
8
|
+
from infrahub.core.manager import NodeManager
|
|
9
|
+
from infrahub.core.node import Node
|
|
10
|
+
from infrahub.core.protocols import CoreObjectTemplate
|
|
11
|
+
from infrahub.dependencies.registry import get_component_registry
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from infrahub.core.branch import Branch
|
|
15
|
+
from infrahub.core.relationship.model import RelationshipManager
|
|
16
|
+
from infrahub.core.schema import MainSchemaTypes, NonGenericSchemaTypes, RelationshipSchema
|
|
17
|
+
from infrahub.database import InfrahubDatabase
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def get_template_relationship_peers(
|
|
21
|
+
db: InfrahubDatabase, template: CoreObjectTemplate, relationship: RelationshipSchema
|
|
22
|
+
) -> Mapping[str, CoreObjectTemplate]:
|
|
23
|
+
"""For a given relationship on the template, fetch the related peers."""
|
|
24
|
+
template_relationship_manager: RelationshipManager = getattr(template, relationship.name)
|
|
25
|
+
if relationship.cardinality == RelationshipCardinality.MANY:
|
|
26
|
+
return await template_relationship_manager.get_peers(db=db, peer_type=CoreObjectTemplate)
|
|
27
|
+
|
|
28
|
+
peers: dict[str, CoreObjectTemplate] = {}
|
|
29
|
+
template_relationship_peer = await template_relationship_manager.get_peer(db=db, peer_type=CoreObjectTemplate)
|
|
30
|
+
if template_relationship_peer:
|
|
31
|
+
peers[template_relationship_peer.id] = template_relationship_peer
|
|
32
|
+
return peers
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def extract_peer_data(
|
|
36
|
+
db: InfrahubDatabase,
|
|
37
|
+
template_peer: CoreObjectTemplate,
|
|
38
|
+
obj_peer_schema: MainSchemaTypes,
|
|
39
|
+
parent_obj: Node,
|
|
40
|
+
current_template: CoreObjectTemplate,
|
|
41
|
+
) -> Mapping[str, Any]:
|
|
42
|
+
obj_peer_data: dict[str, Any] = {}
|
|
43
|
+
|
|
44
|
+
for attr in template_peer.get_schema().attribute_names:
|
|
45
|
+
if attr not in obj_peer_schema.attribute_names:
|
|
46
|
+
continue
|
|
47
|
+
obj_peer_data[attr] = {"value": getattr(template_peer, attr).value, "source": template_peer.id}
|
|
48
|
+
|
|
49
|
+
for rel in template_peer.get_schema().relationship_names:
|
|
50
|
+
rel_manager: RelationshipManager = getattr(template_peer, rel)
|
|
51
|
+
if (
|
|
52
|
+
rel_manager.schema.kind not in [RelationshipKind.COMPONENT, RelationshipKind.PARENT]
|
|
53
|
+
or rel_manager.schema.name not in obj_peer_schema.relationship_names
|
|
54
|
+
):
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
if list(await rel_manager.get_peers(db=db)) == [current_template.id]:
|
|
58
|
+
obj_peer_data[rel] = {"id": parent_obj.id}
|
|
59
|
+
|
|
60
|
+
return obj_peer_data
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def handle_template_relationships(
|
|
64
|
+
db: InfrahubDatabase,
|
|
65
|
+
branch: Branch,
|
|
66
|
+
obj: Node,
|
|
67
|
+
template: CoreObjectTemplate,
|
|
68
|
+
fields: list,
|
|
69
|
+
constraint_runner: NodeConstraintRunner | None = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
if constraint_runner is None:
|
|
72
|
+
component_registry = get_component_registry()
|
|
73
|
+
constraint_runner = await component_registry.get_component(NodeConstraintRunner, db=db, branch=branch)
|
|
74
|
+
|
|
75
|
+
for relationship in obj.get_relationships(kind=RelationshipKind.COMPONENT, exclude=fields):
|
|
76
|
+
template_relationship_peers = await get_template_relationship_peers(
|
|
77
|
+
db=db, template=template, relationship=relationship
|
|
78
|
+
)
|
|
79
|
+
if not template_relationship_peers:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
for template_relationship_peer in template_relationship_peers.values():
|
|
83
|
+
# We retrieve peer schema for each peer in case we are processing a relationship which is based on a generic
|
|
84
|
+
obj_peer_schema = registry.schema.get_node_schema(
|
|
85
|
+
name=template_relationship_peer.get_schema().kind.removeprefix("Template"),
|
|
86
|
+
branch=branch,
|
|
87
|
+
duplicate=False,
|
|
88
|
+
)
|
|
89
|
+
obj_peer_data = await extract_peer_data(
|
|
90
|
+
db=db,
|
|
91
|
+
template_peer=template_relationship_peer,
|
|
92
|
+
obj_peer_schema=obj_peer_schema,
|
|
93
|
+
parent_obj=obj,
|
|
94
|
+
current_template=template,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
obj_peer = await Node.init(schema=obj_peer_schema, db=db, branch=branch)
|
|
98
|
+
await obj_peer.new(db=db, **obj_peer_data)
|
|
99
|
+
await constraint_runner.check(node=obj_peer, field_filters=list(obj_peer_data))
|
|
100
|
+
await obj_peer.save(db=db)
|
|
101
|
+
|
|
102
|
+
await handle_template_relationships(
|
|
103
|
+
db=db,
|
|
104
|
+
branch=branch,
|
|
105
|
+
constraint_runner=constraint_runner,
|
|
106
|
+
obj=obj_peer,
|
|
107
|
+
template=template_relationship_peer,
|
|
108
|
+
fields=fields,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
async def get_profile_ids(db: InfrahubDatabase, obj: Node) -> set[str]:
|
|
113
|
+
if not hasattr(obj, "profiles"):
|
|
114
|
+
return set()
|
|
115
|
+
profile_rels = await obj.profiles.get_relationships(db=db)
|
|
116
|
+
return {pr.peer_id for pr in profile_rels}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def refresh_for_profile_update(
|
|
120
|
+
db: InfrahubDatabase,
|
|
121
|
+
branch: Branch,
|
|
122
|
+
obj: Node,
|
|
123
|
+
schema: NonGenericSchemaTypes,
|
|
124
|
+
previous_profile_ids: set[str] | None = None,
|
|
125
|
+
) -> Node:
|
|
126
|
+
if not hasattr(obj, "profiles"):
|
|
127
|
+
return obj
|
|
128
|
+
current_profile_ids = await get_profile_ids(db=db, obj=obj)
|
|
129
|
+
if previous_profile_ids is None or previous_profile_ids != current_profile_ids:
|
|
130
|
+
refreshed_node = await NodeManager.get_one_by_id_or_default_filter(
|
|
131
|
+
db=db,
|
|
132
|
+
kind=schema.kind,
|
|
133
|
+
id=obj.get_id(),
|
|
134
|
+
branch=branch,
|
|
135
|
+
include_owner=True,
|
|
136
|
+
include_source=True,
|
|
137
|
+
)
|
|
138
|
+
refreshed_node._node_changelog = obj.node_changelog
|
|
139
|
+
return refreshed_node
|
|
140
|
+
return obj
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def _do_create_node(
|
|
144
|
+
node_class: type[Node],
|
|
145
|
+
db: InfrahubDatabase,
|
|
146
|
+
data: dict,
|
|
147
|
+
schema: NonGenericSchemaTypes,
|
|
148
|
+
fields_to_validate: list,
|
|
149
|
+
branch: Branch,
|
|
150
|
+
node_constraint_runner: NodeConstraintRunner,
|
|
151
|
+
) -> Node:
|
|
152
|
+
obj = await node_class.init(db=db, schema=schema, branch=branch)
|
|
153
|
+
await obj.new(db=db, **data)
|
|
154
|
+
await node_constraint_runner.check(node=obj, field_filters=fields_to_validate)
|
|
155
|
+
await obj.save(db=db)
|
|
156
|
+
|
|
157
|
+
object_template = await obj.get_object_template(db=db)
|
|
158
|
+
if object_template:
|
|
159
|
+
await handle_template_relationships(
|
|
160
|
+
db=db,
|
|
161
|
+
branch=branch,
|
|
162
|
+
template=object_template,
|
|
163
|
+
obj=obj,
|
|
164
|
+
fields=fields_to_validate,
|
|
165
|
+
)
|
|
166
|
+
return obj
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def create_node(
|
|
170
|
+
data: dict,
|
|
171
|
+
db: InfrahubDatabase,
|
|
172
|
+
branch: Branch,
|
|
173
|
+
schema: NonGenericSchemaTypes,
|
|
174
|
+
) -> Node:
|
|
175
|
+
"""Create a node in the database if constraint checks succeed."""
|
|
176
|
+
|
|
177
|
+
component_registry = get_component_registry()
|
|
178
|
+
node_constraint_runner = await component_registry.get_component(
|
|
179
|
+
NodeConstraintRunner, db=db.start_session() if not db.is_transaction else db, branch=branch
|
|
180
|
+
)
|
|
181
|
+
node_class = Node
|
|
182
|
+
if schema.kind in registry.node:
|
|
183
|
+
node_class = registry.node[schema.kind]
|
|
184
|
+
|
|
185
|
+
fields_to_validate = list(data)
|
|
186
|
+
if db.is_transaction:
|
|
187
|
+
obj = await _do_create_node(
|
|
188
|
+
node_class=node_class,
|
|
189
|
+
node_constraint_runner=node_constraint_runner,
|
|
190
|
+
db=db,
|
|
191
|
+
schema=schema,
|
|
192
|
+
branch=branch,
|
|
193
|
+
fields_to_validate=fields_to_validate,
|
|
194
|
+
data=data,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
async with db.start_transaction() as dbt:
|
|
198
|
+
obj = await _do_create_node(
|
|
199
|
+
node_class=node_class,
|
|
200
|
+
node_constraint_runner=node_constraint_runner,
|
|
201
|
+
db=dbt,
|
|
202
|
+
schema=schema,
|
|
203
|
+
branch=branch,
|
|
204
|
+
fields_to_validate=fields_to_validate,
|
|
205
|
+
data=data,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if await get_profile_ids(db=db, obj=obj):
|
|
209
|
+
obj = await refresh_for_profile_update(db=db, branch=branch, schema=schema, obj=obj)
|
|
210
|
+
|
|
211
|
+
return obj
|
|
@@ -2,22 +2,44 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
+
from infrahub.core import registry
|
|
5
6
|
from infrahub.core.query.resource_manager import NumberPoolGetReserved, NumberPoolGetUsed, NumberPoolSetReserved
|
|
7
|
+
from infrahub.core.schema.attribute_parameters import NumberAttributeParameters
|
|
6
8
|
from infrahub.exceptions import PoolExhaustedError
|
|
7
9
|
|
|
8
10
|
from .. import Node
|
|
9
11
|
|
|
10
12
|
if TYPE_CHECKING:
|
|
13
|
+
from infrahub.core.attribute import BaseAttribute
|
|
11
14
|
from infrahub.core.branch import Branch
|
|
12
15
|
from infrahub.database import InfrahubDatabase
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
class CoreNumberPool(Node):
|
|
19
|
+
def get_attribute_nb_excluded_values(self) -> int:
|
|
20
|
+
"""
|
|
21
|
+
Returns the number of excluded values for the attribute of the number pool.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
pool_node = registry.schema.get(name=self.node.value) # type: ignore [attr-defined]
|
|
25
|
+
attribute = [attribute for attribute in pool_node.attributes if attribute.name == self.node_attribute.value][0] # type: ignore [attr-defined]
|
|
26
|
+
if not isinstance(attribute.parameters, NumberAttributeParameters):
|
|
27
|
+
return 0
|
|
28
|
+
|
|
29
|
+
sum_excluded_values = 0
|
|
30
|
+
excluded_ranges = attribute.parameters.get_excluded_ranges()
|
|
31
|
+
for start_range, end_range in excluded_ranges:
|
|
32
|
+
sum_excluded_values += end_range - start_range + 1
|
|
33
|
+
|
|
34
|
+
res = len(attribute.parameters.get_excluded_single_values()) + sum_excluded_values
|
|
35
|
+
return res
|
|
36
|
+
|
|
16
37
|
async def get_resource(
|
|
17
38
|
self,
|
|
18
39
|
db: InfrahubDatabase,
|
|
19
40
|
branch: Branch,
|
|
20
41
|
node: Node,
|
|
42
|
+
attribute: BaseAttribute,
|
|
21
43
|
identifier: str | None = None,
|
|
22
44
|
) -> int:
|
|
23
45
|
identifier = identifier or node.get_id()
|
|
@@ -31,23 +53,24 @@ class CoreNumberPool(Node):
|
|
|
31
53
|
return reservation
|
|
32
54
|
|
|
33
55
|
# If we have not returned a value we need to find one if avaiable
|
|
34
|
-
number = await self.get_next(db=db, branch=branch)
|
|
56
|
+
number = await self.get_next(db=db, branch=branch, attribute=attribute)
|
|
35
57
|
|
|
36
58
|
query_set = await NumberPoolSetReserved.init(
|
|
37
59
|
db=db, pool_id=self.get_id(), identifier=identifier, reserved=number
|
|
38
60
|
)
|
|
39
61
|
await query_set.execute(db=db)
|
|
40
|
-
|
|
41
62
|
return number
|
|
42
63
|
|
|
43
|
-
async def get_next(self, db: InfrahubDatabase, branch: Branch) -> int:
|
|
64
|
+
async def get_next(self, db: InfrahubDatabase, branch: Branch, attribute: BaseAttribute) -> int:
|
|
44
65
|
query = await NumberPoolGetUsed.init(db=db, branch=branch, pool=self, branch_agnostic=True)
|
|
45
66
|
await query.execute(db=db)
|
|
46
67
|
taken = [result.get_as_optional_type("av.value", return_type=int) for result in query.results]
|
|
68
|
+
parameters = attribute.schema.parameters
|
|
47
69
|
next_number = find_next_free(
|
|
48
70
|
start=self.start_range.value, # type: ignore[attr-defined]
|
|
49
71
|
end=self.end_range.value, # type: ignore[attr-defined]
|
|
50
72
|
taken=taken,
|
|
73
|
+
parameters=parameters if isinstance(parameters, NumberAttributeParameters) else None,
|
|
51
74
|
)
|
|
52
75
|
if next_number is None:
|
|
53
76
|
raise PoolExhaustedError("There are no more values available in this pool.")
|
|
@@ -55,12 +78,15 @@ class CoreNumberPool(Node):
|
|
|
55
78
|
return next_number
|
|
56
79
|
|
|
57
80
|
|
|
58
|
-
def find_next_free(
|
|
81
|
+
def find_next_free(
|
|
82
|
+
start: int, end: int, taken: list[int | None], parameters: NumberAttributeParameters | None
|
|
83
|
+
) -> int | None:
|
|
59
84
|
used_numbers = [number for number in taken if number is not None]
|
|
60
85
|
used_set = set(used_numbers)
|
|
61
86
|
|
|
62
87
|
for num in range(start, end + 1):
|
|
63
88
|
if num not in used_set:
|
|
64
|
-
|
|
89
|
+
if parameters is None or parameters.is_valid_value(num):
|
|
90
|
+
return num
|
|
65
91
|
|
|
66
92
|
return None
|
infrahub/core/node/standard.py
CHANGED
|
@@ -210,7 +210,12 @@ class StandardNode(BaseModel):
|
|
|
210
210
|
|
|
211
211
|
@classmethod
|
|
212
212
|
async def get_list(
|
|
213
|
-
cls,
|
|
213
|
+
cls,
|
|
214
|
+
db: InfrahubDatabase,
|
|
215
|
+
limit: int = 1000,
|
|
216
|
+
ids: list[str] | None = None,
|
|
217
|
+
name: str | None = None,
|
|
218
|
+
**kwargs: dict[str, Any],
|
|
214
219
|
) -> list[Self]:
|
|
215
220
|
query: Query = await StandardNodeGetListQuery.init(
|
|
216
221
|
db=db, node_class=cls, ids=ids, node_name=name, limit=limit, **kwargs
|
infrahub/core/protocols.py
CHANGED
|
@@ -62,6 +62,12 @@ class BuiltinIPPrefix(CoreNode):
|
|
|
62
62
|
children: RelationshipManager
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
class CoreAction(CoreNode):
|
|
66
|
+
name: String
|
|
67
|
+
description: StringOptional
|
|
68
|
+
triggers: RelationshipManager
|
|
69
|
+
|
|
70
|
+
|
|
65
71
|
class CoreArtifactTarget(CoreNode):
|
|
66
72
|
artifacts: RelationshipManager
|
|
67
73
|
|
|
@@ -148,6 +154,10 @@ class CoreMenu(CoreNode):
|
|
|
148
154
|
children: RelationshipManager
|
|
149
155
|
|
|
150
156
|
|
|
157
|
+
class CoreNodeTriggerMatch(CoreNode):
|
|
158
|
+
trigger: RelationshipManager
|
|
159
|
+
|
|
160
|
+
|
|
151
161
|
class CoreObjectComponentTemplate(CoreNode):
|
|
152
162
|
template_name: String
|
|
153
163
|
|
|
@@ -189,6 +199,14 @@ class CoreTransformation(CoreNode):
|
|
|
189
199
|
tags: RelationshipManager
|
|
190
200
|
|
|
191
201
|
|
|
202
|
+
class CoreTriggerRule(CoreNode):
|
|
203
|
+
name: String
|
|
204
|
+
description: StringOptional
|
|
205
|
+
active: Boolean
|
|
206
|
+
branch_scope: Dropdown
|
|
207
|
+
action: RelationshipManager
|
|
208
|
+
|
|
209
|
+
|
|
192
210
|
class CoreValidator(CoreNode):
|
|
193
211
|
label: StringOptional
|
|
194
212
|
state: Enum
|
|
@@ -326,6 +344,10 @@ class CoreFileThread(CoreThread):
|
|
|
326
344
|
repository: RelationshipManager
|
|
327
345
|
|
|
328
346
|
|
|
347
|
+
class CoreGeneratorAction(CoreAction):
|
|
348
|
+
generator: RelationshipManager
|
|
349
|
+
|
|
350
|
+
|
|
329
351
|
class CoreGeneratorCheck(CoreCheck):
|
|
330
352
|
instance: String
|
|
331
353
|
|
|
@@ -380,6 +402,16 @@ class CoreGraphQLQueryGroup(CoreGroup):
|
|
|
380
402
|
query: RelationshipManager
|
|
381
403
|
|
|
382
404
|
|
|
405
|
+
class CoreGroupAction(CoreAction):
|
|
406
|
+
add_members: Boolean
|
|
407
|
+
group: RelationshipManager
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class CoreGroupTriggerRule(CoreTriggerRule):
|
|
411
|
+
members_added: Boolean
|
|
412
|
+
group: RelationshipManager
|
|
413
|
+
|
|
414
|
+
|
|
383
415
|
class CoreIPAddressPool(CoreResourcePool, LineageSource):
|
|
384
416
|
default_address_type: String
|
|
385
417
|
default_prefix_length: IntegerOptional
|
|
@@ -399,6 +431,25 @@ class CoreMenuItem(CoreMenu):
|
|
|
399
431
|
pass
|
|
400
432
|
|
|
401
433
|
|
|
434
|
+
class CoreNodeTriggerAttributeMatch(CoreNodeTriggerMatch):
|
|
435
|
+
attribute_name: String
|
|
436
|
+
value: StringOptional
|
|
437
|
+
value_previous: StringOptional
|
|
438
|
+
value_match: Dropdown
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class CoreNodeTriggerRelationshipMatch(CoreNodeTriggerMatch):
|
|
442
|
+
relationship_name: String
|
|
443
|
+
added: Boolean
|
|
444
|
+
peer: StringOptional
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
class CoreNodeTriggerRule(CoreTriggerRule):
|
|
448
|
+
node_kind: String
|
|
449
|
+
mutation_action: Enum
|
|
450
|
+
matches: RelationshipManager
|
|
451
|
+
|
|
452
|
+
|
|
402
453
|
class CoreNumberPool(CoreResourcePool, LineageSource):
|
|
403
454
|
node: String
|
|
404
455
|
node_attribute: String
|
|
@@ -446,6 +497,11 @@ class CoreRepository(LineageOwner, LineageSource, CoreGenericRepository, CoreTas
|
|
|
446
497
|
commit: StringOptional
|
|
447
498
|
|
|
448
499
|
|
|
500
|
+
class CoreRepositoryGroup(CoreGroup):
|
|
501
|
+
content: Dropdown
|
|
502
|
+
repository: RelationshipManager
|
|
503
|
+
|
|
504
|
+
|
|
449
505
|
class CoreRepositoryValidator(CoreValidator):
|
|
450
506
|
repository: RelationshipManager
|
|
451
507
|
|
infrahub/core/protocols_base.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing_extensions import Self
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from neo4j import AsyncResult, AsyncSession, AsyncTransaction, Record
|
|
9
9
|
|
|
10
|
+
from infrahub.core.schema import NonGenericSchemaTypes
|
|
10
11
|
from infrahub.core.schema.schema_branch import SchemaBranch
|
|
11
12
|
|
|
12
13
|
|
|
@@ -70,6 +71,8 @@ class CoreNode(Protocol):
|
|
|
70
71
|
|
|
71
72
|
def get_id(self) -> str: ...
|
|
72
73
|
def get_kind(self) -> str: ...
|
|
74
|
+
def get_schema(self) -> NonGenericSchemaTypes: ...
|
|
75
|
+
|
|
73
76
|
@classmethod
|
|
74
77
|
async def init(
|
|
75
78
|
cls,
|
infrahub/core/query/__init__.py
CHANGED
|
@@ -424,8 +424,8 @@ class Query(ABC):
|
|
|
424
424
|
else:
|
|
425
425
|
self.query_lines.extend([line.strip() for line in query.split("\n") if line.strip()])
|
|
426
426
|
|
|
427
|
-
def add_subquery(self, subquery: str, with_clause: str | None = None) -> None:
|
|
428
|
-
self.add_to_query("CALL {")
|
|
427
|
+
def add_subquery(self, subquery: str, node_alias: str, with_clause: str | None = None) -> None:
|
|
428
|
+
self.add_to_query(f"CALL ({node_alias}) {{")
|
|
429
429
|
self.add_to_query(subquery)
|
|
430
430
|
self.add_to_query("}")
|
|
431
431
|
if with_clause:
|