|
|
@ -39,7 +39,7 @@ options:
|
|
|
|
- The type of compression to use. Can be 'gz', 'bz2', or 'zip'.
|
|
|
|
- The type of compression to use. Can be 'gz', 'bz2', or 'zip'.
|
|
|
|
choices: [ 'gz', 'bz2', 'zip' ]
|
|
|
|
choices: [ 'gz', 'bz2', 'zip' ]
|
|
|
|
default: 'gz'
|
|
|
|
default: 'gz'
|
|
|
|
creates:
|
|
|
|
dest:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The file name of the destination archive. This is required when C(path) refers to multiple files by either specifying a glob, a directory or multiple paths in a list.
|
|
|
|
- The file name of the destination archive. This is required when C(path) refers to multiple files by either specifying a glob, a directory or multiple paths in a list.
|
|
|
|
required: false
|
|
|
|
required: false
|
|
|
@ -59,7 +59,7 @@ notes:
|
|
|
|
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
EXAMPLES = '''
|
|
|
|
# Compress directory /path/to/foo/ into /path/to/foo.tgz
|
|
|
|
# Compress directory /path/to/foo/ into /path/to/foo.tgz
|
|
|
|
- archive: path=/path/to/foo creates=/path/to/foo.tgz
|
|
|
|
- archive: path=/path/to/foo dest=/path/to/foo.tgz
|
|
|
|
|
|
|
|
|
|
|
|
# Compress regular file /path/to/foo into /path/to/foo.gz and remove it
|
|
|
|
# Compress regular file /path/to/foo into /path/to/foo.gz and remove it
|
|
|
|
- archive: path=/path/to/foo remove=True
|
|
|
|
- archive: path=/path/to/foo remove=True
|
|
|
@ -72,7 +72,7 @@ EXAMPLES = '''
|
|
|
|
path:
|
|
|
|
path:
|
|
|
|
- /path/to/foo
|
|
|
|
- /path/to/foo
|
|
|
|
- /path/wong/foo
|
|
|
|
- /path/wong/foo
|
|
|
|
creates: /path/file.tar.bz2
|
|
|
|
dest: /path/file.tar.bz2
|
|
|
|
compression: bz2
|
|
|
|
compression: bz2
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
@ -118,7 +118,7 @@ def main():
|
|
|
|
argument_spec = dict(
|
|
|
|
argument_spec = dict(
|
|
|
|
path = dict(type='list', required=True),
|
|
|
|
path = dict(type='list', required=True),
|
|
|
|
compression = dict(choices=['gz', 'bz2', 'zip'], default='gz', required=False),
|
|
|
|
compression = dict(choices=['gz', 'bz2', 'zip'], default='gz', required=False),
|
|
|
|
creates = dict(required=False),
|
|
|
|
dest = dict(required=False),
|
|
|
|
remove = dict(required=False, default=False, type='bool'),
|
|
|
|
remove = dict(required=False, default=False, type='bool'),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
add_file_common_args=True,
|
|
|
|
add_file_common_args=True,
|
|
|
@ -127,7 +127,7 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
params = module.params
|
|
|
|
params = module.params
|
|
|
|
paths = params['path']
|
|
|
|
paths = params['path']
|
|
|
|
creates = params['creates']
|
|
|
|
dest = params['dest']
|
|
|
|
remove = params['remove']
|
|
|
|
remove = params['remove']
|
|
|
|
expanded_paths = []
|
|
|
|
expanded_paths = []
|
|
|
|
compression = params['compression']
|
|
|
|
compression = params['compression']
|
|
|
@ -157,12 +157,12 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
# Default created file name (for single-file archives) to
|
|
|
|
# Default created file name (for single-file archives) to
|
|
|
|
# <file>.<compression>
|
|
|
|
# <file>.<compression>
|
|
|
|
if not archive and not creates:
|
|
|
|
if not archive and not dest:
|
|
|
|
creates = '%s.%s' % (expanded_paths[0], compression)
|
|
|
|
dest = '%s.%s' % (expanded_paths[0], compression)
|
|
|
|
|
|
|
|
|
|
|
|
# Force archives to specify 'creates'
|
|
|
|
# Force archives to specify 'dest'
|
|
|
|
if archive and not creates:
|
|
|
|
if archive and not dest:
|
|
|
|
module.fail_json(creates=creates, path=', '.join(paths), msg='Error, must specify "creates" when archiving multiple files or trees')
|
|
|
|
module.fail_json(dest=dest, path=', '.join(paths), msg='Error, must specify "dest" when archiving multiple files or trees')
|
|
|
|
|
|
|
|
|
|
|
|
archive_paths = []
|
|
|
|
archive_paths = []
|
|
|
|
missing = []
|
|
|
|
missing = []
|
|
|
@ -185,7 +185,7 @@ def main():
|
|
|
|
arcroot += os.sep
|
|
|
|
arcroot += os.sep
|
|
|
|
|
|
|
|
|
|
|
|
# Don't allow archives to be created anywhere within paths to be removed
|
|
|
|
# Don't allow archives to be created anywhere within paths to be removed
|
|
|
|
if remove and os.path.isdir(path) and creates.startswith(path):
|
|
|
|
if remove and os.path.isdir(path) and dest.startswith(path):
|
|
|
|
module.fail_json(path=', '.join(paths), msg='Error, created archive can not be contained in source paths when remove=True')
|
|
|
|
module.fail_json(path=', '.join(paths), msg='Error, created archive can not be contained in source paths when remove=True')
|
|
|
|
|
|
|
|
|
|
|
|
if os.path.lexists(path):
|
|
|
|
if os.path.lexists(path):
|
|
|
@ -194,9 +194,9 @@ def main():
|
|
|
|
missing.append(path)
|
|
|
|
missing.append(path)
|
|
|
|
|
|
|
|
|
|
|
|
# No source files were found but the named archive exists: are we 'compress' or 'archive' now?
|
|
|
|
# No source files were found but the named archive exists: are we 'compress' or 'archive' now?
|
|
|
|
if len(missing) == len(expanded_paths) and creates and os.path.exists(creates):
|
|
|
|
if len(missing) == len(expanded_paths) and dest and os.path.exists(dest):
|
|
|
|
# Just check the filename to know if it's an archive or simple compressed file
|
|
|
|
# Just check the filename to know if it's an archive or simple compressed file
|
|
|
|
if re.search(r'(\.tar\.gz|\.tgz|.tbz2|\.tar\.bz2|\.zip)$', os.path.basename(creates), re.IGNORECASE):
|
|
|
|
if re.search(r'(\.tar\.gz|\.tgz|.tbz2|\.tar\.bz2|\.zip)$', os.path.basename(dest), re.IGNORECASE):
|
|
|
|
state = 'archive'
|
|
|
|
state = 'archive'
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
state = 'compress'
|
|
|
|
state = 'compress'
|
|
|
@ -205,7 +205,7 @@ def main():
|
|
|
|
elif archive:
|
|
|
|
elif archive:
|
|
|
|
if len(archive_paths) == 0:
|
|
|
|
if len(archive_paths) == 0:
|
|
|
|
# No source files were found, but the archive is there.
|
|
|
|
# No source files were found, but the archive is there.
|
|
|
|
if os.path.lexists(creates):
|
|
|
|
if os.path.lexists(dest):
|
|
|
|
state = 'archive'
|
|
|
|
state = 'archive'
|
|
|
|
elif len(missing) > 0:
|
|
|
|
elif len(missing) > 0:
|
|
|
|
# SOME source files were found, but not all of them
|
|
|
|
# SOME source files were found, but not all of them
|
|
|
@ -215,19 +215,19 @@ def main():
|
|
|
|
size = 0
|
|
|
|
size = 0
|
|
|
|
errors = []
|
|
|
|
errors = []
|
|
|
|
|
|
|
|
|
|
|
|
if os.path.lexists(creates):
|
|
|
|
if os.path.lexists(dest):
|
|
|
|
size = os.path.getsize(creates)
|
|
|
|
size = os.path.getsize(dest)
|
|
|
|
|
|
|
|
|
|
|
|
if state != 'archive':
|
|
|
|
if state != 'archive':
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
|
|
|
|
|
|
|
|
# Slightly more difficult (and less efficient!) compression using zipfile module
|
|
|
|
# Slightly more difficult (and less efficient!) compression using zipfile module
|
|
|
|
if compression == 'zip':
|
|
|
|
if compression == 'zip':
|
|
|
|
arcfile = zipfile.ZipFile(creates, 'w', zipfile.ZIP_DEFLATED)
|
|
|
|
arcfile = zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED)
|
|
|
|
|
|
|
|
|
|
|
|
# Easier compression using tarfile module
|
|
|
|
# Easier compression using tarfile module
|
|
|
|
elif compression == 'gz' or compression == 'bz2':
|
|
|
|
elif compression == 'gz' or compression == 'bz2':
|
|
|
|
arcfile = tarfile.open(creates, 'w|' + compression)
|
|
|
|
arcfile = tarfile.open(dest, 'w|' + compression)
|
|
|
|
|
|
|
|
|
|
|
|
for path in archive_paths:
|
|
|
|
for path in archive_paths:
|
|
|
|
basename = ''
|
|
|
|
basename = ''
|
|
|
@ -253,7 +253,7 @@ def main():
|
|
|
|
for filename in filenames:
|
|
|
|
for filename in filenames:
|
|
|
|
fullpath = dirpath + os.sep + filename
|
|
|
|
fullpath = dirpath + os.sep + filename
|
|
|
|
|
|
|
|
|
|
|
|
if not filecmp.cmp(fullpath, creates):
|
|
|
|
if not filecmp.cmp(fullpath, dest):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
if compression == 'zip':
|
|
|
|
if compression == 'zip':
|
|
|
|
arcfile.write(fullpath, basename + filename)
|
|
|
|
arcfile.write(fullpath, basename + filename)
|
|
|
@ -267,14 +267,14 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
|
e = get_exception()
|
|
|
|
e = get_exception()
|
|
|
|
return module.fail_json(msg='Error when writing %s archive at %s: %s' % (compression == 'zip' and 'zip' or ('tar.' + compression), creates, str(e)))
|
|
|
|
return module.fail_json(msg='Error when writing %s archive at %s: %s' % (compression == 'zip' and 'zip' or ('tar.' + compression), dest, str(e)))
|
|
|
|
|
|
|
|
|
|
|
|
if arcfile:
|
|
|
|
if arcfile:
|
|
|
|
arcfile.close()
|
|
|
|
arcfile.close()
|
|
|
|
state = 'archive'
|
|
|
|
state = 'archive'
|
|
|
|
|
|
|
|
|
|
|
|
if len(errors) > 0:
|
|
|
|
if len(errors) > 0:
|
|
|
|
module.fail_json(msg='Errors when writing archive at %s: %s' % (creates, '; '.join(errors)))
|
|
|
|
module.fail_json(msg='Errors when writing archive at %s: %s' % (dest, '; '.join(errors)))
|
|
|
|
|
|
|
|
|
|
|
|
if state in ['archive', 'incomplete'] and remove:
|
|
|
|
if state in ['archive', 'incomplete'] and remove:
|
|
|
|
for path in successes:
|
|
|
|
for path in successes:
|
|
|
@ -288,10 +288,10 @@ def main():
|
|
|
|
errors.append(path)
|
|
|
|
errors.append(path)
|
|
|
|
|
|
|
|
|
|
|
|
if len(errors) > 0:
|
|
|
|
if len(errors) > 0:
|
|
|
|
module.fail_json(creates=creates, msg='Error deleting some source files: ' + str(e), files=errors)
|
|
|
|
module.fail_json(dest=dest, msg='Error deleting some source files: ' + str(e), files=errors)
|
|
|
|
|
|
|
|
|
|
|
|
# Rudimentary check: If size changed then file changed. Not perfect, but easy.
|
|
|
|
# Rudimentary check: If size changed then file changed. Not perfect, but easy.
|
|
|
|
if os.path.getsize(creates) != size:
|
|
|
|
if os.path.getsize(dest) != size:
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
|
|
if len(successes) and state != 'incomplete':
|
|
|
|
if len(successes) and state != 'incomplete':
|
|
|
@ -302,27 +302,27 @@ def main():
|
|
|
|
path = expanded_paths[0]
|
|
|
|
path = expanded_paths[0]
|
|
|
|
|
|
|
|
|
|
|
|
# No source or compressed file
|
|
|
|
# No source or compressed file
|
|
|
|
if not (os.path.exists(path) or os.path.lexists(creates)):
|
|
|
|
if not (os.path.exists(path) or os.path.lexists(dest)):
|
|
|
|
state = 'absent'
|
|
|
|
state = 'absent'
|
|
|
|
|
|
|
|
|
|
|
|
# if it already exists and the source file isn't there, consider this done
|
|
|
|
# if it already exists and the source file isn't there, consider this done
|
|
|
|
elif not os.path.lexists(path) and os.path.lexists(creates):
|
|
|
|
elif not os.path.lexists(path) and os.path.lexists(dest):
|
|
|
|
state = 'compress'
|
|
|
|
state = 'compress'
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
if module.check_mode:
|
|
|
|
if module.check_mode:
|
|
|
|
if not os.path.exists(creates):
|
|
|
|
if not os.path.exists(dest):
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
size = 0
|
|
|
|
size = 0
|
|
|
|
f_in = f_out = archive = None
|
|
|
|
f_in = f_out = archive = None
|
|
|
|
|
|
|
|
|
|
|
|
if os.path.lexists(creates):
|
|
|
|
if os.path.lexists(dest):
|
|
|
|
size = os.path.getsize(creates)
|
|
|
|
size = os.path.getsize(dest)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
if compression == 'zip':
|
|
|
|
if compression == 'zip':
|
|
|
|
archive = zipfile.ZipFile(creates, 'w', zipfile.ZIP_DEFLATED)
|
|
|
|
archive = zipfile.ZipFile(dest, 'w', zipfile.ZIP_DEFLATED)
|
|
|
|
archive.write(path, path[len(arcroot):])
|
|
|
|
archive.write(path, path[len(arcroot):])
|
|
|
|
archive.close()
|
|
|
|
archive.close()
|
|
|
|
state = 'archive' # because all zip files are archives
|
|
|
|
state = 'archive' # because all zip files are archives
|
|
|
@ -331,9 +331,9 @@ def main():
|
|
|
|
f_in = open(path, 'rb')
|
|
|
|
f_in = open(path, 'rb')
|
|
|
|
|
|
|
|
|
|
|
|
if compression == 'gz':
|
|
|
|
if compression == 'gz':
|
|
|
|
f_out = gzip.open(creates, 'wb')
|
|
|
|
f_out = gzip.open(dest, 'wb')
|
|
|
|
elif compression == 'bz2':
|
|
|
|
elif compression == 'bz2':
|
|
|
|
f_out = bz2.BZ2File(creates, 'wb')
|
|
|
|
f_out = bz2.BZ2File(dest, 'wb')
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise OSError("Invalid compression")
|
|
|
|
raise OSError("Invalid compression")
|
|
|
|
|
|
|
|
|
|
|
@ -344,7 +344,7 @@ def main():
|
|
|
|
except OSError:
|
|
|
|
except OSError:
|
|
|
|
e = get_exception()
|
|
|
|
e = get_exception()
|
|
|
|
|
|
|
|
|
|
|
|
module.fail_json(path=path, creates=creates, msg='Unable to write to compressed file: %s' % str(e))
|
|
|
|
module.fail_json(path=path, dest=dest, msg='Unable to write to compressed file: %s' % str(e))
|
|
|
|
|
|
|
|
|
|
|
|
if archive:
|
|
|
|
if archive:
|
|
|
|
archive.close()
|
|
|
|
archive.close()
|
|
|
@ -354,7 +354,7 @@ def main():
|
|
|
|
f_out.close()
|
|
|
|
f_out.close()
|
|
|
|
|
|
|
|
|
|
|
|
# Rudimentary check: If size changed then file changed. Not perfect, but easy.
|
|
|
|
# Rudimentary check: If size changed then file changed. Not perfect, but easy.
|
|
|
|
if os.path.getsize(creates) != size:
|
|
|
|
if os.path.getsize(dest) != size:
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
|
|
state = 'compress'
|
|
|
|
state = 'compress'
|
|
|
@ -367,7 +367,7 @@ def main():
|
|
|
|
e = get_exception()
|
|
|
|
e = get_exception()
|
|
|
|
module.fail_json(path=path, msg='Unable to remove source file: %s' % str(e))
|
|
|
|
module.fail_json(path=path, msg='Unable to remove source file: %s' % str(e))
|
|
|
|
|
|
|
|
|
|
|
|
module.exit_json(archived=successes, creates=creates, changed=changed, state=state, arcroot=arcroot, missing=missing, expanded_paths=expanded_paths)
|
|
|
|
module.exit_json(archived=successes, dest=dest, changed=changed, state=state, arcroot=arcroot, missing=missing, expanded_paths=expanded_paths)
|
|
|
|
|
|
|
|
|
|
|
|
# import module snippets
|
|
|
|
# import module snippets
|
|
|
|
from ansible.module_utils.basic import *
|
|
|
|
from ansible.module_utils.basic import *
|
|
|
|