diff --git a/tests/test_node_image_definitions.py b/tests/test_node_image_definitions.py index cfb2a65..e1e6eab 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] == defs._URL_TEMPLATES["reload_defs"] + + # 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..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 @@ -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,30 @@ def remove_image_definition(self, definition_id: str) -> None: url = self._url_for("image_def", definition_id=definition_id) self._session.delete(url) + @_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() + 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