infrahub-server 1.3.0a0__py3-none-any.whl → 1.3.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrahub/actions/tasks.py +4 -11
- infrahub/branch/__init__.py +0 -0
- infrahub/branch/tasks.py +29 -0
- infrahub/branch/triggers.py +22 -0
- infrahub/cli/db.py +2 -2
- infrahub/computed_attribute/gather.py +3 -1
- infrahub/computed_attribute/tasks.py +23 -29
- infrahub/core/attribute.py +3 -3
- infrahub/core/constants/__init__.py +10 -0
- infrahub/core/constants/database.py +1 -0
- infrahub/core/constants/infrahubkind.py +2 -0
- infrahub/core/convert_object_type/conversion.py +1 -1
- infrahub/core/diff/query/save.py +67 -40
- infrahub/core/diff/query/time_range_query.py +0 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/migrations/graph/__init__.py +6 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +0 -2
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +662 -0
- infrahub/core/migrations/graph/m030_illegal_edges.py +82 -0
- infrahub/core/migrations/query/attribute_add.py +13 -9
- infrahub/core/migrations/query/attribute_rename.py +2 -4
- infrahub/core/migrations/query/delete_element_in_schema.py +16 -11
- infrahub/core/migrations/query/node_duplicate.py +16 -15
- infrahub/core/migrations/query/relationship_duplicate.py +16 -12
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -2
- infrahub/core/migrations/schema/node_remove.py +16 -14
- infrahub/core/node/__init__.py +74 -14
- infrahub/core/node/base.py +1 -1
- infrahub/core/node/resource_manager/ip_address_pool.py +6 -2
- infrahub/core/node/resource_manager/ip_prefix_pool.py +6 -2
- infrahub/core/node/resource_manager/number_pool.py +31 -5
- infrahub/core/node/standard.py +6 -1
- infrahub/core/path.py +1 -1
- infrahub/core/protocols.py +10 -0
- infrahub/core/query/node.py +1 -1
- infrahub/core/query/relationship.py +4 -6
- infrahub/core/query/standard_node.py +19 -5
- infrahub/core/relationship/constraints/peer_relatives.py +72 -0
- infrahub/core/relationship/model.py +1 -1
- infrahub/core/schema/attribute_parameters.py +129 -5
- infrahub/core/schema/attribute_schema.py +62 -14
- infrahub/core/schema/basenode_schema.py +2 -2
- infrahub/core/schema/definitions/core/__init__.py +16 -2
- infrahub/core/schema/definitions/core/group.py +45 -0
- infrahub/core/schema/definitions/core/resource_pool.py +29 -0
- infrahub/core/schema/definitions/internal.py +25 -4
- infrahub/core/schema/generated/attribute_schema.py +12 -5
- infrahub/core/schema/generated/relationship_schema.py +6 -1
- infrahub/core/schema/manager.py +7 -2
- infrahub/core/schema/schema_branch.py +69 -5
- infrahub/core/validators/__init__.py +8 -0
- infrahub/core/validators/attribute/choices.py +0 -1
- infrahub/core/validators/attribute/enum.py +0 -1
- infrahub/core/validators/attribute/kind.py +0 -1
- infrahub/core/validators/attribute/length.py +0 -1
- infrahub/core/validators/attribute/min_max.py +118 -0
- infrahub/core/validators/attribute/number_pool.py +106 -0
- infrahub/core/validators/attribute/optional.py +0 -2
- infrahub/core/validators/attribute/regex.py +0 -1
- infrahub/core/validators/enum.py +5 -0
- infrahub/core/validators/tasks.py +1 -1
- infrahub/database/__init__.py +16 -4
- infrahub/database/validation.py +100 -0
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/peer_relatives.py +8 -0
- infrahub/dependencies/builder/diff/deserializer.py +1 -1
- infrahub/dependencies/registry.py +2 -0
- infrahub/events/models.py +1 -1
- infrahub/git/base.py +5 -3
- infrahub/git/integrator.py +102 -3
- infrahub/graphql/mutations/main.py +1 -1
- infrahub/graphql/mutations/resource_manager.py +54 -6
- infrahub/graphql/queries/resource_manager.py +7 -1
- infrahub/graphql/queries/task.py +10 -0
- infrahub/graphql/resolvers/many_relationship.py +1 -1
- infrahub/graphql/resolvers/resolver.py +2 -2
- infrahub/graphql/resolvers/single_relationship.py +1 -1
- infrahub/graphql/types/task_log.py +3 -2
- infrahub/menu/menu.py +8 -7
- infrahub/message_bus/operations/refresh/registry.py +3 -3
- infrahub/patch/queries/delete_duplicated_edges.py +40 -29
- infrahub/pools/number.py +5 -3
- infrahub/pools/registration.py +22 -0
- infrahub/pools/tasks.py +56 -0
- infrahub/schema/__init__.py +0 -0
- infrahub/schema/tasks.py +27 -0
- infrahub/schema/triggers.py +23 -0
- infrahub/task_manager/task.py +44 -4
- infrahub/trigger/catalogue.py +4 -0
- infrahub/trigger/models.py +5 -4
- infrahub/trigger/setup.py +26 -2
- infrahub/trigger/tasks.py +1 -1
- infrahub/types.py +6 -0
- infrahub/webhook/tasks.py +6 -9
- infrahub/workflows/catalogue.py +27 -1
- 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.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/METADATA +3 -3
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/RECORD +122 -100
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/WHEEL +1 -1
- infrahub_testcontainers/container.py +239 -64
- infrahub_testcontainers/docker-compose-cluster.test.yml +321 -0
- infrahub_testcontainers/docker-compose.test.yml +1 -0
- infrahub_testcontainers/helpers.py +15 -1
- infrahub_testcontainers/plugin.py +9 -0
- infrahub/patch/queries/consolidate_duplicated_nodes.py +0 -106
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.3.0a0.dist-info → infrahub_server-1.3.0b2.dist-info}/entry_points.txt +0 -0
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Self
|
|
4
5
|
|
|
6
|
+
from pydantic import ConfigDict, Field, model_validator
|
|
7
|
+
|
|
8
|
+
from infrahub import config
|
|
5
9
|
from infrahub.core.constants.schema import UpdateSupport
|
|
6
10
|
from infrahub.core.models import HashableModel
|
|
7
11
|
|
|
8
12
|
|
|
9
13
|
def get_attribute_parameters_class_for_kind(kind: str) -> type[AttributeParameters]:
|
|
10
|
-
|
|
14
|
+
param_classes: dict[str, type[AttributeParameters]] = {
|
|
15
|
+
"NumberPool": NumberPoolParameters,
|
|
11
16
|
"Text": TextAttributeParameters,
|
|
12
17
|
"TextArea": TextAttributeParameters,
|
|
13
|
-
|
|
18
|
+
"Number": NumberAttributeParameters,
|
|
19
|
+
}
|
|
20
|
+
return param_classes.get(kind, AttributeParameters)
|
|
14
21
|
|
|
15
22
|
|
|
16
23
|
class AttributeParameters(HashableModel):
|
|
17
|
-
|
|
18
|
-
extra = "forbid"
|
|
24
|
+
model_config = ConfigDict(extra="forbid")
|
|
19
25
|
|
|
20
26
|
|
|
21
27
|
class TextAttributeParameters(AttributeParameters):
|
|
@@ -34,3 +40,121 @@ class TextAttributeParameters(AttributeParameters):
|
|
|
34
40
|
description="Set a maximum number of characters allowed.",
|
|
35
41
|
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
36
42
|
)
|
|
43
|
+
|
|
44
|
+
@model_validator(mode="after")
|
|
45
|
+
def validate_min_max(self) -> Self:
|
|
46
|
+
if (
|
|
47
|
+
config.SETTINGS.initialized
|
|
48
|
+
and config.SETTINGS.main.schema_strict_mode
|
|
49
|
+
and self.min_length is not None
|
|
50
|
+
and self.max_length is not None
|
|
51
|
+
):
|
|
52
|
+
if self.min_length > self.max_length:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"`max_length` can't be less than `min_length` when the schema is configured with strict mode"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class NumberAttributeParameters(AttributeParameters):
|
|
61
|
+
min_value: int | None = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="Set a minimum value allowed.",
|
|
64
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
65
|
+
)
|
|
66
|
+
max_value: int | None = Field(
|
|
67
|
+
default=None,
|
|
68
|
+
description="Set a maximum value allowed.",
|
|
69
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
70
|
+
)
|
|
71
|
+
excluded_values: str | None = Field(
|
|
72
|
+
default=None,
|
|
73
|
+
description="List of values or range of values not allowed for the attribute, format is: '100,150-200,280,300-400'",
|
|
74
|
+
pattern=r"^(\d+(?:-\d+)?)(?:,\d+(?:-\d+)?)*$",
|
|
75
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@model_validator(mode="after")
|
|
79
|
+
def validate_ranges(self) -> Self:
|
|
80
|
+
ranges = self.get_excluded_ranges()
|
|
81
|
+
for i, (start_range_1, end_range_1) in enumerate(ranges):
|
|
82
|
+
if start_range_1 > end_range_1:
|
|
83
|
+
raise ValueError("`start_range` can't be less than `end_range`")
|
|
84
|
+
|
|
85
|
+
# Check for overlapping ranges
|
|
86
|
+
for start_range_2, end_range_2 in ranges[i + 1 :]:
|
|
87
|
+
if not (end_range_1 < start_range_2 or start_range_1 > end_range_2):
|
|
88
|
+
raise ValueError("Excluded ranges cannot overlap")
|
|
89
|
+
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
@model_validator(mode="after")
|
|
93
|
+
def validate_min_max(self) -> Self:
|
|
94
|
+
if (
|
|
95
|
+
config.SETTINGS.initialized
|
|
96
|
+
and config.SETTINGS.main.schema_strict_mode
|
|
97
|
+
and self.min_value is not None
|
|
98
|
+
and self.max_value is not None
|
|
99
|
+
):
|
|
100
|
+
if self.min_value > self.max_value:
|
|
101
|
+
raise ValueError(
|
|
102
|
+
"`max_value` can't be less than `min_value` when the schema is configured with strict mode"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
def get_excluded_single_values(self) -> list[int]:
|
|
108
|
+
if not self.excluded_values:
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
results = [int(value) for value in self.excluded_values.split(",") if "-" not in value]
|
|
112
|
+
return results
|
|
113
|
+
|
|
114
|
+
def get_excluded_ranges(self) -> list[tuple[int, int]]:
|
|
115
|
+
if not self.excluded_values:
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
ranges = []
|
|
119
|
+
for value in self.excluded_values.split(","):
|
|
120
|
+
if "-" in value:
|
|
121
|
+
start, end = map(int, value.split("-"))
|
|
122
|
+
ranges.append((start, end))
|
|
123
|
+
|
|
124
|
+
return ranges
|
|
125
|
+
|
|
126
|
+
def is_valid_value(self, value: int) -> bool:
|
|
127
|
+
if self.min_value is not None and value < self.min_value:
|
|
128
|
+
return False
|
|
129
|
+
if self.max_value is not None and value > self.max_value:
|
|
130
|
+
return False
|
|
131
|
+
if value in self.get_excluded_single_values():
|
|
132
|
+
return False
|
|
133
|
+
for start, end in self.get_excluded_ranges():
|
|
134
|
+
if start <= value <= end:
|
|
135
|
+
return False
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class NumberPoolParameters(AttributeParameters):
|
|
140
|
+
end_range: int = Field(
|
|
141
|
+
default=sys.maxsize,
|
|
142
|
+
description="End range for numbers for the associated NumberPool",
|
|
143
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
144
|
+
)
|
|
145
|
+
start_range: int = Field(
|
|
146
|
+
default=1,
|
|
147
|
+
description="Start range for numbers for the associated NumberPool",
|
|
148
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
149
|
+
)
|
|
150
|
+
number_pool_id: str | None = Field(
|
|
151
|
+
default=None,
|
|
152
|
+
description="The ID of the numberpool associated with this attribute",
|
|
153
|
+
json_schema_extra={"update": UpdateSupport.NOT_SUPPORTED.value},
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
@model_validator(mode="after")
|
|
157
|
+
def validate_ranges(self) -> Self:
|
|
158
|
+
if self.start_range > self.end_range:
|
|
159
|
+
raise ValueError("`start_range` can't be less than `end_range`")
|
|
160
|
+
return self
|
|
@@ -12,7 +12,13 @@ from infrahub.core.enums import generate_python_enum
|
|
|
12
12
|
from infrahub.core.query.attribute import default_attribute_query_filter
|
|
13
13
|
from infrahub.types import ATTRIBUTE_KIND_LABELS, ATTRIBUTE_TYPES
|
|
14
14
|
|
|
15
|
-
from .attribute_parameters import
|
|
15
|
+
from .attribute_parameters import (
|
|
16
|
+
AttributeParameters,
|
|
17
|
+
NumberAttributeParameters,
|
|
18
|
+
NumberPoolParameters,
|
|
19
|
+
TextAttributeParameters,
|
|
20
|
+
get_attribute_parameters_class_for_kind,
|
|
21
|
+
)
|
|
16
22
|
from .generated.attribute_schema import GeneratedAttributeSchema
|
|
17
23
|
|
|
18
24
|
if TYPE_CHECKING:
|
|
@@ -24,10 +30,6 @@ if TYPE_CHECKING:
|
|
|
24
30
|
|
|
25
31
|
|
|
26
32
|
def get_attribute_schema_class_for_kind(kind: str) -> type[AttributeSchema]:
|
|
27
|
-
attribute_schema_class_by_kind: dict[str, type[AttributeSchema]] = {
|
|
28
|
-
"Text": TextAttributeSchema,
|
|
29
|
-
"TextArea": TextAttributeSchema,
|
|
30
|
-
}
|
|
31
33
|
return attribute_schema_class_by_kind.get(kind, AttributeSchema)
|
|
32
34
|
|
|
33
35
|
|
|
@@ -35,6 +37,24 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
35
37
|
_sort_by: list[str] = ["name"]
|
|
36
38
|
_enum_class: type[enum.Enum] | None = None
|
|
37
39
|
|
|
40
|
+
@classmethod
|
|
41
|
+
def model_json_schema(cls, *args: Any, **kwargs: Any) -> dict[str, Any]:
|
|
42
|
+
schema = super().model_json_schema(*args, **kwargs)
|
|
43
|
+
|
|
44
|
+
# Build conditional schema based on attribute_schema_class_by_kind mapping
|
|
45
|
+
# This override allows people using the Yaml language server to get the correct mappings
|
|
46
|
+
# for the parameters when selecting the appropriate kind
|
|
47
|
+
schema["allOf"] = []
|
|
48
|
+
for kind, schema_class in attribute_schema_class_by_kind.items():
|
|
49
|
+
schema["allOf"].append(
|
|
50
|
+
{
|
|
51
|
+
"if": {"properties": {"kind": {"const": kind}}},
|
|
52
|
+
"then": {"properties": {"parameters": {"$ref": f"#/definitions/{schema_class.__name__}"}}},
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return schema
|
|
57
|
+
|
|
38
58
|
@property
|
|
39
59
|
def is_attribute(self) -> bool:
|
|
40
60
|
return True
|
|
@@ -93,6 +113,16 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
93
113
|
return expected_parameters_class(**value.model_dump())
|
|
94
114
|
return value
|
|
95
115
|
|
|
116
|
+
@model_validator(mode="after")
|
|
117
|
+
def validate_parameters(self) -> Self:
|
|
118
|
+
if isinstance(self.parameters, NumberPoolParameters) and not self.kind == "NumberPool":
|
|
119
|
+
raise ValueError(f"NumberPoolParameters can't be used as parameters for {self.kind}")
|
|
120
|
+
|
|
121
|
+
if isinstance(self.parameters, TextAttributeParameters) and self.kind not in ["Text", "TextArea"]:
|
|
122
|
+
raise ValueError(f"TextAttributeParameters can't be used as parameters for {self.kind}")
|
|
123
|
+
|
|
124
|
+
return self
|
|
125
|
+
|
|
96
126
|
def get_class(self) -> type[BaseAttribute]:
|
|
97
127
|
return ATTRIBUTE_TYPES[self.kind].get_infrahub_class()
|
|
98
128
|
|
|
@@ -185,6 +215,14 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
185
215
|
)
|
|
186
216
|
|
|
187
217
|
|
|
218
|
+
class NumberPoolSchema(AttributeSchema):
|
|
219
|
+
parameters: NumberPoolParameters = Field(
|
|
220
|
+
default_factory=NumberPoolParameters,
|
|
221
|
+
description="Extra parameters specific to NumberPool attributes",
|
|
222
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
188
226
|
class TextAttributeSchema(AttributeSchema):
|
|
189
227
|
parameters: TextAttributeParameters = Field(
|
|
190
228
|
default_factory=TextAttributeParameters,
|
|
@@ -195,19 +233,13 @@ class TextAttributeSchema(AttributeSchema):
|
|
|
195
233
|
@model_validator(mode="after")
|
|
196
234
|
def reconcile_parameters(self) -> Self:
|
|
197
235
|
if self.regex != self.parameters.regex:
|
|
198
|
-
final_regex = self.parameters.regex
|
|
199
|
-
if not final_regex: # falsy parameters.regex override falsy regex
|
|
200
|
-
final_regex = self.parameters.regex
|
|
236
|
+
final_regex = self.parameters.regex if self.parameters.regex is not None else self.regex
|
|
201
237
|
self.regex = self.parameters.regex = final_regex
|
|
202
238
|
if self.min_length != self.parameters.min_length:
|
|
203
|
-
final_min_length = self.parameters.min_length
|
|
204
|
-
if not final_min_length: # falsy parameters.min_length override falsy min_length
|
|
205
|
-
final_min_length = self.parameters.min_length
|
|
239
|
+
final_min_length = self.parameters.min_length if self.parameters.min_length is not None else self.min_length
|
|
206
240
|
self.min_length = self.parameters.min_length = final_min_length
|
|
207
241
|
if self.max_length != self.parameters.max_length:
|
|
208
|
-
final_max_length = self.parameters.max_length
|
|
209
|
-
if not final_max_length: # falsy parameters.max_length override falsy max_length
|
|
210
|
-
final_max_length = self.parameters.max_length
|
|
242
|
+
final_max_length = self.parameters.max_length if self.parameters.max_length is not None else self.max_length
|
|
211
243
|
self.max_length = self.parameters.max_length = final_max_length
|
|
212
244
|
return self
|
|
213
245
|
|
|
@@ -219,3 +251,19 @@ class TextAttributeSchema(AttributeSchema):
|
|
|
219
251
|
|
|
220
252
|
def get_max_length(self) -> int | None:
|
|
221
253
|
return self.parameters.max_length
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class NumberAttributeSchema(AttributeSchema):
|
|
257
|
+
parameters: NumberAttributeParameters = Field(
|
|
258
|
+
default_factory=NumberAttributeParameters,
|
|
259
|
+
description="Extra parameters specific to number attributes",
|
|
260
|
+
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
attribute_schema_class_by_kind: dict[str, type[AttributeSchema]] = {
|
|
265
|
+
"NumberPool": NumberPoolSchema,
|
|
266
|
+
"Text": TextAttributeSchema,
|
|
267
|
+
"TextArea": TextAttributeSchema,
|
|
268
|
+
"Number": NumberAttributeSchema,
|
|
269
|
+
}
|
|
@@ -386,7 +386,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
386
386
|
if not self.display_labels:
|
|
387
387
|
return None
|
|
388
388
|
|
|
389
|
-
fields: dict[str, str |
|
|
389
|
+
fields: dict[str, str | dict[str, None] | None] = {}
|
|
390
390
|
for item in self.display_labels:
|
|
391
391
|
fields.update(self.convert_path_to_graphql_fields(path=item))
|
|
392
392
|
return fields
|
|
@@ -401,7 +401,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
401
401
|
if not self.human_friendly_id:
|
|
402
402
|
return None
|
|
403
403
|
|
|
404
|
-
fields: dict[str, str |
|
|
404
|
+
fields: dict[str, str | dict[str, None] | None] = {}
|
|
405
405
|
for item in self.human_friendly_id:
|
|
406
406
|
fields.update(self.convert_path_to_graphql_fields(path=item))
|
|
407
407
|
return fields
|
|
@@ -28,7 +28,13 @@ from .check import core_check_definition
|
|
|
28
28
|
from .core import core_node, core_task_target
|
|
29
29
|
from .generator import core_generator_definition, core_generator_instance
|
|
30
30
|
from .graphql_query import core_graphql_query
|
|
31
|
-
from .group import
|
|
31
|
+
from .group import (
|
|
32
|
+
core_generator_group,
|
|
33
|
+
core_graphql_query_group,
|
|
34
|
+
core_group,
|
|
35
|
+
core_repository_group,
|
|
36
|
+
core_standard_group,
|
|
37
|
+
)
|
|
32
38
|
from .ipam import builtin_ip_address, builtin_ip_prefix, builtin_ipam, core_ipam_namespace
|
|
33
39
|
from .lineage import lineage_owner, lineage_source
|
|
34
40
|
from .menu import generic_menu_item, menu_item
|
|
@@ -68,7 +74,13 @@ from .propose_change_validator import (
|
|
|
68
74
|
core_user_validator,
|
|
69
75
|
)
|
|
70
76
|
from .repository import core_generic_repository, core_read_only_repository, core_repository
|
|
71
|
-
from .resource_pool import
|
|
77
|
+
from .resource_pool import (
|
|
78
|
+
core_ip_address_pool,
|
|
79
|
+
core_ip_prefix_pool,
|
|
80
|
+
core_number_pool,
|
|
81
|
+
core_resource_pool,
|
|
82
|
+
core_weighted_pool_resource,
|
|
83
|
+
)
|
|
72
84
|
from .template import core_object_component_template, core_object_template
|
|
73
85
|
from .transform import core_transform, core_transform_jinja2, core_transform_python
|
|
74
86
|
from .webhook import core_custom_webhook, core_standard_webhook, core_webhook
|
|
@@ -96,6 +108,7 @@ core_models_mixed: dict[str, list] = {
|
|
|
96
108
|
builtin_ip_prefix,
|
|
97
109
|
builtin_ip_address,
|
|
98
110
|
core_resource_pool,
|
|
111
|
+
core_weighted_pool_resource,
|
|
99
112
|
core_generic_account,
|
|
100
113
|
core_base_permission,
|
|
101
114
|
core_credential,
|
|
@@ -109,6 +122,7 @@ core_models_mixed: dict[str, list] = {
|
|
|
109
122
|
core_standard_group,
|
|
110
123
|
core_generator_group,
|
|
111
124
|
core_graphql_query_group,
|
|
125
|
+
core_repository_group,
|
|
112
126
|
builtin_tag,
|
|
113
127
|
core_account,
|
|
114
128
|
core_account_token,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from infrahub.core.constants import (
|
|
2
2
|
BranchSupportType,
|
|
3
3
|
InfrahubKind,
|
|
4
|
+
RepositoryObjects,
|
|
4
5
|
)
|
|
5
6
|
from infrahub.core.constants import RelationshipCardinality as Cardinality
|
|
6
7
|
from infrahub.core.constants import RelationshipKind as RelKind
|
|
8
|
+
from infrahub.core.schema.dropdown import DropdownChoice
|
|
7
9
|
|
|
8
10
|
from ...attribute_schema import AttributeSchema as Attr
|
|
9
11
|
from ...generic_schema import GenericSchema
|
|
@@ -80,6 +82,7 @@ core_generator_group = NodeSchema(
|
|
|
80
82
|
generate_profile=False,
|
|
81
83
|
)
|
|
82
84
|
|
|
85
|
+
|
|
83
86
|
core_graphql_query_group = NodeSchema(
|
|
84
87
|
name="GraphQLQueryGroup",
|
|
85
88
|
namespace="Core",
|
|
@@ -106,3 +109,45 @@ core_graphql_query_group = NodeSchema(
|
|
|
106
109
|
),
|
|
107
110
|
],
|
|
108
111
|
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
core_repository_group = NodeSchema(
|
|
115
|
+
name="RepositoryGroup",
|
|
116
|
+
namespace="Core",
|
|
117
|
+
description="Group of nodes associated with a given repository.",
|
|
118
|
+
include_in_menu=False,
|
|
119
|
+
icon="mdi:account-group",
|
|
120
|
+
label="Repository Group",
|
|
121
|
+
default_filter="name__value",
|
|
122
|
+
order_by=["name__value"],
|
|
123
|
+
display_labels=["name__value"],
|
|
124
|
+
branch=BranchSupportType.LOCAL,
|
|
125
|
+
inherit_from=[InfrahubKind.GENERICGROUP],
|
|
126
|
+
generate_profile=False,
|
|
127
|
+
attributes=[
|
|
128
|
+
Attr(
|
|
129
|
+
name="content",
|
|
130
|
+
kind="Dropdown",
|
|
131
|
+
description="Type of data to load, can be either `object` or `menu`",
|
|
132
|
+
choices=[
|
|
133
|
+
DropdownChoice(
|
|
134
|
+
name=RepositoryObjects.OBJECT.value,
|
|
135
|
+
label="Objects",
|
|
136
|
+
),
|
|
137
|
+
DropdownChoice(
|
|
138
|
+
name=RepositoryObjects.MENU.value,
|
|
139
|
+
label="Menus",
|
|
140
|
+
),
|
|
141
|
+
],
|
|
142
|
+
optional=False,
|
|
143
|
+
),
|
|
144
|
+
],
|
|
145
|
+
relationships=[
|
|
146
|
+
Rel(
|
|
147
|
+
name="repository",
|
|
148
|
+
peer=InfrahubKind.GENERICREPOSITORY,
|
|
149
|
+
optional=False,
|
|
150
|
+
cardinality=Cardinality.ONE,
|
|
151
|
+
),
|
|
152
|
+
],
|
|
153
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from infrahub.core.constants import (
|
|
2
2
|
BranchSupportType,
|
|
3
3
|
InfrahubKind,
|
|
4
|
+
NumberPoolType,
|
|
4
5
|
)
|
|
5
6
|
from infrahub.core.constants import RelationshipCardinality as Cardinality
|
|
6
7
|
from infrahub.core.constants import RelationshipKind as RelKind
|
|
@@ -32,6 +33,26 @@ core_resource_pool = GenericSchema(
|
|
|
32
33
|
],
|
|
33
34
|
)
|
|
34
35
|
|
|
36
|
+
core_weighted_pool_resource = GenericSchema(
|
|
37
|
+
name="WeightedPoolResource",
|
|
38
|
+
namespace="Core",
|
|
39
|
+
label="Weighted Pool Resource",
|
|
40
|
+
description="Resource to be used in a pool, its weight is used to determine its priority on allocation.",
|
|
41
|
+
include_in_menu=False,
|
|
42
|
+
branch=BranchSupportType.AWARE,
|
|
43
|
+
generate_profile=False,
|
|
44
|
+
attributes=[
|
|
45
|
+
Attr(
|
|
46
|
+
name="allocation_weight",
|
|
47
|
+
label="Weight",
|
|
48
|
+
description="Weight determines allocation priority, resources with higher values are selected first.",
|
|
49
|
+
kind="Number",
|
|
50
|
+
optional=True,
|
|
51
|
+
order_weight=10000,
|
|
52
|
+
)
|
|
53
|
+
],
|
|
54
|
+
)
|
|
55
|
+
|
|
35
56
|
core_ip_prefix_pool = NodeSchema(
|
|
36
57
|
name="IPPrefixPool",
|
|
37
58
|
namespace="Core",
|
|
@@ -166,5 +187,13 @@ core_number_pool = NodeSchema(
|
|
|
166
187
|
Attr(
|
|
167
188
|
name="end_range", kind="Number", optional=False, description="The end range for the pool", order_weight=6000
|
|
168
189
|
),
|
|
190
|
+
Attr(
|
|
191
|
+
name="pool_type",
|
|
192
|
+
kind="Text",
|
|
193
|
+
description="Defines how this number pool was created",
|
|
194
|
+
default_value=NumberPoolType.USER.value,
|
|
195
|
+
enum=NumberPoolType.available_types(),
|
|
196
|
+
read_only=True,
|
|
197
|
+
),
|
|
169
198
|
],
|
|
170
199
|
)
|
|
@@ -31,7 +31,12 @@ from infrahub.core.constants import (
|
|
|
31
31
|
RelationshipKind,
|
|
32
32
|
UpdateSupport,
|
|
33
33
|
)
|
|
34
|
-
from infrahub.core.schema.attribute_parameters import
|
|
34
|
+
from infrahub.core.schema.attribute_parameters import (
|
|
35
|
+
AttributeParameters,
|
|
36
|
+
NumberAttributeParameters,
|
|
37
|
+
NumberPoolParameters,
|
|
38
|
+
TextAttributeParameters,
|
|
39
|
+
)
|
|
35
40
|
from infrahub.core.schema.attribute_schema import AttributeSchema
|
|
36
41
|
from infrahub.core.schema.computed_attribute import ComputedAttribute
|
|
37
42
|
from infrahub.core.schema.dropdown import DropdownChoice
|
|
@@ -48,7 +53,7 @@ class SchemaAttribute(BaseModel):
|
|
|
48
53
|
kind: str
|
|
49
54
|
description: str
|
|
50
55
|
extra: ExtraField
|
|
51
|
-
internal_kind: type[Any] | GenericAlias | None = None
|
|
56
|
+
internal_kind: type[Any] | GenericAlias | list[type[Any]] | None = None
|
|
52
57
|
regex: str | None = None
|
|
53
58
|
unique: bool | None = None
|
|
54
59
|
optional: bool | None = None
|
|
@@ -77,7 +82,7 @@ class SchemaAttribute(BaseModel):
|
|
|
77
82
|
|
|
78
83
|
@property
|
|
79
84
|
def optional_in_model(self) -> bool:
|
|
80
|
-
if self.optional and self.default_value is None and self.default_factory is None or self.default_to_none:
|
|
85
|
+
if (self.optional and self.default_value is None and self.default_factory is None) or self.default_to_none:
|
|
81
86
|
return True
|
|
82
87
|
|
|
83
88
|
return False
|
|
@@ -94,6 +99,9 @@ class SchemaAttribute(BaseModel):
|
|
|
94
99
|
if isinstance(self.internal_kind, GenericAlias):
|
|
95
100
|
return str(self.internal_kind)
|
|
96
101
|
|
|
102
|
+
if isinstance(self.internal_kind, list):
|
|
103
|
+
return " | ".join([internal_kind.__name__ for internal_kind in self.internal_kind])
|
|
104
|
+
|
|
97
105
|
if self.internal_kind and self.kind == "List":
|
|
98
106
|
return f"list[{self.internal_kind.__name__}]"
|
|
99
107
|
|
|
@@ -621,7 +629,12 @@ attribute_schema = SchemaNode(
|
|
|
621
629
|
SchemaAttribute(
|
|
622
630
|
name="parameters",
|
|
623
631
|
kind="JSON",
|
|
624
|
-
internal_kind=
|
|
632
|
+
internal_kind=[
|
|
633
|
+
AttributeParameters,
|
|
634
|
+
TextAttributeParameters,
|
|
635
|
+
NumberAttributeParameters,
|
|
636
|
+
NumberPoolParameters,
|
|
637
|
+
],
|
|
625
638
|
optional=True,
|
|
626
639
|
description="Extra parameters specific to this kind of attribute",
|
|
627
640
|
extra={"update": UpdateSupport.VALIDATE_CONSTRAINT},
|
|
@@ -741,6 +754,14 @@ relationship_schema = SchemaNode(
|
|
|
741
754
|
optional=True,
|
|
742
755
|
extra={"update": UpdateSupport.VALIDATE_CONSTRAINT},
|
|
743
756
|
),
|
|
757
|
+
SchemaAttribute(
|
|
758
|
+
name="common_relatives",
|
|
759
|
+
kind="List",
|
|
760
|
+
internal_kind=str,
|
|
761
|
+
optional=True,
|
|
762
|
+
description="List of relationship names on the peer schema for which all objects must share the same set of peers.",
|
|
763
|
+
extra={"update": UpdateSupport.VALIDATE_CONSTRAINT},
|
|
764
|
+
),
|
|
744
765
|
SchemaAttribute(
|
|
745
766
|
name="order_weight",
|
|
746
767
|
kind="Number",
|
|
@@ -8,7 +8,12 @@ from pydantic import Field
|
|
|
8
8
|
|
|
9
9
|
from infrahub.core.constants import AllowOverrideType, BranchSupportType, HashableModelState
|
|
10
10
|
from infrahub.core.models import HashableModel
|
|
11
|
-
from infrahub.core.schema.attribute_parameters import
|
|
11
|
+
from infrahub.core.schema.attribute_parameters import (
|
|
12
|
+
AttributeParameters,
|
|
13
|
+
NumberAttributeParameters,
|
|
14
|
+
NumberPoolParameters,
|
|
15
|
+
TextAttributeParameters,
|
|
16
|
+
)
|
|
12
17
|
from infrahub.core.schema.computed_attribute import ComputedAttribute # noqa: TC001
|
|
13
18
|
from infrahub.core.schema.dropdown import DropdownChoice # noqa: TC001
|
|
14
19
|
|
|
@@ -113,10 +118,12 @@ class GeneratedAttributeSchema(HashableModel):
|
|
|
113
118
|
description="Type of allowed override for the attribute.",
|
|
114
119
|
json_schema_extra={"update": "allowed"},
|
|
115
120
|
)
|
|
116
|
-
parameters: AttributeParameters =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
parameters: AttributeParameters | TextAttributeParameters | NumberAttributeParameters | NumberPoolParameters = (
|
|
122
|
+
Field(
|
|
123
|
+
default_factory=AttributeParameters,
|
|
124
|
+
description="Extra parameters specific to this kind of attribute",
|
|
125
|
+
json_schema_extra={"update": "validate_constraint"},
|
|
126
|
+
)
|
|
120
127
|
)
|
|
121
128
|
deprecation: str | None = Field(
|
|
122
129
|
default=None,
|
|
@@ -12,7 +12,7 @@ from infrahub.core.constants import (
|
|
|
12
12
|
RelationshipDeleteBehavior,
|
|
13
13
|
RelationshipDirection,
|
|
14
14
|
RelationshipKind,
|
|
15
|
-
)
|
|
15
|
+
)
|
|
16
16
|
from infrahub.core.models import HashableModel
|
|
17
17
|
|
|
18
18
|
|
|
@@ -73,6 +73,11 @@ class GeneratedRelationshipSchema(HashableModel):
|
|
|
73
73
|
description="Defines the maximum objects allowed on the other side of the relationship.",
|
|
74
74
|
json_schema_extra={"update": "validate_constraint"},
|
|
75
75
|
)
|
|
76
|
+
common_relatives: list[str] | None = Field(
|
|
77
|
+
default=None,
|
|
78
|
+
description="List of relationship names on the peer schema for which all objects must share the same set of peers.",
|
|
79
|
+
json_schema_extra={"update": "validate_constraint"},
|
|
80
|
+
)
|
|
76
81
|
order_weight: int | None = Field(
|
|
77
82
|
default=None,
|
|
78
83
|
description="Number used to order the relationship in the frontend (table and view). Lowest value will be ordered first.",
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -471,7 +471,7 @@ class SchemaManager(NodeManager):
|
|
|
471
471
|
if diff_attributes:
|
|
472
472
|
for item in node.local_attributes:
|
|
473
473
|
# if item is in changed and has no ID, then it is being overridden from a generic and must be added
|
|
474
|
-
if item.name in diff_attributes.added or item.name in diff_attributes.changed and item.id is None:
|
|
474
|
+
if item.name in diff_attributes.added or (item.name in diff_attributes.changed and item.id is None):
|
|
475
475
|
created_item = await self.create_attribute_in_db(
|
|
476
476
|
schema=attribute_schema, item=item, branch=branch, db=db, parent=obj
|
|
477
477
|
)
|
|
@@ -491,7 +491,9 @@ class SchemaManager(NodeManager):
|
|
|
491
491
|
if diff_relationships:
|
|
492
492
|
for item in node.local_relationships:
|
|
493
493
|
# if item is in changed and has no ID, then it is being overridden from a generic and must be added
|
|
494
|
-
if item.name in diff_relationships.added or
|
|
494
|
+
if item.name in diff_relationships.added or (
|
|
495
|
+
item.name in diff_relationships.changed and item.id is None
|
|
496
|
+
):
|
|
495
497
|
created_rel = await self.create_relationship_in_db(
|
|
496
498
|
schema=relationship_schema, item=item, branch=branch, db=db, parent=obj
|
|
497
499
|
)
|
|
@@ -764,3 +766,6 @@ class SchemaManager(NodeManager):
|
|
|
764
766
|
del self._cache[hash_key]
|
|
765
767
|
|
|
766
768
|
return removed_branches
|
|
769
|
+
|
|
770
|
+
def get_branches(self) -> list[str]:
|
|
771
|
+
return list(self._branches.keys())
|