proxmox_template: Add optional checksum validation (#9601)
* Adds support for checksums in Proxmox_template. * Implemented checksum verification * Removed unintended captilization changes * further fixing of unintended changes * removed misspelling * Final adjustementsto proxmox_template.py * fixed typo * fixed a typo in sha512 * add changelog fragment * fixed type in choices for checksum_algortihm * fixed file naming error and add relevant links to changelog * Fix all unintentional refactorings * refactoring changes removed * renamed the function verify_checksum to fetch_and_verify for clarity * Adjusted additions based on feedback * Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Felix Fontein <felix@fontein.de>pull/9625/head
parent
6294f0b747
commit
b9299e633c
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- proxmox_template - add support for checksum validation with new options ``checksum_algorithm`` and ``checksum`` (https://github.com/ansible-collections/community.general/issues/9553, https://github.com/ansible-collections/community.general/pull/9601).
|
|
@ -170,6 +170,15 @@ class ProxmoxAnsible(object):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.module.fail_json(msg='Unable to retrieve API task ID from node %s: %s' % (node, e))
|
self.module.fail_json(msg='Unable to retrieve API task ID from node %s: %s' % (node, e))
|
||||||
|
|
||||||
|
def api_task_failed(self, node, taskid):
|
||||||
|
""" Explicitly check if the task stops but exits with a failed status
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
status = self.proxmox_api.nodes(node).tasks(taskid).status.get()
|
||||||
|
return status['status'] == 'stopped' and status['exitstatus'] != 'OK'
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg='Unable to retrieve API task ID from node %s: %s' % (node, e))
|
||||||
|
|
||||||
def api_task_complete(self, node_name, task_id, timeout):
|
def api_task_complete(self, node_name, task_id, timeout):
|
||||||
"""Wait until the task stops or times out.
|
"""Wait until the task stops or times out.
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,21 @@ options:
|
||||||
type: str
|
type: str
|
||||||
choices: ['present', 'absent']
|
choices: ['present', 'absent']
|
||||||
default: present
|
default: present
|
||||||
|
checksum_algorithm:
|
||||||
|
description:
|
||||||
|
- Algorithm used to verify the checksum.
|
||||||
|
- If specified, O(checksum) must also be specified.
|
||||||
|
type: str
|
||||||
|
choices: ['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']
|
||||||
|
version_added: 10.3.0
|
||||||
|
checksum:
|
||||||
|
description:
|
||||||
|
- The checksum to validate against.
|
||||||
|
- Checksums are often provided by software distributors to verify that a download is not corrupted.
|
||||||
|
- Checksums can usually be found on the distributors download page in the form of a file or string.
|
||||||
|
- If specified, O(checksum_algorithm) must also be specified.
|
||||||
|
type: str
|
||||||
|
version_added: 10.3.0
|
||||||
notes:
|
notes:
|
||||||
- Requires C(proxmoxer) and C(requests) modules on host. Those modules can be installed with M(ansible.builtin.pip).
|
- 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.
|
- C(proxmoxer) >= 1.2.0 requires C(requests_toolbelt) to upload files larger than 256 MB.
|
||||||
|
@ -82,6 +97,7 @@ extends_documentation_fragment:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
EXAMPLES = r"""
|
EXAMPLES = r"""
|
||||||
|
---
|
||||||
- name: Upload new openvz template with minimal options
|
- name: Upload new openvz template with minimal options
|
||||||
community.general.proxmox_template:
|
community.general.proxmox_template:
|
||||||
node: uk-mc02
|
node: uk-mc02
|
||||||
|
@ -147,6 +163,16 @@ EXAMPLES = r"""
|
||||||
storage: local
|
storage: local
|
||||||
content_type: vztmpl
|
content_type: vztmpl
|
||||||
template: ubuntu-20.04-standard_20.04-1_amd64.tar.gz
|
template: ubuntu-20.04-standard_20.04-1_amd64.tar.gz
|
||||||
|
|
||||||
|
- name: Download and verify a template's checksum
|
||||||
|
community.general.proxmox_template:
|
||||||
|
node: uk-mc02
|
||||||
|
api_user: root@pam
|
||||||
|
api_password: 1q2w3e
|
||||||
|
api_host: node1
|
||||||
|
url: ubuntu-20.04-standard_20.04-1_amd64.tar.gz
|
||||||
|
checksum_algorithm: sha256
|
||||||
|
checksum: 65d860160bdc9b98abf72407e14ca40b609417de7939897d3b58d55787aaef69
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -156,7 +182,7 @@ import traceback
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
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.proxmox import (proxmox_auth_argument_spec, ProxmoxAnsible)
|
||||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode
|
||||||
|
|
||||||
REQUESTS_TOOLBELT_ERR = None
|
REQUESTS_TOOLBELT_ERR = None
|
||||||
try:
|
try:
|
||||||
|
@ -183,6 +209,8 @@ class ProxmoxTemplateAnsible(ProxmoxAnsible):
|
||||||
while timeout:
|
while timeout:
|
||||||
if self.api_task_ok(node, taskid):
|
if self.api_task_ok(node, taskid):
|
||||||
return True
|
return True
|
||||||
|
elif self.api_task_failed(node, taskid):
|
||||||
|
self.module.fail_json(msg="Task error: %s" % self.proxmox_api.nodes(node).tasks(taskid).status.get()['exitstatus'])
|
||||||
timeout = timeout - 1
|
timeout = timeout - 1
|
||||||
if timeout == 0:
|
if timeout == 0:
|
||||||
self.module.fail_json(msg='Reached timeout while waiting for uploading/downloading template. Last line in task before timeout: %s' %
|
self.module.fail_json(msg='Reached timeout while waiting for uploading/downloading template. Last line in task before timeout: %s' %
|
||||||
|
@ -235,6 +263,21 @@ class ProxmoxTemplateAnsible(ProxmoxAnsible):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def fetch_and_verify(self, node, storage, url, content_type, timeout, checksum, checksum_algorithm):
|
||||||
|
""" Fetch a template from a web url, then verify it using a checksum.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'url': url,
|
||||||
|
'content': content_type,
|
||||||
|
'filename': os.path.basename(url),
|
||||||
|
'checksum': checksum,
|
||||||
|
'checksum-algorithm': checksum_algorithm}
|
||||||
|
try:
|
||||||
|
taskid = self.proxmox_api.nodes(node).storage(storage).post("download-url?{}".format(urlencode(data)))
|
||||||
|
return self.task_status(node, taskid, timeout)
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="Checksum mismatch: %s" % (e))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module_args = proxmox_auth_argument_spec()
|
module_args = proxmox_auth_argument_spec()
|
||||||
|
@ -248,12 +291,14 @@ def main():
|
||||||
timeout=dict(type='int', default=30),
|
timeout=dict(type='int', default=30),
|
||||||
force=dict(type='bool', default=False),
|
force=dict(type='bool', default=False),
|
||||||
state=dict(default='present', choices=['present', 'absent']),
|
state=dict(default='present', choices=['present', 'absent']),
|
||||||
|
checksum_algorithm=dict(choices=['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']),
|
||||||
|
checksum=dict(type='str'),
|
||||||
)
|
)
|
||||||
module_args.update(template_args)
|
module_args.update(template_args)
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=module_args,
|
argument_spec=module_args,
|
||||||
required_together=[('api_token_id', 'api_token_secret')],
|
required_together=[('api_token_id', 'api_token_secret'), ('checksum', 'checksum_algorithm')],
|
||||||
required_one_of=[('api_password', 'api_token_id')],
|
required_one_of=[('api_password', 'api_token_id')],
|
||||||
required_if=[('state', 'absent', ['template'])],
|
required_if=[('state', 'absent', ['template'])],
|
||||||
mutually_exclusive=[("src", "url")],
|
mutually_exclusive=[("src", "url")],
|
||||||
|
@ -265,6 +310,8 @@ def main():
|
||||||
node = module.params['node']
|
node = module.params['node']
|
||||||
storage = module.params['storage']
|
storage = module.params['storage']
|
||||||
timeout = module.params['timeout']
|
timeout = module.params['timeout']
|
||||||
|
checksum = module.params['checksum']
|
||||||
|
checksum_algorithm = module.params['checksum_algorithm']
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
content_type = module.params['content_type']
|
content_type = module.params['content_type']
|
||||||
|
@ -303,6 +350,9 @@ def main():
|
||||||
elif not proxmox.delete_template(node, storage, content_type, template, timeout):
|
elif not proxmox.delete_template(node, storage, content_type, template, timeout):
|
||||||
module.fail_json(changed=False, msg='failed to delete template with volid=%s:%s/%s' % (storage, content_type, template))
|
module.fail_json(changed=False, msg='failed to delete template with volid=%s:%s/%s' % (storage, content_type, template))
|
||||||
|
|
||||||
|
if checksum:
|
||||||
|
if proxmox.fetch_and_verify(node, storage, url, content_type, timeout, checksum, checksum_algorithm):
|
||||||
|
module.exit_json(changed=True, msg="Checksum verified, template with volid=%s:%s/%s uploaded" % (storage, content_type, template))
|
||||||
if proxmox.fetch_template(node, storage, content_type, url, timeout):
|
if proxmox.fetch_template(node, storage, content_type, url, timeout):
|
||||||
module.exit_json(changed=True, msg='template with volid=%s:%s/%s uploaded' % (storage, content_type, template))
|
module.exit_json(changed=True, msg='template with volid=%s:%s/%s uploaded' % (storage, content_type, template))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue