From fc5ee02cce6676a05232ab62d0b08264dd6aaf03 Mon Sep 17 00:00:00 2001 From: btaran_cisco Date: Thu, 14 May 2026 12:44:35 +0200 Subject: [PATCH 1/2] CMLDEV-1110: added reload_definitions method to NodeImageDefinitions; Included brief test coverage; --- tests/test_node_image_definitions.py | 30 ++++++++++++++++++++ virl2_client/models/node_image_definition.py | 20 +++++++++++++ 2 files changed, 50 insertions(+) diff --git a/tests/test_node_image_definitions.py b/tests/test_node_image_definitions.py index cfb2a65..3fea42e 100644 --- a/tests/test_node_image_definitions.py +++ b/tests/test_node_image_definitions.py @@ -293,6 +293,36 @@ def test_remove_dropfolder_image() -> None: assert session.delete.mock_calls[0].args[0] == "images/manage/image.qcow2" +def test_reload_definitions() -> None: + """reload_definitions returns report with node and image definition changes. + + NOTE: LLM-generated test -- verify for correctness. + """ + session = MagicMock() + defs = NodeImageDefinitions(session) + expected_report = { + "node_definitions": { + "unchanged": ["iosv", "nxosv"], + "updated": ["asav"], + "new": ["custom-node"], + "removed": ["old-node"], + "failed": [], + }, + "image_definitions": { + "unchanged": ["iosv-159-3"], + "updated": [], + "new": ["custom-image"], + "removed": [], + "failed": ["Error loading bad-image.yaml"], + }, + } + session.put.return_value.json.return_value = expected_report + + report = defs.reload_definitions() + assert report == expected_report + assert session.put.mock_calls[0].args[0] == "reload_definitions" + + # everything except str or dict is invalid INVALID_DEFINITIONS: dict[str, Any] = { "none": None, diff --git a/virl2_client/models/node_image_definition.py b/virl2_client/models/node_image_definition.py index 24102bf..3d7128d 100644 --- a/virl2_client/models/node_image_definition.py +++ b/virl2_client/models/node_image_definition.py @@ -49,6 +49,7 @@ class NodeImageDefinitions: "upload": "images/upload", "image_list": "list_image_definition_drop_folder", "image_manage": "images/manage/{filename}", + "reload_defs": "reload_definitions", } def __init__(self, session: httpx.Client) -> None: @@ -313,6 +314,25 @@ def remove_image_definition(self, definition_id: str) -> None: url = self._url_for("image_def", definition_id=definition_id) self._session.delete(url) + def reload_definitions(self) -> dict[str, Any]: + """ + Reload node and image definitions from disk. + + Triggers a re-scan of node and image definition YAML files, + returning a report of changes (unchanged, updated, new, removed, failed). + + Example:: + + report = client_library.definitions.reload_definitions() + print(f"New node definitions: {report['node_definitions']['new']}") + print(f"Updated image definitions: {report['image_definitions']['updated']}") + + :returns: Report dict with node_definitions and image_definitions keys, + each containing lists of unchanged, updated, new, removed, and failed entries. + """ + url = self._url_for("reload_defs") + return self._session.put(url).json() + def print_progress_bar( cur: int, total: int, start_time: float, length: int = 50 From 8c855e99fd109370627ab5d117b8b78075490c97 Mon Sep 17 00:00:00 2001 From: Tomas Mikuska Date: Mon, 18 May 2026 10:03:18 +0200 Subject: [PATCH 2/2] CMLDEV-1110: gate reload_definitions on CML 2.10 and tighten typing Per PR #227 review: - Decorate reload_definitions with @_requires_version("2.10.0") so older controllers raise FeatureNotSupported instead of bubbling up an HTTPError for a missing route. The server endpoint is annotated CMLVersion.V2_10.introduced() and gated by IsAdminPermDepends. - Surface the version floor and admin-only requirement in the docstring. - Tighten return type from dict[str, Any] to dict[str, dict[str, list[str]]], mirroring the server's DefinitionReportResponse shape. - Anchor the test URL assertion to _URL_TEMPLATES["reload_defs"] so renaming the template key would surface a failure. --- tests/test_node_image_definitions.py | 2 +- virl2_client/models/node_image_definition.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_node_image_definitions.py b/tests/test_node_image_definitions.py index 3fea42e..e1e6eab 100644 --- a/tests/test_node_image_definitions.py +++ b/tests/test_node_image_definitions.py @@ -320,7 +320,7 @@ def test_reload_definitions() -> None: report = defs.reload_definitions() assert report == expected_report - assert session.put.mock_calls[0].args[0] == "reload_definitions" + assert session.put.mock_calls[0].args[0] == defs._URL_TEMPLATES["reload_defs"] # everything except str or dict is invalid diff --git a/virl2_client/models/node_image_definition.py b/virl2_client/models/node_image_definition.py index 3d7128d..9eda0ea 100644 --- a/virl2_client/models/node_image_definition.py +++ b/virl2_client/models/node_image_definition.py @@ -28,7 +28,7 @@ from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar from ..exceptions import InvalidContentType, InvalidImageFile -from ..utils import get_url_from_template +from ..utils import _requires_version, get_url_from_template if TYPE_CHECKING: import httpx @@ -314,13 +314,18 @@ def remove_image_definition(self, definition_id: str) -> None: url = self._url_for("image_def", definition_id=definition_id) self._session.delete(url) - def reload_definitions(self) -> dict[str, Any]: + @_requires_version("2.10.0") + def reload_definitions(self) -> dict[str, dict[str, list[str]]]: """ Reload node and image definitions from disk. Triggers a re-scan of node and image definition YAML files, returning a report of changes (unchanged, updated, new, removed, failed). + Requires CML server >= 2.10. + + Requires admin permission. + Example:: report = client_library.definitions.reload_definitions()