win_copy: Add force parameter and check-mode support (#20405)

* win_copy: Add force parameter and check-mode support

The rationale behind this is that if you're working with +3GB files,
creating the checksum takes a lot of time, which we can avoid by simply
testing if the file exists.

I also took the liberty to put the various parameters together. It
probably takes a (neglible) performance hit but makes the code a bit
easier to inspect/work with, as its closer to all other windows modules.

On a normal run, the action plugin does a local checksum of the source
and a remote checksum of the destination. And afterwards, the module
will do another remote checksum of the copied source, a remote checksum
of the original destination, and another remote checksum of the copied
destination.

On a very huge file (think 4GB) that means 5x reading the complete file
(if you have a large cache you may get away with it, otherwise you're
doomed !).

This patch will ensure with `force: no` that not checksums are being
performed.

* Moving presence check before remote checksum

* Adapted to wishes

* Even more performance improvements
This commit is contained in:
Dag Wieers 2017-02-25 03:10:09 +01:00 committed by Matt Davis
parent ce08b4165d
commit 98934939af
4 changed files with 63 additions and 61 deletions

View file

@ -25,7 +25,7 @@ import stat
import tempfile
from ansible.constants import mk_boolean as boolean
from ansible.errors import AnsibleError
from ansible.errors import AnsibleError, AnsibleFileNotFound
from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.plugins.action import ActionBase
from ansible.utils.hashing import checksum
@ -155,10 +155,14 @@ class ActionModule(ActionBase):
diffs = []
for source_full, source_rel in source_files:
source_full = self._loader.get_real_file(source_full)
# Generate a hash of the local file.
local_checksum = checksum(source_full)
# If the local file does not exist, get_real_file() raises AnsibleFileNotFound
try:
source_full = self._loader.get_real_file(source_full)
except AnsibleFileNotFound as e:
result['failed'] = True
result['msg'] = "could not find src=%s, %s" % (source_full, e)
self._remove_tmp_path(tmp)
return result
# Get the local mode and set if user wanted it preserved
# https://github.com/ansible/ansible-modules-core/issues/1124
@ -166,13 +170,6 @@ class ActionModule(ActionBase):
lmode = '0%03o' % stat.S_IMODE(os.stat(source_full).st_mode)
self._task.args['mode'] = lmode
# If local_checksum is not defined we can't find the file so we should fail out.
if local_checksum is None:
result['failed'] = True
result['msg'] = "could not find src=%s" % source_full
self._remove_tmp_path(tmp)
return result
# This is kind of optimization - if user told us destination is
# dir, do path manipulation right away, otherwise we still check
# for dest being a dir via remote call below.
@ -182,7 +179,7 @@ class ActionModule(ActionBase):
dest_file = self._connection._shell.join_path(dest)
# Attempt to get remote file info
dest_status = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=follow, tmp=tmp)
dest_status = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=follow, tmp=tmp, checksum=force)
if dest_status['exists'] and dest_status['isdir']:
# The dest is a directory.
@ -196,12 +193,15 @@ class ActionModule(ActionBase):
else:
# Append the relative source location to the destination and get remote stats again
dest_file = self._connection._shell.join_path(dest, source_rel)
dest_status = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=follow, tmp=tmp)
dest_status = self._execute_remote_stat(dest_file, all_vars=task_vars, follow=follow, tmp=tmp, checksum=force)
if dest_status['exists'] and not force:
# remote_file does not exist so continue to next iteration.
# remote_file exists so continue to next iteration.
continue
# Generate a hash of the local file.
local_checksum = checksum(source_full)
if local_checksum != dest_status['checksum']:
# The checksums don't match and we will change or error out.
changed = True