From 965350757ff5ad7710a5c767b2244e1512f59af7 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 19 Aug 2016 10:57:46 +0100 Subject: [PATCH] Import working (but useless) connection plugin. --- econtext/ansible/__init__.py | 0 econtext/ansible/connection.py | 62 ++++++++++++++++++++++++++++++++++ econtext/ansible/helpers.py | 28 +++++++++++++++ econtext/core.py | 1 + 4 files changed, 91 insertions(+) create mode 100644 econtext/ansible/__init__.py create mode 100644 econtext/ansible/connection.py create mode 100644 econtext/ansible/helpers.py diff --git a/econtext/ansible/__init__.py b/econtext/ansible/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/econtext/ansible/connection.py b/econtext/ansible/connection.py new file mode 100644 index 00000000..d21f8371 --- /dev/null +++ b/econtext/ansible/connection.py @@ -0,0 +1,62 @@ +""" +Basic Ansible connection plug-in mostly useful for testing functionality, +due to Ansible's use of the multiprocessing package a lot more work is required +to share the econtext SSH connection across tasks. + +Enable it by: + + $ cat ansible.cfg + [defaults] + connection_plugins = plugins/connection + + $ mkdir -p plugins/connection + $ cat > plugins/connection/econtext_conn.py <<-EOF + from econtext.ansible.connection import Connection + EOF +""" + +import econtext.master +import econtext.utils +from econtext.ansible import helpers + +import ansible.plugins.connection + + +class Connection(ansible.plugins.connection.ConnectionBase): + broker = None + context = None + + become_methods = [] + transport = 'econtext' + + @property + def connected(self): + return self.broker is not None + + def _connect(self): + if self.connected: + return + self.broker = econtext.master.Broker() + if self._play_context.remote_addr == 'localhost': + self.context = self.broker.get_local() + else: + self.context = self.broker.get_remote(self._play_context.remote_addr) + + def exec_command(self, cmd, in_data=None, sudoable=True): + super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) + if in_data: + raise AnsibleError("does not support module pipelining") + + return self.context.call(helpers.exec_command, cmd, in_data) + + def fetch_file(self, in_path, out_path): + output = self.context.call(helpers.read_path, in_path) + helpers.write_path(out_path, output) + + def put_file(self, in_path, out_path): + self.context.call(helpers.write_path, out_path, + helpers.read_path(in_path)) + + def close(self): + self.broker.shutdown() + self.broker.join() diff --git a/econtext/ansible/helpers.py b/econtext/ansible/helpers.py new file mode 100644 index 00000000..4b70cde2 --- /dev/null +++ b/econtext/ansible/helpers.py @@ -0,0 +1,28 @@ +""" +Ansible is so poorly layered that attempting to import anything under +ansible.plugins automatically triggers import of __main__, which causes +remote execution of the ansible command-line tool. :( + +So here we define helpers in some sanely layered package where the entirety of +Ansible won't be imported. +""" + +import subprocess + + +def exec_command(cmd, in_data=None): + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + shell=True) + stdout, stderr = proc.communicate(in_data) + return proc.returncode, stdout, stderr + + +def read_path(path): + return file(path, 'rb').read() + + +def write_path(path, s): + open(path, 'wb').write(s) diff --git a/econtext/core.py b/econtext/core.py index cfb3b9b2..0e57f91d 100644 --- a/econtext/core.py +++ b/econtext/core.py @@ -155,6 +155,7 @@ class Importer(object): def __init__(self, context): self._context = context self._present = {'econtext': [ + 'econtext.ansible', 'econtext.compat', 'econtext.compat.pkgutil', 'econtext.master',