Repository: ansible/ansible

Issue:
Cannot enter into privileged mode using Eos Network modules in 'eapi' transport mode if 'enable secret' is set on remote Arista switches
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
- eos_config
- eos_command

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/krzysiek/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.13 (default, Jan 19 2017, 14:48:08) [GCC 6.3.0 20170118]
```
Tested also on ansible 2.5.0 and 2.3.2

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).

-->
VOID (didn't change anything)

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Ansible host:
Ubuntu 17.04

Controlled Arista Switches:
Arista vEOS 4.18.1F (Aboot-veos-8.0.0-3255441)
Arista vEOS 4.15.9M (Aboot-veos-8.0.0-3255441)

Switches are virtualized in GNS3 (qemu) on the same Ubuntu machine - I'm connecting to this switches either from the same OS through tap interface or from VM running on the same machine (vmnet interface).

##### SUMMARY
<!--- Explain the problem briefly -->
I'm unable to run my Ansible roles which are using 'eos_config' module in 'eapi' transport mode if 'enable secret' is set on remote Arista switches I want to control. It seems like a bug related to 'authorize' and 'auth_pass' parameters.

My tests indicate that if 'enable secret' is not set on the Arista switch, the "authorize: yes" parameter is not required to get and change configuration (in eapi transport mode - in "cli" mode it is required), however, generally privileged mode is required to perform such operations, also from the Command API Explorer (web tool).

## 1. I want to copy the 'running-config' from remote switch to the "backup" folder in the get_run role root directory

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- name: Copy running-config to the "backup" folder in the get_run role root directory
  eos_config:
    backup: yes
    provider:
      transport: eapi
      authorize: yes
      auth_pass: "{{ enable_secret | default('') }}"
      username: "{{ eapi_username }}"
      password: "{{ eapi_secret }}"
      validate_certs: "{{ validate_SSL_certs | default('no') }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
The 'running-config' is being copied into the "backup" folder in the get_run role root directory.
It works fine if 'enable secret' is not set on the Arista switch - the output is then like this:
```
<192.168.80.101> connection transport is eapi
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/basic.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/eos.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/netcfg.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/_text.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/parsing/convert_bool.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/parsing/__init__.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/pycompat24.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/six/__init__.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/connection.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/network_common.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/urls.py
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/network/eos/eos_config.py
<192.168.80.101> ESTABLISH LOCAL CONNECTION FOR USER: krzysiek
<192.168.80.101> EXEC /bin/sh -c 'echo ~ && sleep 0'
<192.168.80.101> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116 `" && echo ansible-tmp-1506192047.28-278987499054116="` echo /home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116 `" ) && sleep 0'
<192.168.80.101> PUT /tmp/tmp7sSnCD TO /home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116/eos_config.py
<192.168.80.101> EXEC /bin/sh -c 'chmod u+x /home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116/ /home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116/eos_config.py && sleep 0'
<192.168.80.101> EXEC /bin/sh -c '/usr/bin/python /home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116/eos_config.py; rm -rf "/home/krzysiek/.ansible/tmp/ansible-tmp-1506192047.28-278987499054116/" > /dev/null 2>&1 && sleep 0'
ok: [192.168.80.101] => {
    "backup_path": "/home/krzysiek/Desktop/eapi-Azure-EOS-automation-copy-repo/Azure-EOS-automation/eapi/roles/get_run/backup/192.168.80.101_config.2017-09-23@20:40:47", 
    "changed": false, 
    "failed": false, 
    "invocation": {
        "module_args": {
            "after": null, 
            "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "authorize": true, 
            "backup": true, 
            "before": null, 
            "defaults": false, 
            "diff_against": "session", 
            "diff_ignore_lines": null, 
            "force": false, 
            "host": "192.168.80.101", 
            "intended_config": null, 
            "lines": null, 
            "match": "line", 
            "parents": null, 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": 443, 
            "provider": {
                "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "authorize": true, 
                "host": "192.168.80.101", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": 443, 
                "ssh_keyfile": null, 
                "timeout": 10, 
                "transport": "eapi", 
                "use_ssl": true, 
                "username": "eapi", 
                "validate_certs": false
            }, 
            "replace": "line", 
            "running_config": null, 
            "save": false, 
            "save_when": "never", 
            "src": null, 
            "ssh_keyfile": null, 
            "timeout": 10, 
            "transport": "cli", 
            "url_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "url_username": "eapi", 
            "use_ssl": true, 
            "username": "eapi", 
            "validate_certs": false
        }
    }
}
META: ran handlers
META: ran handlers

```
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
If I set the 'enable secret' on the switch, the result is as follows:
<!--- Paste verbatim command output between quotes below -->
```
<192.168.80.101> connection transport is eapi
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/basic.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/eos.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/netcfg.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/_text.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/parsing/convert_bool.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/parsing/__init__.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/pycompat24.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/six/__init__.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/connection.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/network_common.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/urls.py
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/network/eos/eos_config.py
<192.168.80.101> ESTABLISH LOCAL CONNECTION FOR USER: krzysiek
<192.168.80.101> EXEC /bin/sh -c 'echo ~ && sleep 0'
<192.168.80.101> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109 `" && echo ansible-tmp-1506192409.08-244809892338109="` echo /home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109 `" ) && sleep 0'
<192.168.80.101> PUT /tmp/tmpeT1eTQ TO /home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109/eos_config.py
<192.168.80.101> EXEC /bin/sh -c 'chmod u+x /home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109/ /home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109/eos_config.py && sleep 0'
<192.168.80.101> EXEC /bin/sh -c '/usr/bin/python /home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109/eos_config.py; rm -rf "/home/krzysiek/.ansible/tmp/ansible-tmp-1506192409.08-244809892338109/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_i7Ofp_/ansible_module_eos_config.py", line 469, in <module>
    main()
  File "/tmp/ansible_i7Ofp_/ansible_module_eos_config.py", line 362, in main
    contents = get_config(module)
  File "/tmp/ansible_i7Ofp_/ansible_modlib.zip/ansible/module_utils/eos.py", line 462, in get_config
  File "/tmp/ansible_i7Ofp_/ansible_modlib.zip/ansible/module_utils/eos.py", line 377, in get_config
KeyError: 'result'

fatal: [192.168.80.101]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_i7Ofp_/ansible_module_eos_config.py\", line 469, in <module>\n    main()\n  File \"/tmp/ansible_i7Ofp_/ansible_module_eos_config.py\", line 362, in main\n    contents = get_config(module)\n  File \"/tmp/ansible_i7Ofp_/ansible_modlib.zip/ansible/module_utils/eos.py\", line 462, in get_config\n  File \"/tmp/ansible_i7Ofp_/ansible_modlib.zip/ansible/module_utils/eos.py\", line 377, in get_config\nKeyError: 'result'\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 0
}

```
## 2. I want to execute the 'running-config' command using the 'eos_command' module

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- name: show running-config command execution
  eos_command:
    commands: show running-config
    provider:
      transport: eapi
      authorize: yes
      auth_pass: "{{ enable_secret | default('') }}"
      username: "{{ eapi_username }}"
      password: "{{ eapi_secret }}"
      validate_certs: "{{ validate_SSL_certs | default('no') }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
No error, as it's going, when 'enable secret' is not set on the Arista switch.
```

```
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
If I set the 'enable secret' on the switch, the result is as follows:
<!--- Paste verbatim command output between quotes below -->
```
<192.168.80.101> connection transport is eapi
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/six/__init__.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/basic.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/pycompat24.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/network_common.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/netcli.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/eos.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/_text.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/parsing/convert_bool.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/parsing/__init__.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/connection.py
Using module_utils file /usr/lib/python2.7/dist-packages/ansible/module_utils/urls.py
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/network/eos/eos_command.py
<192.168.80.101> ESTABLISH LOCAL CONNECTION FOR USER: krzysiek
<192.168.80.101> EXEC /bin/sh -c 'echo ~ && sleep 0'
<192.168.80.101> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767 `" && echo ansible-tmp-1506193220.75-100220166971767="` echo /home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767 `" ) && sleep 0'
<192.168.80.101> PUT /tmp/tmpK_5PnR TO /home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767/eos_command.py
<192.168.80.101> EXEC /bin/sh -c 'chmod u+x /home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767/ /home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767/eos_command.py && sleep 0'
<192.168.80.101> EXEC /bin/sh -c '/usr/bin/python /home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767/eos_command.py; rm -rf "/home/krzysiek/.ansible/tmp/ansible-tmp-1506193220.75-100220166971767/" > /dev/null 2>&1 && sleep 0'
fatal: [192.168.80.101]: FAILED! => {
    "changed": false, 
    "code": 1005, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "authorize": true, 
            "commands": [
                "show running-config"
            ], 
            "host": "192.168.80.101", 
            "interval": 1, 
            "match": "all", 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": 443, 
            "provider": {
                "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "authorize": true, 
                "host": "192.168.80.101", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "port": 443, 
                "ssh_keyfile": null, 
                "timeout": 10, 
                "transport": "eapi", 
                "use_ssl": true, 
                "username": "eapi", 
                "validate_certs": false
            }, 
            "retries": 10, 
            "ssh_keyfile": null, 
            "timeout": 10, 
            "transport": "cli", 
            "url_password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "url_username": "eapi", 
            "use_ssl": true, 
            "username": "eapi", 
            "validate_certs": false, 
            "wait_for": null
        }
    }, 
    "msg": "CLI command 1 of 2 'enable' failed: permission to run command denied"
}

```
I've made some research related to this error message:
"CLI command 1 of 2 'enable' failed: permission to run command denied"
and I've found this:
https://github.com/arista-eosplus/pyeapi/issues/88
I think it might be helpful for you in order to resolve this bug.


Discussion:
cc @privateip @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@phil-dileo any ideas on this?
@dathelen - have you seen this?
@phil-dileo A customer actually hit this issue recently.   Does ansible use pyeapi for the eapi connection?
My company recently ran into this problem as well.  Any idea when it might get fixed?

Resolving patch:
diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py
--- a/lib/ansible/module_utils/eos.py
+++ b/lib/ansible/module_utils/eos.py
@@ -296,7 +296,7 @@ def send_request(self, commands, output='text'):
         commands = to_list(commands)
 
         if self._enable:
-            commands.insert(0, 'enable')
+            commands.insert(0, self._enable)
 
         body = self._request_builder(commands, output)
         data = self._module.jsonify(body)


Test patch: