Proxmox add storage content listing (#7725)
Add module to list content on proxmox storage
We first add a method to list storage content for proxmox, then use that
new methode to add an Ansible module to list content on storage attached
to a proxmox node. User can also use content filtering to define what
they want to list (backup, iso, images,...).
This commit also include the integration and unit test for that new
module.
Co-authored-by: Julian Vanden Broeck <julian.vandenbroeck@dalibo.com>
(cherry picked from commit 4f92f39720
)
Co-authored-by: Julian <374571+l00ptr@users.noreply.github.com>
pull/7805/head
parent
89c56631c8
commit
d423b27288
|
@ -1051,6 +1051,8 @@ files:
|
|||
maintainers: Kogelvis
|
||||
$modules/proxmox_node_info.py:
|
||||
maintainers: jwbernin
|
||||
$modules/proxmox_storage_contents_info.py:
|
||||
maintainers: l00ptr
|
||||
$modules/proxmox_tasks_info:
|
||||
maintainers: paginabianca
|
||||
$modules/proxmox_template.py:
|
||||
|
|
|
@ -180,3 +180,17 @@ class ProxmoxAnsible(object):
|
|||
return self.proxmox_api.storage.get(type=type)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to retrieve storages information with type %s: %s" % (type, e))
|
||||
|
||||
def get_storage_content(self, node, storage, content=None, vmid=None):
|
||||
try:
|
||||
return (
|
||||
self.proxmox_api.nodes(node)
|
||||
.storage(storage)
|
||||
.content()
|
||||
.get(content=content, vmid=vmid)
|
||||
)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
msg="Unable to list content on %s, %s for %s and %s: %s"
|
||||
% (node, storage, content, vmid, e)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright Julian Vanden Broeck (@l00ptr) <julian.vandenbroeck at dalibo.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 absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: proxmox_storage_contents_info
|
||||
short_description: List content from a Proxmox VE storage
|
||||
version_added: 8.2.0
|
||||
description:
|
||||
- Retrieves information about stored objects on a specific storage attached to a node.
|
||||
options:
|
||||
storage:
|
||||
description:
|
||||
- Only return content stored on that specific storage.
|
||||
aliases: ['name']
|
||||
type: str
|
||||
required: true
|
||||
node:
|
||||
description:
|
||||
- Proxmox node to which the storage is attached.
|
||||
type: str
|
||||
required: true
|
||||
content:
|
||||
description:
|
||||
- Filter on a specific content type.
|
||||
type: str
|
||||
choices: ["all", "backup", "rootdir", "images", "iso"]
|
||||
default: "all"
|
||||
vmid:
|
||||
description:
|
||||
- Filter on a specific VMID.
|
||||
type: int
|
||||
author: Julian Vanden Broeck (@l00ptr)
|
||||
extends_documentation_fragment:
|
||||
- community.general.proxmox.documentation
|
||||
- community.general.attributes
|
||||
- community.general.attributes.info_module
|
||||
"""
|
||||
|
||||
|
||||
EXAMPLES = """
|
||||
- name: List existing storages
|
||||
community.general.proxmox_storage_contents_info:
|
||||
api_host: helldorado
|
||||
api_user: root@pam
|
||||
api_password: "{{ password | default(omit) }}"
|
||||
api_token_id: "{{ token_id | default(omit) }}"
|
||||
api_token_secret: "{{ token_secret | default(omit) }}"
|
||||
storage: lvm2
|
||||
content: backup
|
||||
vmid: 130
|
||||
"""
|
||||
|
||||
|
||||
RETURN = """
|
||||
proxmox_storage_content:
|
||||
description: Content of of storage attached to a node.
|
||||
type: list
|
||||
returned: success
|
||||
elements: dict
|
||||
contains:
|
||||
content:
|
||||
description: Proxmox content of listed objects on this storage.
|
||||
type: str
|
||||
returned: success
|
||||
ctime:
|
||||
description: Creation time of the listed objects.
|
||||
type: str
|
||||
returned: success
|
||||
format:
|
||||
description: Format of the listed objects (can be V(raw), V(pbs-vm), V(iso),...).
|
||||
type: str
|
||||
returned: success
|
||||
size:
|
||||
description: Size of the listed objects.
|
||||
type: int
|
||||
returned: success
|
||||
subtype:
|
||||
description: Subtype of the listed objects (can be V(qemu) or V(lxc)).
|
||||
type: str
|
||||
returned: When storage is dedicated to backup, typically on PBS storage.
|
||||
verification:
|
||||
description: Backup verification status of the listed objects.
|
||||
type: dict
|
||||
returned: When storage is dedicated to backup, typically on PBS storage.
|
||||
sample: {
|
||||
"state": "ok",
|
||||
"upid": "UPID:backup-srv:00130F49:1A12D8375:00001CD7:657A2258:verificationjob:daily\\x3av\\x2dd0cc18c5\\x2d8707:root@pam:"
|
||||
}
|
||||
volid:
|
||||
description: Volume identifier of the listed objects.
|
||||
type: str
|
||||
returned: success
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.proxmox import (
|
||||
ProxmoxAnsible, proxmox_auth_argument_spec)
|
||||
|
||||
|
||||
def proxmox_storage_info_argument_spec():
|
||||
return dict(
|
||||
storage=dict(type="str", required=True, aliases=["name"]),
|
||||
content=dict(type="str", required=False, default="all", choices=["all", "backup", "rootdir", "images", "iso"]),
|
||||
vmid=dict(type="int"),
|
||||
node=dict(required=True, type="str"),
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
module_args = proxmox_auth_argument_spec()
|
||||
storage_info_args = proxmox_storage_info_argument_spec()
|
||||
module_args.update(storage_info_args)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
required_one_of=[("api_password", "api_token_id")],
|
||||
required_together=[("api_token_id", "api_token_secret")],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
result = dict(changed=False)
|
||||
proxmox = ProxmoxAnsible(module)
|
||||
res = proxmox.get_storage_content(
|
||||
node=module.params["node"],
|
||||
storage=module.params["storage"],
|
||||
content=None if module.params["content"] == "all" else module.params["content"],
|
||||
vmid=module.params["vmid"],
|
||||
)
|
||||
result["proxmox_storage_content"] = res
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -129,6 +129,25 @@
|
|||
- results_storage.proxmox_storages|length == 1
|
||||
- results_storage.proxmox_storages[0].storage == "{{ storage }}"
|
||||
|
||||
- name: List content on storage
|
||||
proxmox_storage_contents_info:
|
||||
api_host: "{{ api_host }}"
|
||||
api_user: "{{ user }}@{{ domain }}"
|
||||
api_password: "{{ api_password | default(omit) }}"
|
||||
api_token_id: "{{ api_token_id | default(omit) }}"
|
||||
api_token_secret: "{{ api_token_secret | default(omit) }}"
|
||||
validate_certs: "{{ validate_certs }}"
|
||||
storage: "{{ storage }}"
|
||||
node: "{{ node }}"
|
||||
content: images
|
||||
register: results_list_storage
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- results_storage is not changed
|
||||
- results_storage.proxmox_storage_content is defined
|
||||
- results_storage.proxmox_storage_content |length == 1
|
||||
|
||||
- name: VM creation
|
||||
tags: [ 'create' ]
|
||||
block:
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2023, Julian Vanden Broeck <julian.vandenbroeck at dalibo.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 absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
||||
proxmoxer = pytest.importorskip("proxmoxer")
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import proxmox_storage_contents_info
|
||||
from ansible_collections.community.general.tests.unit.compat.mock import patch
|
||||
from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
|
||||
AnsibleExitJson,
|
||||
AnsibleFailJson,
|
||||
ModuleTestCase,
|
||||
set_module_args,
|
||||
)
|
||||
import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
|
||||
|
||||
NODE1 = "pve"
|
||||
RAW_LIST_OUTPUT = [
|
||||
{
|
||||
"content": "backup",
|
||||
"ctime": 1702528474,
|
||||
"format": "pbs-vm",
|
||||
"size": 273804166061,
|
||||
"subtype": "qemu",
|
||||
"vmid": 931,
|
||||
"volid": "datastore:backup/vm/931/2023-12-14T04:34:34Z",
|
||||
},
|
||||
{
|
||||
"content": "backup",
|
||||
"ctime": 1702582560,
|
||||
"format": "pbs-vm",
|
||||
"size": 273804166059,
|
||||
"subtype": "qemu",
|
||||
"vmid": 931,
|
||||
"volid": "datastore:backup/vm/931/2023-12-14T19:36:00Z",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_module_args(node, storage, content="all", vmid=None):
|
||||
return {
|
||||
"api_host": "host",
|
||||
"api_user": "user",
|
||||
"api_password": "password",
|
||||
"node": node,
|
||||
"storage": storage,
|
||||
"content": content,
|
||||
"vmid": vmid,
|
||||
}
|
||||
|
||||
|
||||
class TestProxmoxStorageContentsInfo(ModuleTestCase):
|
||||
def setUp(self):
|
||||
super(TestProxmoxStorageContentsInfo, self).setUp()
|
||||
proxmox_utils.HAS_PROXMOXER = True
|
||||
self.module = proxmox_storage_contents_info
|
||||
self.connect_mock = patch(
|
||||
"ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect",
|
||||
).start()
|
||||
self.connect_mock.return_value.nodes.return_value.storage.return_value.content.return_value.get.return_value = (
|
||||
RAW_LIST_OUTPUT
|
||||
)
|
||||
self.connect_mock.return_value.nodes.get.return_value = [{"node": NODE1}]
|
||||
|
||||
def tearDown(self):
|
||||
self.connect_mock.stop()
|
||||
super(TestProxmoxStorageContentsInfo, self).tearDown()
|
||||
|
||||
def test_module_fail_when_required_args_missing(self):
|
||||
with pytest.raises(AnsibleFailJson) as exc_info:
|
||||
set_module_args({})
|
||||
self.module.main()
|
||||
|
||||
def test_storage_contents_info(self):
|
||||
with pytest.raises(AnsibleExitJson) as exc_info:
|
||||
set_module_args(get_module_args(node=NODE1, storage="datastore"))
|
||||
expected_output = {}
|
||||
self.module.main()
|
||||
|
||||
result = exc_info.value.args[0]
|
||||
assert not result["changed"]
|
||||
assert result["proxmox_storage_content"] == RAW_LIST_OUTPUT
|
Loading…
Reference in New Issue