Repository: ansible/ansible

Issue:
Fix pipelining with copy module.
##### ISSUE TYPE
- Bugfix Pull Request
##### COMPONENT NAME

lib/ansible/plugins/action/copy.py
##### ANSIBLE VERSION

<!--- Paste verbatim output from “ansible --version” between quotes below -->

```
stbale-2.1
```
##### SUMMARY

The fix to always clean temp directories broke pipelining with
the copy module.  This reverts part of the change in
a5d79a39d51a256937d6e5742befa169629b7caf



Discussion:
Playbook to see the problem:

```
- hosts: all
  tasks:
  - copy:
      src: /etc/os-release
      dest: "/var/tmp/r"
```

And invocation:

```
ANSIBLE_SSH_PIPELINING=1 ansible-playbook -i '127.0.0.2' -c ssh test.yml -vvvv
```

When broken, you'll see that the copy module is sftp'd to the remote machine despite pipelining.  After copying, the module is still invoked remotely by being piped to the stdin of python on the remote machine.

Talked with @bcoca, and _execute_module() does need to get the tmp directories so we need to change _execute_module() to figure out whether to send the module via the pipelining flag, not whether tmpdir exists or not.

Also, the tmp cleanup change is unoptimizing the removal of the directory in copy (requiring an additional connection to the remote machine rather than doing it as  part of the last call to execute_module).  We should look into fixing that as well, perhaps for 2.3.  (bcoca and I agree that the copy code is very arcane in this area... Could do with some simplification as we work on fixing the unoptimization).


Resolving patch:
diff --git a/lib/ansible/cli/playbook.py b/lib/ansible/cli/playbook.py
--- a/lib/ansible/cli/playbook.py
+++ b/lib/ansible/cli/playbook.py
@@ -148,6 +148,11 @@ def run(self):
             # Invalid limit
             raise AnsibleError("Specified --limit does not match any hosts")
 
+        # flush fact cache if requested
+        if self.options.flush_cache:
+            for host in inventory.list_hosts():
+                variable_manager.clear_facts(host)
+
         # create the playbook executor, which manages running the plays via a task queue manager
         pbex = PlaybookExecutor(playbooks=self.args, inventory=inventory, variable_manager=variable_manager, loader=loader, options=self.options, passwords=passwords)
 
diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py
--- a/lib/ansible/plugins/action/__init__.py
+++ b/lib/ansible/plugins/action/__init__.py
@@ -615,7 +615,12 @@ def _execute_module(self, module_name=None, module_args=None, tmp=None, task_var
         if not tmp and self._late_needs_tmp_path(tmp, module_style):
             tmp = self._make_tmp_path(remote_user)
 
-        if tmp:
+        if tmp and \
+         (module_style != 'new' or \
+         not self._connection.has_pipelining or \
+         not self._play_context.pipelining or \
+         C.DEFAULT_KEEP_REMOTE_FILES or \
+         self._play_context.become_method == 'su'):
             remote_module_filename = self._connection._shell.get_remote_filename(module_path)
             remote_module_path = self._connection._shell.join_path(tmp, remote_module_filename)
             if module_style in ('old', 'non_native_want_json', 'binary'):


Test patch: