Base connection class for network-style connections (#41839)

* Create base class for network-style connections

* clean up some differences

* Move NetworkConnectionBase

* Tweak netconf for tests

* Tweak when network_os is checked to avoid failing tests

* Pull back exec_command
This commit is contained in:
Nathaniel Case 2018-07-02 08:41:00 -04:00 committed by GitHub
parent 380d08af5d
commit 5850591d14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 198 additions and 253 deletions

View file

@ -140,9 +140,6 @@ options:
- name: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT
"""
import os
from ansible import constants as C
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_bytes
from ansible.module_utils.six import PY3
@ -150,9 +147,8 @@ from ansible.module_utils.six.moves import cPickle
from ansible.module_utils.six.moves.urllib.error import URLError
from ansible.module_utils.urls import open_url
from ansible.playbook.play_context import PlayContext
from ansible.plugins.loader import cliconf_loader, connection_loader, httpapi_loader
from ansible.plugins.connection import ConnectionBase
from ansible.utils.path import unfrackpath
from ansible.plugins.loader import cliconf_loader, httpapi_loader
from ansible.plugins.connection import NetworkConnectionBase
try:
from __main__ import display
@ -161,62 +157,24 @@ except ImportError:
display = Display()
class Connection(ConnectionBase):
class Connection(NetworkConnectionBase):
'''Network API connection'''
transport = 'httpapi'
has_pipelining = True
force_persistence = True
# Do not use _remote_is_local in other connections
_remote_is_local = True
def __init__(self, play_context, new_stdin, *args, **kwargs):
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
self._matched_prompt = None
self._matched_pattern = None
self._last_response = None
self._history = list()
self._url = None
self._auth = None
self._local = connection_loader.get('local', play_context, '/dev/null')
self._local.set_options()
self._implementation_plugins = []
self._ansible_playbook_pid = kwargs.get('ansible_playbook_pid')
self._network_os = self._play_context.network_os
if not self._network_os:
raise AnsibleConnectionFailure(
'Unable to automatically determine host network os. Please '
'manually configure ansible_network_os value for this host'
)
self._url = None
self._auth = None
# reconstruct the socket_path and set instance values accordingly
self._update_connection_state()
def __getattr__(self, name):
try:
return self.__dict__[name]
except KeyError:
if not name.startswith('_'):
for plugin in self._implementation_plugins:
method = getattr(plugin, name, None)
if method:
return method
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))
def exec_command(self, cmd, in_data=None, sudoable=True):
return self._local.exec_command(cmd, in_data, sudoable)
def put_file(self, in_path, out_path):
return self._local.put_file(in_path, out_path)
def fetch_file(self, in_path, out_path):
return self._local.fetch_file(in_path, out_path)
display.display('network_os is set to %s' % self._network_os, log_only=True)
def update_play_context(self, pc_data):
"""Updates the play context information for the connection"""
@ -230,7 +188,11 @@ class Connection(ConnectionBase):
messages = ['updating play_context for connection']
if self._play_context.become ^ play_context.become:
self._httpapi.set_become(play_context)
self.set_become(play_context)
if play_context.become is True:
messages.append('authorizing connection')
else:
messages.append('deauthorizing connection')
self._play_context = play_context
return messages
@ -260,43 +222,6 @@ class Connection(ConnectionBase):
self._connected = True
def _update_connection_state(self):
'''
Reconstruct the connection socket_path and check if it exists
If the socket path exists then the connection is active and set
both the _socket_path value to the path and the _connected value
to True. If the socket path doesn't exist, leave the socket path
value to None and the _connected value to False
'''
ssh = connection_loader.get('ssh', class_only=True)
cp = ssh._create_control_path(
self._play_context.remote_addr, self._play_context.port,
self._play_context.remote_user, self._play_context.connection,
self._ansible_playbook_pid
)
tmp_path = unfrackpath(C.PERSISTENT_CONTROL_PATH_DIR)
socket_path = unfrackpath(cp % dict(directory=tmp_path))
if os.path.exists(socket_path):
self._connected = True
self._socket_path = socket_path
def reset(self):
'''
Reset the connection
'''
if self._socket_path:
display.vvvv('resetting persistent connection for socket_path %s' % self._socket_path, host=self._play_context.remote_addr)
self.close()
display.vvvv('reset call on connection instance', host=self._play_context.remote_addr)
def close(self):
self._implementation_plugins = []
if self._connected:
self._connected = False
def send(self, path, data, **kwargs):
'''
Sends the command to the device over api