@ -1,7 +1,7 @@
#!/usr/bin/python
#!/usr/bin/python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
# Copyright: (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import , division , print_function
from __future__ import absolute_import , division , print_function
@ -226,6 +226,11 @@ dest:
returned : success
returned : success
type : string
type : string
sample : / path / to / file . txt
sample : / path / to / file . txt
elapsed :
description : The number of seconds that elapsed while performing the download
returned : always
type : int
sample : 23
gid :
gid :
description : group id of the file
description : group id of the file
returned : success
returned : success
@ -268,7 +273,7 @@ size:
sample : 1220
sample : 1220
src :
src :
description : source file used after download
description : source file used after download
returned : changed
returned : always
type : string
type : string
sample : / tmp / tmpAdFLdV
sample : / tmp / tmpAdFLdV
state :
state :
@ -327,17 +332,19 @@ def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, head
else :
else :
method = ' GET '
method = ' GET '
start = datetime . datetime . utcnow ( )
rsp , info = fetch_url ( module , url , use_proxy = use_proxy , force = force , last_mod_time = last_mod_time , timeout = timeout , headers = headers , method = method )
rsp , info = fetch_url ( module , url , use_proxy = use_proxy , force = force , last_mod_time = last_mod_time , timeout = timeout , headers = headers , method = method )
elapsed = ( datetime . datetime . utcnow ( ) - start ) . seconds
if info [ ' status ' ] == 304 :
if info [ ' status ' ] == 304 :
module . exit_json ( url = url , dest = dest , changed = False , msg = info . get ( ' msg ' , ' ' ) )
module . exit_json ( url = url , dest = dest , changed = False , msg = info . get ( ' msg ' , ' ' ) , elapsed = elapsed )
# Exceptions in fetch_url may result in a status -1, the ensures a proper error to the user in all cases
# Exceptions in fetch_url may result in a status -1, the ensures a proper error to the user in all cases
if info [ ' status ' ] == - 1 :
if info [ ' status ' ] == - 1 :
module . fail_json ( msg = info [ ' msg ' ] , url = url , dest = dest )
module . fail_json ( msg = info [ ' msg ' ] , url = url , dest = dest , elapsed = elapsed )
if info [ ' status ' ] != 200 and not url . startswith ( ' file:/ ' ) and not ( url . startswith ( ' ftp:/ ' ) and info . get ( ' msg ' , ' ' ) . startswith ( ' OK ' ) ) :
if info [ ' status ' ] != 200 and not url . startswith ( ' file:/ ' ) and not ( url . startswith ( ' ftp:/ ' ) and info . get ( ' msg ' , ' ' ) . startswith ( ' OK ' ) ) :
module . fail_json ( msg = " Request failed " , status_code = info [ ' status ' ] , response = info [ ' msg ' ] , url = url , dest = dest )
module . fail_json ( msg = " Request failed " , status_code = info [ ' status ' ] , response = info [ ' msg ' ] , url = url , dest = dest , elapsed = elapsed )
# create a temporary file and copy content to do checksum-based replacement
# create a temporary file and copy content to do checksum-based replacement
if tmp_dest :
if tmp_dest :
@ -345,9 +352,9 @@ def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, head
tmp_dest_is_dir = os . path . isdir ( tmp_dest )
tmp_dest_is_dir = os . path . isdir ( tmp_dest )
if not tmp_dest_is_dir :
if not tmp_dest_is_dir :
if os . path . exists ( tmp_dest ) :
if os . path . exists ( tmp_dest ) :
module . fail_json ( msg = " %s is a file but should be a directory. " % tmp_dest )
module . fail_json ( msg = " %s is a file but should be a directory. " % tmp_dest , elapsed = elapsed )
else :
else :
module . fail_json ( msg = " %s directory does not exist. " % tmp_dest )
module . fail_json ( msg = " %s directory does not exist. " % tmp_dest , elapsed = elapsed )
else :
else :
tmp_dest = module . tmpdir
tmp_dest = module . tmpdir
@ -358,7 +365,7 @@ def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, head
shutil . copyfileobj ( rsp , f )
shutil . copyfileobj ( rsp , f )
except Exception as e :
except Exception as e :
os . remove ( tempname )
os . remove ( tempname )
module . fail_json ( msg = " failed to create temporary content file: %s " % to_native ( e ) , e xception= traceback . format_exc ( ) )
module . fail_json ( msg = " failed to create temporary content file: %s " % to_native ( e ) , e lapsed= elapsed , e xception= traceback . format_exc ( ) )
f . close ( )
f . close ( )
rsp . close ( )
rsp . close ( )
return tempname , info
return tempname , info
@ -418,6 +425,15 @@ def main():
timeout = module . params [ ' timeout ' ]
timeout = module . params [ ' timeout ' ]
tmp_dest = module . params [ ' tmp_dest ' ]
tmp_dest = module . params [ ' tmp_dest ' ]
result = dict (
changed = False ,
checksum_dest = None ,
checksum_src = None ,
dest = dest ,
elapsed = 0 ,
url = url ,
)
# Parse headers to dict
# Parse headers to dict
if isinstance ( module . params [ ' headers ' ] , dict ) :
if isinstance ( module . params [ ' headers ' ] , dict ) :
headers = module . params [ ' headers ' ]
headers = module . params [ ' headers ' ]
@ -426,7 +442,7 @@ def main():
headers = dict ( item . split ( ' : ' , 1 ) for item in module . params [ ' headers ' ] . split ( ' , ' ) )
headers = dict ( item . split ( ' : ' , 1 ) for item in module . params [ ' headers ' ] . split ( ' , ' ) )
module . deprecate ( ' Supplying `headers` as a string is deprecated. Please use dict/hash format for `headers` ' , version = ' 2.10 ' )
module . deprecate ( ' Supplying `headers` as a string is deprecated. Please use dict/hash format for `headers` ' , version = ' 2.10 ' )
except Exception :
except Exception :
module . fail_json ( msg = " The string representation for the `headers` parameter requires a key:value,key:value syntax to be properly parsed. " )
module . fail_json ( msg = " The string representation for the `headers` parameter requires a key:value,key:value syntax to be properly parsed. " , * * result )
else :
else :
headers = None
headers = None
@ -456,7 +472,7 @@ def main():
# Ensure the checksum portion is a hexdigest
# Ensure the checksum portion is a hexdigest
int ( checksum , 16 )
int ( checksum , 16 )
except ValueError :
except ValueError :
module . fail_json ( msg = " The checksum parameter has to be in format <algorithm>:<checksum> " )
module . fail_json ( msg = " The checksum parameter has to be in format <algorithm>:<checksum> " , * * result )
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
@ -472,10 +488,10 @@ def main():
module . params [ ' path ' ] = dest
module . params [ ' path ' ] = dest
file_args = module . load_file_common_arguments ( module . params )
file_args = module . load_file_common_arguments ( module . params )
file_args [ ' path ' ] = dest
file_args [ ' path ' ] = dest
changed = module . set_fs_attributes_if_different ( file_args , False )
result[ ' changed' ] = module . set_fs_attributes_if_different ( file_args , False )
if changed:
if result[ ' changed' ] :
module . exit_json ( msg = " file already exists but file attributes changed " , dest = dest , url = url , changed = changed )
module . exit_json ( msg = " file already exists but file attributes changed " , * * result )
module . exit_json ( msg = " file already exists " , dest = dest , url = url , changed = changed )
module . exit_json ( msg = " file already exists " , * * result )
checksum_mismatch = True
checksum_mismatch = True
@ -490,7 +506,10 @@ def main():
force = True
force = True
# download to tmpsrc
# download to tmpsrc
start = datetime . datetime . utcnow ( )
tmpsrc , info = url_get ( module , url , dest , use_proxy , last_mod_time , force , timeout , headers , tmp_dest )
tmpsrc , info = url_get ( module , url , dest , use_proxy , last_mod_time , force , timeout , headers , tmp_dest )
result [ ' elapsed ' ] = ( datetime . datetime . utcnow ( ) - start ) . seconds
result [ ' src ' ] = tmpsrc
# Now the request has completed, we can finally generate the final
# Now the request has completed, we can finally generate the final
# destination file name from the info dict.
# destination file name from the info dict.
@ -504,44 +523,41 @@ def main():
filename = url_filename ( info [ ' url ' ] )
filename = url_filename ( info [ ' url ' ] )
dest = os . path . join ( dest , filename )
dest = os . path . join ( dest , filename )
checksum_src = None
checksum_dest = None
# If the remote URL exists, we're done with check mode
# If the remote URL exists, we're done with check mode
if module . check_mode :
if module . check_mode :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
res _args = dict ( url = url , dest = dest , src = tmpsrc , changed = True , msg = info . get ( ' msg ' , ' ' ) )
res ult[ ' changed ' ] = True
module . exit_json ( * * res_args )
module . exit_json ( msg = info . get ( ' msg ' , ' ' ) , * * result )
# raise an error if there is no tmpsrc file
# raise an error if there is no tmpsrc file
if not os . path . exists ( tmpsrc ) :
if not os . path . exists ( tmpsrc ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " Request failed " , status_code = info [ ' status ' ] , response = info [ ' msg ' ] )
module . fail_json ( msg = " Request failed " , status_code = info [ ' status ' ] , response = info [ ' msg ' ] , * * result )
if not os . access ( tmpsrc , os . R_OK ) :
if not os . access ( tmpsrc , os . R_OK ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " Source %s is not readable " % ( tmpsrc ) )
module . fail_json ( msg = " Source %s is not readable " % ( tmpsrc ) , * * result )
checksum_src = module . sha1 ( tmpsrc )
result[ ' checksum_src' ] = module . sha1 ( tmpsrc )
# check if there is no dest file
# check if there is no dest file
if os . path . exists ( dest ) :
if os . path . exists ( dest ) :
# raise an error if copy has no permission on dest
# raise an error if copy has no permission on dest
if not os . access ( dest , os . W_OK ) :
if not os . access ( dest , os . W_OK ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " Destination %s is not writable " % ( dest ) )
module . fail_json ( msg = " Destination %s is not writable " % ( dest ) , * * result )
if not os . access ( dest , os . R_OK ) :
if not os . access ( dest , os . R_OK ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " Destination %s is not readable " % ( dest ) )
module . fail_json ( msg = " Destination %s is not readable " % ( dest ) , * * result )
checksum_dest = module . sha1 ( dest )
result[ ' checksum_dest' ] = module . sha1 ( dest )
else :
else :
if not os . path . exists ( os . path . dirname ( dest ) ) :
if not os . path . exists ( os . path . dirname ( dest ) ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " Destination %s does not exist " % ( os . path . dirname ( dest ) ) )
module . fail_json ( msg = " Destination %s does not exist " % ( os . path . dirname ( dest ) ) , * * result )
if not os . access ( os . path . dirname ( dest ) , os . W_OK ) :
if not os . access ( os . path . dirname ( dest ) , os . W_OK ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " Destination %s is not writable " % ( os . path . dirname ( dest ) ) )
module . fail_json ( msg = " Destination %s is not writable " % ( os . path . dirname ( dest ) ) , * * result )
backup_file = None
backup_file = None
if checksum_src != checksum_dest:
if result[ ' checksum_src' ] != result[ ' checksum_dest' ] :
try :
try :
if backup :
if backup :
if os . path . exists ( dest ) :
if os . path . exists ( dest ) :
@ -551,10 +567,10 @@ def main():
if os . path . exists ( tmpsrc ) :
if os . path . exists ( tmpsrc ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
module . fail_json ( msg = " failed to copy %s to %s : %s " % ( tmpsrc , dest , to_native ( e ) ) ,
module . fail_json ( msg = " failed to copy %s to %s : %s " % ( tmpsrc , dest , to_native ( e ) ) ,
exception = traceback . format_exc ( ) )
exception = traceback . format_exc ( ) , * * result )
changed = True
result[ ' changed' ] = True
else :
else :
changed = False
result[ ' changed' ] = False
if os . path . exists ( tmpsrc ) :
if os . path . exists ( tmpsrc ) :
os . remove ( tmpsrc )
os . remove ( tmpsrc )
@ -563,29 +579,25 @@ def main():
if checksum != destination_checksum :
if checksum != destination_checksum :
os . remove ( dest )
os . remove ( dest )
module . fail_json ( msg = " The checksum for %s did not match %s ; it was %s . " % ( dest , checksum , destination_checksum ) )
module . fail_json ( msg = " The checksum for %s did not match %s ; it was %s . " % ( dest , checksum , destination_checksum ) , * * result )
# allow file attribute changes
# allow file attribute changes
module . params [ ' path ' ] = dest
module . params [ ' path ' ] = dest
file_args = module . load_file_common_arguments ( module . params )
file_args = module . load_file_common_arguments ( module . params )
file_args [ ' path ' ] = dest
file_args [ ' path ' ] = dest
changed = module . set_fs_attributes_if_different ( file_args , changed)
result[ ' changed' ] = module . set_fs_attributes_if_different ( file_args , result[ ' changed' ] )
# Backwards compat only. We'll return None on FIPS enabled systems
# Backwards compat only. We'll return None on FIPS enabled systems
try :
try :
md5sum = module . md5 ( dest )
result[ ' md5sum' ] = module . md5 ( dest )
except ValueError :
except ValueError :
md5sum = None
result[ ' md5sum' ] = None
res_args = dict (
url = url , dest = dest , src = tmpsrc , md5sum = md5sum , checksum_src = checksum_src ,
checksum_dest = checksum_dest , changed = changed , msg = info . get ( ' msg ' , ' ' ) , status_code = info . get ( ' status ' , ' ' )
)
if backup_file :
if backup_file :
res _args [ ' backup_file ' ] = backup_file
result [ ' backup_file ' ] = backup_file
# Mission complete
# Mission complete
module . exit_json ( * * res_args )
module . exit_json ( msg = info . get ( ' msg ' , ' ' ) , status_code = info . get ( ' status ' , ' ' ) , * * result )
if __name__ == ' __main__ ' :
if __name__ == ' __main__ ' :