Skip to content

Commit a0f0955

Browse files
Added more storage types (#184)
* Added dir and zfspool storage types * Added unit tests for dir and zfspool * Added changelog * Fix sanity * Fix more sanity * Add validation tests for missing required parameters in storage types * Fix sanity --------- Co-authored-by: Kevin Quick <[email protected]>
1 parent 787ba59 commit a0f0955

File tree

3 files changed

+203
-2
lines changed

3 files changed

+203
-2
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
minor_changes:
3+
- proxmox_storage - added `dir` and `zfspool` storage types (https://github.com/ansible-collections/community.proxmox/pull/184)

plugins/modules/proxmox_storage.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
- The storage type/protocol to use when adding the storage.
4444
type: str
4545
required: true
46-
choices: ['cephfs', 'cifs', 'iscsi', 'nfs', 'pbs']
46+
choices: ['cephfs', 'cifs', 'dir', 'iscsi', 'nfs', 'pbs', 'zfspool']
4747
cephfs_options:
4848
description:
4949
- Extended information for adding CephFS storage.
@@ -121,6 +121,16 @@
121121
- The minimum SMB version to use for.
122122
type: str
123123
required: false
124+
dir_options:
125+
description:
126+
- Extended information for adding Directory storage.
127+
type: dict
128+
suboptions:
129+
path:
130+
description:
131+
- The path of the direcotry on the node(s).
132+
type: str
133+
required: false
124134
nfs_options:
125135
description:
126136
- Extended information for adding NFS storage.
@@ -186,6 +196,16 @@
186196
- The required fingerprint of the Proxmox Backup Server system.
187197
type: str
188198
required: false
199+
zfspool_options:
200+
description:
201+
- Extended information for adding ZFS storage.
202+
type: dict
203+
suboptions:
204+
pool:
205+
description:
206+
- The name of the ZFS pool to use.
207+
type: str
208+
required: false
189209
content:
190210
description:
191211
- The desired content that should be used with this storage type.
@@ -322,6 +342,14 @@ def add_storage(self):
322342
payload['server'] = server
323343
payload['share'] = share
324344

345+
if storage_type == "dir":
346+
dir_options = self.module.params.get(f'{storage_type}_options', {})
347+
path = dir_options.get('path')
348+
if not all([path]):
349+
self.module.fail_json(msg="Directory storage requires 'path' parameter.")
350+
else:
351+
payload['path'] = path
352+
325353
if storage_type == "iscsi":
326354
iscsi_options = self.module.params.get(f'{storage_type}_options', {})
327355
portal = iscsi_options.get('portal')
@@ -359,6 +387,14 @@ def add_storage(self):
359387
if fingerprint:
360388
payload['fingerprint'] = fingerprint
361389

390+
if storage_type == "zfspool":
391+
zfspool_options = self.module.params.get(f'{storage_type}_options', {})
392+
pool = zfspool_options.get('pool')
393+
if not all([pool]):
394+
self.module.fail_json(msg="ZFS storage requires 'pool' parameter.")
395+
else:
396+
payload['pool'] = pool
397+
362398
# Check Mode validation
363399
if self.module.check_mode:
364400
try:
@@ -440,7 +476,10 @@ def main():
440476
nodes=dict(type='list', elements='str',),
441477
name=dict(type='str', required=True),
442478
state=dict(choices=['present', 'absent']),
443-
type=dict(choices=['cephfs', 'cifs', 'iscsi', 'nfs', 'pbs'], required=True),
479+
type=dict(choices=['cephfs', 'cifs', 'dir', 'iscsi', 'nfs', 'pbs', 'zfspool'], required=True),
480+
dir_options=dict(type='dict', options={
481+
'path': dict(type='str')
482+
}),
444483
cephfs_options=dict(type='dict', options={
445484
'monhost': dict(type='list', elements='str'),
446485
'username': dict(type='str'),
@@ -474,6 +513,9 @@ def main():
474513
'datastore': dict(type='str'),
475514
'fingerprint': dict(type='str')
476515
}),
516+
zfspool_options=dict(type='dict', options={
517+
'pool': dict(type='str')
518+
}),
477519
content=dict(type='list', elements='str', choices=["images", "snippets", "import", "iso", "backup", "rootdir", "vztmpl"]),
478520
)
479521

tests/unit/plugins/modules/test_proxmox_storage.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@
1111
from ansible_collections.community.proxmox.plugins.module_utils.proxmox import ProxmoxAnsible
1212

1313

14+
@pytest.fixture
15+
def dir_storage_args():
16+
return {
17+
"api_host": "localhost",
18+
"api_user": "root@pam",
19+
"api_password": "secret",
20+
"validate_certs": False,
21+
"node_name": "pve01",
22+
"nodes": ["pve01", "pve02"],
23+
"state": "present",
24+
"name": "dir-storage",
25+
"type": "dir",
26+
"dir_options": {
27+
"path": "/dir",
28+
},
29+
"content": ["images"]
30+
}
31+
32+
1433
@pytest.fixture
1534
def pbs_storage_args():
1635
return {
@@ -54,6 +73,25 @@ def nfs_storage_args():
5473
}
5574

5675

76+
@pytest.fixture
77+
def zfspool_storage_args():
78+
return {
79+
"api_host": "localhost",
80+
"api_user": "root@pam",
81+
"api_password": "secret",
82+
"validate_certs": False,
83+
"node_name": "pve01",
84+
"nodes": ["pve01", "pve02"],
85+
"state": "present",
86+
"name": "zfspool-storage",
87+
"type": "zfspooldir",
88+
"zfspool_options": {
89+
"pool": "mypool",
90+
},
91+
"content": ["images"]
92+
}
93+
94+
5795
@pytest.fixture
5896
def existing_storages():
5997
return [
@@ -62,6 +100,29 @@ def existing_storages():
62100
]
63101

64102

103+
@patch.object(ProxmoxAnsible, "__init__", return_value=None)
104+
@patch.object(ProxmoxAnsible, "proxmox_api", create=True)
105+
def test_add_dir_storage(mock_api, mock_init, dir_storage_args):
106+
module = MagicMock(spec=AnsibleModule)
107+
module.params = dir_storage_args
108+
module.check_mode = False
109+
110+
mock_api_instance = MagicMock()
111+
mock_api.return_value = mock_api_instance
112+
mock_api_instance.nodes.get.return_value = [{"node": "pve01", "status": "online"}]
113+
mock_api_instance.storage.get.return_value = []
114+
mock_api_instance.storage.post.return_value = {}
115+
116+
proxmox = proxmox_storage.ProxmoxNodeAnsible(module)
117+
proxmox.module = module
118+
proxmox.proxmox_api = mock_api_instance
119+
120+
changed, msg = proxmox.add_storage()
121+
122+
assert changed is True
123+
assert "created successfully" in msg
124+
125+
65126
@patch.object(ProxmoxAnsible, "__init__", return_value=None)
66127
@patch.object(ProxmoxAnsible, "proxmox_api", create=True)
67128
def test_add_pbs_storage(mock_api, mock_init, pbs_storage_args):
@@ -231,3 +292,98 @@ def test_add_cephfs_storage(mock_api, mock_init):
231292

232293
assert changed is True
233294
assert "created successfully" in msg
295+
296+
297+
@patch.object(ProxmoxAnsible, "__init__", return_value=None)
298+
@patch.object(ProxmoxAnsible, "proxmox_api", create=True)
299+
def test_add_zfspool_storage(mock_api, mock_init, zfspool_storage_args):
300+
module = MagicMock(spec=AnsibleModule)
301+
module.params = zfspool_storage_args
302+
module.check_mode = False
303+
304+
mock_api_instance = MagicMock()
305+
mock_api.return_value = mock_api_instance
306+
mock_api_instance.nodes.get.return_value = [{"node": "pve01", "status": "online"}]
307+
mock_api_instance.storage.get.return_value = []
308+
mock_api_instance.storage.post.return_value = {}
309+
310+
proxmox = proxmox_storage.ProxmoxNodeAnsible(module)
311+
proxmox.module = module
312+
proxmox.proxmox_api = mock_api_instance
313+
314+
changed, msg = proxmox.add_storage()
315+
316+
assert changed is True
317+
assert "created successfully" in msg
318+
319+
320+
@patch.object(ProxmoxAnsible, "__init__", return_value=None)
321+
@patch.object(ProxmoxAnsible, "proxmox_api", create=True)
322+
def test_add_dir_missing_required_path(mock_api, mock_init):
323+
dir_args = {
324+
"api_host": "localhost",
325+
"api_user": "root@pam",
326+
"api_password": "secret",
327+
"validate_certs": False,
328+
"node_name": "pve01",
329+
"nodes": ["pve01"],
330+
"state": "present",
331+
"name": "dir-storage",
332+
"type": "dir",
333+
"dir_options": {}, # Missing 'path' parameter
334+
"content": ["images"]
335+
}
336+
337+
module = MagicMock(spec=AnsibleModule)
338+
module.params = dir_args
339+
module.check_mode = False
340+
module.fail_json = lambda **kwargs: (result for result in ()).throw(SystemExit(kwargs))
341+
342+
mock_api_instance = MagicMock()
343+
mock_api.return_value = mock_api_instance
344+
345+
proxmox = proxmox_storage.ProxmoxNodeAnsible(module)
346+
proxmox.module = module
347+
proxmox.proxmox_api = mock_api_instance
348+
349+
with pytest.raises(SystemExit) as exc:
350+
proxmox.add_storage()
351+
352+
result = exc.value.args[0]
353+
assert "Directory storage requires 'path' parameter" in result["msg"]
354+
355+
356+
@patch.object(ProxmoxAnsible, "__init__", return_value=None)
357+
@patch.object(ProxmoxAnsible, "proxmox_api", create=True)
358+
def test_add_zfspool_missing_required_pool(mock_api, mock_init):
359+
zfspool_args = {
360+
"api_host": "localhost",
361+
"api_user": "root@pam",
362+
"api_password": "secret",
363+
"validate_certs": False,
364+
"node_name": "pve01",
365+
"nodes": ["pve01"],
366+
"state": "present",
367+
"name": "zfspool-storage",
368+
"type": "zfspool",
369+
"zfspool_options": {}, # Missing 'pool' parameter
370+
"content": ["images"]
371+
}
372+
373+
module = MagicMock(spec=AnsibleModule)
374+
module.params = zfspool_args
375+
module.check_mode = False
376+
module.fail_json = lambda **kwargs: (result for result in ()).throw(SystemExit(kwargs))
377+
378+
mock_api_instance = MagicMock()
379+
mock_api.return_value = mock_api_instance
380+
381+
proxmox = proxmox_storage.ProxmoxNodeAnsible(module)
382+
proxmox.module = module
383+
proxmox.proxmox_api = mock_api_instance
384+
385+
with pytest.raises(SystemExit) as exc:
386+
proxmox.add_storage()
387+
388+
result = exc.value.args[0]
389+
assert "ZFS storage requires 'pool' parameter" in result["msg"]

0 commit comments

Comments
 (0)