Allow additional hashing algorithms. Directly use hashlib and check if

used algorithm is supported.
This commit is contained in:
muffl0n 2014-10-06 09:53:35 +02:00 committed by Toshio Kuratomi
parent d10f3f7a7e
commit 4f0cf6d2ca

View file

@ -72,9 +72,22 @@ options:
- If a SHA-256 checksum is passed to this parameter, the digest of the - If a SHA-256 checksum is passed to this parameter, the digest of the
destination file will be calculated after it is downloaded to ensure destination file will be calculated after it is downloaded to ensure
its integrity and verify that the transfer completed successfully. its integrity and verify that the transfer completed successfully.
This option is deprecated. Use 'checksum'.
version_added: "1.3" version_added: "1.3"
required: false required: false
default: null default: null
checksum:
description:
- If a checksum is passed to this parameter, the digest of the
destination file will be calculated after it is downloaded to ensure
its integrity and verify that the transfer completed successfully.
Format: <algorithm>:<checksum>, e.g.: checksum="sha256:d98291acbedd510e3dbd36dbfdd83cbca8415220af43b327c0a0c574b6dc7b97"
If you worry about portability, only the sha1 algorithm is available
on all platforms and python versions. The third party hashlib
library can be installed for access to additional algorithms.
version_added: "2.0"
required: false
default: null
use_proxy: use_proxy:
description: description:
- if C(no), it will not use a proxy, even if one is defined in - if C(no), it will not use a proxy, even if one is defined in
@ -136,24 +149,19 @@ EXAMPLES='''
- name: download foo.conf - name: download foo.conf
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440 get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440
- name: download file with sha256 check
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf sha256sum=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
- name: download file and force basic auth - name: download file and force basic auth
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf force_basic_auth=yes get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf force_basic_auth=yes
- name: download file with custom HTTP headers - name: download file with custom HTTP headers
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf headers: 'key:value,key:value' get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf headers: 'key:value,key:value'
- name: download file with check
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf checksum=sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf checksum=md5:66dffb5228a211e61d6d7ef4a86f5758
''' '''
import urlparse import urlparse
try:
import hashlib
HAS_HASHLIB=True
except ImportError:
HAS_HASHLIB=False
# ============================================================== # ==============================================================
# url handling # url handling
@ -209,6 +217,7 @@ def extract_filename_from_headers(headers):
return res return res
# ============================================================== # ==============================================================
# main # main
@ -219,6 +228,7 @@ def main():
url = dict(required=True), url = dict(required=True),
dest = dict(required=True), dest = dict(required=True),
sha256sum = dict(default=''), sha256sum = dict(default=''),
checksum = dict(default=''),
timeout = dict(required=False, type='int', default=10), timeout = dict(required=False, type='int', default=10),
headers = dict(required=False, default=None), headers = dict(required=False, default=None),
) )
@ -233,6 +243,7 @@ def main():
dest = os.path.expanduser(module.params['dest']) dest = os.path.expanduser(module.params['dest'])
force = module.params['force'] force = module.params['force']
sha256sum = module.params['sha256sum'] sha256sum = module.params['sha256sum']
checksum = module.params['checksum']
use_proxy = module.params['use_proxy'] use_proxy = module.params['use_proxy']
timeout = module.params['timeout'] timeout = module.params['timeout']
@ -248,28 +259,37 @@ def main():
dest_is_dir = os.path.isdir(dest) dest_is_dir = os.path.isdir(dest)
last_mod_time = None last_mod_time = None
# Remove any non-alphanumeric characters, including the infamous # workaround for usage of deprecated sha256sum parameter
# Unicode zero-width space if sha256sum != '':
stripped_sha256sum = re.sub(r'\W+', '', sha256sum) checksum = 'sha256:%s' % (sha256sum)
# checksum specified, parse for algorithm and checksum
if checksum != '':
try:
algorithm, checksum = checksum.rsplit(':', 1)
# Remove any non-alphanumeric characters, including the infamous
# Unicode zero-width space
checksum = re.sub(r'\W+', '', checksum).lower()
# Ensure the checksum portion is a hexdigest
int(checksum, 16)
except ValueError:
module.fail_json(msg="The checksum parameter has to be in format <algorithm>:<checksum>")
# Fail early if sha256 is not supported
if sha256sum != '' and not HAS_HASHLIB:
module.fail_json(msg="The sha256sum parameter requires hashlib, which is available in Python 2.5 and higher")
if not dest_is_dir and os.path.exists(dest): if not dest_is_dir and os.path.exists(dest):
checksum_mismatch = False checksum_mismatch = False
# If the download is not forced and there is a checksum, allow # If the download is not forced and there is a checksum, allow
# checksum match to skip the download. # checksum match to skip the download.
if not force and sha256sum != '': if not force and checksum != '':
destination_checksum = module.sha256(dest) destination_checksum = module.digest_from_file(dest, algorithm)
if stripped_sha256sum.lower() == destination_checksum: if checksum == destination_checksum:
module.exit_json(msg="file already exists", dest=dest, url=url, changed=False) module.exit_json(msg="file already exists", dest=dest, url=url, changed=False)
checksum_mismatch = True checksum_mismatch = True
# Not forcing redownload, unless sha256sum has already failed # Not forcing redownload, unless checksum does not match
if not force and not checksum_mismatch: if not force and not checksum_mismatch:
module.exit_json(msg="file already exists", dest=dest, url=url, changed=False) module.exit_json(msg="file already exists", dest=dest, url=url, changed=False)
@ -330,14 +350,12 @@ def main():
else: else:
changed = False changed = False
# Check the digest of the destination file and ensure that it matches the if checksum != '':
# sha256sum parameter if it is present destination_checksum = module.digest_from_file(dest, algorithm)
if sha256sum != '':
destination_checksum = module.sha256(dest)
if stripped_sha256sum.lower() != destination_checksum: if checksum != destination_checksum:
os.remove(dest) os.remove(dest)
module.fail_json(msg="The SHA-256 checksum for %s did not match %s; it was %s." % (dest, sha256sum, destination_checksum)) module.fail_json(msg="The checksum for %s did not match %s; it was %s." % (dest, checksum, destination_checksum))
os.remove(tmpsrc) os.remove(tmpsrc)
@ -354,9 +372,8 @@ def main():
md5sum = None md5sum = None
# Mission complete # Mission complete
module.exit_json(url=url, dest=dest, src=tmpsrc, md5sum=md5sum, checksum_src=checksum_src,
module.exit_json(url=url, dest=dest, src=tmpsrc, md5sum=md5sum, checksum=checksum_src, checksum_dest=checksum_dest, changed=changed, msg=info.get('msg', ''))
sha256sum=sha256sum, changed=changed, msg=info.get('msg', ''))
# import module snippets # import module snippets
from ansible.module_utils.basic import * from ansible.module_utils.basic import *