From 3ce6b3693236935c09de156f3a9c8bc319c5c5d3 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Fri, 27 Apr 2018 01:32:51 +0100 Subject: [PATCH] docs: major Ansible page update. --- docs/ansible.rst | 666 +++++++++++++++++++++++++++-------------------- 1 file changed, 377 insertions(+), 289 deletions(-) diff --git a/docs/ansible.rst b/docs/ansible.rst index f1b6a6e1..18ee53ea 100644 --- a/docs/ansible.rst +++ b/docs/ansible.rst @@ -1,64 +1,57 @@ -Ansible Extension -================= - .. image:: images/ansible/cell_division.png :align: right -An extension to `Ansible`_ is included that implements host connections over +Mitogen for Ansible +=================== + + +An extension to `Ansible`_ is included that implements connections over Mitogen, replacing embedded shell invocations with pure-Python equivalents -invoked via highly efficient remote procedure calls tunnelled over SSH. No -changes are required to the target hosts. +invoked via highly efficient remote procedure calls to persistent interpreters +tunnelled over SSH. No changes are required to target hosts. -The extension is approaching a generally dependable state, and works well for -many real-world playbooks. `Bug reports`_ in this area are very welcome – -Ansible is a huge beast, and only significant testing will prove the -extension's soundness. - -Divergence from Ansible's normal behaviour is considered a bug, so please -report anything you notice, regardless of how inconsequential it may seem. +The extension is approaching stability and real-world testing is now +encouraged. `Bug reports`_ are welcome: Ansible is huge, and only wide testing +will ensure soundness. .. _Ansible: https://www.ansible.com/ .. _Bug reports: https://goo.gl/yLKZiJ - Overview -------- -You should **expect a 1.25x - 7x speedup** and a **CPU usage reduction of at -least 2x**, depending on network conditions, the specific modules executed, and -time spent by the target host already doing useful work. Mitogen cannot speed -up a module once it is executing, it can only ensure the module executes as -quickly as possible. +**Expect a 1.25x - 7x speedup** and a **CPU usage reduction of at least 2x**, +depending on network conditions, modules executed, and time already spent by +targets on useful work. Mitogen cannot improve a module once it is executing, +it can only ensure the module executes as quickly as possible. -* **A single SSH connection is used for each target host**, in addition to one - sudo invocation per distinct user account. Subsequent playbook steps always - reuse the same connection. This is much better than SSH multiplexing combined - with pipelining, as significant state can be maintained in RAM between steps, - and the system logs aren't filled with spam from repeat SSH and sudo - invocations. +* **One connection is used per target**, in addition to one sudo invocation per + user account. This is much better than SSH multiplexing combined with + pipelining, as significant state can be maintained in RAM between steps, and + system logs aren't spammed with repeat authentication events. -* **A single Python interpreter is used** per host and sudo account combination - for the duration of the run, avoiding the repeat cost of invoking multiple - interpreters and recompiling imports, saving 300-800 ms for every playbook - step. +* **A single network roundtrip is used** to execute a step whose code already + exists in RAM on the target. Eliminating multiplexed SSH channel creation + saves 5 ms runtime per 1 ms of network latency for every playbook step. -* Remote interpreters reuse Mitogen's module import mechanism, caching uploaded - dependencies between steps at the host and user account level. As a - consequence, **bandwidth usage is consistently an order of magnitude lower** - compared to SSH pipelining, and around 5x fewer frames are required to - traverse the wire for a run to complete successfully. +* **Processes are aggressively reused**, avoiding the cost of invoking Python + and recompiling imports, saving 300-800 ms for every playbook step. -* **No writes to the target host's filesystem occur**, unless explicitly - triggered by a playbook step. In all typical configurations, Ansible - repeatedly rewrites and extracts ZIP files to multiple temporary directories - on the target host. Since no temporary files are used, security issues - relating to those files in cross-account scenarios are entirely avoided. +* Code is ephemerally cached in RAM, **reducing bandwidth usage by an order + of magnitude** compared to SSH pipelining, with around 5x fewer frames + traversing the network in a typical run. + +* **No writes to the target's filesystem occur**, unless explicitly triggered + by a playbook step. In all typical configurations, Ansible repeatedly + rewrites and extracts ZIP files to multiple temporary directories on the + target. Since no temporary files are used, security issues relating to those + files in cross-account scenarios are entirely avoided. Demo ----- +~~~~ This demonstrates Ansible running a subset of the Mitogen integration tests concurrent to an equivalent run using the extension. @@ -71,7 +64,7 @@ concurrent to an equivalent run using the extension. Testimonials ------------- +~~~~~~~~~~~~ * "With mitogen **my playbook runtime went from 45 minutes to just under 3 minutes**. Awesome work!" @@ -96,14 +89,11 @@ Testimonials Installation ------------ -.. caution:: - - Please review the behavioural differences documented below prior to use. - -1. Verify Ansible 2.4 and Python 2.7 are listed in the output of ``ansible - --version`` -2. Download and extract https://github.com/dw/mitogen/archive/master.zip -3. Modify ``ansible.cfg``: +1. Thoroughly review the documented behavioural differences. +2. Verify Ansible 2.4/2.5 and Python 2.7 are listed in ``ansible --version`` + output. +3. Download and extract https://github.com/dw/mitogen/archive/master.zip +4. Modify ``ansible.cfg``: .. code-block:: dosini @@ -111,70 +101,256 @@ Installation strategy_plugins = /path/to/mitogen-master/ansible_mitogen/plugins/strategy strategy = mitogen_linear - The ``strategy`` key is optional. If omitted, you can set the - ``ANSIBLE_STRATEGY=mitogen_linear`` environment variable on a per-run basis. - Like ``mitogen_linear``, the ``mitogen_free`` strategy also exists to mimic - the built-in ``free`` strategy. - -4. Cross your fingers and try it. + The ``strategy`` key is optional. If omitted, the + ``ANSIBLE_STRATEGY=mitogen_linear`` environment variable can be set on a + per-run basis. Like ``mitogen_linear``, the ``mitogen_free`` strategy exists + to mimic the ``free`` strategy. -Limitations ------------ +Noteworthy Differences +---------------------- -* Only Ansible 2.4 is being used for development, with occasional tests under - 2.5, 2.3 and 2.2. It should be more than possible to fully support at least - 2.3, if not also 2.2. +* Ansible 2.4 and 2.5 are supported. File bugs to register interest in older + releases. -* Only the ``sudo`` become method is available, however adding new methods is - straightforward, and eventually at least ``su`` will be included. +* The ``sudo`` become method is available and ``su`` is planned. File bugs to + register interest in additional methods. -* The extension's performance benefits do not scale perfectly linearly with the - number of targets. This is a subject of ongoing investigation and - improvements will appear in time. +* The ``ssh``, ``local`` and ``docker`` connection types are available, with + more planned. File bugs to register interest. -* "Module Replacer" style modules are not yet supported. These rarely appear in - practice, and light Github code searches failed to reveal many examples of - them. +* Local commands execute in a reuseable interpreter created identically to + interpreters on targets. Presently one interpreter per ``become_user`` + exists, and so only one local action may execute simultaneously. + + Ansible usually permits up to ``forks`` simultaneous local actions. Any + long-running local actions that execute for every target will experience + artificial serialization, causing slowdown equivalent to `task_duration * + num_targets`. This will be fixed soon. + +* Asynchronous jobs presently exist only for the duration of a run, and time + limits are not implemented. + +* Due to use of :func:`select.select` the IO multiplexer breaks down around 100 + targets, expect performance degradation as this number is approached and + errant behaviour as it is exceeded. A replacement will appear soon. + +* The undocumented ability to extend :mod:`ansible.module_utils` by supplying a + ``module_utils`` directory alongside a custom new-style module is not yet + supported. + +* "Module Replacer" style modules are not supported. These rarely appear in + practice, and light web searches failed to reveal many examples of them. + +* Ansible permits up to ``forks`` connections to be setup in parallel, whereas + in Mitogen this is handled by a fixed-size thread pool. Up to 16 connections + may be established in parallel by default, this can be modified by setting + the ``MITOGEN_POOL_SIZE`` environment variable. + +* Performance does not scale perfectly linearly with target count. This will + improve over time. + +* Timeouts normally apply to the combined runtime of the SSH and become steps + of a task. As Mitogen treats SSH and sudo distincly, during a failure the + effective timeout may appear to double. -Behavioural Differences ------------------------ +New Features & Notes +-------------------- -* Ansible permits up to ``forks`` SSH connections to be setup simultaneously, - whereas in Mitogen this is handled by a thread pool. Eventually this pool - will become per-CPU, but meanwhile, a maximum of 16 SSH connections may be - established simultaneously by default. This can be increased or decreased - setting the ``MITOGEN_POOL_SIZE`` environment variable. -* Mitogen treats connection timeouts for the SSH and become steps of a task - invocation separately, meaning that in some circumstances the configured - timeout may appear to be doubled. This is since Mitogen internally treats the - creation of an SSH account context separately to the creation of a sudo - account context proxied via that SSH account. +Connection Delegation +~~~~~~~~~~~~~~~~~~~~~ - A future revision may detect a sudo account context created immediately - following its parent SSH account, and try to emulate Ansible's existing - timeout semantics. +.. image:: images/jumpbox.png + :align: right -* Local commands are executed in a reuseable Python interpreter created - identically to interpreters used on remote hosts. At present only one such - interpreter per ``become_user`` exists, and so only one local action may be - executed simultaneously per local user account. +Included is a preview of **Connection Delegation**, a Mitogen-specific +implementation of `stackable connection plug-ins`_. This enables multi-hop +connections via a bastion, or Docker connections delegated via their host +machine, where reaching the host may itself entail recursive delegation. - Ansible usually permits up to ``ansible.cfg:forks`` simultaneous local - actions. Any long-running local actions that execute for every target will - experience artificial serialization, causing slowdown equivalent to - `task_duration * num_targets`. This will be fixed soon. +.. _Stackable connection plug-ins: https://github.com/ansible/proposals/issues/25 -* Asynchronous jobs exist only for the duration of a run, and cannot be - queried by subsequent ansible-playbook invocations. Since the ability to - query job IDs across runs relied on an implementation detail, it is not - expected this will break any real-world playbooks. +Unlike with SSH forwarding Ansible has complete visibility of the final +topology, declarative configuration via static/dynamic inventory is possible, +and data can be cached and re-served, and code executed on every intermediary. + +For example when targeting Docker containers on a remote machine, each module +need only be uploaded once for the first task and container that requires it, +then cached and served from the SSH account for every future task in any +container. + +.. raw:: html + +
+ + +.. caution:: + + Connection delegation is a work in progress, bug reports are welcome. + + * While imports are cached on intermediaries, module scripts are needlessly + reuploaded for each target. Fixing this is equivalent to implementing + **Topology-Aware File Synchronization**, so it may remain unfixed until + that feature is started. + + * Delegated connection setup is single-threaded; only one connection can be + constructed in parallel per intermediary. + + * Unbounded queue RAM growth may occur in an intermediary during large file + transfers if the link between any two hops is slower than the link + between the controller and the first hop. + + * Inferring the configuration of intermediaries may be buggy, manifesting + as duplicate connections between hops, due to not perfectly replicating + the configuration Ansible would normally use for the intermediary. + + * The extension does not understand the difference between a delegated + connection and a ``become_user``. If interpreter recycling kicks in, a + delegated connection could be prematurely recycled. + +To enable connection delegation, set ``mitogen_via=`` on the +command line, or as host and group variables. + +.. code-block:: ini + + # Docker container on web1.dc1 is reachable via web1.dc1. + [app-containers.web1.dc1] + app1.web1.dc1 ansible_host=app1 ansible_connection=docker mitogen_via=web1.dc1 + + # Web servers in DC1 are reachable via bastion.dc1 + [dc1] + web1.dc1 + web2.dc1 + web3.dc1 + + [dc1:vars] + mitogen_via = bastion.dc1 + + # Web servers in DC2 are reachable via bastion.dc2 + [dc2] + web1.dc2 + web2.dc2 + web3.dc2 + + [dc2:vars] + mitogen_via = bastion.dc2 + + # Prod bastions are reachable via a corporate network gateway. + [bastions] + bastion.dc1 mitogen_via=corp-gateway.internal + bastion.dc2 mitogen_via=corp-gateway.internal + + [corp-gateway] + corp-gateway.internal + + +File Transfer +~~~~~~~~~~~~~ + +Normally a tool like ``scp`` is used to copy a file with the ``copy`` or +``template`` actions, or when uploading modules with pipelining disabled. With +Mitogen copies are implemented natively using the same interpreters, connection +tree, and routed message bus that carries RPCs. + +This permits streaming directly between endpoints regardless of execution +environment, without necessitating temporary copies in intermediary accounts or +machines, for example when ``become`` is active, or in the presence of +connection delegation. It also neatly avoids the problem of securely sharing +temporary files between accounts and machines. + +One roundtrip is required to initiate a transfer. For any tool that operates +via SSH multiplexing, 5 are required to configure the associated IO channel, in +addition to the time needed to start the local and remote processes. A complete +localhost invocation of ``scp`` requires around 15 ms. + +As the implementation is self-contained, it is simple to make future +improvements like prioritizing transfers, supporting resume, or displaying +progress bars. + + +Interpreter Reuse +~~~~~~~~~~~~~~~~~ + +Python interpreters are aggressively reused to execute modules. While this +works well, it violates an unwritten assumption, and so it is possible an +earlier module execution could cause a subsequent module to fail, or for +unrelated modules to interact poorly due to bad hygiene, such as +monkey-patching that becomes stacked over repeat invocations. + +Before reporting a bug relating to a misbehaving module, please re-run with +``-e mitogen_task_isolation=fork`` to see if the problem abates. This may be +set per-task, paying attention to the possibility an earlier task may be the +true cause of a failure. + +.. code-block:: yaml + + - name: My task. + broken_module: + some_option: true + vars: + mitogen_task_isolation: fork + +If forking solves your problem, **please report a bug regardless**, as an +internal list can be updated to prevent others bumping into the same problem. + + +Interpreter Recycling +~~~~~~~~~~~~~~~~~~~~~ + +There is a per-target limit on the number of interpreters. Once 20 exist, the +youngest is terminated before starting any new interpreter, preventing +situations like below from triggering memory exhaustion. + +.. code-block:: yaml + + - hosts: corp_boxes + vars: + user_directory: [ + # 10,000 corporate user accounts + ] + tasks: + - name: Create user bashrc + become: true + vars: + ansible_become_user: "{{item}}" + copy: + src: bashrc + dest: "~{{item}}/.bashrc" + with_items: "{{user_directory}}" + +The youngest is chosen to preserve useful accounts like ``root`` and +``postgresql`` that often appear early in a run, however it is simple to +construct a playbook that defeats this strategy. A future version will key +interpreters on the identity of their creating task, avoiding useful account +recycling in every scenario. + +To modify the limit, set the ``MITOGEN_MAX_INTERPRETERS`` environment variable. + + +Standard IO +~~~~~~~~~~~ + +Ansible uses pseudo TTYs for most invocations to allow it to type interactive +passwords, however pseudo TTYs are disabled where standard input is required or +``sudo`` is not in use. Additionally when SSH multiplexing is enabled, a string +like ``Shared connection to localhost closed\r\n`` appears in ``stderr`` of +every invocation. + +Mitogen does not naturally require either of these, as command output is always +embedded within framed messages, and it can simply call :py:func:`pty.openpty` +in any location an interactive password must be typed. + +A major downside to Ansible's behaviour is that ``stdout`` and ``stderr`` are +merged together into a single ``stdout`` variable, with carriage returns +inserted in the output by the TTY layer. However ugly, the extension emulates +this precisely, to avoid breaking playbooks that expect text to appear in +specific variables with a particular linefeed style. How Modules Execute -------------------- +~~~~~~~~~~~~~~~~~~~ Ansible usually modifies, recompresses and reuploads modules every time they run on a target, work that must be repeated by the controller for every @@ -218,6 +394,111 @@ cached in RAM for the remainder of the run. key2=repr(value2)[ ..]] "``. +Runtime Patches +~~~~~~~~~~~~~~~ + +Three small runtime patches are employed in ``strategy.py`` to hook into +desirable locations, in order to override uses of shell, the module executor, +and the mechanism for selecting a connection plug-in. While it is hoped the +patches can be avoided in future, for interesting versions of Ansible deployed +today this simply is not possible, and so they continue to be required. + +The patches are concise and behave conservatively, including by disabling +themselves when non-Mitogen connections are in use. Additional third party +plug-ins are unlikely to attempt similar patches, so the risk to an established +configuration should be minimal. + + +Flag Emulation +~~~~~~~~~~~~~~ + +Mitogen re-parses ``sudo_flags``, ``become_flags``, and ``ssh_flags`` using +option parsers extracted from `sudo(1)` and `ssh(1)` in order to emulate their +equivalent semantics. This allows: + +* robust support for common ``ansible.cfg`` tricks without reconfiguration, + such as forwarding SSH agents across ``sudo`` invocations, +* reporting on conflicting flag combinations, +* reporting on unsupported flag combinations, +* internally special-casing certain behaviour (like recursive agent forwarding) + without boring the user with the details, +* avoiding opening the extension up to untestable scenarios where users can + insert arbitrary garbage between Mitogen and the components it integrates + with, +* precise emulation by an alternative implementation, for example if Mitogen + grew support for Paramiko. + + +Supported Variables +------------------- + +Matching Ansible's model, variables are treated on a per-task basis, causing +establishment of additional reuseable interpreters as necessary to match the +configuration of each task. + + +SSH +~~~ + +This list will grow as more missing pieces are discovered. + +* ``ansible_ssh_timeout`` +* ``ansible_host``, ``ansible_ssh_host`` +* ``ansible_user``, ``ansible_ssh_user`` +* ``ansible_port``, ``ssh_port`` +* ``ansible_ssh_executable``, ``ssh_executable`` +* ``ansible_ssh_private_key_file`` +* ``ansible_ssh_pass``, ``ansible_password`` (default: assume passwordless) +* ``ssh_args``, ``ssh_common_args``, ``ssh_extra_args`` + + +Sudo +~~~~ + +* ``ansible_python_interpreter`` +* ``ansible_sudo_exe``, ``ansible_become_exe`` +* ``ansible_sudo_user``, ``ansible_become_user`` (default: ``root``) +* ``ansible_sudo_pass``, ``ansible_become_pass`` (default: assume passwordless) +* ``sudo_flags``, ``become_flags`` +* ansible.cfg: ``timeout`` + + +Docker +~~~~~~ + +Docker support has received relatively little testing, expect increased +probability of surprises for the time being. + +* ``ansible_host`` + + +Debugging +--------- + +Diagnostics and use of the :py:mod:`logging` package output on the target +machine are usually discarded. With Mitogen, all of this is captured and +returned to the controller, where it can be viewed as desired with ``-vvv``. +Basic high level logs are produced with ``-vvv``, with logging of all IO on the +controller with ``-vvvv`` or higher. + +Although use of standard IO and the logging package on the target is forwarded +to the controller, it is not possible to receive IO activity logs, as the +processs of receiving those logs would would itself generate IO activity. To +receive a complete trace of every process on every machine, file-based logging +is necessary. File-based logging can be enabled by setting +``MITOGEN_ROUTER_DEBUG=1`` in your environment. + +When file-based logging is enabled, one file per context will be created on the +local machine and every target machine, as ``/tmp/mitogen..log``. + + +Getting Help +~~~~~~~~~~~~ +Some users and developers hang out on the +`#mitogen `_ channel on the +FreeNode IRC network. + + Sample Profiles --------------- @@ -253,196 +534,3 @@ operation. .. image:: images/ansible/costapp.png -SSH Variables -------------- - -Matching Ansible's existing model, these variables are treated on a per-task -basis, causing establishment of additional reuseable interpreters as necessary -to match the configuration of each task. - -This list will grow as more missing pieces are discovered. - -* ``ansible_ssh_timeout`` -* ``ansible_host``, ``ansible_ssh_host`` -* ``ansible_user``, ``ansible_ssh_user`` -* ``ansible_port``, ``ssh_port`` -* ``ansible_ssh_executable``, ``ssh_executable`` -* ``ansible_ssh_private_key_file`` -* ``ansible_ssh_pass``, ``ansible_password`` (default: assume passwordless) -* ``ssh_args``, ``ssh_common_args``, ``ssh_extra_args`` - - -Sudo Variables --------------- - -* ``ansible_python_interpreter`` -* ``ansible_sudo_exe``, ``ansible_become_exe`` -* ``ansible_sudo_user``, ``ansible_become_user`` (default: ``root``) -* ``ansible_sudo_pass``, ``ansible_become_pass`` (default: assume passwordless) -* ``sudo_flags``, ``become_flags`` -* ansible.cfg: ``timeout`` - - -Docker Variables ----------------- - -Note: Docker support is only intended for developer testing, it might disappear -entirely prior to a stable release. - -* ansible_host - - -Chat on IRC ------------ - -Some users and developers hang out on the -`#mitogen `_ channel on the -FreeNode IRC network. - - -Debugging ---------- - -Normally with Ansible, diagnostics and use of the :py:mod:`logging` package -output on the target machine are discarded. With Mitogen, all of this is -captured and returned to the host machine, where it can be viewed as desired -with ``-vvv``. Basic high level logs are produced with ``-vvv``, with logging -of all IO on the controller with ``-vvvv`` or higher. - -Although use of standard IO and the logging package on the target is forwarded -to the controller, it is not possible to receive IO activity logs, as the -processs of receiving those logs would would itself generate IO activity. To -receive a complete trace of every process on every machine, file-based logging -is necessary. File-based logging can be enabled by setting -``MITOGEN_ROUTER_DEBUG=1`` in your environment. - -When file-based logging is enabled, one file per context will be created on the -local machine and every target machine, as ``/tmp/mitogen..log``. - - -Implementation Notes --------------------- - -Interpreter Reuse -~~~~~~~~~~~~~~~~~ - -The extension aggressively reuses the single target Python interpreter to -execute every module. While this generally works well, it violates an unwritten -assumption regarding Ansible modules, and so it is possible a buggy module -could cause a run to fail, or for unrelated modules to interact with each other -due to bad hygiene. - -Before reporting a bug relating to a module behaving incorrectly, please re-run -your playbook with ``-e mitogen_task_isolation=fork`` to see if the problem -abates. This may also be set on a per-task basis: - -:: - - - name: My task. - broken_module: - some_option: true - vars: - mitogen_task_isolation: fork - -If forking fixes your problem, **please report a bug regardless**, as an -internal list can be updated to prevent users bumping into the same problem in -future. - - -Interpreter Recycling -~~~~~~~~~~~~~~~~~~~~~ - -The extension limits the number of persistent interpreters in use. When the -limit is reached, the youngest interpreter is terminated before starting a new -interpreter, preventing situations like below from triggering memory -exhaustion. - -.. code-block:: yaml - - - hosts: corp_boxes - vars: - user_directory: [ - # 10,000 corporate user accounts - ] - tasks: - - name: Create user bashrc - become: true - vars: - ansible_become_user: "{{item}}" - copy: - src: bashrc - dest: "~{{item}}/.bashrc" - with_items: "{{user_directory}}" - -This recycling does not occur for direct connections from the controller, and -it is keyed on a per-target basis, i.e. up to 20 interpreters may exist for -each directly connected target. - -The youngest interpreter is chosen to preserve useful accounts, like "root" or -"postgresql" that tend to appear early in a run, however it is simple to -construct a playbook that defeats this strategy. A future version will key -interpreters on the identity of their creating task, file and/or playbook, -avoiding useful account recycling in every scenario. - -To raise or lower the limit from 20, set the ``MITOGEN_MAX_INTERPRETERS`` -environment variable to a new value. - - -Runtime Patches -~~~~~~~~~~~~~~~ - -Three small runtime patches are employed in ``strategy.py`` to hook into -desirable locations, in order to override uses of shell, the module executor, -and the mechanism for selecting a connection plug-in. While it is hoped the -patches can be avoided in future, for interesting versions of Ansible deployed -today this simply is not possible, and so they continue to be required. - -The patches are concise and behave conservatively, including by disabling -themselves when non-Mitogen connections are in use. Additional third party -plug-ins are unlikely to attempt similar patches, so the risk to an established -configuration should be minimal. - - -Standard IO -~~~~~~~~~~~ - -Ansible uses pseudo TTYs for most invocations, to allow it to handle typing -passwords interactively, however it disables pseudo TTYs for certain commands -where standard input is required or ``sudo`` is not in use. Additionally when -SSH multiplexing is enabled, a string like ``Shared connection to localhost -closed\r\n`` appears in ``stderr`` of every invocation. - -Mitogen does not naturally require either of these, as command output is -embedded within the SSH stream, and it can simply call :py:func:`pty.openpty` -in every location an interactive password must be typed. - -A major downside to Ansible's behaviour is that ``stdout`` and ``stderr`` are -merged together into a single ``stdout`` variable, with carriage returns -inserted in the output by the TTY layer. However ugly, the extension emulates -all of this behaviour precisely, to avoid breaking playbooks that expect -certain text to appear in certain variables with certain linefeed characters. - -See `Ansible#14377`_ for related discussion. - -.. _Ansible#14377: https://github.com/ansible/ansible/issues/14377 - - -Flag Emulation -~~~~~~~~~~~~~~ - -Mitogen re-parses ``sudo_flags``, ``become_flags``, and ``ssh_flags`` using -option parsers extracted from `sudo(1)` and `ssh(1)` in order to emulate their -equivalent semantics. This allows: - -* robust support for common ``ansible.cfg`` tricks without reconfiguration, - such as forwarding SSH agents across ``sudo`` invocations, -* reporting on conflicting flag combinations, -* reporting on unsupported flag combinations, -* internally special-casing certain behaviour (like recursive agent forwarding) - without boring the user with the details, -* avoiding opening the extension up to untestable scenarios where users can - insert arbitrary garbage between Mitogen and the components it integrates - with, -* precise emulation by an alternative implementation, for example if Mitogen - grew support for Paramiko. -