Add module proxmox_backup_info (#9437)

* Create proxmox_backup_info.py

The `proxmox_backup_info` module displays information such as backup times, VM name, VM ID, mode, backup type, and backup schedule using the Proxmox Server API.

* Create test_proxmox_backup_info.py

create test for proxmox_backup_info.py module

* Update plugins/modules/proxmox_backup_info.py

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* check tests proxmox_backup_info.py

* check tests test_proxmox_backup_info.py

* Update check tests test_proxmox_backup_info.py

* Update check tests proxmox_backup_info.py

* Update authors proxmox_backup_info.py

* Update active maintainers for proxmox_backup_info module

* Update add proxmox_backup_info module in proxmox group

* edit timestamp to UTC test_proxmox_backup_info.py

* Update vm name or vmid to VM name or VM id proxmox_backup_info.py

* update  documentation in proxmox_backup_info.py

* Update backup_section decription in proxmox_backup_info.py

* Update plugins/modules/proxmox_backup_info.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* change backup_section to backup_jobs

* change backup_section to backup_jobs

* remove whitespace in line 35 and 36

* improve descriptions

* check again proxmox_backup_info.py module

* change vmid type and some descriptions proxmox_backup_info.py

* delete comment #if ...

* solve trailing whitespace error

* Update the name of the functions

* Update proxmox_backup_info.py

* Update proxmox_backup_info.py

* Update tests/unit/plugins/modules/test_proxmox_backup_info.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update test_proxmox_backup_info.py

* Update runtime.yml

* add proxmox_backup_schedule module in runtime.yml

* add proxmox_backup_schedule.py module in BOTMETA.yml

* remove proxmox_backup_schedule module runtime.yml

* remove proxmox_backup_schedule.py module in BOTMETA.yml

* change some id to ID proxmox_backup_info.py

* Update proxmox_backup_info.py

* remove required: falsefrom documentations

* change vimd values to str format

* add samples to output documentations

* the exact output of vimd in endpoint of proxmox resoures is like int

---------

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: mmayabi <137920552+mmayabi@users.noreply.github.com>
pull/9646/head
raoufnezhad 2025-01-26 23:25:56 +03:30 committed by GitHub
parent d71ba0fae8
commit 96f465ddf8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 522 additions and 0 deletions

2
.github/BOTMETA.yml vendored
View File

@ -1146,6 +1146,8 @@ files:
maintainers: helldorado krauthosting maintainers: helldorado krauthosting
$modules/proxmox_backup.py: $modules/proxmox_backup.py:
maintainers: IamLunchbox maintainers: IamLunchbox
$modules/proxmox_backup_info.py:
maintainers: raoufnezhad mmayabi
$modules/proxmox_nic.py: $modules/proxmox_nic.py:
maintainers: Kogelvis krauthosting maintainers: Kogelvis krauthosting
$modules/proxmox_node_info.py: $modules/proxmox_node_info.py:

View File

@ -17,6 +17,7 @@ action_groups:
proxmox: proxmox:
- proxmox - proxmox
- proxmox_backup - proxmox_backup
- proxmox_backup_info
- proxmox_disk - proxmox_disk
- proxmox_domain_info - proxmox_domain_info
- proxmox_group_info - proxmox_group_info

View File

@ -0,0 +1,244 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024 Marzieh Raoufnezhad <raoufnezhad at gmail.com>
# Copyright (c) 2024 Maryam Mayabi <mayabi.ahm at 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 absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
---
module: proxmox_backup_info
short_description: Retrieve information on Proxmox scheduled backups
version_added: 10.3.0
description:
- Retrieve information such as backup times, VM name, VM ID, mode, backup type, and backup schedule using the Proxmox Server API.
author:
- "Marzieh Raoufnezhad (@raoufnezhad) <raoufnezhad@gmail.com>"
- "Maryam Mayabi (@mmayabi) <mayabi.ahm@gmail.com>"
options:
vm_name:
description:
- The name of the Proxmox VM.
- If defined, the returned list will contain backup jobs that have been parsed and filtered based on O(vm_name) value.
- Mutually exclusive with O(vm_id) and O(backup_jobs).
type: str
vm_id:
description:
- The ID of the Proxmox VM.
- If defined, the returned list will contain backup jobs that have been parsed and filtered based on O(vm_id) value.
- Mutually exclusive with O(vm_name) and O(backup_jobs).
type: str
backup_jobs:
description:
- If V(true), the module will return all backup jobs information.
- If V(false), the module will parse all backup jobs based on VM IDs and return a list of VMs' backup information.
- Mutually exclusive with O(vm_id) and O(vm_name).
default: false
type: bool
extends_documentation_fragment:
- community.general.proxmox.documentation
- community.general.attributes
- community.general.attributes.info_module
- community.general.proxmox.actiongroup_proxmox
"""
EXAMPLES = """
- name: Print all backup information by VM ID and VM name
community.general.proxmox_backup_info:
api_user: 'myUser@pam'
api_password: '*******'
api_host: '192.168.20.20'
- name: Print Proxmox backup information for a specific VM based on its name
community.general.proxmox_backup_info:
api_user: 'myUser@pam'
api_password: '*******'
api_host: '192.168.20.20'
vm_name: 'mailsrv'
- name: Print Proxmox backup information for a specific VM based on its VM ID
community.general.proxmox_backup_info:
api_user: 'myUser@pam'
api_password: '*******'
api_host: '192.168.20.20'
vm_id: '150'
- name: Print Proxmox all backup job information
community.general.proxmox_backup_info:
api_user: 'myUser@pam'
api_password: '*******'
api_host: '192.168.20.20'
backup_jobs: true
"""
RETURN = """
---
backup_info:
description: The return value provides backup job information based on VM ID or VM name, or total backup job information.
returned: on success, but can be empty
type: list
elements: dict
contains:
bktype:
description: The type of the backup.
returned: on success
type: str
sample: vzdump
enabled:
description: V(1) if backup is enabled else V(0).
returned: on success
type: int
sample: 1
id:
description: The backup job ID.
returned: on success
type: str
sample: backup-83831498-c631
mode:
description: The backup job mode such as snapshot.
returned: on success
type: str
sample: snapshot
next-run:
description: The next backup time.
returned: on success
type: str
sample: "2024-12-28 11:30:00"
schedule:
description: The backup job schedule.
returned: on success
type: str
sample: "sat 15:00"
storage:
description: The backup storage location.
returned: on success
type: str
sample: local
vm_name:
description: The VM name.
returned: on success
type: str
sample: test01
vmid:
description: The VM ID.
returned: on success
type: str
sample: "100"
"""
from datetime import datetime
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.community.general.plugins.module_utils.proxmox import (
proxmox_auth_argument_spec, ProxmoxAnsible, HAS_PROXMOXER, PROXMOXER_IMP_ERR)
class ProxmoxBackupInfoAnsible(ProxmoxAnsible):
# Get all backup information
def get_jobs_list(self):
try:
backupJobs = self.proxmox_api.cluster.backup.get()
except Exception as e:
self.module.fail_json(msg="Getting backup jobs failed: %s" % e)
return backupJobs
# Get VM information
def get_vms_list(self):
try:
vms = self.proxmox_api.cluster.resources.get(type='vm')
except Exception as e:
self.module.fail_json(msg="Getting VMs info from cluster failed: %s" % e)
return vms
# Get all backup information by VM ID and VM name
def vms_backup_info(self):
backupList = self.get_jobs_list()
vmInfo = self.get_vms_list()
bkInfo = []
for backupItem in backupList:
nextrun = datetime.fromtimestamp(backupItem['next-run'])
vmids = backupItem['vmid'].split(',')
for vmid in vmids:
for vm in vmInfo:
if vm['vmid'] == int(vmid):
vmName = vm['name']
break
bkInfoData = {'id': backupItem['id'],
'schedule': backupItem['schedule'],
'storage': backupItem['storage'],
'mode': backupItem['mode'],
'next-run': nextrun.strftime("%Y-%m-%d %H:%M:%S"),
'enabled': backupItem['enabled'],
'bktype': backupItem['type'],
'vmid': vmid,
'vm_name': vmName}
bkInfo.append(bkInfoData)
return bkInfo
# Get proxmox backup information for a specific VM based on its VM ID or VM name
def specific_vmbackup_info(self, vm_name_id):
fullBackupInfo = self.vms_backup_info()
vmBackupJobs = []
for vm in fullBackupInfo:
if (vm["vm_name"] == vm_name_id or vm["vmid"] == vm_name_id):
vmBackupJobs.append(vm)
return vmBackupJobs
def main():
# Define module args
args = proxmox_auth_argument_spec()
backup_info_args = dict(
vm_id=dict(type='str'),
vm_name=dict(type='str'),
backup_jobs=dict(type='bool', default=False)
)
args.update(backup_info_args)
module = AnsibleModule(
argument_spec=args,
mutually_exclusive=[('backup_jobs', 'vm_id', 'vm_name')],
supports_check_mode=True
)
# Define (init) result value
result = dict(
changed=False
)
# Check if proxmoxer exist
if not HAS_PROXMOXER:
module.fail_json(msg=missing_required_lib('proxmoxer'), exception=PROXMOXER_IMP_ERR)
# Start to connect to proxmox to get backup data
proxmox = ProxmoxBackupInfoAnsible(module)
vm_id = module.params['vm_id']
vm_name = module.params['vm_name']
backup_jobs = module.params['backup_jobs']
# Update result value based on what requested (module args)
if backup_jobs:
result['backup_info'] = proxmox.get_jobs_list()
elif vm_id:
result['backup_info'] = proxmox.specific_vmbackup_info(vm_id)
elif vm_name:
result['backup_info'] = proxmox.specific_vmbackup_info(vm_name)
else:
result['backup_info'] = proxmox.vms_backup_info()
# Return result value
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,275 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2024 Marzieh Raoufnezhad <raoufnezhad at gmail.com>
# Copyright (c) 2024 Maryam Mayabi <mayabi.ahm at 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 absolute_import, division, print_function
__metaclass__ = type
import pytest
proxmoxer = pytest.importorskip("proxmoxer")
from ansible_collections.community.general.plugins.modules import proxmox_backup_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
RESOURCE_LIST = [
{
"uptime": 0,
"diskwrite": 0,
"name": "test01",
"maxcpu": 0,
"node": "NODE1",
"mem": 0,
"netout": 0,
"netin": 0,
"maxmem": 0,
"diskread": 0,
"disk": 0,
"maxdisk": 0,
"status": "running",
"cpu": 0,
"id": "qemu/100",
"template": 0,
"vmid": 100,
"type": "qemu"
},
{
"uptime": 0,
"diskwrite": 0,
"name": "test02",
"maxcpu": 0,
"node": "NODE1",
"mem": 0,
"netout": 0,
"netin": 0,
"maxmem": 0,
"diskread": 0,
"disk": 0,
"maxdisk": 0,
"status": "running",
"cpu": 0,
"id": "qemu/101",
"template": 0,
"vmid": 101,
"type": "qemu"
},
{
"uptime": 0,
"diskwrite": 0,
"name": "test03",
"maxcpu": 0,
"node": "NODE2",
"mem": 0,
"netout": 0,
"netin": 0,
"maxmem": 0,
"diskread": 0,
"disk": 0,
"maxdisk": 0,
"status": "running",
"cpu": 0,
"id": "qemu/102",
"template": 0,
"vmid": 102,
"type": "qemu"
}
]
BACKUP_JOBS = [
{
"type": "vzdump",
"id": "backup-83831498-c631",
"storage": "local",
"vmid": "100",
"enabled": 1,
"next-run": 1735138800,
"mailnotification": "always",
"schedule": "06,18:30",
"mode": "snapshot",
"notes-template": "guestname"
},
{
"schedule": "sat 15:00",
"notes-template": "guestname",
"mode": "snapshot",
"mailnotification": "always",
"next-run": 1735385400,
"type": "vzdump",
"enabled": 1,
"vmid": "100,101,102",
"storage": "local",
"id": "backup-70025700-2302",
}
]
EXPECTED_BACKUP_OUTPUT = [
{
"bktype": "vzdump",
"enabled": 1,
"id": "backup-83831498-c631",
"mode": "snapshot",
"next-run": "2024-12-25 15:00:00",
"schedule": "06,18:30",
"storage": "local",
"vm_name": "test01",
"vmid": "100"
},
{
"bktype": "vzdump",
"enabled": 1,
"id": "backup-70025700-2302",
"mode": "snapshot",
"next-run": "2024-12-28 11:30:00",
"schedule": "sat 15:00",
"storage": "local",
"vm_name": "test01",
"vmid": "100"
},
{
"bktype": "vzdump",
"enabled": 1,
"id": "backup-70025700-2302",
"mode": "snapshot",
"next-run": "2024-12-28 11:30:00",
"schedule": "sat 15:00",
"storage": "local",
"vm_name": "test02",
"vmid": "101"
},
{
"bktype": "vzdump",
"enabled": 1,
"id": "backup-70025700-2302",
"mode": "snapshot",
"next-run": "2024-12-28 11:30:00",
"schedule": "sat 15:00",
"storage": "local",
"vm_name": "test03",
"vmid": "102"
}
]
EXPECTED_BACKUP_JOBS_OUTPUT = [
{
"enabled": 1,
"id": "backup-83831498-c631",
"mailnotification": "always",
"mode": "snapshot",
"next-run": 1735138800,
"notes-template": "guestname",
"schedule": "06,18:30",
"storage": "local",
"type": "vzdump",
"vmid": "100"
},
{
"enabled": 1,
"id": "backup-70025700-2302",
"mailnotification": "always",
"mode": "snapshot",
"next-run": 1735385400,
"notes-template": "guestname",
"schedule": "sat 15:00",
"storage": "local",
"type": "vzdump",
"vmid": "100,101,102"
}
]
class TestProxmoxBackupInfoModule(ModuleTestCase):
def setUp(self):
super(TestProxmoxBackupInfoModule, self).setUp()
proxmox_utils.HAS_PROXMOXER = True
self.module = proxmox_backup_info
self.connect_mock = patch(
"ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect",
).start()
self.connect_mock.return_value.cluster.resources.get.return_value = (
RESOURCE_LIST
)
self.connect_mock.return_value.cluster.backup.get.return_value = (
BACKUP_JOBS
)
def tearDown(self):
self.connect_mock.stop()
super(TestProxmoxBackupInfoModule, self).tearDown()
def test_module_fail_when_required_args_missing(self):
with pytest.raises(AnsibleFailJson) as exc_info:
set_module_args({})
self.module.main()
result = exc_info.value.args[0]
assert result["msg"] == "missing required arguments: api_host, api_user"
def test_get_all_backups_information(self):
with pytest.raises(AnsibleExitJson) as exc_info:
set_module_args({
'api_host': 'proxmoxhost',
'api_user': 'root@pam',
'api_password': 'supersecret'
})
self.module.main()
result = exc_info.value.args[0]
assert result["backup_info"] == EXPECTED_BACKUP_OUTPUT
def test_get_specific_backup_information_by_vmname(self):
with pytest.raises(AnsibleExitJson) as exc_info:
vmname = 'test01'
expected_output = [
backup for backup in EXPECTED_BACKUP_OUTPUT if backup["vm_name"] == vmname
]
set_module_args({
'api_host': 'proxmoxhost',
'api_user': 'root@pam',
'api_password': 'supersecret',
'vm_name': vmname
})
self.module.main()
result = exc_info.value.args[0]
assert result["backup_info"] == expected_output
assert len(result["backup_info"]) == 2
def test_get_specific_backup_information_by_vmid(self):
with pytest.raises(AnsibleExitJson) as exc_info:
vmid = "101"
expected_output = [
backup for backup in EXPECTED_BACKUP_OUTPUT if backup["vmid"] == vmid
]
set_module_args({
'api_host': 'proxmoxhost',
'api_user': 'root@pam',
'api_password': 'supersecret',
'vm_id': vmid
})
self.module.main()
result = exc_info.value.args[0]
assert result["backup_info"] == expected_output
assert len(result["backup_info"]) == 1
def test_get_specific_backup_information_by_backupjobs(self):
with pytest.raises(AnsibleExitJson) as exc_info:
backupjobs = True
set_module_args({
'api_host': 'proxmoxhost',
'api_user': 'root@pam',
'api_password': 'supersecret',
'backup_jobs': backupjobs
})
self.module.main()
result = exc_info.value.args[0]
assert result["backup_info"] == EXPECTED_BACKUP_JOBS_OUTPUT