infrahub-server 1.2.1__py3-none-any.whl → 1.2.3__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/computed_attribute/tasks.py +71 -67
- infrahub/config.py +3 -0
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/migrations/graph/__init__.py +4 -1
- infrahub/core/migrations/graph/m024_missing_hierarchy_backfill.py +69 -0
- infrahub/core/models.py +6 -0
- infrahub/core/node/__init__.py +4 -4
- infrahub/core/node/constraints/grouped_uniqueness.py +24 -9
- infrahub/core/query/ipam.py +1 -1
- infrahub/core/query/node.py +16 -5
- infrahub/core/schema/schema_branch.py +14 -5
- infrahub/exceptions.py +30 -2
- infrahub/git/base.py +80 -29
- infrahub/git/integrator.py +9 -31
- infrahub/menu/repository.py +6 -6
- infrahub/trigger/tasks.py +19 -18
- infrahub/workflows/utils.py +5 -5
- infrahub_sdk/client.py +6 -6
- infrahub_sdk/ctl/cli_commands.py +32 -37
- infrahub_sdk/ctl/render.py +39 -0
- infrahub_sdk/exceptions.py +6 -2
- infrahub_sdk/generator.py +1 -1
- infrahub_sdk/node.py +41 -12
- infrahub_sdk/protocols_base.py +8 -1
- infrahub_sdk/pytest_plugin/items/jinja2_transform.py +22 -26
- infrahub_sdk/store.py +351 -75
- infrahub_sdk/template/__init__.py +209 -0
- infrahub_sdk/template/exceptions.py +38 -0
- infrahub_sdk/template/filters.py +151 -0
- infrahub_sdk/template/models.py +10 -0
- infrahub_sdk/utils.py +7 -0
- {infrahub_server-1.2.1.dist-info → infrahub_server-1.2.3.dist-info}/METADATA +2 -1
- {infrahub_server-1.2.1.dist-info → infrahub_server-1.2.3.dist-info}/RECORD +39 -36
- infrahub_testcontainers/container.py +2 -0
- infrahub_testcontainers/docker-compose.test.yml +1 -0
- infrahub_testcontainers/haproxy.cfg +3 -3
- infrahub/support/__init__.py +0 -0
- infrahub/support/macro.py +0 -69
- {infrahub_server-1.2.1.dist-info → infrahub_server-1.2.3.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.1.dist-info → infrahub_server-1.2.3.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.1.dist-info → infrahub_server-1.2.3.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import linecache
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Callable, NoReturn
|
|
6
|
+
|
|
7
|
+
import jinja2
|
|
8
|
+
from jinja2 import meta, nodes
|
|
9
|
+
from jinja2.sandbox import SandboxedEnvironment
|
|
10
|
+
from netutils.utils import jinja2_convenience_function
|
|
11
|
+
from rich.syntax import Syntax
|
|
12
|
+
from rich.traceback import Traceback
|
|
13
|
+
|
|
14
|
+
from .exceptions import (
|
|
15
|
+
JinjaTemplateError,
|
|
16
|
+
JinjaTemplateNotFoundError,
|
|
17
|
+
JinjaTemplateOperationViolationError,
|
|
18
|
+
JinjaTemplateSyntaxError,
|
|
19
|
+
JinjaTemplateUndefinedError,
|
|
20
|
+
)
|
|
21
|
+
from .filters import AVAILABLE_FILTERS
|
|
22
|
+
from .models import UndefinedJinja2Error
|
|
23
|
+
|
|
24
|
+
netutils_filters = jinja2_convenience_function()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Jinja2Template:
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
template: str | Path,
|
|
31
|
+
template_directory: Path | None = None,
|
|
32
|
+
filters: dict[str, Callable] | None = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
self.is_string_based = isinstance(template, str)
|
|
35
|
+
self.is_file_based = isinstance(template, Path)
|
|
36
|
+
self._template = str(template)
|
|
37
|
+
self._template_directory = template_directory
|
|
38
|
+
self._environment: jinja2.Environment | None = None
|
|
39
|
+
|
|
40
|
+
self._available_filters = [filter_definition.name for filter_definition in AVAILABLE_FILTERS]
|
|
41
|
+
self._trusted_filters = [
|
|
42
|
+
filter_definition.name for filter_definition in AVAILABLE_FILTERS if filter_definition.trusted
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
self._filters = filters or {}
|
|
46
|
+
for user_filter in self._filters:
|
|
47
|
+
self._available_filters.append(user_filter)
|
|
48
|
+
self._trusted_filters.append(user_filter)
|
|
49
|
+
|
|
50
|
+
self._template_definition: jinja2.Template | None = None
|
|
51
|
+
|
|
52
|
+
def get_environment(self) -> jinja2.Environment:
|
|
53
|
+
if self._environment:
|
|
54
|
+
return self._environment
|
|
55
|
+
|
|
56
|
+
if self.is_string_based:
|
|
57
|
+
return self._get_string_based_environment()
|
|
58
|
+
|
|
59
|
+
return self._get_file_based_environment()
|
|
60
|
+
|
|
61
|
+
def get_template(self) -> jinja2.Template:
|
|
62
|
+
if self._template_definition:
|
|
63
|
+
return self._template_definition
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
if self.is_string_based:
|
|
67
|
+
template = self._get_string_based_template()
|
|
68
|
+
else:
|
|
69
|
+
template = self._get_file_based_template()
|
|
70
|
+
except jinja2.TemplateSyntaxError as exc:
|
|
71
|
+
self._raise_template_syntax_error(error=exc)
|
|
72
|
+
except jinja2.TemplateNotFound as exc:
|
|
73
|
+
raise JinjaTemplateNotFoundError(message=exc.message, filename=str(exc.name))
|
|
74
|
+
|
|
75
|
+
return template
|
|
76
|
+
|
|
77
|
+
def get_variables(self) -> list[str]:
|
|
78
|
+
env = self.get_environment()
|
|
79
|
+
|
|
80
|
+
template_source = self._template
|
|
81
|
+
if self.is_file_based and env.loader:
|
|
82
|
+
template_source = env.loader.get_source(env, self._template)[0]
|
|
83
|
+
|
|
84
|
+
template = env.parse(template_source)
|
|
85
|
+
|
|
86
|
+
return sorted(meta.find_undeclared_variables(template))
|
|
87
|
+
|
|
88
|
+
def validate(self, restricted: bool = True) -> None:
|
|
89
|
+
allowed_list = self._available_filters
|
|
90
|
+
if restricted:
|
|
91
|
+
allowed_list = self._trusted_filters
|
|
92
|
+
|
|
93
|
+
env = self.get_environment()
|
|
94
|
+
template_source = self._template
|
|
95
|
+
if self.is_file_based and env.loader:
|
|
96
|
+
template_source = env.loader.get_source(env, self._template)[0]
|
|
97
|
+
|
|
98
|
+
template = env.parse(template_source)
|
|
99
|
+
for node in template.find_all(nodes.Filter):
|
|
100
|
+
if node.name not in allowed_list:
|
|
101
|
+
raise JinjaTemplateOperationViolationError(f"The '{node.name}' filter isn't allowed to be used")
|
|
102
|
+
|
|
103
|
+
forbidden_operations = ["Call", "Import", "Include"]
|
|
104
|
+
if self.is_string_based and any(node.__class__.__name__ in forbidden_operations for node in template.body):
|
|
105
|
+
raise JinjaTemplateOperationViolationError(
|
|
106
|
+
f"These operations are forbidden for string based templates: {forbidden_operations}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
async def render(self, variables: dict[str, Any]) -> str:
|
|
110
|
+
template = self.get_template()
|
|
111
|
+
try:
|
|
112
|
+
output = await template.render_async(variables)
|
|
113
|
+
except jinja2.exceptions.TemplateNotFound as exc:
|
|
114
|
+
raise JinjaTemplateNotFoundError(message=exc.message, filename=str(exc.name), base_template=template.name)
|
|
115
|
+
except jinja2.TemplateSyntaxError as exc:
|
|
116
|
+
self._raise_template_syntax_error(error=exc)
|
|
117
|
+
except jinja2.UndefinedError as exc:
|
|
118
|
+
traceback = Traceback(show_locals=False)
|
|
119
|
+
errors = _identify_faulty_jinja_code(traceback=traceback)
|
|
120
|
+
raise JinjaTemplateUndefinedError(message=exc.message, errors=errors)
|
|
121
|
+
except Exception as exc:
|
|
122
|
+
if error_message := getattr(exc, "message", None):
|
|
123
|
+
message = error_message
|
|
124
|
+
else:
|
|
125
|
+
message = str(exc)
|
|
126
|
+
raise JinjaTemplateError(message=message or "Unknown template error")
|
|
127
|
+
|
|
128
|
+
return output
|
|
129
|
+
|
|
130
|
+
def _get_string_based_environment(self) -> jinja2.Environment:
|
|
131
|
+
env = SandboxedEnvironment(enable_async=True, undefined=jinja2.StrictUndefined)
|
|
132
|
+
self._set_filters(env=env)
|
|
133
|
+
self._environment = env
|
|
134
|
+
return self._environment
|
|
135
|
+
|
|
136
|
+
def _get_file_based_environment(self) -> jinja2.Environment:
|
|
137
|
+
template_loader = jinja2.FileSystemLoader(searchpath=str(self._template_directory))
|
|
138
|
+
env = jinja2.Environment(
|
|
139
|
+
loader=template_loader,
|
|
140
|
+
trim_blocks=True,
|
|
141
|
+
lstrip_blocks=True,
|
|
142
|
+
enable_async=True,
|
|
143
|
+
)
|
|
144
|
+
self._set_filters(env=env)
|
|
145
|
+
self._environment = env
|
|
146
|
+
return self._environment
|
|
147
|
+
|
|
148
|
+
def _set_filters(self, env: jinja2.Environment) -> None:
|
|
149
|
+
for default_filter in list(env.filters.keys()):
|
|
150
|
+
if default_filter not in self._available_filters:
|
|
151
|
+
del env.filters[default_filter]
|
|
152
|
+
|
|
153
|
+
# Add filters from netutils
|
|
154
|
+
env.filters.update(
|
|
155
|
+
{name: jinja_filter for name, jinja_filter in netutils_filters.items() if name in self._available_filters}
|
|
156
|
+
)
|
|
157
|
+
# Add user supplied filters
|
|
158
|
+
env.filters.update(self._filters)
|
|
159
|
+
|
|
160
|
+
def _get_string_based_template(self) -> jinja2.Template:
|
|
161
|
+
env = self.get_environment()
|
|
162
|
+
self._template_definition = env.from_string(self._template)
|
|
163
|
+
return self._template_definition
|
|
164
|
+
|
|
165
|
+
def _get_file_based_template(self) -> jinja2.Template:
|
|
166
|
+
env = self.get_environment()
|
|
167
|
+
self._template_definition = env.get_template(self._template)
|
|
168
|
+
return self._template_definition
|
|
169
|
+
|
|
170
|
+
def _raise_template_syntax_error(self, error: jinja2.TemplateSyntaxError) -> NoReturn:
|
|
171
|
+
filename: str | None = None
|
|
172
|
+
if error.filename and self._template_directory:
|
|
173
|
+
filename = error.filename
|
|
174
|
+
if error.filename.startswith(str(self._template_directory)):
|
|
175
|
+
filename = error.filename[len(str(self._template_directory)) :]
|
|
176
|
+
|
|
177
|
+
raise JinjaTemplateSyntaxError(message=error.message, filename=filename, lineno=error.lineno)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _identify_faulty_jinja_code(traceback: Traceback, nbr_context_lines: int = 3) -> list[UndefinedJinja2Error]:
|
|
181
|
+
"""This function identifies the faulty Jinja2 code and beautify it to provide meaningful information to the user.
|
|
182
|
+
|
|
183
|
+
We use the rich's Traceback to parse the complete stack trace and extract Frames for each exception found in the trace.
|
|
184
|
+
"""
|
|
185
|
+
response = []
|
|
186
|
+
|
|
187
|
+
# Extract only the Jinja related exception
|
|
188
|
+
for frame in [frame for frame in traceback.trace.stacks[0].frames if not frame.filename.endswith(".py")]:
|
|
189
|
+
code = "".join(linecache.getlines(frame.filename))
|
|
190
|
+
if frame.filename == "<template>":
|
|
191
|
+
lexer_name = "text"
|
|
192
|
+
else:
|
|
193
|
+
lexer_name = Traceback._guess_lexer(frame.filename, code)
|
|
194
|
+
syntax = Syntax(
|
|
195
|
+
code,
|
|
196
|
+
lexer_name,
|
|
197
|
+
line_numbers=True,
|
|
198
|
+
line_range=(
|
|
199
|
+
frame.lineno - nbr_context_lines,
|
|
200
|
+
frame.lineno + nbr_context_lines,
|
|
201
|
+
),
|
|
202
|
+
highlight_lines={frame.lineno},
|
|
203
|
+
code_width=88,
|
|
204
|
+
theme=traceback.theme,
|
|
205
|
+
dedent=False,
|
|
206
|
+
)
|
|
207
|
+
response.append(UndefinedJinja2Error(frame=frame, syntax=syntax))
|
|
208
|
+
|
|
209
|
+
return response
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from infrahub_sdk.exceptions import Error
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .models import UndefinedJinja2Error
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class JinjaTemplateError(Error):
|
|
12
|
+
def __init__(self, message: str) -> None:
|
|
13
|
+
self.message = message
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JinjaTemplateNotFoundError(JinjaTemplateError):
|
|
17
|
+
def __init__(self, message: str | None, filename: str, base_template: str | None = None) -> None:
|
|
18
|
+
self.message = message or "Template Not Found"
|
|
19
|
+
self.filename = filename
|
|
20
|
+
self.base_template = base_template
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class JinjaTemplateSyntaxError(JinjaTemplateError):
|
|
24
|
+
def __init__(self, message: str | None, lineno: int, filename: str | None = None) -> None:
|
|
25
|
+
self.message = message or "Syntax Error"
|
|
26
|
+
self.filename = filename
|
|
27
|
+
self.lineno = lineno
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class JinjaTemplateUndefinedError(JinjaTemplateError):
|
|
31
|
+
def __init__(self, message: str | None, errors: list[UndefinedJinja2Error]) -> None:
|
|
32
|
+
self.message = message or "Undefined Error"
|
|
33
|
+
self.errors = errors
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class JinjaTemplateOperationViolationError(JinjaTemplateError):
|
|
37
|
+
def __init__(self, message: str | None = None) -> None:
|
|
38
|
+
self.message = message or "Forbidden code found in the template"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class FilterDefinition:
|
|
6
|
+
name: str
|
|
7
|
+
trusted: bool
|
|
8
|
+
source: str
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
BUILTIN_FILTERS = [
|
|
12
|
+
FilterDefinition(name="abs", trusted=True, source="jinja2"),
|
|
13
|
+
FilterDefinition(name="attr", trusted=False, source="jinja2"),
|
|
14
|
+
FilterDefinition(name="batch", trusted=False, source="jinja2"),
|
|
15
|
+
FilterDefinition(name="capitalize", trusted=True, source="jinja2"),
|
|
16
|
+
FilterDefinition(name="center", trusted=True, source="jinja2"),
|
|
17
|
+
FilterDefinition(name="count", trusted=True, source="jinja2"),
|
|
18
|
+
FilterDefinition(name="d", trusted=True, source="jinja2"),
|
|
19
|
+
FilterDefinition(name="default", trusted=True, source="jinja2"),
|
|
20
|
+
FilterDefinition(name="dictsort", trusted=False, source="jinja2"),
|
|
21
|
+
FilterDefinition(name="e", trusted=True, source="jinja2"),
|
|
22
|
+
FilterDefinition(name="escape", trusted=True, source="jinja2"),
|
|
23
|
+
FilterDefinition(name="filesizeformat", trusted=True, source="jinja2"),
|
|
24
|
+
FilterDefinition(name="first", trusted=True, source="jinja2"),
|
|
25
|
+
FilterDefinition(name="float", trusted=True, source="jinja2"),
|
|
26
|
+
FilterDefinition(name="forceescape", trusted=True, source="jinja2"),
|
|
27
|
+
FilterDefinition(name="format", trusted=True, source="jinja2"),
|
|
28
|
+
FilterDefinition(name="groupby", trusted=False, source="jinja2"),
|
|
29
|
+
FilterDefinition(name="indent", trusted=True, source="jinja2"),
|
|
30
|
+
FilterDefinition(name="int", trusted=True, source="jinja2"),
|
|
31
|
+
FilterDefinition(name="items", trusted=False, source="jinja2"),
|
|
32
|
+
FilterDefinition(name="join", trusted=True, source="jinja2"),
|
|
33
|
+
FilterDefinition(name="last", trusted=True, source="jinja2"),
|
|
34
|
+
FilterDefinition(name="length", trusted=True, source="jinja2"),
|
|
35
|
+
FilterDefinition(name="list", trusted=True, source="jinja2"),
|
|
36
|
+
FilterDefinition(name="lower", trusted=True, source="jinja2"),
|
|
37
|
+
FilterDefinition(name="map", trusted=False, source="jinja2"),
|
|
38
|
+
FilterDefinition(name="max", trusted=True, source="jinja2"),
|
|
39
|
+
FilterDefinition(name="min", trusted=True, source="jinja2"),
|
|
40
|
+
FilterDefinition(name="pprint", trusted=False, source="jinja2"),
|
|
41
|
+
FilterDefinition(name="random", trusted=False, source="jinja2"),
|
|
42
|
+
FilterDefinition(name="reject", trusted=False, source="jinja2"),
|
|
43
|
+
FilterDefinition(name="rejectattr", trusted=False, source="jinja2"),
|
|
44
|
+
FilterDefinition(name="replace", trusted=True, source="jinja2"),
|
|
45
|
+
FilterDefinition(name="reverse", trusted=True, source="jinja2"),
|
|
46
|
+
FilterDefinition(name="round", trusted=True, source="jinja2"),
|
|
47
|
+
FilterDefinition(name="safe", trusted=False, source="jinja2"),
|
|
48
|
+
FilterDefinition(name="select", trusted=False, source="jinja2"),
|
|
49
|
+
FilterDefinition(name="selectattr", trusted=False, source="jinja2"),
|
|
50
|
+
FilterDefinition(name="slice", trusted=True, source="jinja2"),
|
|
51
|
+
FilterDefinition(name="sort", trusted=False, source="jinja2"),
|
|
52
|
+
FilterDefinition(name="string", trusted=True, source="jinja2"),
|
|
53
|
+
FilterDefinition(name="striptags", trusted=True, source="jinja2"),
|
|
54
|
+
FilterDefinition(name="sum", trusted=True, source="jinja2"),
|
|
55
|
+
FilterDefinition(name="title", trusted=True, source="jinja2"),
|
|
56
|
+
FilterDefinition(name="tojson", trusted=False, source="jinja2"),
|
|
57
|
+
FilterDefinition(name="trim", trusted=True, source="jinja2"),
|
|
58
|
+
FilterDefinition(name="truncate", trusted=True, source="jinja2"),
|
|
59
|
+
FilterDefinition(name="unique", trusted=False, source="jinja2"),
|
|
60
|
+
FilterDefinition(name="upper", trusted=True, source="jinja2"),
|
|
61
|
+
FilterDefinition(name="urlencode", trusted=True, source="jinja2"),
|
|
62
|
+
FilterDefinition(name="urlize", trusted=False, source="jinja2"),
|
|
63
|
+
FilterDefinition(name="wordcount", trusted=True, source="jinja2"),
|
|
64
|
+
FilterDefinition(name="wordwrap", trusted=True, source="jinja2"),
|
|
65
|
+
FilterDefinition(name="xmlattr", trusted=False, source="jinja2"),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
NETUTILS_FILTERS = [
|
|
70
|
+
FilterDefinition(name="abbreviated_interface_name", trusted=True, source="netutils"),
|
|
71
|
+
FilterDefinition(name="abbreviated_interface_name_list", trusted=True, source="netutils"),
|
|
72
|
+
FilterDefinition(name="asn_to_int", trusted=True, source="netutils"),
|
|
73
|
+
FilterDefinition(name="bits_to_name", trusted=True, source="netutils"),
|
|
74
|
+
FilterDefinition(name="bytes_to_name", trusted=True, source="netutils"),
|
|
75
|
+
FilterDefinition(name="canonical_interface_name", trusted=True, source="netutils"),
|
|
76
|
+
FilterDefinition(name="canonical_interface_name_list", trusted=True, source="netutils"),
|
|
77
|
+
FilterDefinition(name="cidr_to_netmask", trusted=True, source="netutils"),
|
|
78
|
+
FilterDefinition(name="cidr_to_netmaskv6", trusted=True, source="netutils"),
|
|
79
|
+
FilterDefinition(name="clean_config", trusted=True, source="netutils"),
|
|
80
|
+
FilterDefinition(name="compare_version_loose", trusted=True, source="netutils"),
|
|
81
|
+
FilterDefinition(name="compare_version_strict", trusted=True, source="netutils"),
|
|
82
|
+
FilterDefinition(name="config_compliance", trusted=True, source="netutils"),
|
|
83
|
+
FilterDefinition(name="config_section_not_parsed", trusted=True, source="netutils"),
|
|
84
|
+
FilterDefinition(name="delimiter_change", trusted=True, source="netutils"),
|
|
85
|
+
FilterDefinition(name="diff_network_config", trusted=True, source="netutils"),
|
|
86
|
+
FilterDefinition(name="feature_compliance", trusted=True, source="netutils"),
|
|
87
|
+
FilterDefinition(name="find_unordered_cfg_lines", trusted=True, source="netutils"),
|
|
88
|
+
FilterDefinition(name="fqdn_to_ip", trusted=False, source="netutils"),
|
|
89
|
+
FilterDefinition(name="get_all_host", trusted=False, source="netutils"),
|
|
90
|
+
FilterDefinition(name="get_broadcast_address", trusted=True, source="netutils"),
|
|
91
|
+
FilterDefinition(name="get_first_usable", trusted=True, source="netutils"),
|
|
92
|
+
FilterDefinition(name="get_ips_sorted", trusted=True, source="netutils"),
|
|
93
|
+
FilterDefinition(name="get_nist_urls", trusted=True, source="netutils"),
|
|
94
|
+
FilterDefinition(name="get_nist_vendor_platform_urls", trusted=True, source="netutils"),
|
|
95
|
+
FilterDefinition(name="get_oui", trusted=True, source="netutils"),
|
|
96
|
+
FilterDefinition(name="get_peer_ip", trusted=True, source="netutils"),
|
|
97
|
+
FilterDefinition(name="get_range_ips", trusted=True, source="netutils"),
|
|
98
|
+
FilterDefinition(name="get_upgrade_path", trusted=True, source="netutils"),
|
|
99
|
+
FilterDefinition(name="get_usable_range", trusted=True, source="netutils"),
|
|
100
|
+
FilterDefinition(name="hash_data", trusted=True, source="netutils"),
|
|
101
|
+
FilterDefinition(name="int_to_asdot", trusted=True, source="netutils"),
|
|
102
|
+
FilterDefinition(name="interface_range_compress", trusted=True, source="netutils"),
|
|
103
|
+
FilterDefinition(name="interface_range_expansion", trusted=True, source="netutils"),
|
|
104
|
+
FilterDefinition(name="ip_addition", trusted=True, source="netutils"),
|
|
105
|
+
FilterDefinition(name="ip_subtract", trusted=True, source="netutils"),
|
|
106
|
+
FilterDefinition(name="ip_to_bin", trusted=True, source="netutils"),
|
|
107
|
+
FilterDefinition(name="ip_to_hex", trusted=True, source="netutils"),
|
|
108
|
+
FilterDefinition(name="ipaddress_address", trusted=True, source="netutils"),
|
|
109
|
+
FilterDefinition(name="ipaddress_interface", trusted=True, source="netutils"),
|
|
110
|
+
FilterDefinition(name="ipaddress_network", trusted=True, source="netutils"),
|
|
111
|
+
FilterDefinition(name="is_classful", trusted=True, source="netutils"),
|
|
112
|
+
FilterDefinition(name="is_fqdn_resolvable", trusted=False, source="netutils"),
|
|
113
|
+
FilterDefinition(name="is_ip", trusted=True, source="netutils"),
|
|
114
|
+
FilterDefinition(name="is_ip_range", trusted=True, source="netutils"),
|
|
115
|
+
FilterDefinition(name="is_ip_within", trusted=True, source="netutils"),
|
|
116
|
+
FilterDefinition(name="is_netmask", trusted=True, source="netutils"),
|
|
117
|
+
FilterDefinition(name="is_network", trusted=True, source="netutils"),
|
|
118
|
+
FilterDefinition(name="is_reversible_wildcardmask", trusted=True, source="netutils"),
|
|
119
|
+
FilterDefinition(name="is_valid_mac", trusted=True, source="netutils"),
|
|
120
|
+
FilterDefinition(name="longest_prefix_match", trusted=True, source="netutils"),
|
|
121
|
+
FilterDefinition(name="mac_normalize", trusted=True, source="netutils"),
|
|
122
|
+
FilterDefinition(name="mac_to_format", trusted=True, source="netutils"),
|
|
123
|
+
FilterDefinition(name="mac_to_int", trusted=True, source="netutils"),
|
|
124
|
+
FilterDefinition(name="mac_type", trusted=True, source="netutils"),
|
|
125
|
+
FilterDefinition(name="name_to_bits", trusted=True, source="netutils"),
|
|
126
|
+
FilterDefinition(name="name_to_bytes", trusted=True, source="netutils"),
|
|
127
|
+
FilterDefinition(name="name_to_name", trusted=True, source="netutils"),
|
|
128
|
+
FilterDefinition(name="netmask_to_cidr", trusted=True, source="netutils"),
|
|
129
|
+
FilterDefinition(name="netmask_to_wildcardmask", trusted=True, source="netutils"),
|
|
130
|
+
FilterDefinition(name="normalise_delimiter_caret_c", trusted=True, source="netutils"),
|
|
131
|
+
FilterDefinition(name="paloalto_panos_brace_to_set", trusted=True, source="netutils"),
|
|
132
|
+
FilterDefinition(name="paloalto_panos_clean_newlines", trusted=True, source="netutils"),
|
|
133
|
+
FilterDefinition(name="regex_findall", trusted=False, source="netutils"),
|
|
134
|
+
FilterDefinition(name="regex_match", trusted=False, source="netutils"),
|
|
135
|
+
FilterDefinition(name="regex_search", trusted=False, source="netutils"),
|
|
136
|
+
FilterDefinition(name="regex_split", trusted=False, source="netutils"),
|
|
137
|
+
FilterDefinition(name="regex_sub", trusted=False, source="netutils"),
|
|
138
|
+
FilterDefinition(name="sanitize_config", trusted=True, source="netutils"),
|
|
139
|
+
FilterDefinition(name="section_config", trusted=True, source="netutils"),
|
|
140
|
+
FilterDefinition(name="sort_interface_list", trusted=True, source="netutils"),
|
|
141
|
+
FilterDefinition(name="split_interface", trusted=True, source="netutils"),
|
|
142
|
+
FilterDefinition(name="uptime_seconds_to_string", trusted=True, source="netutils"),
|
|
143
|
+
FilterDefinition(name="uptime_string_to_seconds", trusted=True, source="netutils"),
|
|
144
|
+
FilterDefinition(name="version_metadata", trusted=True, source="netutils"),
|
|
145
|
+
FilterDefinition(name="vlanconfig_to_list", trusted=True, source="netutils"),
|
|
146
|
+
FilterDefinition(name="vlanlist_to_config", trusted=True, source="netutils"),
|
|
147
|
+
FilterDefinition(name="wildcardmask_to_netmask", trusted=True, source="netutils"),
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
AVAILABLE_FILTERS = BUILTIN_FILTERS + NETUTILS_FILTERS
|
infrahub_sdk/utils.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
import hashlib
|
|
4
5
|
import json
|
|
6
|
+
import uuid
|
|
5
7
|
from itertools import groupby
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
from typing import TYPE_CHECKING, Any
|
|
@@ -25,6 +27,11 @@ if TYPE_CHECKING:
|
|
|
25
27
|
from whenever import TimeDelta
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
def generate_short_id() -> str:
|
|
31
|
+
"""Generate a short unique ID"""
|
|
32
|
+
return base64.urlsafe_b64encode(uuid.uuid4().bytes).rstrip(b"=").decode("ascii").lower()
|
|
33
|
+
|
|
34
|
+
|
|
28
35
|
def base36encode(number: int) -> str:
|
|
29
36
|
if not isinstance(number, (int)):
|
|
30
37
|
raise TypeError("number must be an integer")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: infrahub-server
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run.
|
|
5
5
|
Home-page: https://opsmill.com
|
|
6
6
|
License: AGPL-3.0-only
|
|
@@ -32,6 +32,7 @@ Requires-Dist: nats-py (>=2.7.2,<3.0.0)
|
|
|
32
32
|
Requires-Dist: neo4j (>=5.28,<5.29)
|
|
33
33
|
Requires-Dist: neo4j-rust-ext (>=5.28,<5.29)
|
|
34
34
|
Requires-Dist: netaddr (==1.3.0)
|
|
35
|
+
Requires-Dist: netutils (==1.12.0)
|
|
35
36
|
Requires-Dist: numpy (>=1.24.2,<2.0.0) ; python_version >= "3.9" and python_version < "3.12"
|
|
36
37
|
Requires-Dist: numpy (>=1.26.2,<2.0.0) ; python_version >= "3.12"
|
|
37
38
|
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (==1.28.1)
|