1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/proxmox_template.py
patchback[bot] 0dfd02c1ca
[PR #8516/70c8042c backport][stable-9] proxmox_template: small refactor in get_template() (#8561)
proxmox_template: small refactor in get_template() (#8516)

* proxmox_template: small refactor in get_template()

* add changelog frag

* Update plugins/modules/proxmox_template.py

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

* rename function as per PR suggestion

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 70c8042c99)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-27 12:19:24 +02:00

274 lines
9.9 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright Ansible Project
# 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_template
short_description: Management of OS templates in Proxmox VE cluster
description:
- allows you to upload/delete templates in Proxmox VE cluster
attributes:
check_mode:
support: none
diff_mode:
support: none
action_group:
version_added: 9.0.0
options:
node:
description:
- Proxmox VE node on which to operate.
type: str
src:
description:
- Path to uploaded file.
- Required only for O(state=present).
type: path
template:
description:
- The template name.
- Required for O(state=absent) to delete a template.
- Required for O(state=present) to download an appliance container template (pveam).
type: str
content_type:
description:
- Content type.
- Required only for O(state=present).
type: str
default: 'vztmpl'
choices: ['vztmpl', 'iso']
storage:
description:
- Target storage.
type: str
default: 'local'
timeout:
description:
- Timeout for operations.
type: int
default: 30
force:
description:
- It can only be used with O(state=present), existing template will be overwritten.
type: bool
default: false
state:
description:
- Indicate desired state of the template.
type: str
choices: ['present', 'absent']
default: present
notes:
- Requires C(proxmoxer) and C(requests) modules on host. Those modules can be installed with M(ansible.builtin.pip).
- C(proxmoxer) >= 1.2.0 requires C(requests_toolbelt) to upload files larger than 256 MB.
author: Sergei Antipov (@UnderGreen)
extends_documentation_fragment:
- community.general.proxmox.actiongroup_proxmox
- community.general.proxmox.documentation
- community.general.attributes
'''
EXAMPLES = '''
- name: Upload new openvz template with minimal options
community.general.proxmox_template:
node: uk-mc02
api_user: root@pam
api_password: 1q2w3e
api_host: node1
src: ~/ubuntu-14.04-x86_64.tar.gz
- name: >
Upload new openvz template with minimal options use environment
PROXMOX_PASSWORD variable(you should export it before)
community.general.proxmox_template:
node: uk-mc02
api_user: root@pam
api_host: node1
src: ~/ubuntu-14.04-x86_64.tar.gz
- name: Upload new openvz template with all options and force overwrite
community.general.proxmox_template:
node: uk-mc02
api_user: root@pam
api_password: 1q2w3e
api_host: node1
storage: local
content_type: vztmpl
src: ~/ubuntu-14.04-x86_64.tar.gz
force: true
- name: Delete template with minimal options
community.general.proxmox_template:
node: uk-mc02
api_user: root@pam
api_password: 1q2w3e
api_host: node1
template: ubuntu-14.04-x86_64.tar.gz
state: absent
- name: Download proxmox appliance container template
community.general.proxmox_template:
node: uk-mc02
api_user: root@pam
api_password: 1q2w3e
api_host: node1
storage: local
content_type: vztmpl
template: ubuntu-20.04-standard_20.04-1_amd64.tar.gz
'''
import os
import time
import traceback
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)
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_TOOLBELT_ERR = None
try:
# requests_toolbelt is used internally by proxmoxer module
import requests_toolbelt # noqa: F401, pylint: disable=unused-import
HAS_REQUESTS_TOOLBELT = True
except ImportError:
HAS_REQUESTS_TOOLBELT = False
REQUESTS_TOOLBELT_ERR = traceback.format_exc()
class ProxmoxTemplateAnsible(ProxmoxAnsible):
def has_template(self, node, storage, content_type, template):
volid = '%s:%s/%s' % (storage, content_type, template)
try:
return any(tmpl['volid'] == volid for tmpl in self.proxmox_api.nodes(node).storage(storage).content.get())
except Exception as e:
self.module.fail_json(msg="Failed to retrieve template '%s': %s" % (volid, e))
def task_status(self, node, taskid, timeout):
"""
Check the task status and wait until the task is completed or the timeout is reached.
"""
while timeout:
if self.api_task_ok(node, taskid):
return True
timeout = timeout - 1
if timeout == 0:
self.module.fail_json(msg='Reached timeout while waiting for uploading/downloading template. Last line in task before timeout: %s' %
self.proxmox_api.node(node).tasks(taskid).log.get()[:1])
time.sleep(1)
return False
def upload_template(self, node, storage, content_type, realpath, timeout):
stats = os.stat(realpath)
if (LooseVersion(self.proxmoxer_version) >= LooseVersion('1.2.0') and
stats.st_size > 268435456 and not HAS_REQUESTS_TOOLBELT):
self.module.fail_json(msg="'requests_toolbelt' module is required to upload files larger than 256MB",
exception=missing_required_lib('requests_toolbelt'))
try:
taskid = self.proxmox_api.nodes(node).storage(storage).upload.post(content=content_type, filename=open(realpath, 'rb'))
return self.task_status(node, taskid, timeout)
except Exception as e:
self.module.fail_json(msg="Uploading template %s failed with error: %s" % (realpath, e))
def download_template(self, node, storage, template, timeout):
try:
taskid = self.proxmox_api.nodes(node).aplinfo.post(storage=storage, template=template)
return self.task_status(node, taskid, timeout)
except Exception as e:
self.module.fail_json(msg="Downloading template %s failed with error: %s" % (template, e))
def delete_template(self, node, storage, content_type, template, timeout):
volid = '%s:%s/%s' % (storage, content_type, template)
self.proxmox_api.nodes(node).storage(storage).content.delete(volid)
while timeout:
if not self.has_template(node, storage, content_type, template):
return True
timeout = timeout - 1
if timeout == 0:
self.module.fail_json(msg='Reached timeout while waiting for deleting template.')
time.sleep(1)
return False
def main():
module_args = proxmox_auth_argument_spec()
template_args = dict(
node=dict(),
src=dict(type='path'),
template=dict(),
content_type=dict(default='vztmpl', choices=['vztmpl', 'iso']),
storage=dict(default='local'),
timeout=dict(type='int', default=30),
force=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent']),
)
module_args.update(template_args)
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
required_if=[('state', 'absent', ['template'])]
)
proxmox = ProxmoxTemplateAnsible(module)
state = module.params['state']
node = module.params['node']
storage = module.params['storage']
timeout = module.params['timeout']
if state == 'present':
content_type = module.params['content_type']
src = module.params['src']
# download appliance template
if content_type == 'vztmpl' and not src:
template = module.params['template']
if not template:
module.fail_json(msg='template param for downloading appliance template is mandatory')
if proxmox.has_template(node, storage, content_type, template) and not module.params['force']:
module.exit_json(changed=False, msg='template with volid=%s:%s/%s already exists' % (storage, content_type, template))
if proxmox.download_template(node, storage, template, timeout):
module.exit_json(changed=True, msg='template with volid=%s:%s/%s downloaded' % (storage, content_type, template))
template = os.path.basename(src)
if proxmox.has_template(node, storage, content_type, template) and not module.params['force']:
module.exit_json(changed=False, msg='template with volid=%s:%s/%s is already exists' % (storage, content_type, template))
elif not src:
module.fail_json(msg='src param to uploading template file is mandatory')
elif not (os.path.exists(src) and os.path.isfile(src)):
module.fail_json(msg='template file on path %s not exists' % src)
if proxmox.upload_template(node, storage, content_type, src, timeout):
module.exit_json(changed=True, msg='template with volid=%s:%s/%s uploaded' % (storage, content_type, template))
elif state == 'absent':
try:
content_type = module.params['content_type']
template = module.params['template']
if not proxmox.has_template(node, storage, content_type, template):
module.exit_json(changed=False, msg='template with volid=%s:%s/%s is already deleted' % (storage, content_type, template))
if proxmox.delete_template(node, storage, content_type, template, timeout):
module.exit_json(changed=True, msg='template with volid=%s:%s/%s deleted' % (storage, content_type, template))
except Exception as e:
module.fail_json(msg="deleting of template %s failed with exception: %s" % (template, e))
if __name__ == '__main__':
main()