Repository: ansible/ansible
Pull request: nxos_file_copy network_cli and httpapi fix
##### SUMMARY
<!--- Describe the change, including rationale and design decisions -->
fixes https://github.com/ansible/ansible/issues/40013#issuecomment-391129484

- Pass play_context host, user, password to the module code.
- Remove provider

<!--- If you are fixing an existing issue, please include "Fixes #nnn" in your
commit message and your description; but you should still explain what
the change does.-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bugfix Pull Request

##### COMPONENT NAME
<!--- Name of the module, plugin, module or task -->
modules/network/nxos/nxos_file_copy.py
plugins/action/nxos.py
test/integration/targets/nxos_file_copy/
##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
devel
```

**Result**
All the tests passed.
```
PLAY RECAP ********************************************************************************************************************************************************************************************************
nxos9k-02                  : ok=84   changed=12   unreachable=0    failed=0   
```

Problem statement:
Intermittent Issue: Unable to copy large image files using nxos_file_copy module

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
nxos_file_copy

##### ANSIBLE VERSION
```
ansible 2.6.0 (rel260/nxos_upgrade 7bb4da05b6) last updated 2018/05/11 09:14:42 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /root/agents-ci/ansible/lib/ansible
  executable location = /root/agents-ci/ansible/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
```
DEFAULT_ROLES_PATH(env: ANSIBLE_ROLES_PATH) = [u'/root/agents-ci/ansible/test/integration/targets']
PARAMIKO_HOST_KEY_AUTO_ADD(env: ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD) = True
PERSISTENT_COMMAND_TIMEOUT(env: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT) = 600
PERSISTENT_CONNECT_TIMEOUT(env: ANSIBLE_PERSISTENT_CONNECT_TIMEOUT) = 600
```

##### OS / ENVIRONMENT
Ubuntu

##### SUMMARY
We used to be able to copy large image files using the `nxos_file_copy` module but now larger files fail to copy with the following error

```shell
  File "/usr/local/lib/python2.7/dist-packages/paramiko/transport.py", line 1610, in _send_user_message
    raise SSHException('Key-exchange timed out waiting for key negotiation')
```
##### STEPS TO REPRODUCE
Here are the image sizes of the files I am trying to copy

agent-lab9-pm:integration:2053> ls -lh /root/agents_images/
total 4.3G
-rw-r--r-- 1 root root  37M Mar  2 11:45 n3000-uk9-kickstart.6.0.2.U6.1a.bin
-rw-r--r-- 1 root root 845M Mar  2 18:42 nxos.7.0.3.I7.2.bin

```yaml
- name: "Copy {{ si }} to bootflash"
  nxos_file_copy:
    local_file: "{{image_dir}}{{ si }}"
    file_system: "bootflash:"
    provider: "{{ connection }}"
  register: result
```

##### EXPECTED RESULTS
Here is an example using a smaller image file
```shell
TASK [nxos_install_os : Copy n3000-uk9-kickstart.6.0.2.U6.1a.bin to bootflash] *******************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_install_os/tasks/upgrade/copy_kick_system_images.yaml:8
<n9k.example.com> connection transport is cli
<n9k.example.com> using connection plugin network_cli (was local)
<n9k.example.com> starting connection from persistent connection plugin
<n9k.example.com> found existing local domain socket, using it!
<n9k.example.com> updating play_context for connection
<n9k.example.com> 
<n9k.example.com> local domain socket path is /root/.ansible/pc/fe6d223170
<n9k.example.com> socket_path: /root/.ansible/pc/fe6d223170
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_file_copy.py
<n9k.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n9k.example.com> EXEC /bin/sh -c 'echo ~root && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313 `" && echo ansible-tmp-1526046585.58-51328088639313="` echo /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313 `" ) && sleep 0'
<n9k.example.com> PUT /root/.ansible/tmp/ansible-local-31112XrsRA_/tmpPn8qX7 TO /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313/nxos_file_copy.py
<n9k.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313/ /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313/nxos_file_copy.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313/nxos_file_copy.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1526046585.58-51328088639313/ > /dev/null 2>&1 && sleep 0'
changed: [n9k.example.com] => {
    "changed": true, 
    "file_system": "bootflash:", 
    "invocation": {
        "module_args": {
            "auth_pass": null, 
            "authorize": null, 
            "connect_ssh_port": 22, 
            "file_system": "bootflash:", 
            "host": "n9k.example.com", 
            "local_file": "/root/agents_images/n3000-uk9-kickstart.6.0.2.U6.1a.bin", 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": null, 
            "provider": {
                "auth_pass": null, 
                "authorize": null, 
                "host": "n9k.example.com", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": null, 
                "ssh_keyfile": null, 
                "timeout": null, 
                "transport": "cli", 
                "use_proxy": true, 
                "use_ssl": null, 
                "username": "admin", 
                "validate_certs": null
            }, 
            "remote_file": null, 
            "ssh_keyfile": null, 
            "timeout": null, 
            "transport": "cli", 
            "use_proxy": true, 
            "use_ssl": null, 
            "username": "admin", 
            "validate_certs": null
        }
    }, 
    "local_file": "/root/agents_images/n3000-uk9-kickstart.6.0.2.U6.1a.bin", 
    "remote_file": "n3000-uk9-kickstart.6.0.2.U6.1a.bin", 
    "transfer_status": "Sent"
}
```
##### ACTUAL RESULTS
Here is an example using a larger image file
```
TASK [nxos_install_os : Copy nxos.7.0.3.I7.2.bin to bootflash] ***********************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_install_os/tasks/upgrade/copy_kick_system_images.yaml:8
<n9k.example.com> connection transport is cli
<n9k.example.com> using connection plugin network_cli (was local)
<n9k.example.com> starting connection from persistent connection plugin
<n9k.example.com> found existing local domain socket, using it!
<n9k.example.com> updating play_context for connection
<n9k.example.com> 
<n9k.example.com> local domain socket path is /root/.ansible/pc/ee1ab4a788
<n9k.example.com> socket_path: /root/.ansible/pc/ee1ab4a788
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_file_copy.py
<n9k.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n9k.example.com> EXEC /bin/sh -c 'echo ~root && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141 `" && echo ansible-tmp-1526046216.79-32071527780141="` echo /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141 `" ) && sleep 0'
<n9k.example.com> PUT /root/.ansible/tmp/ansible-local-30569sYjrvT/tmpbMF3OR TO /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141/nxos_file_copy.py
<n9k.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141/ /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141/nxos_file_copy.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141/nxos_file_copy.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1526046216.79-32071527780141/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
  File "/tmp/ansible_HFaPp8/ansible_module_nxos_file_copy.py", line 174, in transfer_file
    scp.put(module.params['local_file'], full_remote_path)
  File "/usr/local/lib/python2.7/dist-packages/scp.py", line 154, in put
    self._send_files(files)
  File "/usr/local/lib/python2.7/dist-packages/scp.py", line 249, in _send_files
    chan.sendall(file_hdl.read(buff_size))
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 813, in sendall
    sent = self.send(s)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 767, in send
    return self._send(s, m)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 1144, in _send
    self.transport._send_user_message(m)
  File "/usr/local/lib/python2.7/dist-packages/paramiko/transport.py", line 1610, in _send_user_message
    raise SSHException('Key-exchange timed out waiting for key negotiation')

fatal: [n9k.example.com]: FAILED! => {
    "changed": false, 
    "file_size": 885554688, 
    "invocation": {
        "module_args": {
            "auth_pass": null, 
            "authorize": null, 
            "connect_ssh_port": 22, 
            "file_system": "bootflash:", 
            "host": "n9k.example.com", 
            "local_file": "/root/agents_images/nxos.7.0.3.I7.2.bin", 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": null, 
            "provider": {
                "auth_pass": null, 
                "authorize": null, 
                "host": "n9k.example.com", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": null, 
                "ssh_keyfile": null, 
                "timeout": null, 
                "transport": "cli", 
                "use_proxy": true, 
                "use_ssl": null, 
                "username": "admin", 
                "validate_certs": null
            }, 
            "remote_file": null, 
            "ssh_keyfile": null, 
            "timeout": null, 
            "transport": "cli", 
            "use_proxy": true, 
            "use_ssl": null, 
            "username": "admin", 
            "validate_certs": null
        }
    }, 
    "msg": "Could not transfer file. There was an error during transfer. Please make sure remote permissions are set.", 
    "temp_size": "534495232"
}
	to retry, use: --limit @/root/agents-ci/ansible/test/integration/nxos.retry

PLAY RECAP ***************************************************************************************************************************************************************************************
n9k.example.com          : ok=41   changed=1    unreachable=0    failed=1 
```

Hints from issue discussion:
**NOTE:** The permissions issue is misleading.  Even if I set the permissions to 777 the file copy will fail.
cc @GGabriele @jedelman8 @privateip @rahushen @rcarrillocruz @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Additional Data Point: This issue appears to be intermittent.  Sometimes the copy succeeds and sometimes it fails.  Here is a case where it succeeds.  I simply re-ran the playbook after it has just failed.

```shell
TASK [nxos_install_os : Copy nxos.7.0.3.I7.2.bin to bootflash] ***********************************************************************************************************************************
task path: /root/agents-ci/ansible/test/integration/targets/nxos_install_os/tasks/upgrade/copy_kick_system_images.yaml:8
<n9k.example.com> connection transport is cli
<n9k.example.com> using connection plugin network_cli (was local)
<n9k.example.com> starting connection from persistent connection plugin
<n9k.example.com> found existing local domain socket, using it!
<n9k.example.com> updating play_context for connection
<n9k.example.com> 
<n9k.example.com> local domain socket path is /root/.ansible/pc/f9061e5170
<n9k.example.com> socket_path: /root/.ansible/pc/f9061e5170
Using module file /root/agents-ci/ansible/lib/ansible/modules/network/nxos/nxos_file_copy.py
<n9k.example.com> ESTABLISH LOCAL CONNECTION FOR USER: root
<n9k.example.com> EXEC /bin/sh -c 'echo ~root && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154 `" && echo ansible-tmp-1526051276.51-251080045226154="` echo /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154 `" ) && sleep 0'
<n9k.example.com> PUT /root/.ansible/tmp/ansible-local-1399OU89VN/tmpnk6VaD TO /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154/nxos_file_copy.py
<n9k.example.com> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154/ /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154/nxos_file_copy.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c '/usr/bin/python /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154/nxos_file_copy.py && sleep 0'
<n9k.example.com> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1526051276.51-251080045226154/ > /dev/null 2>&1 && sleep 0'
changed: [n9k.example.com] => {
    "changed": true, 
    "file_system": "bootflash:", 
    "invocation": {
        "module_args": {
            "auth_pass": null, 
            "authorize": null, 
            "connect_ssh_port": 22, 
            "file_system": "bootflash:", 
            "host": "n9k.example.com", 
            "local_file": "/root/agents_images/nxos.7.0.3.I7.2.bin", 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": null, 
            "provider": {
                "auth_pass": null, 
                "authorize": null, 
                "host": "n9k.example.com", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": null, 
                "ssh_keyfile": null, 
                "timeout": null, 
                "transport": "cli", 
                "use_proxy": true, 
                "use_ssl": null, 
                "username": "admin", 
                "validate_certs": null
            }, 
            "remote_file": null, 
            "ssh_keyfile": null, 
            "timeout": null, 
            "transport": "cli", 
            "use_proxy": true, 
            "use_ssl": null, 
            "username": "admin", 
            "validate_certs": null
        }
    }, 
    "local_file": "/root/agents_images/nxos.7.0.3.I7.2.bin", 
    "remote_file": "nxos.7.0.3.I7.2.bin", 
    "transfer_status": "Sent"
}
```
Few ideas
1) When it fails is it usually the same `temp_size`  (the amount copied)

2) If you modify https://github.com/ansible/ansible/blob/4fd770f79215471a0468e21a83ded63056dacc64/lib/ansible/modules/network/nxos/nxos_file_copy.py#L175-L185
To get a copy of the exception and add that into `module.fail_json` does that give any extra information

3) Try increasing the timeout
https://github.com/ansible/ansible/blob/4fd770f79215471a0468e21a83ded63056dacc64/lib/ansible/modules/network/nxos/nxos_file_copy.py#L172

`scp = SCPClient(ssh.get_transport(), longtimeout)`

4) Is the task generally failing after the same amount of time?
@mikewiebe Can you please try `net_put` module? https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/files/net_put.py
The playbook should look like the following:
```
- name: "Copy {{ si }} to bootflash"
  net_put:
    src: "{{image_dir}}{{ si }}"
    dest: "bootflash:{{ si }}"
```
Requirements: connection should be `network_cli` and `ansible_network_os: nxos` needs to be in vars.
make sure `command_timeout` is set to higher value. 

If this works, I assume that we will need to pass `socket_timeout` when creating the scpclient object `scp = SCPClient(ssh.get_transport())`
else I believe this is paramiko issue https://github.com/paramiko/paramiko/issues/822
@gundalow I modified the Exception code to dump the exception but we were already getting it with the verbose mode.  No real additional info here:

```python
    except Exception, err:
        time.sleep(10)
        temp_size = verify_remote_file_exists(
            module, dest, file_system=module.params['file_system'])
        if int(temp_size) == int(file_size):
            pass
        else:
            msg = 'Could not transfer file. There was an error '
            msg += '\n %s' % err
            module.fail_json(msg=msg, temp_size=temp_size,
                             file_size=file_size)
```

```shell
FAILED! => {"changed": false, "file_size": 759941120, "msg": "Could not transfer file. There was an error \n Key-exchange timed out waiting for key negotiation", "temp_size": "489848832"}
```

I also increased the timeout but it had no impact.

```python
    scp = SCPClient(ssh.get_transport(), 500)
```
@mikewiebe hmm I can reproduce this also very easily. The issue is because `provider` is ignored when using `network_cli` and `httpapi` as connection plugin. 
We will need to leverage action plugin to pass username, host and password in the module.
@mikewiebe can please you try the module with the patch https://github.com/ansible/ansible/pull/40592?
Thanks

Commit message: blacklist nxapi

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

Patch to review:
diff --git a/lib/ansible/modules/network/nxos/nxos_file_copy.py b/lib/ansible/modules/network/nxos/nxos_file_copy.py
index 435968e46f28b0..dd55757a379cd5 100644
--- a/lib/ansible/modules/network/nxos/nxos_file_copy.py
+++ b/lib/ansible/modules/network/nxos/nxos_file_copy.py
@@ -28,7 +28,8 @@
 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.
+    on NXOS devices. This module only supports the use of connection
+    C(network_cli) or C(Cli) transport with connection C(local).
 author:
   - Jason Edelman (@jedelman8)
   - Gabriele Gerbino (@GGabriele)
@@ -65,8 +66,6 @@
 - nxos_file_copy:
     local_file: "./test_file.txt"
     remote_file: "test_file.txt"
-    provider: "{{ cli }}"
-    connect_ssh_port: "{{ ansible_ssh_port }}"
 '''
 
 RETURN = '''
@@ -153,12 +152,10 @@ def transfer_file(module, dest):
     if not enough_space(module):
         module.fail_json(msg='Could not transfer file. Not enough space on device.')
 
-    provider = module.params['provider']
-
-    hostname = module.params.get('host') or provider.get('host')
-    username = module.params.get('username') or provider.get('username')
-    password = module.params.get('password') or provider.get('password')
-    port = module.params.get('connect_ssh_port')
+    hostname = module.params['host']
+    username = module.params['username']
+    password = module.params['password']
+    port = module.params['connect_ssh_port']
 
     ssh = paramiko.SSHClient()
     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
diff --git a/lib/ansible/plugins/action/nxos.py b/lib/ansible/plugins/action/nxos.py
index d8c40638b68b95..f57dbe71644ea1 100644
--- a/lib/ansible/plugins/action/nxos.py
+++ b/lib/ansible/plugins/action/nxos.py
@@ -43,8 +43,18 @@ def run(self, tmp=None, task_vars=None):
 
         socket_path = None
 
+        if self._task.action == 'nxos_file_copy':
+            if self._play_context.connection == 'network_cli':
+                self._task.args['host'] = self._play_context.remote_addr
+                self._task.args['username'] = self._play_context.remote_user
+                self._task.args['password'] = self._play_context.password
+            elif self._play_context.connection == 'local':
+                self._task.args['host'] = self._play_context.remote_addr
+                self._task.args['username'] = self._play_context.connection_user
+                self._task.args['password'] = self._play_context.password
+
         if (self._play_context.connection == 'httpapi' or self._task.args.get('provider', {}).get('transport') == 'nxapi') \
-                and self._task.action == 'nxos_nxapi':
+                and self._task.action in ('nxos_file_copy', 'nxos_nxapi'):
             return {'failed': True, 'msg': "Transport type 'nxapi' is not valid for '%s' module." % (self._task.action)}
 
         if self._play_context.connection in ('network_cli', 'httpapi'):
@@ -55,6 +65,7 @@ def run(self, tmp=None, task_vars=None):
             if self._task.args.get('transport'):
                 display.warning('transport is unnecessary when using %s and will be ignored' % self._play_context.connection)
                 del self._task.args['transport']
+
         elif self._play_context.connection == 'local':
             provider = load_provider(nxos_provider_spec, self._task.args)
             transport = provider['transport'] or 'cli'
diff --git a/test/integration/targets/nxos_file_copy/tasks/cli.yaml b/test/integration/targets/nxos_file_copy/tasks/cli.yaml
index edbff7dfafbb0c..ac06cd7ea72e36 100644
--- a/test/integration/targets/nxos_file_copy/tasks/cli.yaml
+++ b/test/integration/targets/nxos_file_copy/tasks/cli.yaml
@@ -1,5 +1,5 @@
 ---
-- name: collect common cli test cases
+- name: collect common test cases
   find:
     paths: "{{ role_path }}/tests/common"
     patterns: "{{ testcase }}.yaml"
@@ -21,13 +21,13 @@
   set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
 
 - name: run test cases (connection=network_cli)
-  include: "{{ test_case_to_run }} ansible_connection=network_cli connection={}"
+  include: "{{ test_case_to_run }} ansible_connection=network_cli"
   with_items: "{{ test_items }}"
   loop_control:
     loop_var: test_case_to_run
 
-- name: run test case (connection=local)
-  include: "{{ test_case_to_run }} ansible_connection=local connection={{ cli }}"
-  with_first_found: "{{ test_items }}"
+- name: run test cases (connection=local)
+  include: "{{ test_case_to_run }} ansible_connection=local"
+  with_items: "{{ test_items }}"
   loop_control:
     loop_var: test_case_to_run
diff --git a/test/integration/targets/nxos_file_copy/tasks/nxapi.yaml b/test/integration/targets/nxos_file_copy/tasks/nxapi.yaml
index 653bcfefe96028..c85edec402affc 100644
--- a/test/integration/targets/nxos_file_copy/tasks/nxapi.yaml
+++ b/test/integration/targets/nxos_file_copy/tasks/nxapi.yaml
@@ -25,3 +25,9 @@
   with_items: "{{ test_items }}"
   loop_control:
     loop_var: test_case_to_run
+
+- name: run test cases (connection=local)
+  include: "{{ test_case_to_run }} ansible_connection=local"
+  with_items: "{{ test_items }}"
+  loop_control:
+    loop_var: test_case_to_run
diff --git a/test/integration/targets/nxos_file_copy/tests/cli/sanity.yaml b/test/integration/targets/nxos_file_copy/tests/cli/sanity.yaml
index c6ea9bd64d060c..806128d0c555dd 100644
--- a/test/integration/targets/nxos_file_copy/tests/cli/sanity.yaml
+++ b/test/integration/targets/nxos_file_copy/tests/cli/sanity.yaml
@@ -1,7 +1,5 @@
 ---
-- debug: msg="START TRANSPORT:CLI nxos_file_copy sanity test"
-- debug: msg="Using provider={{ connection.transport }}"
-  when: ansible_connection == "local"
+- debug: msg="START connection={{ ansible_connection }} nxos_file_copy sanity test"
 
 - name: "Setup - Remove existing file"
   nxos_command: &remove_file
@@ -20,9 +18,6 @@
     nxos_file_copy: &copy_file_same_name
       local_file: "./network-integration.cfg"
       file_system: "bootflash:"
-      username: "{{ ansible_ssh_user }}"
-      password: "{{ ansible_ssh_pass }}"
-      host: "{{ ansible_host }}"
       connect_ssh_port: "{{ ansible_ssh_port }}"
     register: result
 
@@ -47,9 +42,6 @@
       local_file: "./inventory.networking.template"
       remote_file: "network-integration.cfg"
       file_system: "bootflash:"
-      username: "{{ ansible_ssh_user }}"
-      password: "{{ ansible_ssh_pass }}"
-      host: "{{ ansible_host }}"
       connect_ssh_port: "{{ ansible_ssh_port }}"
     register: result
 
@@ -80,4 +72,4 @@
       feature: scp-server
       state: disabled
 
-  - debug: msg="END TRANSPORT:CLI nxos_file_copy sanity test"  
+  - debug: msg="END connection={{ ansible_connection }} nxos_file_copy sanity test"
diff --git a/test/integration/targets/nxos_file_copy/tests/nxapi/badtransport.yaml b/test/integration/targets/nxos_file_copy/tests/nxapi/badtransport.yaml
new file mode 100644
index 00000000000000..de0693d51aa9de
--- /dev/null
+++ b/test/integration/targets/nxos_file_copy/tests/nxapi/badtransport.yaml
@@ -0,0 +1,17 @@
+---
+- debug: msg="START nxapi/badtransport.yaml"
+
+- name: Sending transport other than cli should fail
+  nxos_file_copy:
+    local_file: "./network-integration.cfg"
+    file_system: "bootflash:"
+    connect_ssh_port: "{{ ansible_ssh_port }}"
+    provider: "{{ nxapi }}"
+  register: result
+  ignore_errors: yes
+
+- assert:
+     that:
+        - result.failed and result.msg is search('Transport')
+
+- debug: msg="END nxapi/badtransport.yaml"
diff --git a/test/integration/targets/nxos_file_copy/tests/nxapi/sanity.yaml b/test/integration/targets/nxos_file_copy/tests/nxapi/sanity.yaml
deleted file mode 100644
index 91ea22cb6a8a27..00000000000000
--- a/test/integration/targets/nxos_file_copy/tests/nxapi/sanity.yaml
+++ /dev/null
@@ -1,87 +0,0 @@
----
-- debug: msg="START TRANSPORT:NXAPI nxos_file_copy sanity test"
-- debug: msg="Using provider={{ connection.transport }}"
-  when: ansible_connection == "local"
-
-- name: "Setup - Remove existing file"
-  nxos_command: &remove_file
-    commands:
-      - command: terminal dont-ask
-        output: text
-      - command: delete network-integration.cfg
-        output: text
-  ignore_errors: yes
-
-- name: "Setup - Turn on feature scp-server"
-  nxos_feature: 
-    feature: scp-server
-    state: enabled
-
-- block:
-  - name: "Copy network-integration.cfg to bootflash"
-    nxos_file_copy: &copy_file_same_name
-      local_file: "./network-integration.cfg"
-      file_system: "bootflash:"
-      username: "{{ ansible_ssh_user }}"
-      password: "{{ ansible_ssh_pass }}"
-      host: "{{ ansible_host }}"
-      connect_ssh_port: "{{ ansible_ssh_port }}"
-    register: result
-
-  - assert: &true
-      that:
-        - "result.changed == true"
-
-  - name: "Check Idempotence - Copy network-integration.cfg to bootflash"
-    nxos_file_copy: *copy_file_same_name
-    register: result
-
-  - assert: &false
-      that:
-        - "result.changed == false"
-
-  - name: "Setup - Remove existing file"
-    nxos_command: *remove_file
-    register: result
-    ignore_errors: yes
-
-  - name: "Copy inventory.networking.template to bootflash as another name"
-    nxos_file_copy: &copy_file_different_name
-      local_file: "./inventory.networking.template"
-      remote_file: "network-integration.cfg"
-      file_system: "bootflash:"
-      username: "{{ ansible_ssh_user }}"
-      password: "{{ ansible_ssh_pass }}"
-      host: "{{ ansible_host }}"
-      connect_ssh_port: "{{ ansible_ssh_port }}"
-    register: result
-
-  - assert: *true
-
-  - name: "Check Idempotence - Copy inventory.networking.template to bootflash as another name"
-    nxos_file_copy: *copy_file_different_name
-    register: result
-
-  - assert: *false
-
-  - name: "Setup - Remove existing file"
-    nxos_command: *remove_file
-    register: result
-    ignore_errors: yes
-
-  rescue:
-
-  - debug: msg="TRANSPORT:NXAPI nxos_file_copy failure detected"
-
-  always:
-
-  - name: "Remove file"
-    nxos_command: *remove_file
-    ignore_errors: yes
-
-  - name: "Turn off feature scp-server"
-    nxos_feature:
-      feature: scp-server
-      state: disabled
-
-  - debug: msg="END TRANSPORT:NXAPI nxos_file_copy sanity test"  


Code review comments:
diff_hunk: @@ -43,8 +43,18 @@ def run(self, tmp=None, task_vars=None):
 
         socket_path = None
 
+        if self._task.action == 'nxos_file_copy':
+            if self._play_context.connection == 'network_cli':
path: lib/ansible/plugins/action/nxos.py
text: @user1:
`host` and `password` don't change, I would just set them all as if using network_cli, then `if connection == local: username = connection_user`. This has the side benefit of being really easy to clean up when local goes away

@author:
@user1 Of course. I just did that to keep the blocks distinct. But I don't have strong opinion on this. Makes sense and pushed.