@ -1,6 +1,10 @@
#!/usr/bin/python
#!/usr/bin/python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# import module snippets
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . pycompat24 import get_exception
"""
"""
( c ) 2016 , Ben Doherty < bendohmv @gmail.com >
( c ) 2016 , Ben Doherty < bendohmv @gmail.com >
Sponsored by Oomph , Inc . http : / / www . oomphinc . com
Sponsored by Oomph , Inc . http : / / www . oomphinc . com
@ -34,7 +38,7 @@ options:
description :
description :
- Remote absolute path , glob , or list of paths or globs for the file or files to compress or archive .
- Remote absolute path , glob , or list of paths or globs for the file or files to compress or archive .
required : true
required : true
compression :
format :
description :
description :
- 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 ' ]
@ -65,7 +69,7 @@ EXAMPLES = '''
- archive : path = / path / to / foo remove = True
- archive : path = / path / to / foo remove = True
# Create a zip archive of /path/to/foo
# Create a zip archive of /path/to/foo
- archive : path = / path / to / foo compression = zip
- archive : path = / path / to / foo format = zip
# Create a bz2 archive of multiple files, rooted at /path
# Create a bz2 archive of multiple files, rooted at /path
- archive :
- archive :
@ -73,7 +77,7 @@ EXAMPLES = '''
- / path / to / foo
- / path / to / foo
- / path / wong / foo
- / path / wong / foo
dest : / path / file . tar . bz2
dest : / path / file . tar . bz2
compression : bz2
format : bz2
'''
'''
RETURN = '''
RETURN = '''
@ -102,9 +106,8 @@ expanded_paths:
type : list
type : list
'''
'''
import stat
import os
import os
import errno
import re
import glob
import glob
import shutil
import shutil
import gzip
import gzip
@ -117,8 +120,8 @@ def main():
module = AnsibleModule (
module = AnsibleModule (
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 ) ,
format = dict ( choices = [ ' gz ' , ' bz2 ' , ' zip ' , ' tar ' ] , default = ' gz ' , required = False ) ,
dest = dict ( required = False ),
dest = dict ( required = False , type = ' path ' ),
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 ,
@ -126,11 +129,13 @@ def main():
)
)
params = module . params
params = module . params
check_mode = module . check_mode
paths = params [ ' path ' ]
paths = params [ ' path ' ]
dest = params [ ' dest ' ]
dest = params [ ' dest ' ]
remove = params [ ' remove ' ]
remove = params [ ' remove ' ]
expanded_paths = [ ]
expanded_paths = [ ]
compression = params [ ' compression ' ]
format = params [ ' format ' ]
globby = False
globby = False
changed = False
changed = False
state = ' absent '
state = ' absent '
@ -140,11 +145,16 @@ def main():
successes = [ ]
successes = [ ]
for i , path in enumerate ( paths ) :
for i , path in enumerate ( paths ) :
path = os . path . expanduser ( path)
path = os . path . expanduser ( os. path. expandvars ( path ) )
# Detect glob-like characters
# Expand any glob characters. If found, add the expanded glob to the
if any ( ( c in set ( ' *? ' ) ) for c in path ) :
# list of expanded_paths, which might be empty.
if ( ' * ' in path or ' ? ' in path ) :
expanded_paths = expanded_paths + glob . glob ( path )
expanded_paths = expanded_paths + glob . glob ( path )
globby = True
# If there are no glob characters the path is added to the expanded paths
# whether the path exists or not
else :
else :
expanded_paths . append ( path )
expanded_paths . append ( path )
@ -156,11 +166,9 @@ def main():
archive = globby or os . path . isdir ( expanded_paths [ 0 ] ) or len ( expanded_paths ) > 1
archive = globby or os . path . isdir ( expanded_paths [ 0 ] ) or len ( expanded_paths ) > 1
# Default created file name (for single-file archives) to
# Default created file name (for single-file archives) to
# <file>.<compression>
# <file>.<format>
if dest :
if not dest and not archive :
dest = os . path . expanduser ( dest )
dest = ' %s . %s ' % ( expanded_paths [ 0 ] , format )
elif not archive :
dest = ' %s . %s ' % ( expanded_paths [ 0 ] , compression )
# Force archives to specify 'dest'
# Force archives to specify 'dest'
if archive and not dest :
if archive and not dest :
@ -168,7 +176,6 @@ def main():
archive_paths = [ ]
archive_paths = [ ]
missing = [ ]
missing = [ ]
exclude = [ ]
arcroot = ' '
arcroot = ' '
for path in expanded_paths :
for path in expanded_paths :
@ -177,7 +184,7 @@ def main():
if arcroot == ' ' :
if arcroot == ' ' :
arcroot = os . path . dirname ( path ) + os . sep
arcroot = os . path . dirname ( path ) + os . sep
else :
else :
for i in x range( len ( arcroot ) ) :
for i in range( len ( arcroot ) ) :
if path [ i ] != arcroot [ i ] :
if path [ i ] != arcroot [ i ] :
break
break
@ -198,7 +205,7 @@ def main():
# 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 dest and os . path . exists ( dest ) :
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 ( dest ) , re . IGNORECASE ) :
if re . search ( r ' ( \ .tar |\ .tar \ .gz| \ .tgz|.tbz2| \ .tar \ .bz2| \ .zip)$ ' , os . path . basename ( dest ) , re . IGNORECASE ) :
state = ' archive '
state = ' archive '
else :
else :
state = ' compress '
state = ' compress '
@ -221,77 +228,84 @@ def main():
size = os . path . getsize ( dest )
size = os . path . getsize ( dest )
if state != ' archive ' :
if state != ' archive ' :
try :
if check_mode :
changed = True
else :
try :
# Slightly more difficult (and less efficient!) compression using zipfile module
if format == ' zip ' :
arcfile = zipfile . ZipFile ( dest , ' w ' , zipfile . ZIP_DEFLATED )
# Slightly more difficult (and less efficient!) compression using zipfile module
# Easier compression using tar file module
if compression == ' zip ' :
elif format == ' gz ' or format == ' bz2 ' :
arcfile = zipfile . ZipFile ( dest , ' w ' , zipfile . ZIP_DEFLATED )
arcfile = tarfile . open ( dest , ' w| ' + format )
# Easier compression using tarfile module
# Or plain tar archiving
elif compression == ' gz ' or compression == ' bz2 ' :
elif format == ' tar ' :
arcfile = tarfile . open ( dest , ' w| ' + compression )
arcfile = tarfile . open ( dest , ' w ' )
for path in archive_paths :
if os . path . isdir ( path ) :
# Recurse into directories
for dirpath , dirnames , filenames in os . walk ( path , topdown = True ) :
if not dirpath . endswith ( os . sep ) :
dirpath + = os . sep
for dirname in dirnames :
fullpath = dirpath + dirname
arcname = fullpath [ len ( arcroot ) : ]
for path in archive_paths :
if os . path . isdir ( path ) :
# Recurse into directories
for dirpath , dirnames , filenames in os . walk ( path , topdown = True ) :
if not dirpath . endswith ( os . sep ) :
dirpath + = os . sep
for dirname in dirnames :
fullpath = dirpath + dirname
arcname = fullpath [ len ( arcroot ) : ]
try :
if compression == ' zip ' :
arcfile . write ( fullpath , arcname )
else :
arcfile . add ( fullpath , arcname , recursive = False )
except Exception :
e = get_exception ( )
errors . append ( ' %s : %s ' % ( fullpath , str ( e ) ) )
for filename in filenames :
fullpath = dirpath + filename
arcname = fullpath [ len ( arcroot ) : ]
if not filecmp . cmp ( fullpath , dest ) :
try :
try :
if compression == ' zip ' :
if format == ' zip ' :
arcfile . write ( fullpath , arcname )
arcfile . write ( fullpath , arcname )
else :
else :
arcfile . add ( fullpath , arcname , recursive = False )
arcfile . add ( fullpath , arcname , recursive = False )
successes . append ( fullpath )
except Exception :
except Exception :
e = get_exception ( )
e = get_exception ( )
errors . append ( ' Adding %s : %s ' % ( path , str ( e ) ) )
errors . append ( ' %s : %s ' % ( fullpath , str ( e ) ) )
else :
if compression == ' zip ' :
for filename in filenames :
arcfile . write ( path , path [ len ( arcroot ) : ] )
fullpath = dirpath + filename
arcname = fullpath [ len ( arcroot ) : ]
if not filecmp . cmp ( fullpath , dest ) :
try :
if format == ' zip ' :
arcfile . write ( fullpath , arcname )
else :
arcfile . add ( fullpath , arcname , recursive = False )
successes . append ( fullpath )
except Exception :
e = get_exception ( )
errors . append ( ' Adding %s : %s ' % ( path , str ( e ) ) )
else :
else :
arcfile . add ( path , path [ len ( arcroot ) : ] , recursive = False )
if format == ' zip ' :
arcfile . write ( path , path [ len ( arcroot ) : ] )
else :
arcfile . add ( path , path [ len ( arcroot ) : ] , recursive = False )
successes . append ( path )
successes . append ( path )
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 ) , dest , str ( e ) ) )
return module . fail_json ( msg = ' Error when writing %s archive at %s : %s ' % ( format == ' zip ' and ' zip ' or ( ' tar. ' + format ) , 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 ' % ( dest , ' ; ' . 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 :
try :
try :
if os . path . isdir ( path ) :
if os . path . isdir ( path ) :
shutil . rmtree ( path )
shutil . rmtree ( path )
el s e:
el if not check_mod e:
os . remove ( path )
os . remove ( path )
except OSError :
except OSError :
e = get_exception ( )
e = get_exception ( )
@ -331,7 +345,7 @@ def main():
size = os . path . getsize ( dest )
size = os . path . getsize ( dest )
try :
try :
if compression == ' zip ' :
if format == ' zip ' :
arcfile = zipfile . ZipFile ( dest , ' w ' , zipfile . ZIP_DEFLATED )
arcfile = zipfile . ZipFile ( dest , ' w ' , zipfile . ZIP_DEFLATED )
arcfile . write ( path , path [ len ( arcroot ) : ] )
arcfile . write ( path , path [ len ( arcroot ) : ] )
arcfile . close ( )
arcfile . close ( )
@ -340,12 +354,12 @@ def main():
else :
else :
f_in = open ( path , ' rb ' )
f_in = open ( path , ' rb ' )
if compression == ' gz ' :
if format == ' gz ' :
f_out = gzip . open ( dest , ' wb ' )
f_out = gzip . open ( dest , ' wb ' )
elif compression == ' bz2 ' :
elif format == ' bz2 ' :
f_out = bz2 . BZ2File ( dest , ' wb ' )
f_out = bz2 . BZ2File ( dest , ' wb ' )
else :
else :
raise OSError ( " Invalid compression " )
raise OSError ( " Invalid format " )
shutil . copyfileobj ( f_in , f_out )
shutil . copyfileobj ( f_in , f_out )
@ -353,7 +367,6 @@ def main():
except OSError :
except OSError :
e = get_exception ( )
e = get_exception ( )
module . fail_json ( path = path , dest = dest , 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 arcfile :
if arcfile :
@ -369,7 +382,7 @@ def main():
state = ' compress '
state = ' compress '
if remove :
if remove and not check_mode :
try :
try :
os . remove ( path )
os . remove ( path )
@ -377,9 +390,12 @@ 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 ) )
params [ ' path ' ] = dest
file_args = module . load_file_common_arguments ( params )
changed = module . set_fs_attributes_if_different ( file_args , changed )
module . exit_json ( archived = successes , dest = dest , 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
from ansible . module_utils . basic import *
if __name__ == ' __main__ ' :
if __name__ == ' __main__ ' :
main ( )
main ( )