issue #412: add docstrings/boilerplate to transport_config.py.

issue510
David Wilson 6 years ago
parent 2ad05f1238
commit 115c3c5657

@ -58,15 +58,6 @@ import ansible_mitogen.transport_config
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def optional_secret(value):
"""
Wrap `value` in :class:`mitogen.core.Secret` if it is not :data:`None`,
otherwise return :data:`None`.
"""
if value is not None:
return mitogen.core.Secret(value)
def optional_int(value): def optional_int(value):
""" """
Convert `value` to an integer if it is not :data:`None`, otherwise return Convert `value` to an integer if it is not :data:`None`, otherwise return
@ -111,7 +102,7 @@ def _connect_ssh(spec):
'check_host_keys': check_host_keys, 'check_host_keys': check_host_keys,
'hostname': spec.remote_addr(), 'hostname': spec.remote_addr(),
'username': spec.remote_user(), 'username': spec.remote_user(),
'password': optional_secret(spec.password()), 'password': spec.password(),
'port': spec.port(), 'port': spec.port(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'identity_file': private_key_file, 'identity_file': private_key_file,
@ -235,7 +226,7 @@ def _connect_su(spec):
'enable_lru': True, 'enable_lru': True,
'kwargs': { 'kwargs': {
'username': spec.become_user(), 'username': spec.become_user(),
'password': optional_secret(spec.become_pass()), 'password': spec.become_pass(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'su_path': spec.become_exe(), 'su_path': spec.become_exe(),
'connect_timeout': spec.timeout(), 'connect_timeout': spec.timeout(),
@ -252,7 +243,7 @@ def _connect_sudo(spec):
'enable_lru': True, 'enable_lru': True,
'kwargs': { 'kwargs': {
'username': spec.become_user(), 'username': spec.become_user(),
'password': optional_secret(spec.become_pass()), 'password': spec.become_pass(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'sudo_path': spec.become_exe(), 'sudo_path': spec.become_exe(),
'connect_timeout': spec.timeout(), 'connect_timeout': spec.timeout(),
@ -270,7 +261,7 @@ def _connect_doas(spec):
'enable_lru': True, 'enable_lru': True,
'kwargs': { 'kwargs': {
'username': spec.become_user(), 'username': spec.become_user(),
'password': optional_secret(spec.become_pass()), 'password': spec.become_pass(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'doas_path': spec.become_exe(), 'doas_path': spec.become_exe(),
'connect_timeout': spec.timeout(), 'connect_timeout': spec.timeout(),
@ -286,7 +277,7 @@ def _connect_mitogen_su(spec):
'method': 'su', 'method': 'su',
'kwargs': { 'kwargs': {
'username': spec.remote_user(), 'username': spec.remote_user(),
'password': optional_secret(spec.password()), 'password': spec.password(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'su_path': spec.become_exe(), 'su_path': spec.become_exe(),
'connect_timeout': spec.timeout(), 'connect_timeout': spec.timeout(),
@ -302,7 +293,7 @@ def _connect_mitogen_sudo(spec):
'method': 'sudo', 'method': 'sudo',
'kwargs': { 'kwargs': {
'username': spec.remote_user(), 'username': spec.remote_user(),
'password': optional_secret(spec.password()), 'password': spec.password(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'sudo_path': spec.become_exe(), 'sudo_path': spec.become_exe(),
'connect_timeout': spec.timeout(), 'connect_timeout': spec.timeout(),
@ -319,7 +310,7 @@ def _connect_mitogen_doas(spec):
'method': 'doas', 'method': 'doas',
'kwargs': { 'kwargs': {
'username': spec.remote_user(), 'username': spec.remote_user(),
'password': optional_secret(spec.password()), 'password': spec.password(),
'python_path': spec.python_path(), 'python_path': spec.python_path(),
'doas_path': spec.become_exe(), 'doas_path': spec.become_exe(),
'connect_timeout': spec.timeout(), 'connect_timeout': spec.timeout(),

@ -1,9 +1,39 @@
# Copyright 2017, David Wilson
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import absolute_import
from __future__ import unicode_literals
""" """
Mitogen extends Ansible's regular host configuration mechanism in several ways Mitogen extends Ansible's target configuration mechanism in several ways that
that require quite a lot of care: require some care:
* Some per-task configurables in Ansible like ansible_python_interpreter are * Per-task configurables in Ansible like ansible_python_interpreter are
connection-layer configurables in Mitogen. They must be extracted during each connection-layer configurables in Mitogen. They must be extracted during each
task execution to form the complete connection-layer configuration. task execution to form the complete connection-layer configuration.
@ -13,28 +43,31 @@ that require quite a lot of care:
* Mitogen allows connections to be delegated to other machines. Ansible has no * Mitogen allows connections to be delegated to other machines. Ansible has no
internal framework for this, and so Mitogen must figure out a delegated internal framework for this, and so Mitogen must figure out a delegated
connection configuration all for itself. This means it cannot reuse much of connection configuration all on its own. It cannot reuse much of the Ansible
the Ansible machinery for building a connection configuration, as that machinery for building a connection configuration, as that machinery is
machinery is deeply spread and out hard-wired to expect Ansible's usual mode deeply spread out and hard-wired to expect Ansible's usual mode of operation.
of operation.
For normal and delegate_to connections, Ansible's PlayContext is reused where
For delegated connections, Ansible's PlayContext information is reused where possible to maximize compatibility, but for proxy hops, configurations are
possible, but for proxy hops, configurations are built up using the HostVars built up using the HostVars magic class to call VariableManager.get_vars()
magic class to call VariableManager.get_vars() behind the scenes on our behalf. behind the scenes on our behalf. Where Ansible has multiple sources of a
Where Ansible has multiple sources of a configuration item, for example, configuration item, for example, ansible_ssh_extra_args, Mitogen must (ideally
ansible_ssh_extra_args, Mitogen must (ideally perfectly) reproduce how Ansible perfectly) reproduce how Ansible arrives at its value, without using mechanisms
arrives at its value, without using mechanisms that are hard-wired or change that are hard-wired or change across Ansible versions.
across Ansible versions.
That is what this file is for. It exports two spec classes, one that takes all That is what this file is for. It exports two spec classes, one that takes all
information from PlayContext, and another that takes (almost) all information information from PlayContext, and another that takes (almost) all information
from HostVars. from HostVars.
""" """
import abc
import os import os
import ansible.utils.shlex import ansible.utils.shlex
import ansible.constants as C import ansible.constants as C
from ansible.module_utils.six import with_metaclass
import mitogen.core import mitogen.core
@ -47,11 +80,201 @@ def parse_python_path(s):
return ansible.utils.shlex.shlex_split(s) return ansible.utils.shlex.shlex_split(s)
class PlayContextSpec: def optional_secret(value):
"""
Wrap `value` in :class:`mitogen.core.Secret` if it is not :data:`None`,
otherwise return :data:`None`.
"""
if value is not None:
return mitogen.core.Secret(value)
class Spec(with_metaclass(abc.ABCMeta, object)):
"""
A source for variables that comprise a connection configuration.
"""
@abc.abstractmethod
def transport(self):
"""
The name of the Ansible plug-in implementing the connection.
"""
@abc.abstractmethod
def inventory_name(self):
"""
The name of the target being connected to as it appears in Ansible's
inventory.
"""
@abc.abstractmethod
def remote_addr(self):
"""
The network address of the target, or for container and other special
targets, some other unique identifier.
"""
@abc.abstractmethod
def remote_user(self):
"""
The username of the login account on the target.
"""
@abc.abstractmethod
def password(self):
"""
The password of the login account on the target.
"""
@abc.abstractmethod
def become(self):
"""
:data:`True` if privilege escalation should be active.
"""
@abc.abstractmethod
def become_method(self):
"""
The name of the Ansible become method to use.
"""
@abc.abstractmethod
def become_user(self):
"""
The username of the target account for become.
"""
@abc.abstractmethod
def become_pass(self):
"""
The password of the target account for become.
"""
@abc.abstractmethod
def port(self):
"""
The port of the login service on the target machine.
"""
@abc.abstractmethod
def python_path(self):
"""
Path to the Python interpreter on the target machine.
"""
@abc.abstractmethod
def private_key_file(self):
"""
Path to the SSH private key file to use to login.
"""
@abc.abstractmethod
def ssh_executable(self):
"""
Path to the SSH executable.
"""
@abc.abstractmethod
def timeout(self):
"""
The generic timeout for all connections.
"""
@abc.abstractmethod
def ansible_ssh_timeout(self):
"""
The SSH-specific timeout for a connection.
"""
@abc.abstractmethod
def ssh_args(self):
"""
The list of additional arguments that should be included in an SSH
invocation.
"""
@abc.abstractmethod
def become_exe(self):
"""
The path to the executable implementing the become method on the remote
machine.
"""
@abc.abstractmethod
def sudo_args(self):
"""
The list of additional arguments that should be included in a become
invocation.
"""
# TODO: split out into sudo_args/become_args.
@abc.abstractmethod
def mitogen_via(self):
"""
The value of the mitogen_via= variable for this connection. Indicates
the connection should be established via an intermediary.
"""
@abc.abstractmethod
def mitogen_kind(self):
"""
The type of container to use with the "setns" transport.
"""
@abc.abstractmethod
def mitogen_docker_path(self):
"""
The path to the "docker" program for the 'docker' transport.
"""
@abc.abstractmethod
def mitogen_kubectl_path(self):
"""
The path to the "kubectl" program for the 'docker' transport.
"""
@abc.abstractmethod
def mitogen_lxc_path(self):
"""
The path to the "lxc" program for the 'lxd' transport.
"""
@abc.abstractmethod
def mitogen_lxc_attach_path(self):
"""
The path to the "lxc-attach" program for the 'lxc' transport.
"""
@abc.abstractmethod
def mitogen_lxc_info_path(self):
"""
The path to the "lxc-info" program for the 'lxc' transport.
"""
@abc.abstractmethod
def mitogen_machinectl_path(self):
"""
The path to the "machinectl" program for the 'setns' transport.
"""
@abc.abstractmethod
def mitogen_ssh_debug_level(self):
"""
The SSH debug level.
"""
@abc.abstractmethod
def extra_args(self):
"""
Connection-specific arguments.
"""
class PlayContextSpec(Spec):
""" """
Return a dict representing all important connection configuration, allowing PlayContextSpec takes almost all its information as-is from Ansible's
the same functions to work regardless of whether configuration came from PlayContext. It is used for normal connections and delegate_to connections,
play_context (direct connection) or host vars (mitogen_via=). and should always be accurate.
""" """
def __init__(self, connection, play_context, transport, inventory_name): def __init__(self, connection, play_context, transport, inventory_name):
self._connection = connection self._connection = connection
@ -81,10 +304,10 @@ class PlayContextSpec:
return self._play_context.become_user return self._play_context.become_user
def become_pass(self): def become_pass(self):
return self._play_context.become_pass return optional_secret(self._play_context.become_pass)
def password(self): def password(self):
return self._play_context.password return optional_secret(self._play_context.password)
def port(self): def port(self):
return self._play_context.port return self._play_context.port
@ -107,7 +330,7 @@ class PlayContextSpec:
return ( return (
self._connection.get_task_var('ansible_timeout') or self._connection.get_task_var('ansible_timeout') or
self._connection.get_task_var('ansible_ssh_timeout') or self._connection.get_task_var('ansible_ssh_timeout') or
self._play_context.timeout self.timeout()
) )
def ssh_args(self): def ssh_args(self):
@ -165,7 +388,23 @@ class PlayContextSpec:
return self._connection.get_extra_args() return self._connection.get_extra_args()
class MitogenViaSpec: class MitogenViaSpec(Spec):
"""
MitogenViaSpec takes most of its information from the HostVars of the
running task. HostVars is a lightweight wrapper around VariableManager, so
it is better to say that VariableManager.get_vars() is the ultimate source
of MitogenViaSpec's information.
Due to this, mitogen_via= hosts must have all their configuration
information represented as host and group variables. We cannot use any
per-task configuration, as all that data belongs to the real target host.
Ansible uses all kinds of strange historical logic for calculating
variables, including making their precedence configurable. MitogenViaSpec
must ultimately reimplement all of that logic. It is likely that if you are
having a configruation problem with connection delegation, the answer to
your problem lies in the method implementations below!
"""
def __init__(self, inventory_name, host_vars, def __init__(self, inventory_name, host_vars,
become_method, become_user): become_method, become_user):
self._inventory_name = inventory_name self._inventory_name = inventory_name
@ -205,14 +444,14 @@ class MitogenViaSpec:
return self._become_user return self._become_user
def become_pass(self): def become_pass(self):
return ( return optional_secret(
# TODO: Might have to come from PlayContext. # TODO: Might have to come from PlayContext.
self._host_vars.get('ansible_become_password') or self._host_vars.get('ansible_become_password') or
self._host_vars.get('ansible_become_pass') self._host_vars.get('ansible_become_pass')
) )
def password(self): def password(self):
return ( return optional_secret(
# TODO: Might have to come from PlayContext. # TODO: Might have to come from PlayContext.
self._host_vars.get('ansible_ssh_pass') or self._host_vars.get('ansible_ssh_pass') or
self._host_vars.get('ansible_password') self._host_vars.get('ansible_password')
@ -225,13 +464,11 @@ class MitogenViaSpec:
) )
def python_path(self): def python_path(self):
s = parse_python_path( return parse_python_path(
self._host_vars.get('ansible_python_interpreter') self._host_vars.get('ansible_python_interpreter')
# This variable has no default for remote hosts. For local hosts it # This variable has no default for remote hosts. For local hosts it
# is sys.executable. # is sys.executable.
) )
print('hi ho', self.inventory_name(), s)
return s
def private_key_file(self): def private_key_file(self):
# TODO: must come from PlayContext too. # TODO: must come from PlayContext too.

Loading…
Cancel
Save