Repository: ansible/ansible

Issue:
eos_banner eapi: "CLI command 4 of 5 'Junk login banner single line' failed: invalid command"
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
eos_banner

##### ANSIBLE VERSION
```
fatal: [veos01]: FAILED! => {
    "changed": false, 
    "code": 1002, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "auth_pass": null, 
            "authorize": true, 
            "banner": "login", 
            "host": "veos01", 
            "password": "admin", 
            "port": null, 
            "provider": {
                "auth_pass": null, 
                "authorize": true, 
                "host": "veos01", 
                "password": "admin", 
                "port": null, 
                "timeout": 10, 
                "transport": "eapi", 
                "use_ssl": false, 
                "username": "admin", 
                "validate_certs": true
            }, 
            "ssh_keyfile": null, 
            "state": "present", 
            "text": "Junk login banner single line", 
            "timeout": 10, 
            "transport": "eapi", 
            "url_password": "admin", 
            "url_username": "admin", 
            "use_ssl": false, 
            "username": "admin", 
            "validate_certs": true
        }
    }, 
    "msg": "CLI command 4 of 5 'Junk login banner single line' failed: invalid command"
}
```

##### SUMMARY
`eos_banner` fails when used with `eapi`

##### STEPS TO REPRODUCE
```yaml

```


##### EXPECTED RESULTS

##### ACTUAL RESULTS
```
```



Discussion:
cc @privateip
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
eos_banner tests have been pushed into devel, so if you update your branch you can do

```
cd test/integration
time  ANSIBLE_ROLES_PATH=targets  ansible-playbook -vvvvv  -i  inventory.network-jobarker eos.yaml  -e "limit_to=eos_banner" -u admin -k
```

Resolving patch:
diff --git a/lib/ansible/modules/network/eos/eos_banner.py b/lib/ansible/modules/network/eos/eos_banner.py
--- a/lib/ansible/modules/network/eos/eos_banner.py
+++ b/lib/ansible/modules/network/eos/eos_banner.py
@@ -107,9 +107,15 @@ def map_obj_to_commands(updates, module):
 
     elif state == 'present':
         if want['text'] and (want['text'] != have.get('text')):
-            commands.append('banner %s' % module.params['banner'])
-            commands.extend(want['text'].strip().split('\n'))
-            commands.append('EOF')
+            if module.params['transport'] == 'cli':
+                commands.append('banner %s' % module.params['banner'])
+                commands.extend(want['text'].strip().split('\n'))
+                commands.append('EOF')
+            else:
+                # For EAPI we need to construct a dict with cmd/input
+                # key/values for the banner
+                commands.append({'cmd': 'banner %s' % module.params['banner'],
+                                 'input': want['text'].strip('\n')})
 
     return commands
 
@@ -117,7 +123,12 @@ def map_config_to_obj(module):
     output = run_commands(module, ['show banner %s' % module.params['banner']])
     obj = {'banner': module.params['banner'], 'state': 'absent'}
     if output:
-        obj['text'] = output[0]
+        if module.params['transport'] == 'cli':
+            obj['text'] = output[0]
+        else:
+            # On EAPI we need to extract the banner text from dict key
+            # 'loginBanner'
+            obj['text'] = output[0]['loginBanner'].strip('\n')
         obj['state'] = 'present'
     return obj
 


Test patch:
diff --git a/test/units/modules/network/eos/eos_module.py b/test/units/modules/network/eos/eos_module.py
--- a/test/units/modules/network/eos/eos_module.py
+++ b/test/units/modules/network/eos/eos_module.py
@@ -62,9 +62,9 @@ class AnsibleFailJson(Exception):
 class TestEosModule(unittest.TestCase):
 
     def execute_module(self, failed=False, changed=False, commands=None,
-            sort=True, defaults=False):
+            sort=True, defaults=False, transport='cli'):
 
-        self.load_fixtures(commands)
+        self.load_fixtures(commands, transport=transport)
 
         if failed:
             result = self.failed()
@@ -108,6 +108,6 @@ def exit_json(*args, **kwargs):
         self.assertEqual(result['changed'], changed, result)
         return result
 
-    def load_fixtures(self, commands=None):
+    def load_fixtures(self, commands=None, transport='cli'):
         pass
 
diff --git a/test/units/modules/network/eos/test_eos_banner.py b/test/units/modules/network/eos/test_eos_banner.py
--- a/test/units/modules/network/eos/test_eos_banner.py
+++ b/test/units/modules/network/eos/test_eos_banner.py
@@ -39,22 +39,42 @@ def tearDown(self):
         self.mock_run_commands.stop()
         self.mock_load_config.stop()
 
-    def load_fixtures(self, commands=None):
-        self.run_commands.return_value = [load_fixture('eos_banner_show_banner.txt').strip()]
+    def load_fixtures(self, commands=None, transport='cli'):
+        if transport == 'cli':
+            self.run_commands.return_value = [load_fixture('eos_banner_show_banner.txt').strip()]
+        else:
+            self.run_commands.return_value = [{'loginBanner': load_fixture('eos_banner_show_banner.txt').strip()}]
+
         self.load_config.return_value = dict(diff=None, session='session')
 
-    def test_eos_banner_create(self):
-        set_module_args(dict(banner='login', text='test\nbanner\nstring'))
+    def test_eos_banner_create_with_cli_transport(self):
+        set_module_args(dict(banner='login', text='test\nbanner\nstring',
+                             transport='cli'))
         commands = ['banner login', 'test', 'banner', 'string', 'EOF']
         self.execute_module(changed=True, commands=commands)
 
-    def test_eos_banner_remove(self):
-        set_module_args(dict(banner='login', state='absent'))
+    def test_eos_banner_create_with_eapi_transport(self):
+        set_module_args(dict(banner='login', text='test\nbanner\nstring',
+                             transport='eapi'))
+        commands = [{'cmd': 'banner login', 'input': 'test\nbanner\nstring'}]
+        self.execute_module(changed=True, commands=commands, transport='eapi')
+
+    def test_eos_banner_remove_with_cli_transport(self):
+        set_module_args(dict(banner='login', state='absent', transport='cli'))
         commands = ['no banner login']
         self.execute_module(changed=True, commands=commands)
 
-    def test_eos_banner_nochange(self):
+    def test_eos_banner_remove_with_eapi_transport(self):
+        set_module_args(dict(banner='login', state='absent', transport='eapi'))
+        commands = ['no banner login']
+        self.execute_module(changed=True, commands=commands, transport='eapi')
+
+    def test_eos_banner_nochange_with_cli_transport(self):
         banner_text = load_fixture('eos_banner_show_banner.txt').strip()
-        set_module_args(dict(banner='login', text=banner_text))
+        set_module_args(dict(banner='login', text=banner_text, transport='cli'))
         self.execute_module()
 
+    def test_eos_banner_nochange_with_eapi_transport(self):
+        banner_text = load_fixture('eos_banner_show_banner.txt').strip()
+        set_module_args(dict(banner='login', text=banner_text, transport='eapi'))
+        self.execute_module(transport='eapi')
diff --git a/test/units/modules/network/eos/test_eos_command.py b/test/units/modules/network/eos/test_eos_command.py
--- a/test/units/modules/network/eos/test_eos_command.py
+++ b/test/units/modules/network/eos/test_eos_command.py
@@ -36,7 +36,7 @@ def setUp(self):
     def tearDown(self):
         self.mock_run_commands.stop()
 
-    def load_fixtures(self, commands=None):
+    def load_fixtures(self, commands=None, transport='cli'):
         def load_from_file(*args, **kwargs):
             module, commands = args
             output = list()
diff --git a/test/units/modules/network/eos/test_eos_config.py b/test/units/modules/network/eos/test_eos_config.py
--- a/test/units/modules/network/eos/test_eos_config.py
+++ b/test/units/modules/network/eos/test_eos_config.py
@@ -41,7 +41,7 @@ def tearDown(self):
         self.mock_get_config.stop()
         self.mock_load_config.stop()
 
-    def load_fixtures(self, commands=None):
+    def load_fixtures(self, commands=None, transport='cli'):
         self.get_config.return_value = load_fixture('eos_config_config.cfg')
         self.load_config.return_value = dict(diff=None, session='session')
 
diff --git a/test/units/modules/network/eos/test_eos_eapi.py b/test/units/modules/network/eos/test_eos_eapi.py
--- a/test/units/modules/network/eos/test_eos_eapi.py
+++ b/test/units/modules/network/eos/test_eos_eapi.py
@@ -53,7 +53,7 @@ def tearDown(self):
         except RuntimeError:
             pass
 
-    def load_fixtures(self, commands=None):
+    def load_fixtures(self, commands=None, transport='eapi'):
         def run_commands(module, commands, **kwargs):
             output = list()
             for cmd in commands:
diff --git a/test/units/modules/network/eos/test_eos_system.py b/test/units/modules/network/eos/test_eos_system.py
--- a/test/units/modules/network/eos/test_eos_system.py
+++ b/test/units/modules/network/eos/test_eos_system.py
@@ -40,7 +40,7 @@ def tearDown(self):
         self.mock_get_config.stop()
         self.mock_load_config.stop()
 
-    def load_fixtures(self, commands=None):
+    def load_fixtures(self, commands=None, transport='cli'):
         self.get_config.return_value = load_fixture('eos_system_config.cfg')
         self.load_config.return_value = dict(diff=None, session='session')
 
diff --git a/test/units/modules/network/eos/test_eos_user.py b/test/units/modules/network/eos/test_eos_user.py
--- a/test/units/modules/network/eos/test_eos_user.py
+++ b/test/units/modules/network/eos/test_eos_user.py
@@ -39,7 +39,7 @@ def tearDown(self):
         self.mock_get_config.stop()
         self.mock_load_config.stop()
 
-    def load_fixtures(self, commands=None):
+    def load_fixtures(self, commands=None, transport='cli'):
         self.get_config.return_value = load_fixture('eos_user_config.cfg')
         self.load_config.return_value = dict(diff=None, session='session')