Repository: ansible/ansible

Issue:
eos_config using eAPI on Ansible 2.3.1.0 fails with KeyError


##### ISSUE TYPE
 - Bug Report


##### COMPONENT NAME
eos_config using eAPI

##### ANSIBLE VERSION
```
ansible-playbook --version
ansible-playbook 2.3.1.0
  config file = /home/kbyers/.ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Sep  1 2016, 22:14:00) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]
```

##### CONFIGURATION


##### SUMMARY
Simple playbook for eos_config using eAPI works fine on Ansible 2.3.0.0 fails on Ansible 2.3.1.0

Playbook
```
---
- name: IOS
  hosts: cisco
  tasks:
    - name: Global Items (IOS)
      ios_config:
        provider: "{{ std_ssh }}"
        lines:
          - "ip domain-name {{ default_domain }}"
          - "ip name-server {{ name_server1 }}"
          - "ip name-server {{ name_server2 }}"
          - "ntp server {{ ntp1 }}"
          - "ntp server {{ ntp2 }}"

- name: EOS
  hosts: pynet-sw1
  tasks:
    - name: Global Items (EOS)
      eos_config:
        provider: "{{ eapi }}"
        lines:
          - "ip domain-name {{ default_domain }}"
          - "ip name-server {{ name_server1 }}"
          - "ip name-server {{ name_server2 }}"
          - "ntp server {{ ntp1 }}"
          - "ntp server {{ ntp2 }}"
```

Provider is in group_vars:
```
$ cat group_vars/arista.yml 
---
eapi:
  host: "{{ ansible_host }}"
  username: "{{ username }}"
  password: "{{ password }}"
  transport: eapi
  validate_certs: no
```


##### ACTUAL RESULTS
```
$ ansible-playbook ios_config4a.yml -i ./ansible-hosts 

PLAY [IOS] *********************************************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************************************
ok: [pynet-rtr2]
ok: [pynet-rtr1]

TASK [Global Items (IOS)] ******************************************************************************************************************************************************************************
changed: [pynet-rtr2]
changed: [pynet-rtr1]

PLAY [EOS] *********************************************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************************************
ok: [pynet-sw1]

TASK [Global Items (EOS)] ******************************************************************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: KeyError: 'default'
fatal: [pynet-sw1]: FAILED! => {"failed": true, "msg": "Unexpected failure during module execution.", "stdout": ""}
	to retry, use: --limit @/home/kbyers/ansible_course/sandbox_week4/REAL/ios_config4a.retry

PLAY RECAP *********************************************************************************************************************************************************************************************
pynet-rtr1                 : ok=2    changed=1    unreachable=0    failed=0   
pynet-rtr2                 : ok=2    changed=1    unreachable=0    failed=0   
pynet-sw1                  : ok=1    changed=0    unreachable=0    failed=1   

```





Discussion:
Adding: `use_ssl: True` fixed the issue:

```
$ cat group_vars/arista.yml 
---
eapi:
  host: "{{ ansible_host }}"
  username: "{{ username }}"
  password: "{{ password }}"
  transport: eapi
  validate_certs: no
  use_ssl: True
```

I guess it is either a documentation issue or a bug. Documentation states use_ssl defaults to True in the provider.

https://docs.ansible.com/ansible/eos_config_module.html
More detailed stack trace on failure (when the provider has no setting specified for `use_ssl`)

```
The full traceback is:
Traceback (most recent call last):
  File "/home/kbyers/VENV/ansible/local/lib/python2.7/site-packages/ansible/executor/task_executor.py", line 125, in run
    res = self._execute()
  File "/home/kbyers/VENV/ansible/local/lib/python2.7/site-packages/ansible/executor/task_executor.py", line 522, in _execute
    result = self._handler.run(task_vars=variables)
  File "/home/kbyers/VENV/ansible/local/lib/python2.7/site-packages/ansible/plugins/action/eos_config.py", line 46, in run
    result = super(ActionModule, self).run(tmp, task_vars)
  File "/home/kbyers/VENV/ansible/local/lib/python2.7/site-packages/ansible/plugins/action/eos.py", line 100, in run
    provider['use_ssl'] = eos_argument_spec['use_ssl']['default']
KeyError: 'default'

fatal: [pynet-sw1]: FAILED! => {
    "failed": true, 
    "msg": "Unexpected failure during module execution.", 
    "stdout": ""
}
```
Playbook works without an issue on Ansible 2.3.0.0 with use_ssl not specified in the provider (i.e. using the default of use_ssl = True).
Also, I am using a `transport` in the provider for eos_config using eAPI and am receiving this warning:

```
[WARNING]: argument transport has been deprecated and will be removed in a future version
```

Is that a separate bug? In other words, I thought that `transport` was going to be removed from directly specifying it as a module argument, but not from using it in a provider?

Resolving patch:
diff --git a/lib/ansible/plugins/action/eos.py b/lib/ansible/plugins/action/eos.py
--- a/lib/ansible/plugins/action/eos.py
+++ b/lib/ansible/plugins/action/eos.py
@@ -27,7 +27,7 @@
 from ansible.utils.path import unfrackpath
 from ansible.plugins import connection_loader
 from ansible.compat.six import iteritems
-from ansible.module_utils.eos import eos_argument_spec
+from ansible.module_utils.eos import ARGS_DEFAULT_VALUE, eos_argument_spec
 from ansible.module_utils.basic import AnsibleFallbackNotFound
 from ansible.module_utils._text import to_bytes
 
@@ -97,7 +97,7 @@ def run(self, tmp=None, task_vars=None):
                 provider['host'] = self._play_context.remote_addr
 
             if provider.get('use_ssl') is None:
-                provider['use_ssl'] = eos_argument_spec['use_ssl']['default']
+                provider['use_ssl'] = ARGS_DEFAULT_VALUE['use_ssl']
 
             if provider.get('port') is None:
                 default_port = 443 if provider['use_ssl'] else 80
@@ -116,7 +116,7 @@ def run(self, tmp=None, task_vars=None):
                 provider['authorize'] = False
 
             if provider.get('validate_certs') is None:
-                provider['validate_certs'] = eos_argument_spec['validate_certs']['default']
+                provider['validate_certs'] = ARGS_DEFAULT_VALUE['validate_certs']
 
             self._task.args['provider'] = provider
 


Test patch: