infrahub-server 1.3.2__py3-none-any.whl → 1.3.4__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/schema.py +2 -2
- infrahub/cli/db.py +194 -13
- infrahub/core/branch/enums.py +8 -0
- infrahub/core/branch/models.py +28 -5
- infrahub/core/branch/tasks.py +5 -7
- infrahub/core/convert_object_type/conversion.py +10 -0
- infrahub/core/diff/coordinator.py +32 -34
- infrahub/core/diff/diff_locker.py +26 -0
- infrahub/core/diff/enricher/hierarchy.py +7 -3
- infrahub/core/diff/query_parser.py +7 -3
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +4 -3
- infrahub/core/merge.py +31 -16
- infrahub/core/migrations/graph/__init__.py +26 -0
- infrahub/core/migrations/graph/m012_convert_account_generic.py +4 -3
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +4 -3
- infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py +105 -0
- infrahub/core/migrations/graph/m033_deduplicate_relationship_vertices.py +97 -0
- infrahub/core/migrations/graph/m034_find_orphaned_schema_fields.py +84 -0
- infrahub/core/migrations/schema/node_attribute_add.py +55 -2
- infrahub/core/migrations/shared.py +37 -9
- infrahub/core/node/__init__.py +44 -21
- infrahub/core/node/resource_manager/ip_address_pool.py +5 -3
- infrahub/core/node/resource_manager/ip_prefix_pool.py +7 -4
- infrahub/core/node/resource_manager/number_pool.py +62 -22
- infrahub/core/node/standard.py +4 -0
- infrahub/core/query/branch.py +25 -56
- infrahub/core/query/node.py +78 -24
- infrahub/core/query/relationship.py +11 -8
- infrahub/core/query/resource_manager.py +117 -20
- infrahub/core/relationship/model.py +10 -5
- infrahub/core/schema/__init__.py +5 -0
- infrahub/core/schema/attribute_parameters.py +6 -0
- infrahub/core/schema/attribute_schema.py +6 -0
- infrahub/core/schema/manager.py +5 -11
- infrahub/core/schema/relationship_schema.py +6 -0
- infrahub/core/schema/schema_branch.py +50 -11
- infrahub/core/validators/node/attribute.py +15 -0
- infrahub/core/validators/tasks.py +12 -4
- infrahub/dependencies/builder/diff/coordinator.py +3 -0
- infrahub/dependencies/builder/diff/locker.py +8 -0
- infrahub/graphql/mutations/main.py +7 -2
- infrahub/graphql/mutations/tasks.py +2 -0
- infrahub/graphql/queries/resource_manager.py +4 -4
- infrahub/tasks/registry.py +63 -35
- infrahub_sdk/client.py +7 -8
- infrahub_sdk/ctl/utils.py +3 -0
- infrahub_sdk/node/node.py +6 -6
- infrahub_sdk/node/relationship.py +43 -2
- infrahub_sdk/yaml.py +13 -7
- infrahub_server-1.3.4.dist-info/LICENSE.txt +201 -0
- {infrahub_server-1.3.2.dist-info → infrahub_server-1.3.4.dist-info}/METADATA +3 -3
- {infrahub_server-1.3.2.dist-info → infrahub_server-1.3.4.dist-info}/RECORD +58 -52
- infrahub_testcontainers/container.py +1 -1
- infrahub_testcontainers/docker-compose-cluster.test.yml +3 -0
- infrahub_testcontainers/docker-compose.test.yml +1 -0
- infrahub_server-1.3.2.dist-info/LICENSE.txt +0 -661
- {infrahub_server-1.3.2.dist-info → infrahub_server-1.3.4.dist-info}/WHEEL +0 -0
- {infrahub_server-1.3.2.dist-info → infrahub_server-1.3.4.dist-info}/entry_points.txt +0 -0
infrahub/core/node/__init__.py
CHANGED
|
@@ -26,6 +26,7 @@ from infrahub.core.protocols import CoreNumberPool, CoreObjectTemplate
|
|
|
26
26
|
from infrahub.core.query.node import NodeCheckIDQuery, NodeCreateAllQuery, NodeDeleteQuery, NodeGetListQuery
|
|
27
27
|
from infrahub.core.schema import (
|
|
28
28
|
AttributeSchema,
|
|
29
|
+
GenericSchema,
|
|
29
30
|
NodeSchema,
|
|
30
31
|
NonGenericSchemaTypes,
|
|
31
32
|
ProfileSchema,
|
|
@@ -82,6 +83,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
82
83
|
def get_schema(self) -> NonGenericSchemaTypes:
|
|
83
84
|
return self._schema
|
|
84
85
|
|
|
86
|
+
def get_branch(self) -> Branch:
|
|
87
|
+
return self._branch
|
|
88
|
+
|
|
85
89
|
def get_kind(self) -> str:
|
|
86
90
|
"""Return the main Kind of the Object."""
|
|
87
91
|
return self._schema.kind
|
|
@@ -260,11 +264,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
260
264
|
within the create code.
|
|
261
265
|
"""
|
|
262
266
|
|
|
263
|
-
number_pool_parameters: NumberPoolParameters | None = None
|
|
264
267
|
if attribute.schema.kind == "NumberPool" and isinstance(attribute.schema.parameters, NumberPoolParameters):
|
|
265
268
|
attribute.from_pool = {"id": attribute.schema.parameters.number_pool_id}
|
|
266
269
|
attribute.is_default = False
|
|
267
|
-
number_pool_parameters = attribute.schema.parameters
|
|
268
270
|
|
|
269
271
|
if not attribute.from_pool:
|
|
270
272
|
return
|
|
@@ -274,9 +276,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
274
276
|
db=db, id=attribute.from_pool["id"], kind=CoreNumberPool
|
|
275
277
|
)
|
|
276
278
|
except NodeNotFoundError:
|
|
277
|
-
if
|
|
278
|
-
number_pool = await self.
|
|
279
|
-
db=db,
|
|
279
|
+
if attribute.schema.kind == "NumberPool" and isinstance(attribute.schema.parameters, NumberPoolParameters):
|
|
280
|
+
number_pool = await self.fetch_or_create_number_pool(
|
|
281
|
+
db=db, schema_node=self._schema, schema_attribute=attribute.schema, branch=self._branch
|
|
280
282
|
)
|
|
281
283
|
|
|
282
284
|
else:
|
|
@@ -292,7 +294,9 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
292
294
|
and number_pool.node_attribute.value == attribute.name
|
|
293
295
|
):
|
|
294
296
|
try:
|
|
295
|
-
next_free = await number_pool.get_resource(
|
|
297
|
+
next_free = await number_pool.get_resource(
|
|
298
|
+
db=db, branch=self._branch, node=self, attribute=attribute.schema
|
|
299
|
+
)
|
|
296
300
|
except PoolExhaustedError:
|
|
297
301
|
errors.append(
|
|
298
302
|
ValidationError({f"{attribute.name}.from_pool": f"The pool {number_pool.node.value} is exhausted."})
|
|
@@ -310,10 +314,28 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
310
314
|
)
|
|
311
315
|
)
|
|
312
316
|
|
|
313
|
-
|
|
314
|
-
|
|
317
|
+
@staticmethod
|
|
318
|
+
async def fetch_or_create_number_pool(
|
|
319
|
+
db: InfrahubDatabase,
|
|
320
|
+
schema_node: NodeSchema | GenericSchema,
|
|
321
|
+
schema_attribute: AttributeSchema,
|
|
322
|
+
branch: Branch | None = None,
|
|
315
323
|
) -> CoreNumberPool:
|
|
324
|
+
"""Fetch or create a number pool based on the schema attribute parameters.
|
|
325
|
+
|
|
326
|
+
Warning, ideally this method should be outside of the Node class, but it is itself using the Node class to create the pool node.
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
if (
|
|
330
|
+
schema_attribute.kind != "NumberPool"
|
|
331
|
+
or not schema_attribute.parameters
|
|
332
|
+
or not isinstance(schema_attribute.parameters, NumberPoolParameters)
|
|
333
|
+
):
|
|
334
|
+
raise ValueError("Attribute is not of type NumberPool")
|
|
335
|
+
|
|
316
336
|
number_pool_from_db: CoreNumberPool | None = None
|
|
337
|
+
number_pool_parameters: NumberPoolParameters = schema_attribute.parameters
|
|
338
|
+
|
|
317
339
|
lock_definition = NumberPoolLockDefinition(pool_id=str(number_pool_parameters.number_pool_id))
|
|
318
340
|
async with lock.registry.get(
|
|
319
341
|
name=lock_definition.lock_name, namespace=lock_definition.namespace_name, local=False
|
|
@@ -322,37 +344,37 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
322
344
|
number_pool_from_db = await registry.manager.get_one_by_id_or_default_filter(
|
|
323
345
|
db=db, id=str(number_pool_parameters.number_pool_id), kind=CoreNumberPool
|
|
324
346
|
)
|
|
347
|
+
return number_pool_from_db # type: ignore[return-value]
|
|
348
|
+
|
|
325
349
|
except NodeNotFoundError:
|
|
326
350
|
schema = db.schema.get_node_schema(name="CoreNumberPool", duplicate=False)
|
|
327
351
|
|
|
328
|
-
pool_node =
|
|
329
|
-
schema_attribute = self._schema.get_attribute(attribute.schema.name)
|
|
352
|
+
pool_node = schema_node.kind
|
|
330
353
|
if schema_attribute.inherited:
|
|
331
|
-
for generic_name in
|
|
354
|
+
for generic_name in schema_node.inherit_from:
|
|
332
355
|
generic_node = db.schema.get_generic_schema(name=generic_name, duplicate=False)
|
|
333
|
-
if
|
|
356
|
+
if schema_attribute.name in generic_node.attribute_names:
|
|
334
357
|
pool_node = generic_node.kind
|
|
335
358
|
break
|
|
336
359
|
|
|
337
|
-
number_pool = await Node.init(db=db, schema=schema, branch=
|
|
360
|
+
number_pool = await Node.init(db=db, schema=schema, branch=branch)
|
|
338
361
|
await number_pool.new(
|
|
339
362
|
db=db,
|
|
340
363
|
id=number_pool_parameters.number_pool_id,
|
|
341
|
-
name=f"{pool_node}.{
|
|
364
|
+
name=f"{pool_node}.{schema_attribute.name} [{number_pool_parameters.number_pool_id}]",
|
|
342
365
|
node=pool_node,
|
|
343
|
-
node_attribute=
|
|
366
|
+
node_attribute=schema_attribute.name,
|
|
344
367
|
start_range=number_pool_parameters.start_range,
|
|
345
368
|
end_range=number_pool_parameters.end_range,
|
|
346
369
|
pool_type=NumberPoolType.SCHEMA.value,
|
|
347
370
|
)
|
|
348
371
|
await number_pool.save(db=db)
|
|
349
372
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return created_pool
|
|
373
|
+
# Do a lookup of the number pool to get the correct mapped type from the registry
|
|
374
|
+
# without this we don't get access to the .get_resource() method.
|
|
375
|
+
return await registry.manager.get_one_by_id_or_default_filter(
|
|
376
|
+
db=db, id=number_pool.id, kind=CoreNumberPool
|
|
377
|
+
)
|
|
356
378
|
|
|
357
379
|
async def handle_object_template(self, fields: dict, db: InfrahubDatabase, errors: list) -> None:
|
|
358
380
|
"""Fill the `fields` parameters with values from an object template if one is in use."""
|
|
@@ -816,6 +838,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta):
|
|
|
816
838
|
|
|
817
839
|
query = await NodeDeleteQuery.init(db=db, node=self, at=delete_at)
|
|
818
840
|
await query.execute(db=db)
|
|
841
|
+
|
|
819
842
|
self._node_changelog = node_changelog
|
|
820
843
|
|
|
821
844
|
async def to_graphql(
|
|
@@ -18,6 +18,7 @@ from .. import Node
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from infrahub.core.branch import Branch
|
|
20
20
|
from infrahub.core.ipam.constants import IPAddressType
|
|
21
|
+
from infrahub.core.timestamp import Timestamp
|
|
21
22
|
from infrahub.database import InfrahubDatabase
|
|
22
23
|
|
|
23
24
|
|
|
@@ -30,6 +31,7 @@ class CoreIPAddressPool(Node):
|
|
|
30
31
|
data: dict[str, Any] | None = None,
|
|
31
32
|
address_type: str | None = None,
|
|
32
33
|
prefixlen: int | None = None,
|
|
34
|
+
at: Timestamp | None = None,
|
|
33
35
|
) -> Node:
|
|
34
36
|
# Check if there is already a resource allocated with this identifier
|
|
35
37
|
# if not, pull all existing prefixes and allocated the next available
|
|
@@ -63,18 +65,18 @@ class CoreIPAddressPool(Node):
|
|
|
63
65
|
next_address = await self.get_next(db=db, prefixlen=prefixlen)
|
|
64
66
|
|
|
65
67
|
target_schema = registry.get_node_schema(name=address_type, branch=branch)
|
|
66
|
-
node = await Node.init(db=db, schema=target_schema, branch=branch)
|
|
68
|
+
node = await Node.init(db=db, schema=target_schema, branch=branch, at=at)
|
|
67
69
|
try:
|
|
68
70
|
await node.new(db=db, address=str(next_address), ip_namespace=ip_namespace, **data)
|
|
69
71
|
except ValidationError as exc:
|
|
70
72
|
raise ValueError(f"IPAddressPool: {self.name.value} | {exc!s}") from exc # type: ignore[attr-defined]
|
|
71
|
-
await node.save(db=db)
|
|
73
|
+
await node.save(db=db, at=at)
|
|
72
74
|
reconciler = IpamReconciler(db=db, branch=branch)
|
|
73
75
|
await reconciler.reconcile(ip_value=next_address, namespace=ip_namespace.id, node_uuid=node.get_id())
|
|
74
76
|
|
|
75
77
|
if identifier:
|
|
76
78
|
query_set = await IPAddressPoolSetReserved.init(
|
|
77
|
-
db=db, pool_id=self.id, identifier=identifier, address_id=node.id
|
|
79
|
+
db=db, pool_id=self.id, identifier=identifier, address_id=node.id, at=at
|
|
78
80
|
)
|
|
79
81
|
await query_set.execute(db=db)
|
|
80
82
|
|
|
@@ -20,6 +20,7 @@ from .. import Node
|
|
|
20
20
|
if TYPE_CHECKING:
|
|
21
21
|
from infrahub.core.branch import Branch
|
|
22
22
|
from infrahub.core.ipam.constants import IPNetworkType
|
|
23
|
+
from infrahub.core.timestamp import Timestamp
|
|
23
24
|
from infrahub.database import InfrahubDatabase
|
|
24
25
|
|
|
25
26
|
|
|
@@ -33,6 +34,7 @@ class CoreIPPrefixPool(Node):
|
|
|
33
34
|
prefixlen: int | None = None,
|
|
34
35
|
member_type: str | None = None,
|
|
35
36
|
prefix_type: str | None = None,
|
|
37
|
+
at: Timestamp | None = None,
|
|
36
38
|
) -> Node:
|
|
37
39
|
# Check if there is already a resource allocated with this identifier
|
|
38
40
|
# if not, pull all existing prefixes and allocated the next available
|
|
@@ -68,20 +70,21 @@ class CoreIPPrefixPool(Node):
|
|
|
68
70
|
)
|
|
69
71
|
|
|
70
72
|
member_type = member_type or data.get("member_type", None) or self.default_member_type.value.value # type: ignore[attr-defined]
|
|
73
|
+
data["member_type"] = member_type
|
|
71
74
|
|
|
72
75
|
target_schema = registry.get_node_schema(name=prefix_type, branch=branch)
|
|
73
|
-
node = await Node.init(db=db, schema=target_schema, branch=branch)
|
|
76
|
+
node = await Node.init(db=db, schema=target_schema, branch=branch, at=at)
|
|
74
77
|
try:
|
|
75
|
-
await node.new(db=db, prefix=str(next_prefix),
|
|
78
|
+
await node.new(db=db, prefix=str(next_prefix), ip_namespace=ip_namespace, **data)
|
|
76
79
|
except ValidationError as exc:
|
|
77
80
|
raise ValueError(f"IPPrefixPool: {self.name.value} | {exc!s}") from exc # type: ignore[attr-defined]
|
|
78
|
-
await node.save(db=db)
|
|
81
|
+
await node.save(db=db, at=at)
|
|
79
82
|
reconciler = IpamReconciler(db=db, branch=branch)
|
|
80
83
|
await reconciler.reconcile(ip_value=next_prefix, namespace=ip_namespace.id, node_uuid=node.get_id())
|
|
81
84
|
|
|
82
85
|
if identifier:
|
|
83
86
|
query_set = await PrefixPoolSetReserved.init(
|
|
84
|
-
db=db, pool_id=self.id, identifier=identifier, prefix_id=node.id
|
|
87
|
+
db=db, pool_id=self.id, identifier=identifier, prefix_id=node.id, at=at
|
|
85
88
|
)
|
|
86
89
|
await query_set.execute(db=db)
|
|
87
90
|
|
|
@@ -10,16 +10,15 @@ from infrahub.exceptions import PoolExhaustedError
|
|
|
10
10
|
from .. import Node
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
|
-
from infrahub.core.attribute import BaseAttribute
|
|
14
13
|
from infrahub.core.branch import Branch
|
|
14
|
+
from infrahub.core.schema import AttributeSchema
|
|
15
|
+
from infrahub.core.timestamp import Timestamp
|
|
15
16
|
from infrahub.database import InfrahubDatabase
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class CoreNumberPool(Node):
|
|
19
20
|
def get_attribute_nb_excluded_values(self) -> int:
|
|
20
|
-
"""
|
|
21
|
-
Returns the number of excluded values for the attribute of the number pool.
|
|
22
|
-
"""
|
|
21
|
+
"""Returns the number of excluded values for the attribute of the number pool."""
|
|
23
22
|
|
|
24
23
|
pool_node = registry.schema.get(name=self.node.value) # type: ignore [attr-defined]
|
|
25
24
|
attribute = [attribute for attribute in pool_node.attributes if attribute.name == self.node_attribute.value][0] # type: ignore [attr-defined]
|
|
@@ -34,17 +33,42 @@ class CoreNumberPool(Node):
|
|
|
34
33
|
res = len(attribute.parameters.get_excluded_single_values()) + sum_excluded_values
|
|
35
34
|
return res
|
|
36
35
|
|
|
36
|
+
async def get_used(
|
|
37
|
+
self,
|
|
38
|
+
db: InfrahubDatabase,
|
|
39
|
+
branch: Branch,
|
|
40
|
+
) -> list[int]:
|
|
41
|
+
"""Returns a list of used numbers in the pool."""
|
|
42
|
+
|
|
43
|
+
query = await NumberPoolGetUsed.init(db=db, branch=branch, pool=self, branch_agnostic=True)
|
|
44
|
+
await query.execute(db=db)
|
|
45
|
+
used = [result.value for result in query.iter_results()]
|
|
46
|
+
return [item for item in used if item is not None]
|
|
47
|
+
|
|
48
|
+
async def reserve(self, db: InfrahubDatabase, number: int, identifier: str, at: Timestamp | None = None) -> None:
|
|
49
|
+
"""Reserve a number in the pool for a specific identifier."""
|
|
50
|
+
|
|
51
|
+
query = await NumberPoolSetReserved.init(
|
|
52
|
+
db=db, pool_id=self.get_id(), identifier=identifier, reserved=number, at=at
|
|
53
|
+
)
|
|
54
|
+
await query.execute(db=db)
|
|
55
|
+
|
|
37
56
|
async def get_resource(
|
|
38
57
|
self,
|
|
39
58
|
db: InfrahubDatabase,
|
|
40
59
|
branch: Branch,
|
|
41
60
|
node: Node,
|
|
42
|
-
attribute:
|
|
61
|
+
attribute: AttributeSchema,
|
|
43
62
|
identifier: str | None = None,
|
|
63
|
+
at: Timestamp | None = None,
|
|
44
64
|
) -> int:
|
|
65
|
+
# NOTE: ideally we should use the HFID as the identifier (if available)
|
|
66
|
+
# one of the challenge with using the HFID is that it might change over time
|
|
67
|
+
# so we need to ensure that the identifier is stable, or we need to handle the case where the identifier changes
|
|
45
68
|
identifier = identifier or node.get_id()
|
|
69
|
+
|
|
46
70
|
# Check if there is already a resource allocated with this identifier
|
|
47
|
-
# if not, pull all existing
|
|
71
|
+
# if not, pull all existing number and allocate the next available
|
|
48
72
|
# TODO add support for branch, if the node is reserved with this id in another branch we should return an error
|
|
49
73
|
query_get = await NumberPoolGetReserved.init(db=db, branch=branch, pool_id=self.id, identifier=identifier)
|
|
50
74
|
await query_get.execute(db=db)
|
|
@@ -54,35 +78,51 @@ class CoreNumberPool(Node):
|
|
|
54
78
|
|
|
55
79
|
# If we have not returned a value we need to find one if avaiable
|
|
56
80
|
number = await self.get_next(db=db, branch=branch, attribute=attribute)
|
|
57
|
-
|
|
58
|
-
query_set = await NumberPoolSetReserved.init(
|
|
59
|
-
db=db, pool_id=self.get_id(), identifier=identifier, reserved=number
|
|
60
|
-
)
|
|
61
|
-
await query_set.execute(db=db)
|
|
81
|
+
await self.reserve(db=db, number=number, identifier=identifier, at=at)
|
|
62
82
|
return number
|
|
63
83
|
|
|
64
|
-
async def get_next(self, db: InfrahubDatabase, branch: Branch, attribute:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
taken = [result.get_as_optional_type("av.value", return_type=int) for result in query.results]
|
|
68
|
-
parameters = attribute.schema.parameters
|
|
84
|
+
async def get_next(self, db: InfrahubDatabase, branch: Branch, attribute: AttributeSchema) -> int:
|
|
85
|
+
taken = await self.get_used(db=db, branch=branch)
|
|
86
|
+
|
|
69
87
|
next_number = find_next_free(
|
|
70
88
|
start=self.start_range.value, # type: ignore[attr-defined]
|
|
71
89
|
end=self.end_range.value, # type: ignore[attr-defined]
|
|
72
90
|
taken=taken,
|
|
73
|
-
parameters=parameters if isinstance(parameters, NumberAttributeParameters) else None,
|
|
91
|
+
parameters=attribute.parameters if isinstance(attribute.parameters, NumberAttributeParameters) else None,
|
|
74
92
|
)
|
|
75
93
|
if next_number is None:
|
|
76
94
|
raise PoolExhaustedError("There are no more values available in this pool.")
|
|
77
95
|
|
|
78
96
|
return next_number
|
|
79
97
|
|
|
98
|
+
async def get_next_many(
|
|
99
|
+
self, db: InfrahubDatabase, quantity: int, branch: Branch, attribute: AttributeSchema
|
|
100
|
+
) -> list[int]:
|
|
101
|
+
taken = await self.get_used(db=db, branch=branch)
|
|
102
|
+
|
|
103
|
+
allocated: list[int] = []
|
|
104
|
+
|
|
105
|
+
for _ in range(quantity):
|
|
106
|
+
next_number = find_next_free(
|
|
107
|
+
start=self.start_range.value, # type: ignore[attr-defined]
|
|
108
|
+
end=self.end_range.value, # type: ignore[attr-defined]
|
|
109
|
+
taken=list(set(taken) | set(allocated)),
|
|
110
|
+
parameters=attribute.parameters
|
|
111
|
+
if isinstance(attribute.parameters, NumberAttributeParameters)
|
|
112
|
+
else None,
|
|
113
|
+
)
|
|
114
|
+
if next_number is None:
|
|
115
|
+
raise PoolExhaustedError(
|
|
116
|
+
f"There are no more values available in this pool, couldn't allocate {quantity} values, only {len(allocated)} available."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
allocated.append(next_number)
|
|
120
|
+
|
|
121
|
+
return allocated
|
|
122
|
+
|
|
80
123
|
|
|
81
|
-
def find_next_free(
|
|
82
|
-
|
|
83
|
-
) -> int | None:
|
|
84
|
-
used_numbers = [number for number in taken if number is not None]
|
|
85
|
-
used_set = set(used_numbers)
|
|
124
|
+
def find_next_free(start: int, end: int, taken: list[int], parameters: NumberAttributeParameters | None) -> int | None:
|
|
125
|
+
used_set = set(taken)
|
|
86
126
|
|
|
87
127
|
for num in range(start, end + 1):
|
|
88
128
|
if num not in used_set:
|
infrahub/core/node/standard.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from typing import TYPE_CHECKING, Any, Optional, Union, get_args, get_origin
|
|
5
6
|
from uuid import UUID
|
|
6
7
|
|
|
@@ -191,6 +192,9 @@ class StandardNode(BaseModel):
|
|
|
191
192
|
continue
|
|
192
193
|
|
|
193
194
|
attr_value = getattr(self, attr_name)
|
|
195
|
+
if isinstance(attr_value, Enum):
|
|
196
|
+
attr_value = attr_value.value
|
|
197
|
+
|
|
194
198
|
field_type = self.guess_field_type(field)
|
|
195
199
|
|
|
196
200
|
if attr_value is None:
|
infrahub/core/query/branch.py
CHANGED
|
@@ -3,43 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
from infrahub import config
|
|
6
|
-
from infrahub.core.constants import RelationshipStatus
|
|
7
6
|
from infrahub.core.query import Query, QueryType
|
|
8
7
|
|
|
9
8
|
if TYPE_CHECKING:
|
|
10
9
|
from infrahub.database import InfrahubDatabase
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
class AddNodeToBranch(Query):
|
|
14
|
-
name: str = "node_add_to_branch"
|
|
15
|
-
insert_return: bool = False
|
|
16
|
-
|
|
17
|
-
type: QueryType = QueryType.WRITE
|
|
18
|
-
|
|
19
|
-
def __init__(self, node_id: int, **kwargs: Any):
|
|
20
|
-
self.node_id = node_id
|
|
21
|
-
super().__init__(**kwargs)
|
|
22
|
-
|
|
23
|
-
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
24
|
-
query = """
|
|
25
|
-
MATCH (root:Root)
|
|
26
|
-
MATCH (d) WHERE %(id_func)s(d) = $node_id
|
|
27
|
-
WITH root,d
|
|
28
|
-
CREATE (d)-[r:IS_PART_OF { branch: $branch, branch_level: $branch_level, from: $now, status: $status }]->(root)
|
|
29
|
-
RETURN %(id_func)s(r)
|
|
30
|
-
""" % {
|
|
31
|
-
"id_func": db.get_id_function_name(),
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
self.params["node_id"] = db.to_database_id(self.node_id)
|
|
35
|
-
self.params["now"] = self.at.to_string()
|
|
36
|
-
self.params["branch"] = self.branch.name
|
|
37
|
-
self.params["branch_level"] = self.branch.hierarchy_level
|
|
38
|
-
self.params["status"] = RelationshipStatus.ACTIVE.value
|
|
39
|
-
|
|
40
|
-
self.add_to_query(query)
|
|
41
|
-
|
|
42
|
-
|
|
43
12
|
class DeleteBranchRelationshipsQuery(Query):
|
|
44
13
|
name: str = "delete_branch_relationships"
|
|
45
14
|
insert_return: bool = False
|
|
@@ -52,31 +21,31 @@ class DeleteBranchRelationshipsQuery(Query):
|
|
|
52
21
|
|
|
53
22
|
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
54
23
|
query = """
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
24
|
+
// delete all relationships on this branch
|
|
25
|
+
MATCH (s)-[r1]-(d)
|
|
26
|
+
WHERE r1.branch = $branch_name
|
|
27
|
+
CALL (r1) {
|
|
28
|
+
DELETE r1
|
|
29
|
+
} IN TRANSACTIONS
|
|
30
|
+
|
|
31
|
+
// check for any orphaned Node vertices and delete them
|
|
32
|
+
WITH collect(DISTINCT s.uuid) + collect(DISTINCT d.uuid) AS nodes_uuids
|
|
33
|
+
MATCH (s2:Node)-[r2]-(d2)
|
|
34
|
+
WHERE NOT exists((s2)-[:IS_PART_OF]-(:Root))
|
|
35
|
+
AND s2.uuid IN nodes_uuids
|
|
36
|
+
CALL (r2) {
|
|
37
|
+
DELETE r2
|
|
38
|
+
} IN TRANSACTIONS
|
|
39
|
+
|
|
40
|
+
// reduce results to a single row
|
|
41
|
+
WITH 1 AS one LIMIT 1
|
|
42
|
+
|
|
43
|
+
// find any orphaned vertices and delete them
|
|
44
|
+
MATCH (n)
|
|
45
|
+
WHERE NOT exists((n)--())
|
|
46
|
+
CALL (n) {
|
|
47
|
+
DELETE n
|
|
48
|
+
} IN TRANSACTIONS
|
|
80
49
|
"""
|
|
81
50
|
self.params["branch_name"] = self.branch_name
|
|
82
51
|
self.add_to_query(query)
|