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.
Files changed (91) hide show
  1. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.github/workflows/publish.yml +1 -1
  2. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/CHANGELOG.md +34 -0
  3. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/PKG-INFO +3 -2
  4. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/diff.py +110 -22
  5. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/docker-compose.yml +2 -1
  6. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/run_testsuite_and_record_V2.sh +3 -2
  7. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/testsuite +89 -0
  8. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/testsuite-result.json +12299 -4728
  9. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/_version.py +9 -4
  10. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/abstracts.py +21 -12
  11. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/endpoints.py +8 -0
  12. mreg_cli-1.3.0/mreg_cli/api/errors.py +91 -0
  13. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/models.py +588 -74
  14. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/group.py +3 -1
  15. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/core.py +67 -22
  16. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/rr.py +4 -4
  17. mreg_cli-1.3.0/mreg_cli/commands/network.py +1260 -0
  18. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/policy.py +8 -3
  19. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/registry.py +3 -1
  20. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/help_formatter.py +3 -1
  21. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/api.py +25 -18
  22. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/PKG-INFO +3 -2
  23. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/SOURCES.txt +1 -0
  24. mreg_cli-1.2.4/mreg_cli/commands/network.py +0 -515
  25. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.github/workflows/test.yml +0 -0
  26. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.gitignore +0 -0
  27. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/.markdownlint.json +0 -0
  28. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/AUTHORS +0 -0
  29. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/CONTRIBUTING +0 -0
  30. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/LICENSE +0 -0
  31. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/README.md +0 -0
  32. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/Dockerfile +0 -0
  33. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/ci/run_testsuite_and_record.sh +0 -0
  34. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/data/mreg-cli.conf +0 -0
  35. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/__about__.py +0 -0
  36. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/__init__.py +0 -0
  37. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/__main__.py +0 -0
  38. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/__init__.py +0 -0
  39. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/fields.py +0 -0
  40. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/api/history.py +0 -0
  41. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/cli.py +0 -0
  42. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/__init__.py +0 -0
  43. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/base.py +0 -0
  44. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/dhcp.py +0 -0
  45. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/help.py +0 -0
  46. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host.py +0 -0
  47. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/__init__.py +0 -0
  48. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/a_aaaa.py +0 -0
  49. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/bacnet.py +0 -0
  50. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/host_submodules/cname.py +0 -0
  51. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/label.py +0 -0
  52. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/logging.py +0 -0
  53. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/permission.py +0 -0
  54. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/recording.py +0 -0
  55. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/root.py +0 -0
  56. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/commands/zone.py +0 -0
  57. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/config.py +0 -0
  58. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/errorbuilder.py +0 -0
  59. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/exceptions.py +0 -0
  60. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/main.py +0 -0
  61. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/outputmanager.py +0 -0
  62. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/prompt.py +0 -0
  63. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/py.typed +0 -0
  64. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/tags.txt +0 -0
  65. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/tokenfile.py +0 -0
  66. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/types.py +0 -0
  67. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/__init__.py +0 -0
  68. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/shared.py +0 -0
  69. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli/utilities/validators.py +0 -0
  70. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/dependency_links.txt +0 -0
  71. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/entry_points.txt +0 -0
  72. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/requires.txt +0 -0
  73. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/mreg_cli.egg-info/top_level.txt +0 -0
  74. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/pyproject.toml +0 -0
  75. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/run.py +0 -0
  76. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/setup.cfg +0 -0
  77. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/__init__.py +0 -0
  78. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/api/__init__.py +0 -0
  79. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/api/test_fields.py +0 -0
  80. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/api/test_models.py +0 -0
  81. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/conftest.py +0 -0
  82. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_errorbuilder.py +0 -0
  83. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_exceptions.py +0 -0
  84. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_outputmanager.py +0 -0
  85. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_prompt.py +0 -0
  86. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_tokenfile.py +0 -0
  87. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/test_types.py +0 -0
  88. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/utilities/__init__.py +0 -0
  89. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tests/utilities/test_api.py +0 -0
  90. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/tox.ini +0 -0
  91. {mreg_cli-1.2.4 → mreg_cli-1.3.0}/uv.lock +0 -0
@@ -125,7 +125,7 @@ jobs:
125
125
  path: dist
126
126
 
127
127
  - name: Push build artifacts to PyPI
128
- uses: pypa/gh-action-pypi-publish@v1.8.14
128
+ uses: pypa/gh-action-pypi-publish@release/v1
129
129
 
130
130
  publish_github:
131
131
  name: Publish GitHub release
@@ -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.2
1
+ Metadata-Version: 2.4
2
2
  Name: mreg-cli
3
- Version: 1.2.4
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 [![Build Status](https://github.com/unioslo/mreg-cli/actions/workflows/test.yml/badge.svg)](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
- def get_diff(count: int, expected: list[str], result: list[str]) -> Group:
81
- """Print a diff between two lists of strings."""
82
- gen = difflib.ndiff(expected, result)
83
- lines = fmt_lines(gen)
84
- diff_title = f"\n[bold]#{count}[/]"
85
- return Group(diff_title, DIFF_HEADER, Panel(lines, box=box.HORIZONTALS))
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
- def load_commands(file: str, preprocess: bool = True) -> list[dict[str, Any]]:
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: dict[str, Any]
163
- original: dict[str, Any]
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 = 0
212
- self.diff_unresolved = 0
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 = get_diff(diff_n, expected_lines, result_lines)
325
+ d = Diff.from_lines(diff_n, expected_lines, result_lines)
261
326
  if not self.review:
262
- self.diff_unresolved += 1
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.diff_resolved += 1
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.diff_unresolved += 1
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(f"[red]{unresolved} unresolved diffs between {file1} and {file2}.[/]")
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:latest
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 5s
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