bug fixes and updates for eos connections
diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py
index f283fe4cbbcbe8..622331cb8daa67 100644
--- a/lib/ansible/module_utils/eos.py
+++ b/lib/ansible/module_utils/eos.py
@@ -94,6 +94,15 @@ class Cli:
     def __init__(self, module):
         self._module = module
         self._device_configs = {}
+        self._session_support = None
+
+    @property
+    def supports_sessions(self):
+        if self._session_support is not None:
+            return self._session_support
+        rc, out, err = self.exec_command('show configuration sessions')
+        self._session_support = rc == 0
+        return self._session_support
 
     def exec_command(self, command):
         if isinstance(command, dict):
@@ -195,7 +204,7 @@ def load_config(self, commands, commit=False, replace=False):
         except ValueError:
             pass
 
-        if not all((bool(use_session), self.supports_sessions())):
+        if not all((bool(use_session), self.supports_sessions)):
             return configure(self, commands)
 
         conn = get_connection(self)
@@ -255,6 +264,14 @@ def __init__(self, module):
         else:
             self._enable = 'enable'
 
+    @property
+    def supports_sessions(self):
+        if self._session_support:
+            return self._session_support
+        response = self.send_request(['show configuration sessions'])
+        self._session_support = 'error' not in response
+        return self._session_support
+
     def _request_builder(self, commands, output, reqid=None):
         params = dict(version=1, cmds=commands, format=output)
         return dict(jsonrpc='2.0', id=reqid, method='runCmds', params=params)
@@ -344,13 +361,6 @@ def get_config(self, flags=[]):
             self._device_configs[cmd] = cfg
             return cfg
 
-    def supports_sessions(self):
-        if self._session_support:
-            return self._session_support
-        response = self.send_request(['show configuration sessions'])
-        self._session_support = 'error' not in response
-        return self._session_support
-
     def configure(self, commands):
         """Sends the ordered set of commands to the device
         """
@@ -371,7 +381,7 @@ def load_config(self, config, commit=False, replace=False):
         fallback to using configure() to load the commands.  If that happens,
         there will be no returned diff or session values
         """
-        if not supports_sessions():
+        if not self.supports_sessions:
             return configure(self, commands)
 
         session = 'ansible_%s' % int(time.time())
@@ -406,6 +416,8 @@ def load_config(self, config, commit=False, replace=False):
 is_json = lambda x: str(x).endswith('| json')
 is_text = lambda x: not str(x).endswith('| json')
 
+supports_sessions = lambda x: get_connection(module).supports_sessions
+
 def get_config(module, flags=[]):
     conn = get_connection(module)
     return conn.get_config(flags)
diff --git a/lib/ansible/plugins/action/eos.py b/lib/ansible/plugins/action/eos.py
index 9b6d1484bb7acc..ef067392c57ada 100644
--- a/lib/ansible/plugins/action/eos.py
+++ b/lib/ansible/plugins/action/eos.py
@@ -31,20 +31,28 @@
 from ansible.module_utils.basic import AnsibleFallbackNotFound
 from ansible.module_utils._text import to_bytes
 
+try:
+    from __main__ import display
+except ImportError:
+    from ansible.utils.display import Display
+    display = Display()
+
 class ActionModule(_ActionModule):
 
     def run(self, tmp=None, task_vars=None):
         if self._play_context.connection != 'local':
             return dict(
-                fail=True,
+                failed=True,
                 msg='invalid connection specified, expected connection=local, '
                     'got %s' % self._play_context.connection
             )
 
         provider = self.load_provider()
-        transport = provider['transport']
+        transport = provider['transport'] or 'cli'
+
+        display.vvv('transport is %s' % transport, self._play_context.remote_addr)
 
-        if not transport or 'cli' in transport:
+        if transport == 'cli':
             pc = copy.deepcopy(self._play_context)
             pc.connection = 'network_cli'
             pc.network_os = 'eos'
@@ -53,12 +61,14 @@ def run(self, tmp=None, task_vars=None):
             pc.become = provider['authorize'] or False
             pc.become_pass = provider['auth_pass']
 
+            connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
 
             socket_path = self._get_socket_path(pc)
             if not os.path.exists(socket_path):
                 # start the connection if it isn't started
-                connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
-                connection.exec_command('EXEC: show version')
+                rc, out, err = connection.exec_command('open_shell()')
+                if not rc == 0:
+                    return {'failed': True, 'msg': 'unable to open shell', 'rc': rc}
 
             task_vars['ansible_socket'] = socket_path
 
@@ -80,7 +90,13 @@ def run(self, tmp=None, task_vars=None):
             self._play_context.become = False
             self._play_context.become_method = None
 
-        return super(ActionModule, self).run(tmp, task_vars)
+        result = super(ActionModule, self).run(tmp, task_vars)
+
+        if transport == 'cli':
+            display.vvv('closing cli shell connection', self._play_context.remote_addr)
+            rc, out, err = connection.exec_command('close_shell()')
+
+        return result
 
     def _get_socket_path(self, play_context):
         ssh = connection_loader.get('ssh', class_only=True)
diff --git a/lib/ansible/plugins/connection/network_cli.py b/lib/ansible/plugins/connection/network_cli.py
index 1a283cb87a3305..ef3d2017b91b79 100644
--- a/lib/ansible/plugins/connection/network_cli.py
+++ b/lib/ansible/plugins/connection/network_cli.py
@@ -99,7 +99,6 @@ def _connect(self):
 
     @ensure_connect
     def open_shell(self):
-        """Opens the vty shell on the connection"""
         self._shell = self.ssh.invoke_shell()
         self._shell.settimeout(self._play_context.timeout)
 
@@ -112,6 +111,8 @@ def open_shell(self):
             auth_pass = self._play_context.become_pass
             self._terminal.on_authorize(passwd=auth_pass)
 
+        return (0, 'ok', '')
+
     def close(self):
         display.vvv('closing connection', host=self._play_context.remote_addr)
         self.close_shell()
@@ -127,7 +128,7 @@ def close_shell(self):
             self._shell.close()
             self._shell = None
 
-        return (0, 'shell closed', '')
+        return (0, 'ok', '')
 
     def receive(self, obj=None):
         """Handles receiving of output from command"""
@@ -233,6 +234,8 @@ def exec_command(self, cmd):
 
         if obj['command'] == 'close_shell()':
             return self.close_shell()
+        elif obj['command'] == 'open_shell()':
+            return self.open_shell()
         elif obj['command'] == 'prompt()':
             return (0, self._matched_prompt, '')
         elif obj['command'] == 'history()':

* refactors supports_sessions to a property
* exposes supports_sessions as a toplevel function
* adds open_shell() to network_cli
* implements open_shell() in eos action plugin

