mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-02-04 07:51:50 +00:00
lxd_storage_pool_info, lxd_storage_volume_info: new modules (#11198)
* Fix mistaken rebase * plugins/modules/lxd_storage_: include error codes, clean up notes * plugins/modules/lxd_storage_: snap_url, ruff fix * plugins/modules/lxd_storage_volume_info.py: remove checks on expected api returned bits * plugins/modules/lxd_storage_volume_info.py: required: true * tests/integration/targets/lxd_storage_volume_info/tasks/main.yaml: add Test fetching specific volume by name * tests/unit/plugins/modules/test_lxd_storage_: add unit tests * tests/integration/targets/lxd_storage_pool_info/tasks/main.yaml: add integratio tests * tests/integration/targets/lxd_storage_: not required * tests/integration/targets/lxd_storage_: not required perhaps, lxd_project has them * tests/unit/plugins/modules/test_lxd_storage_volume_info.py: fix python3.8 tests * tests/unit/plugins/modules/test_lxd_storage_pool_info.py: fix python3.8 * tests/integration/targets/lxd_storage_: correct paths for aliases * tests/unit/plugins/modules/test_lxd_storage_volume_info.py: remove backticks * tests/unit/plugins/modules/test_lxd_storage_volume_info.py: remove blank line * tests/unit/plugins/modules/test_lxd_storage_: python3.8 changes * tests/unit/plugins/modules/test_lxd_storage_: python3.8 changes * tests/unit/plugins/lookup/test_github_app_access_token.py: restore * tests/unit/plugins/connection/test_wsl.py: restore * plugins/modules/lxd_storage_: use ANSIBLE_LXD_DEFAULT_SNAP_URL and put API version into const * lxd_storage_volume_info: use recursion to gather all volume details * tests/integration/targets/lxd_storage_volume_info/tasks/main.yaml: fix silet skipped failures * tests/integration/targets/lxd_storage_pool_info/tasks/main.yaml: fix silet failures * lxd_storage_pool_info: update to use recursion to gather all details in one shot * Remove unnecessary change. --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
16d51a8233
commit
6365b5a981
9 changed files with 1243 additions and 0 deletions
125
tests/unit/plugins/modules/test_lxd_storage_pool_info.py
Normal file
125
tests/unit/plugins/modules/test_lxd_storage_pool_info.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Copyright (c) 2025, Sean McAvoy (@smcavoy) <seanmcavoy@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import lxd_storage_pool_info as module
|
||||
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
|
||||
AnsibleExitJson,
|
||||
AnsibleFailJson,
|
||||
ModuleTestCase,
|
||||
set_module_args,
|
||||
)
|
||||
|
||||
|
||||
class FakeLXDClient:
|
||||
responses: dict[tuple[str, str], dict] = {}
|
||||
|
||||
def __init__(self, url, key_file=None, cert_file=None, debug=False, **kwargs):
|
||||
self.url = url
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.debug = debug
|
||||
self.logs = [{"type": "fake-request"}] if debug else []
|
||||
|
||||
def authenticate(self, trust_password):
|
||||
self.trust_password = trust_password
|
||||
|
||||
def do(self, method, url, ok_error_codes=None, **kwargs):
|
||||
try:
|
||||
return self.responses[(method, url)]
|
||||
except KeyError as exc:
|
||||
raise AssertionError(f"Unexpected call: {method} {url}") from exc
|
||||
|
||||
|
||||
class TestLXDStoragePoolInfo(ModuleTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.module = module
|
||||
|
||||
def test_returns_storage_pools(self):
|
||||
"""Pool metadata from the API is returned unchanged using recursion."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools?recursion=1"): {
|
||||
"type": "sync",
|
||||
"metadata": [
|
||||
{"name": "default", "driver": "dir"},
|
||||
{"name": "fast", "driver": "zfs"},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_pools"] == [
|
||||
{"name": "default", "driver": "dir"},
|
||||
{"name": "fast", "driver": "zfs"},
|
||||
]
|
||||
|
||||
def test_returns_specific_storage_pool(self):
|
||||
"""When name is specified, only that pool is returned."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools/default"): {"type": "sync", "metadata": {"name": "default", "driver": "dir"}},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({"name": "default"}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_pools"] == [
|
||||
{"name": "default", "driver": "dir"},
|
||||
]
|
||||
|
||||
def test_filters_storage_pools_by_type(self):
|
||||
"""Pools can be filtered by driver type using recursion."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools?recursion=1"): {
|
||||
"type": "sync",
|
||||
"metadata": [
|
||||
{"name": "default", "driver": "dir"},
|
||||
{"name": "fast", "driver": "zfs"},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({"type": ["zfs"]}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_pools"] == [
|
||||
{"name": "fast", "driver": "zfs"},
|
||||
]
|
||||
|
||||
def test_error_code_returned_on_failure(self):
|
||||
"""Failures surface the LXD error code for easier debugging."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools?recursion=1"): {
|
||||
"type": "error",
|
||||
"error": "unavailable",
|
||||
"error_code": 503,
|
||||
}
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleFailJson) as exc:
|
||||
with set_module_args({}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["error_code"] == 503
|
||||
assert "Failed to retrieve storage pools" in result["msg"]
|
||||
156
tests/unit/plugins/modules/test_lxd_storage_volume_info.py
Normal file
156
tests/unit/plugins/modules/test_lxd_storage_volume_info.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
# Copyright (c) 2025, Sean McAvoy (@smcavoy) <seanmcavoy@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import lxd_storage_volume_info as module
|
||||
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
|
||||
AnsibleExitJson,
|
||||
AnsibleFailJson,
|
||||
ModuleTestCase,
|
||||
set_module_args,
|
||||
)
|
||||
|
||||
|
||||
class FakeLXDClient:
|
||||
responses: dict[tuple[str, str], dict] = {}
|
||||
|
||||
def __init__(self, url, key_file=None, cert_file=None, debug=False, **kwargs):
|
||||
self.url = url
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.debug = debug
|
||||
self.logs = [{"type": "fake-request"}] if debug else []
|
||||
|
||||
def authenticate(self, trust_password):
|
||||
self.trust_password = trust_password
|
||||
|
||||
def do(self, method, url, ok_error_codes=None, **kwargs):
|
||||
try:
|
||||
return self.responses[(method, url)]
|
||||
except KeyError as exc:
|
||||
raise AssertionError(f"Unexpected call: {method} {url}") from exc
|
||||
|
||||
|
||||
class TestLXDStorageVolumeInfo(ModuleTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.module = module
|
||||
|
||||
def test_returns_all_volumes_for_pool(self):
|
||||
"""Volume metadata is returned for every volume in the pool using recursion."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools/default"): {"type": "sync", "metadata": {"name": "default"}},
|
||||
("GET", "/1.0/storage-pools/default/volumes?recursion=1"): {
|
||||
"type": "sync",
|
||||
"metadata": [
|
||||
{"name": "data", "type": "custom"},
|
||||
{"name": "web", "type": "container"},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({"pool": "default"}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_volumes"] == [
|
||||
{"name": "data", "type": "custom"},
|
||||
{"name": "web", "type": "container"},
|
||||
]
|
||||
|
||||
def test_returns_specific_storage_volume(self):
|
||||
"""When name is specified without type, recursion is used to find it."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools/default"): {"type": "sync", "metadata": {"name": "default"}},
|
||||
("GET", "/1.0/storage-pools/default/volumes?recursion=1"): {
|
||||
"type": "sync",
|
||||
"metadata": [
|
||||
{"name": "data", "type": "custom"},
|
||||
{"name": "web", "type": "container"},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({"pool": "default", "name": "data"}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_volumes"] == [
|
||||
{"name": "data", "type": "custom"},
|
||||
]
|
||||
|
||||
def test_filters_storage_volumes_by_type(self):
|
||||
"""Volumes can be filtered by type using recursion."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools/default"): {"type": "sync", "metadata": {"name": "default"}},
|
||||
("GET", "/1.0/storage-pools/default/volumes?recursion=1"): {
|
||||
"type": "sync",
|
||||
"metadata": [
|
||||
{"name": "data", "type": "custom"},
|
||||
{"name": "web", "type": "container"},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({"pool": "default", "type": "container"}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_volumes"] == [
|
||||
{"name": "web", "type": "container"},
|
||||
]
|
||||
|
||||
def test_returns_specific_volume_with_type_using_direct_request(self):
|
||||
"""When both name and type are specified, a direct API call is made (no recursion)."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools/default"): {"type": "sync", "metadata": {"name": "default"}},
|
||||
("GET", "/1.0/storage-pools/default/volumes/custom/data"): {
|
||||
"type": "sync",
|
||||
"metadata": {"name": "data", "type": "custom", "config": {"size": "10GiB"}},
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleExitJson) as exc:
|
||||
with set_module_args({"pool": "default", "name": "data", "type": "custom"}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["storage_volumes"] == [
|
||||
{"name": "data", "type": "custom", "config": {"size": "10GiB"}},
|
||||
]
|
||||
|
||||
def test_error_code_returned_when_listing_volumes_fails(self):
|
||||
"""Errors from LXD are surfaced with the numeric code."""
|
||||
FakeLXDClient.responses = {
|
||||
("GET", "/1.0/storage-pools/default"): {"type": "sync", "metadata": {"name": "default"}},
|
||||
("GET", "/1.0/storage-pools/default/volumes?recursion=1"): {
|
||||
"type": "error",
|
||||
"error": "service unavailable",
|
||||
"error_code": 503,
|
||||
},
|
||||
}
|
||||
|
||||
with patch.object(self.module, "LXDClient", FakeLXDClient):
|
||||
with patch.object(self.module.os.path, "exists", return_value=False):
|
||||
with self.assertRaises(AnsibleFailJson) as exc:
|
||||
with set_module_args({"pool": "default"}):
|
||||
self.module.main()
|
||||
|
||||
result = exc.exception.args[0]
|
||||
assert result["error_code"] == 503
|
||||
assert "Failed to retrieve volumes from pool" in result["msg"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue