mreg-cli 1.2.4__tar.gz → 1.3.0__tar.gz
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.
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.github/workflows/publish.yml +1 -1
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/CHANGELOG.md +34 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/PKG-INFO +3 -2
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/diff.py +110 -22
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/docker-compose.yml +2 -1
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/run_testsuite_and_record_V2.sh +3 -2
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/testsuite +89 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/testsuite-result.json +12299 -4728
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/_version.py +9 -4
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/abstracts.py +21 -12
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/endpoints.py +8 -0
- mreg_cli-1.3.0/mreg_cli/api/errors.py +91 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/models.py +588 -74
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/group.py +3 -1
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/core.py +67 -22
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/rr.py +4 -4
- mreg_cli-1.3.0/mreg_cli/commands/network.py +1260 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/policy.py +8 -3
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/registry.py +3 -1
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/help_formatter.py +3 -1
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/api.py +25 -18
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/PKG-INFO +3 -2
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/SOURCES.txt +1 -0
- mreg_cli-1.2.4/mreg_cli/commands/network.py +0 -515
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.github/workflows/test.yml +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.gitignore +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.markdownlint.json +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/AUTHORS +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/CONTRIBUTING +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/LICENSE +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/README.md +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/Dockerfile +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/run_testsuite_and_record.sh +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/data/mreg-cli.conf +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/__about__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/__main__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/fields.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/history.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/cli.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/base.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/dhcp.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/help.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/a_aaaa.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/bacnet.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/cname.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/label.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/logging.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/permission.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/recording.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/root.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/zone.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/config.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/errorbuilder.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/exceptions.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/main.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/outputmanager.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/prompt.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/py.typed +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/tags.txt +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/tokenfile.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/types.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/shared.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/validators.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/dependency_links.txt +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/entry_points.txt +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/requires.txt +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/top_level.txt +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/pyproject.toml +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/run.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/setup.cfg +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/api/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/api/test_fields.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/api/test_models.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/conftest.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_errorbuilder.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_exceptions.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_outputmanager.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_prompt.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_tokenfile.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_types.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/utilities/__init__.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/utilities/test_api.py +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tox.ini +0 -0
- {mreg_cli-1.2.4 → mreg_cli-1.3.0}/uv.lock +0 -0
|
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
<!-- ## Unreleased -->
|
|
9
9
|
|
|
10
|
+
## [1.3.0](https://github.com/unioslo/mreg-cli/releases/tag/1.3.0) - 2025-05-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Network policy commands:
|
|
15
|
+
- `network policy_add`
|
|
16
|
+
- `network policy_create`
|
|
17
|
+
- `network policy_delete`
|
|
18
|
+
- `network policy_info`
|
|
19
|
+
- `network policy_list`
|
|
20
|
+
- `network policy_rename`
|
|
21
|
+
- `network policy_remove`
|
|
22
|
+
- `network policy_set_description`
|
|
23
|
+
- `network policy_set_prefix`
|
|
24
|
+
- `network policy_unset_prefix`
|
|
25
|
+
- `network policy_attribute_add`
|
|
26
|
+
- `network policy_attribute_create`
|
|
27
|
+
- `network policy_attribute_delete`
|
|
28
|
+
- `network policy_attribute_info`
|
|
29
|
+
- `network policy_attribute_list`
|
|
30
|
+
- `network policy_attribute_remove`
|
|
31
|
+
- `network policy_attribute_set_description`
|
|
32
|
+
- `network community_create`
|
|
33
|
+
- `network community_delete`
|
|
34
|
+
- `network community_info`
|
|
35
|
+
- `network community_list`
|
|
36
|
+
- `network community_rename`
|
|
37
|
+
- `network community_set_description`
|
|
38
|
+
- `network community_host_add`
|
|
39
|
+
- `network community_host_remove`
|
|
40
|
+
- `network create -policy` option for specifying the policy of the network to create.
|
|
41
|
+
- Error handling for DRF errors.
|
|
42
|
+
- Handling of 404 errors that do not contain a JSON payload.
|
|
43
|
+
|
|
10
44
|
## [1.2.4](https://github.com/unioslo/mreg-cli/releases/tag/1.2.4) - 2025-01-30
|
|
11
45
|
|
|
12
46
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: mreg-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: MREG interactive command line client
|
|
5
5
|
Author: Others (see AUTHORS)
|
|
6
6
|
Author-email: Øyvind Kolbu <oyvind.kolbu@usit.uio.no>, Safet A <amedov@usit.uio.no>, Magnus Hirth <mhmorgan42@gmail.com>, Nico <nicolaydmohebi@gmail.com>, Marius Bakke <marius.bakke@usit.uio.no>, Fredrik Larsen <fredrik.larsen@usit.uio.no>, Paal Braathen <paal.braathen@usit.uio.no>, Nils Hiorth <nils@nls.me>, Dmytro Karpenko <dmytrok@usit.uio.no>
|
|
@@ -701,6 +701,7 @@ Requires-Dist: typing_extensions>=4.12.0
|
|
|
701
701
|
Requires-Dist: pydantic[email]>=2.7.1
|
|
702
702
|
Requires-Dist: pydantic-extra-types>=2.1.0
|
|
703
703
|
Requires-Dist: platformdirs>=4.3.0
|
|
704
|
+
Dynamic: license-file
|
|
704
705
|
|
|
705
706
|
# MREG CLI [](https://github.com/unioslo/mreg-cli/actions/workflows/test.yml)
|
|
706
707
|
|
|
@@ -7,13 +7,17 @@ import re
|
|
|
7
7
|
import sys
|
|
8
8
|
import urllib.parse
|
|
9
9
|
from enum import StrEnum
|
|
10
|
-
from typing import Any, Generator, Iterable, NamedTuple
|
|
10
|
+
from typing import Any, Generator, Iterable, Literal, NamedTuple, Self, TypeAlias, overload
|
|
11
11
|
|
|
12
12
|
from rich import box
|
|
13
13
|
from rich.console import Console, Group
|
|
14
14
|
from rich.markup import escape
|
|
15
15
|
from rich.panel import Panel
|
|
16
16
|
from rich.prompt import Prompt
|
|
17
|
+
from rich.table import Table
|
|
18
|
+
|
|
19
|
+
ProcessedCommand: TypeAlias = dict[str, Any]
|
|
20
|
+
UnprocessedCommand: TypeAlias = dict[str, Any]
|
|
17
21
|
|
|
18
22
|
console = Console(soft_wrap=True, highlight=False)
|
|
19
23
|
"""Stdout console used to print diffs."""
|
|
@@ -77,12 +81,35 @@ def fmt_lines(lines: Iterable[str]) -> str:
|
|
|
77
81
|
DIFF_HEADER = f"{fmt_line('-expected')}, {fmt_line('+tested')}"
|
|
78
82
|
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
class Diff(NamedTuple):
|
|
85
|
+
"""A diff between two lists of strings."""
|
|
86
|
+
|
|
87
|
+
number: int
|
|
88
|
+
"""The number of the diff."""
|
|
89
|
+
|
|
90
|
+
diff: list[str]
|
|
91
|
+
"""The diff between the two lists of strings."""
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def renderable(self) -> Group:
|
|
95
|
+
"""Console renderable for the diff."""
|
|
96
|
+
diff_lines = fmt_lines(self.diff)
|
|
97
|
+
diff_title = f"\n[bold]#{self.number}[/]"
|
|
98
|
+
return Group(diff_title, DIFF_HEADER, Panel(diff_lines, box=box.HORIZONTALS))
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_lines(cls, number: int, expected: list[str], result: list[str]) -> Self:
|
|
102
|
+
"""Print a diff between two lists of strings."""
|
|
103
|
+
lines = list(difflib.ndiff(expected, result))
|
|
104
|
+
return cls(number, lines)
|
|
105
|
+
|
|
106
|
+
def get_changed_lines(self) -> Iterable[str]:
|
|
107
|
+
"""Get lines with changes (added or removed)."""
|
|
108
|
+
return (line for line in self.diff if line.startswith("+ ") or line.startswith("- "))
|
|
109
|
+
|
|
110
|
+
def get_changes(self) -> str:
|
|
111
|
+
"""Get a formatted string of only the changes in the diff."""
|
|
112
|
+
return "\n".join(fmt_line(line) for line in self.get_changed_lines())
|
|
86
113
|
|
|
87
114
|
|
|
88
115
|
class Choice(StrEnum):
|
|
@@ -142,7 +169,17 @@ def preprocess_json(s: str) -> str:
|
|
|
142
169
|
return s
|
|
143
170
|
|
|
144
171
|
|
|
145
|
-
|
|
172
|
+
@overload
|
|
173
|
+
def load_commands(file: str, *, preprocess: Literal[False]) -> list[UnprocessedCommand]: ...
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@overload
|
|
177
|
+
def load_commands(file: str, *, preprocess: Literal[True]) -> list[ProcessedCommand]: ...
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def load_commands(
|
|
181
|
+
file: str, *, preprocess: bool = True
|
|
182
|
+
) -> list[ProcessedCommand] | list[UnprocessedCommand]:
|
|
146
183
|
"""Load JSON commands from a test suite log file."""
|
|
147
184
|
with open(file, "r") as f:
|
|
148
185
|
s = f.read()
|
|
@@ -159,8 +196,11 @@ def load_commands(file: str, preprocess: bool = True) -> list[dict[str, Any]]:
|
|
|
159
196
|
class TestCommand(NamedTuple):
|
|
160
197
|
"""A single command from a test suite log."""
|
|
161
198
|
|
|
162
|
-
command:
|
|
163
|
-
|
|
199
|
+
command: ProcessedCommand
|
|
200
|
+
"""Processed command data, with placeholders for non-deterministic values."""
|
|
201
|
+
|
|
202
|
+
original: UnprocessedCommand
|
|
203
|
+
"""Unprocessed original command data."""
|
|
164
204
|
|
|
165
205
|
|
|
166
206
|
class TestSuiteResult:
|
|
@@ -168,7 +208,7 @@ class TestSuiteResult:
|
|
|
168
208
|
|
|
169
209
|
def __init__(self, file: str) -> None:
|
|
170
210
|
self.file = file
|
|
171
|
-
self.commands = load_commands(file)
|
|
211
|
+
self.commands = load_commands(file, preprocess=True)
|
|
172
212
|
self.commands_original = load_commands(file, preprocess=False)
|
|
173
213
|
|
|
174
214
|
def iterate(
|
|
@@ -195,6 +235,23 @@ class TestSuiteResult:
|
|
|
195
235
|
)
|
|
196
236
|
|
|
197
237
|
|
|
238
|
+
class CommandDiff(NamedTuple):
|
|
239
|
+
"""(un)resolved diff between two test suite results."""
|
|
240
|
+
|
|
241
|
+
expected: UnprocessedCommand
|
|
242
|
+
"""The command that was expected."""
|
|
243
|
+
|
|
244
|
+
result: UnprocessedCommand
|
|
245
|
+
"""The command that was actually run."""
|
|
246
|
+
|
|
247
|
+
diff: Diff
|
|
248
|
+
"""The diff between the two commands."""
|
|
249
|
+
|
|
250
|
+
def get_command(self) -> str:
|
|
251
|
+
"""Get the name of the command that was run."""
|
|
252
|
+
return self.expected.get("command", self.result.get("command", "<unknown>"))
|
|
253
|
+
|
|
254
|
+
|
|
198
255
|
class CommandDiffer:
|
|
199
256
|
"""Diffs the results (JSON output) of commands from two files."""
|
|
200
257
|
|
|
@@ -208,8 +265,8 @@ class CommandDiffer:
|
|
|
208
265
|
self.expected = TestSuiteResult(file1)
|
|
209
266
|
self.result = TestSuiteResult(file2)
|
|
210
267
|
|
|
211
|
-
self.diff_resolved =
|
|
212
|
-
self.diff_unresolved =
|
|
268
|
+
self.diff_resolved: list[CommandDiff] = []
|
|
269
|
+
self.diff_unresolved: list[CommandDiff] = []
|
|
213
270
|
|
|
214
271
|
def diff(self) -> None:
|
|
215
272
|
"""Diff the two files."""
|
|
@@ -233,6 +290,14 @@ class CommandDiffer:
|
|
|
233
290
|
err_console.print(fmt_line(line))
|
|
234
291
|
raise CommandCountError(len(expected_commands), len(result_commands))
|
|
235
292
|
|
|
293
|
+
def log_unresolved_diff(self, expected: TestCommand, result: TestCommand, diff: Diff) -> None:
|
|
294
|
+
"""Log an unresolved diff between two commands."""
|
|
295
|
+
self.diff_unresolved.append(CommandDiff(expected.original, result.original, diff))
|
|
296
|
+
|
|
297
|
+
def log_resolved_diff(self, expected: TestCommand, result: TestCommand, diff: Diff) -> None:
|
|
298
|
+
"""Log a resolved diff between two commands."""
|
|
299
|
+
self.diff_resolved.append(CommandDiff(expected.original, result.original, diff))
|
|
300
|
+
|
|
236
301
|
def diff_command_results(self) -> None:
|
|
237
302
|
"""Diff the contents of each command in the two files."""
|
|
238
303
|
# NOTE: Only used in review mode
|
|
@@ -257,10 +322,10 @@ class CommandDiffer:
|
|
|
257
322
|
|
|
258
323
|
if expected_lines != result_lines:
|
|
259
324
|
diff_n += 1
|
|
260
|
-
d =
|
|
325
|
+
d = Diff.from_lines(diff_n, expected_lines, result_lines)
|
|
261
326
|
if not self.review:
|
|
262
|
-
self.
|
|
263
|
-
console.print(d)
|
|
327
|
+
self.log_unresolved_diff(expected, result, d)
|
|
328
|
+
console.print(d.renderable)
|
|
264
329
|
continue # Nothing more to do for this command
|
|
265
330
|
|
|
266
331
|
if no_all:
|
|
@@ -268,7 +333,7 @@ class CommandDiffer:
|
|
|
268
333
|
elif yes_all:
|
|
269
334
|
choice = Choice.YES
|
|
270
335
|
else:
|
|
271
|
-
console.print(d)
|
|
336
|
+
console.print(d.renderable)
|
|
272
337
|
choice = Prompt.ask(
|
|
273
338
|
f"Accept change #{diff_n}? ({Choice.as_string()})",
|
|
274
339
|
choices=list(Choice),
|
|
@@ -284,23 +349,42 @@ class CommandDiffer:
|
|
|
284
349
|
if choice == Choice.YES:
|
|
285
350
|
# Accept new line
|
|
286
351
|
new_testsuite_results.append(result.original)
|
|
287
|
-
self.
|
|
352
|
+
self.log_resolved_diff(expected, result, d)
|
|
288
353
|
else:
|
|
289
354
|
# Keep old line
|
|
290
355
|
new_testsuite_results.append(expected.original)
|
|
291
|
-
self.
|
|
356
|
+
self.log_unresolved_diff(expected, result, d)
|
|
292
357
|
else:
|
|
293
358
|
# No diff, keep old line
|
|
294
359
|
new_testsuite_results.append(expected.original)
|
|
295
360
|
|
|
296
361
|
# Only write back changes if we are in review mode and there are changes
|
|
297
|
-
if self.review and self.diff_resolved > 0:
|
|
362
|
+
if self.review and len(self.diff_resolved) > 0:
|
|
298
363
|
# Write accepted changes back to file1
|
|
299
364
|
with open(self.file1, "w") as f:
|
|
300
365
|
json.dump(new_testsuite_results, f, indent=2)
|
|
301
366
|
err_console.print(f"Wrote accepted changes back to {self.file1}")
|
|
302
367
|
|
|
303
368
|
|
|
369
|
+
def print_diff_summary(diffs: list[CommandDiff], title: str) -> None:
|
|
370
|
+
"""Print a summary of diffs."""
|
|
371
|
+
if diffs:
|
|
372
|
+
tbl = Table(
|
|
373
|
+
"Command",
|
|
374
|
+
"Diff",
|
|
375
|
+
title=title,
|
|
376
|
+
show_header=True,
|
|
377
|
+
show_lines=True,
|
|
378
|
+
expand=True,
|
|
379
|
+
)
|
|
380
|
+
for cmd in diffs:
|
|
381
|
+
tbl.add_row(
|
|
382
|
+
cmd.get_command(),
|
|
383
|
+
cmd.diff.get_changes(),
|
|
384
|
+
)
|
|
385
|
+
err_console.print(tbl)
|
|
386
|
+
|
|
387
|
+
|
|
304
388
|
def main() -> None:
|
|
305
389
|
"""Compare two JSON files."""
|
|
306
390
|
parser = argparse.ArgumentParser(prog="diff.py")
|
|
@@ -327,10 +411,14 @@ def main() -> None:
|
|
|
327
411
|
# I.e. resolved msg followed by unresolved msg with non-zero exit code
|
|
328
412
|
resolved = differ.diff_resolved
|
|
329
413
|
unresolved = differ.diff_unresolved
|
|
414
|
+
print_diff_summary(resolved, "[green italic]Resolved diffs[/]")
|
|
415
|
+
print_diff_summary(unresolved, "[red italic]Unresolved diffs[/]")
|
|
330
416
|
if resolved:
|
|
331
|
-
err_console.print(f"[green]Resolved {resolved} diffs between {file1} and {file2}[/]")
|
|
417
|
+
err_console.print(f"[green]Resolved {len(resolved)} diffs between {file1} and {file2}[/]")
|
|
332
418
|
if unresolved: # non-zero exit code if unresolved diffs
|
|
333
|
-
err_console.print(
|
|
419
|
+
err_console.print(
|
|
420
|
+
f"[red]{len(unresolved)} unresolved diffs between {file1} and {file2}.[/]"
|
|
421
|
+
)
|
|
334
422
|
sys.exit(1)
|
|
335
423
|
if not resolved and not unresolved: # no diffs found
|
|
336
424
|
err_console.print(f"No differences found between {file1} and {file2}")
|
|
@@ -13,7 +13,7 @@ services:
|
|
|
13
13
|
start_period: 5s
|
|
14
14
|
|
|
15
15
|
mreg:
|
|
16
|
-
image: ghcr.io/unioslo/mreg:
|
|
16
|
+
image: ghcr.io/unioslo/mreg:master
|
|
17
17
|
container_name: mreg
|
|
18
18
|
depends_on:
|
|
19
19
|
postgres:
|
|
@@ -25,6 +25,7 @@ services:
|
|
|
25
25
|
- MREG_DB_NAME=mreg
|
|
26
26
|
- MREG_DB_USER=mreg
|
|
27
27
|
- MREG_DB_PASSWORD=mreg
|
|
28
|
+
- MREG_MAP_GLOBAL_COMMUNITY_NAMES=1
|
|
28
29
|
- CI=yes
|
|
29
30
|
healthcheck:
|
|
30
31
|
test: sh -c "[ -f /var/run/gunicorn.pid ]"
|
|
@@ -28,7 +28,6 @@ function cleanup {
|
|
|
28
28
|
docker ps -a | grep "mreg-" | awk '{print $1}' | xargs -r docker stop
|
|
29
29
|
docker ps -a | grep "mreg-" | awk '{print $1}' | xargs -r docker rm
|
|
30
30
|
docker images | grep "mreg-" | awk '{print $1}' | xargs -r docker rmi
|
|
31
|
-
rm -f new_testsuite_log.json
|
|
32
31
|
echo "cleanup done."
|
|
33
32
|
}
|
|
34
33
|
trap cleanup EXIT
|
|
@@ -59,7 +58,7 @@ else
|
|
|
59
58
|
fi
|
|
60
59
|
|
|
61
60
|
# give mreg some time to create the database schema and start up
|
|
62
|
-
sleep
|
|
61
|
+
sleep 5
|
|
63
62
|
|
|
64
63
|
# create a superuser
|
|
65
64
|
if docker exec -t mreg uv version >/dev/null; then
|
|
@@ -81,8 +80,10 @@ if [[ -n "$GITHUB_ACTIONS" ]]; then
|
|
|
81
80
|
docker run --rm --tty --entrypoint bash finished-mreg-tests -c 'cd /build/ci; /root/.local/bin/uv run diff.py testsuite-result.json new_testsuite_log.json'
|
|
82
81
|
exit $?
|
|
83
82
|
else
|
|
83
|
+
set +e
|
|
84
84
|
docker run -it --name finished-mreg-tests --entrypoint bash finished-mreg-tests -c 'cd /build/ci; /root/.local/bin/uv run diff.py testsuite-result.json new_testsuite_log.json --review'
|
|
85
85
|
EXITCODE=$?
|
|
86
|
+
docker cp finished-mreg-tests:/build/ci/new_testsuite_log.json .
|
|
86
87
|
docker cp finished-mreg-tests:/build/ci/testsuite-result.json .
|
|
87
88
|
exit $EXITCODE
|
|
88
89
|
fi
|
|
@@ -383,6 +383,95 @@ permission network_remove 192.168.0.0/16 mygroup .*
|
|
|
383
383
|
label remove postit
|
|
384
384
|
|
|
385
385
|
|
|
386
|
+
##### Tests: Network Policy
|
|
387
|
+
network create -network 10.0.2.0/28 -desc "TinyNet"
|
|
388
|
+
network policy_attribute_create foo "Foo attribute"
|
|
389
|
+
network policy_attribute_create bar "Bar attribute"
|
|
390
|
+
network policy_attribute_create baz "Baz attribute"
|
|
391
|
+
network policy_create SecurePolicy "Secure policy for a network" -attribute foo -attribute bar
|
|
392
|
+
network policy_create Autoconfigure "Automatic host configuration" -attribute baz
|
|
393
|
+
network policy_create NoICMP "ICMP disallowed" # no attributes
|
|
394
|
+
network policy_list
|
|
395
|
+
network policy_info SecurePolicy
|
|
396
|
+
network policy_info Autoconfigure
|
|
397
|
+
network policy_add 10.0.2.0/28 SecurePolicy
|
|
398
|
+
network policy_add 10.0.2.0/28 Autoconfigure # requires force
|
|
399
|
+
network policy_add 10.0.2.0/28 Autoconfigure -force
|
|
400
|
+
network info 10.0.2.0/28
|
|
401
|
+
network policy_delete Autoconfigure # requires force
|
|
402
|
+
network policy_delete Autoconfigure -force
|
|
403
|
+
network info 10.0.2.0/28
|
|
404
|
+
network policy_list
|
|
405
|
+
network policy_add 10.0.2.0/28 SecurePolicy
|
|
406
|
+
network policy_rename SecurePolicy InsecurePolicy
|
|
407
|
+
network policy_list
|
|
408
|
+
network info 10.0.2.0/28
|
|
409
|
+
network policy_set_description InsecurePolicy "Insecure policy for a network"
|
|
410
|
+
network policy_attribute_add InsecurePolicy baz
|
|
411
|
+
network policy_attribute_set_description baz "Baz attribute (new)"
|
|
412
|
+
network policy_attribute_list
|
|
413
|
+
network policy_attribute_remove InsecurePolicy foo
|
|
414
|
+
network policy_info InsecurePolicy
|
|
415
|
+
network policy_attribute_delete bar # requires force (in use)
|
|
416
|
+
network policy_attribute_delete bar -force
|
|
417
|
+
|
|
418
|
+
# Communities
|
|
419
|
+
host add tinyhost -ip 10.0.2.0/28 -macaddress 11:22:33:aa:bb:cc -contact tinyhost@example.org
|
|
420
|
+
network community_create 10.0.2.0/28 guests "Guest hosts."
|
|
421
|
+
network community_create 10.0.2.0/28 lab "Lab hosts."
|
|
422
|
+
network community_list 10.0.2.0/28
|
|
423
|
+
network community_host_add tinyhost guests
|
|
424
|
+
host info tinyhost
|
|
425
|
+
network community_host_add tinyhost lab
|
|
426
|
+
host info tinyhost
|
|
427
|
+
# TODO: test network community_host_add with specific IP address on host with multiple IPs
|
|
428
|
+
network community_info 10.0.2.0/28 guests
|
|
429
|
+
network community_info 10.0.2.0/28 lab
|
|
430
|
+
|
|
431
|
+
# Custom communtiy mapping prefix
|
|
432
|
+
network policy_create CustomPrefix "Custom community mapping prefix" -prefix "custom" # no attributes
|
|
433
|
+
network create -network 10.0.3.0/24 -desc "CustomPrefixNet" -policy CustomPrefix # add policy during creation
|
|
434
|
+
network policy_info CustomPrefix
|
|
435
|
+
network community_create 10.0.3.0/24 foo "Foo community"
|
|
436
|
+
network community_info 10.0.3.0/24 foo
|
|
437
|
+
network policy_set_prefix CustomPrefix "newprefix"
|
|
438
|
+
network community_info 10.0.3.0/24 foo
|
|
439
|
+
network policy_unset_prefix CustomPrefix
|
|
440
|
+
network community_info 10.0.3.0/24 foo
|
|
441
|
+
network remove 10.0.3.0/24 -force
|
|
442
|
+
network policy_delete CustomPrefix
|
|
443
|
+
|
|
444
|
+
## Rename community
|
|
445
|
+
network community_rename 10.0.2.0/28 lab labequipment
|
|
446
|
+
network community_set_description 10.0.2.0/28 labequipment "Lab equipment (expensive)"
|
|
447
|
+
network community_info 10.0.2.0/28 labequipment
|
|
448
|
+
|
|
449
|
+
## Add another host to a community before displaying communities with hosts
|
|
450
|
+
host add tinierhost -ip 10.0.2.0/28 -contact tinierhost@example.org
|
|
451
|
+
network community_host_add tinierhost guests
|
|
452
|
+
network community_list 10.0.2.0/28 -hosts
|
|
453
|
+
|
|
454
|
+
## Attempt to add host from other network to community (fails)
|
|
455
|
+
network create -network 10.0.0.0/24 -desc "MediumNet"
|
|
456
|
+
host add mediumhost -ip 10.0.0.0/24 -macaddress 11:22:34:aa:bb:cd -contact mediumhost@example.org
|
|
457
|
+
network community_host_add mediumhost labequipment # fails, community not found in network
|
|
458
|
+
host remove mediumhost -force
|
|
459
|
+
network remove 10.0.0.0/24
|
|
460
|
+
|
|
461
|
+
## Delete community (remove host from community first)
|
|
462
|
+
network community_delete 10.0.2.0/28 labequipment # fails
|
|
463
|
+
network community_host_remove tinyhost labequipment
|
|
464
|
+
network community_delete 10.0.2.0/28 labequipment
|
|
465
|
+
|
|
466
|
+
## Delete community with hosts (force)
|
|
467
|
+
network community_delete 10.0.2.0/28 guests # fails
|
|
468
|
+
network community_delete 10.0.2.0/28 guests -force
|
|
469
|
+
|
|
470
|
+
## Clean up community resources
|
|
471
|
+
host remove tinyhost
|
|
472
|
+
host remove tinierhost
|
|
473
|
+
network remove 10.0.2.0/28 -force
|
|
474
|
+
|
|
386
475
|
##### Tests: Zone
|
|
387
476
|
# - Zone removal
|
|
388
477
|
zone delete example.org
|