Repository: ansible/ansible

Issue:
nxos_file_copy copy file localy if no host is set
##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report
 - Documentation Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
nxos_file_copy

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0
  config file = /home/adminlocal/noiua-ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.9 (default, Jun 29 2016, 13:08:31) [GCC 4.9.2]
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### SUMMARY
nxos_file_copy doesn't use the same method to dertermine the remote host to connect to. If no host is given in parameter (which should not be allowed according to the documentation, but it doesn't raise any error), the module connect to the remote host in ssh for the checking/control part but on the host on which it is running for the scp/data part.

##### STEPS TO REPRODUCE
Use a tasks with only local_file set.

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- nxos_file_copy:
    local_file: "./test_file.txt"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
The module should raise an error as host is a required parameter.
Or, it should use the same host for control and data connection. This would simplify the usage of the module and allow to have the same set of parameters as the others nxos_* module.

##### ACTUAL RESULTS
The file is copied to the localhost and the result is sent. If run again, the file is recopied as the control connection use a connection to the actual device (on which nothing has been copied)


Discussion:
cc @jedelman8 @GGabriele @rahushen @privateip @rcarrillocruz @mikewiebe
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/network/nxos/nxos_file_copy.py b/lib/ansible/modules/network/nxos/nxos_file_copy.py
--- a/lib/ansible/modules/network/nxos/nxos_file_copy.py
+++ b/lib/ansible/modules/network/nxos/nxos_file_copy.py
@@ -16,10 +16,11 @@
 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-ANSIBLE_METADATA = {'metadata_version': '1.0',
-                    'status': ['preview'],
-                    'supported_by': 'community'}
-
+ANSIBLE_METADATA = {
+    'metadata_version': '1.0',
+    'status': ['preview'],
+    'supported_by': 'community'
+}
 
 DOCUMENTATION = '''
 ---
@@ -28,34 +29,34 @@
 version_added: "2.2"
 short_description: Copy a file to a remote NXOS device over SCP.
 description:
-    - Copy a file to the flash (or bootflash) remote network device
-      on NXOS devices.
+  - Copy a file to the flash (or bootflash) remote network device
+    on NXOS devices.
 author:
-    - Jason Edelman (@jedelman8)
-    - Gabriele Gerbino (@GGabriele)
+  - Jason Edelman (@jedelman8)
+  - Gabriele Gerbino (@GGabriele)
 notes:
-    - The feature must be enabled with feature scp-server.
-    - If the file is already present (md5 sums match), no transfer will
-      take place.
-    - Check mode will tell you if the file would be copied.
+  - The feature must be enabled with feature scp-server.
+  - If the file is already present (md5 sums match), no transfer will
+    take place.
+  - Check mode will tell you if the file would be copied.
 options:
-    local_file:
-        description:
-            - Path to local file. Local directory must exist.
-        required: true
-    remote_file:
-        description:
-            - Remote file path of the copy. Remote directories must exist.
-              If omitted, the name of the local file will be used.
-        required: false
-        default: null
-    file_system:
-        description:
-            - The remote file system of the device. If omitted,
-              devices that support a file_system parameter will use
-              their default values.
-        required: false
-        default: null
+  local_file:
+    description:
+      - Path to local file. Local directory must exist.
+    required: true
+  remote_file:
+    description:
+      - Remote file path of the copy. Remote directories must exist.
+        If omitted, the name of the local file will be used.
+    required: false
+    default: null
+  file_system:
+    description:
+      - The remote file system of the device. If omitted,
+        devices that support a file_system parameter will use
+        their default values.
+    required: false
+    default: null
 '''
 
 EXAMPLES = '''
@@ -83,12 +84,11 @@
     type: string
     sample: '/path/to/remote/file'
 '''
+
 import os
 import re
 import time
-
 import paramiko
-
 from ansible.module_utils.nxos import run_commands
 from ansible.module_utils.nxos import nxos_argument_spec, check_args
 from ansible.module_utils.basic import AnsibleModule
@@ -100,31 +100,20 @@
     HAS_SCP = False
 
 
-def execute_show_command(command, module, command_type='cli_show'):
-    if module.params['transport'] == 'cli':
-        cmds = [command]
-        body = run_commands(module, cmds)
-    elif module.params['transport'] == 'nxapi':
-        cmds = [command]
-        body = run_commands(module, cmds)
-
-    return body
-
-
 def remote_file_exists(module, dst, file_system='bootflash:'):
     command = 'dir {0}/{1}'.format(file_system, dst)
-    body = execute_show_command(command, module, command_type='cli_show_ascii')
-    if 'No such file' in body[0]:
+    body = run_commands(module, [command])[0]
+    if 'No such file' in body:
         return False
     return True
 
 
 def verify_remote_file_exists(module, dst, file_system='bootflash:'):
     command = 'dir {0}/{1}'.format(file_system, dst)
-    body = execute_show_command(command, module, command_type='cli_show_ascii')
-    if 'No such file' in body[0]:
+    body = run_commands(module, [command])[0]
+    if 'No such file' in body:
         return 0
-    return body[0].split()[0].strip()
+    return body.split()[0].strip()
 
 
 def local_file_exists(module):
@@ -133,9 +122,9 @@ def local_file_exists(module):
 
 def get_flash_size(module):
     command = 'dir {}'.format(module.params['file_system'])
-    body = execute_show_command(command, module, command_type='cli_show_ascii')
+    body = run_commands(module, [command])[0]
 
-    match = re.search(r'(\d+) bytes free', body[0])
+    match = re.search(r'(\d+) bytes free', body)
     bytes_free = match.group(1)
 
     return int(bytes_free)
@@ -199,8 +188,7 @@ def main():
 
     argument_spec.update(nxos_argument_spec)
 
-    module = AnsibleModule(argument_spec=argument_spec,
-                           supports_check_mode=True)
+    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
 
     if not HAS_SCP:
         module.fail_json(
@@ -210,7 +198,6 @@ def main():
 
     warnings = list()
     check_args(module, warnings)
-
     results = dict(changed=False, warnings=warnings)
 
     local_file = module.params['local_file']
diff --git a/lib/ansible/plugins/action/nxos.py b/lib/ansible/plugins/action/nxos.py
--- a/lib/ansible/plugins/action/nxos.py
+++ b/lib/ansible/plugins/action/nxos.py
@@ -63,7 +63,13 @@ def run(self, tmp=None, task_vars=None):
             pc.password = provider['password'] or self._play_context.password
             pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file
             pc.timeout = provider['timeout'] or self._play_context.timeout
-
+            self._task.args['provider'] = provider.update(
+                host=pc.remote_addr,
+                port=pc.port,
+                username=pc.remote_user,
+                password=pc.password,
+                ssh_keyfile=pc.private_key_file
+            )
             display.vvv('using connection plugin %s' % pc.connection, pc.remote_addr)
             connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
 
@@ -76,8 +82,8 @@ def run(self, tmp=None, task_vars=None):
                 display.vvvv('open_shell() returned %s %s %s' % (rc, out, err))
                 if rc != 0:
                     return {'failed': True,
-                            'msg': 'unable to open shell. Please see: '
-                                   + 'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell',
+                            'msg': 'unable to open shell. Please see: ' +
+                            'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell',
                             'rc': rc}
             else:
                 # make sure we are in the right cli context which should be
@@ -88,12 +94,9 @@ def run(self, tmp=None, task_vars=None):
                     connection.exec_command('exit')
                     rc, out, err = connection.exec_command('prompt()')
 
-
             task_vars['ansible_socket'] = socket_path
-
         else:
             provider['transport'] = 'nxapi'
-
             if provider.get('host') is None:
                 provider['host'] = self._play_context.remote_addr
 


Test patch:
diff --git a/test/sanity/pep8/legacy-files.txt b/test/sanity/pep8/legacy-files.txt
--- a/test/sanity/pep8/legacy-files.txt
+++ b/test/sanity/pep8/legacy-files.txt
@@ -739,7 +739,6 @@ lib/ansible/plugins/action/iosxr_template.py
 lib/ansible/plugins/action/junos.py
 lib/ansible/plugins/action/junos_template.py
 lib/ansible/plugins/action/normal.py
-lib/ansible/plugins/action/nxos.py
 lib/ansible/plugins/action/nxos_template.py
 lib/ansible/plugins/action/ops_config.py
 lib/ansible/plugins/action/ops_template.py