ansible / proposals

Repository for sharing and tracking progress on enhancement proposals for Ansible.
Creative Commons Zero v1.0 Universal
93 stars 19 forks source link

Optional ansible-connection execute_module verb #154

Open dw opened 5 years ago

dw commented 5 years ago

[WIP! But it's fleshed out enough that good feedback may already be possible. This could be further split into multiple proposals? Or just one cohesive proposal split out into multiple PRs? It may also not include every detail from the meeting yet, or include too much]

Proposal: Optional ansible-connection execute_module verb

Author: David Wilson <@dw>

Date: 2019-02-06

Motivation

An extension to Ansible delivered as part of the Mitogen project demonstrated through changes to networking and the process model on targets, it is possible to recover significant speed improvements in a variety of real-world scenarios.

In order to achieve its goals, that project rewrote significant portions of the existing module executor in order to integrate with the newly available networking APIs, and to add the ability cache module source code ephemerally for the duration of a playbook run, yielding a large reduction in bandwidth consumption.

Problems

While the long term goal should probably be to merge away the duplicate module executor, as an initial step towards upstreaming it is desirable to reuse the implementation that has already been designed for this new scenario.

Simultaneous to the needs of the Mitogen project, Ansible already supports diverse target environments, where the existing executor has required heavy special casing for use on Windows, or in the case of networking, only sees vestigial use targeting the local machine, as the majority of networking functionality is implemented in terms of action modules. It is therefore desirable to provide a general mechanism to allow selection of the executor.

Solution proposal

This proposal covers a series of hopefully small pull requests, that pave the way for:

Refactor postprocess_response()

To emulate the existing executor, the Mitogen ActionModule mix-in presently cut-pastes a large chunk of code from the bottom of execute_module(), responsible for normalizing stdout_lines and similar response fields.

This behaviour is an integral part of module response formatting, rather than a feature of the Ansiballz executor, and should therefore be separated from it.

Module invocation type

Since module execution will become a public part of the Connection API and must remain stable over time, it is desirable to introduce a compound type to describe interface parameters, that can be extended without changing the prototype of API methods over time, instead permitting old plug-ins to continue operating even though the compound type has grown new attributes they do not understand.

The alternative of passing parameter lists permits no future backwards-compatible extension of the interface, since it is not possible to know which style of parameter list any particular Connection.execute_module() implementation understands.

The minimum necessary information to execute a module includes:

Additional information, such as the currently active PluginLoader search paths, are available globally and do not need to be passed to the connection plug-in.

The Mitogen extension has a closely related type which includes some attributes that are definitely unneeded for a generic implementation, but exists for the same reason: to delineate an interface boundary.

TBD that type presently includes access to a Templar and task_vars to permit templating of ansible_*_interpreter variables. It is unclear whether a generic interface should include either of these attributes, and if they should not be included, what a replacement should look like.

ActionBase short-circuit

The intention of the ActionBase modification is to have a redirect similar to the following:


    def _execute_module(self, ...):
        # Gather up module invocation context
        invocation = ModuleInvocation(...)

        conn_execute_module = getattr(self._connection, 'execute_module', None)
        try:
            return conn_execute_module(invocation)
        except NotImplementedError:
            pass

        return self._ansiballz_execute_module(invocation)

    def _ansiballz_execute_module(self, invocation):
        # Optional: avoid diff churn by simply passing raw args straight-through.

This avoids assuming that all connection plug-ins would feature an execute_module() method, which may be true for custom plug-ins that may not inherit from ConnectionBase.

Update ConnectionBase

ConnectionBase is updated with a new documentary stub method similar to:

    def execute_module(self, invocation):
        '''
        Arrange for the module described by `invocation` to be invoked on the
        target.
        '''
        raise NotImplementedError()

Update ansible-connection

The ansible-connection program implements a JSON-RPC server over a UNIX socket. Presently it has no support for execute_module() or file transfer.

Update persistent

TBD.

Dependencies (optional)

The changes described may need to occur in set order.

Testing (optional)

Tests to ensure that:

TBD:

Documentation (optional)

Relevant docstrings should suffice.

Anything else?

This proposal is currently missing:

dw commented 5 years ago

I've had doubts overnight whether this should be the first step -- it may make sense to work from the other direction, first landing an SSH-like connection method only, then extending it with the module executor parts, as it (seemingly) requires less structural change and 'forces' the Mitogen-specific pieces out into the open.

Coming from the other direction, we're forced to address the issue of a varying per-task ansible_python_interpreter variable that somehow needs to make it into -connection.

Is it okay to discuss it here, or put an item on the agenda for the next meeting? It is still possible to work from this direction, there is no lost work, but I suspect this per-task variables issue might be trickier business that it would be better to get out of the way upfront.

dagwieers commented 5 years ago

@dw Maybe put it on the agenda so that it can be discussed with core developers? Next meeting is again on Tuesday.

bcoca commented 5 years ago

Some thoughts i had while away:

dw commented 5 years ago

Re: ansible_*_interpreter, that's an incredibly good point. You're right, the Mitogen executor already needs access to those. Actually it is also using mitogen_task_isolation, but ideally that wouldn't need to be user-configurable at all in the long term, so no need to preserve it

dw commented 5 years ago

Just making a note here that ansible-connection lifetime does not match that of existing connection multiplexer -- ansible-connections are torn down in StrategyBase.cleanup() at the end of each play. Mitogen mux exists for the duration of the run