Config continued (#31024)

* included inventory and callback in new config

allow inventory to be configurable
updated connection options settings
also updated winrm to work with new configs
removed now obsolete set_host_overrides
added notes for future bcoca, current one is just punting, it's future's problem
updated docs per feedback
added remove group/host methods to inv data
moved fact cache from data to constructed
cleaner/better options
fix when vars are added
extended ignore list to config dicts
updated paramiko connection docs
removed options from base that paramiko already handles
left the look option as it is used by other plugin types
resolve delegation
updated cache doc options
fixed test_script
better fragment merge for options
fixed proxy command
restore ini for proxy
normalized options
moved pipelining to class
updates for host_key_checking
restructured mixins

* fix typo
This commit is contained in:
Brian Coca
2017-11-16 13:49:57 -05:00
committed by GitHub
parent 46c4f6311a
commit 23b1dbacaf
32 changed files with 667 additions and 366 deletions

View File

@@ -116,17 +116,6 @@ class ConnectionBase(AnsiblePlugin):
raise AnsibleError("Internal Error: this connection module does not support running commands via %s" % self._play_context.become_method)
def set_host_overrides(self, host, hostvars=None):
'''
An optional method, which can be used to set connection plugin parameters
from variables set on the host (or groups to which the host belongs)
Any connection plugin using this should first initialize its attributes in
an overridden `def __init__(self):`, and then use `host.get_vars()` to find
variables which may be used to set those attributes in this method.
'''
pass
@staticmethod
def _split_ssh_args(argstring):
"""

View File

@@ -49,10 +49,11 @@ except ImportError:
class Connection(object):
''' Func-based connections '''
has_pipelining = False
def __init__(self, runner, host, port, *args, **kwargs):
self.runner = runner
self.host = host
self.has_pipelining = False
# port is unused, this go on func
self.port = port

View File

@@ -48,23 +48,18 @@ import json
import logging
import re
import os
import signal
import socket
import traceback
from collections import Sequence
from ansible import constants as C
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils.six import PY3, BytesIO, binary_type
from ansible.module_utils.six import BytesIO, PY3
from ansible.module_utils.six.moves import cPickle
from ansible.module_utils._text import to_bytes, to_text
from ansible.playbook.play_context import PlayContext
from ansible.plugins.loader import cliconf_loader, terminal_loader, connection_loader
from ansible.plugins.connection import ConnectionBase
from ansible.plugins.connection.local import Connection as LocalConnection
from ansible.plugins.connection.paramiko_ssh import Connection as ParamikoSshConnection
from ansible.utils.path import unfrackpath, makedirs_safe
from ansible.utils.path import unfrackpath
try:
from __main__ import display
@@ -91,7 +86,8 @@ class Connection(ConnectionBase):
self._last_response = None
self._history = list()
self._local = LocalConnection(play_context, new_stdin, *args, **kwargs)
self._local = connection_loader.get('local', play_context, '/dev/null')
self._local.set_options()
self._terminal = None
self._cliconf = None
@@ -166,10 +162,9 @@ class Connection(ConnectionBase):
if self.connected:
return
if self._play_context.password and not self._play_context.private_key_file:
C.PARAMIKO_LOOK_FOR_KEYS = False
ssh = ParamikoSshConnection(self._play_context, '/dev/null')._connect()
p = connection_loader.get('paramiko', self._play_context, '/dev/null')
p.set_options(direct={'look_for_keys': bool(self._play_context.password and not self._play_context.private_key_file)})
ssh = p._connect()
self.ssh = ssh.ssh
display.vvvv('ssh connection done, setting terminal', host=self._play_context.remote_addr)

View File

@@ -15,6 +15,7 @@ DOCUMENTATION = """
- This is needed on the Ansible control machine to be reasonably efficient with connections.
Thus paramiko is faster for most users on these platforms.
Users with ControlPersist capability can consider using -c ssh or configuring the transport in the configuration file.
- This plugin also borrows a lot of settings from the ssh plugin as they both cover the same protocol.
version_added: "0.1"
options:
remote_addr:
@@ -28,26 +29,94 @@ DOCUMENTATION = """
remote_user:
description:
- User to login/authenticate as
- Can be set from the CLI via the ``--user`` or ``-u`` options.
vars:
- name: ansible_user
- name: ansible_ssh_user
- name: ansible_paramiko_user
env:
- name: ANSIBLE_REMOTE_USER
- name: ANSIBLE_PARAMIKO_REMOTE_USER
version_added: '2.5'
ini:
- section: defaults
key: remote_user
- section: paramiko_connection
key: remote_user
version_added: '2.5'
password:
description:
- Secret used to either login the ssh server or as a passphrase for ssh keys that require it
- Can be set from the CLI via the ``--ask-pass`` option.
vars:
- name: ansible_password
- name: ansible_ssh_pass
- name: ansible_paramiko_pass
version_added: '2.5'
host_key_auto_add:
description: 'TODO: write it'
env: [{name: ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD}]
ini:
- {key: host_key_auto_add, section: paramiko_connection}
type: boolean
look_for_keys:
default: True
description: 'TODO: write it'
env: [{name: ANSIBLE_PARAMIKO_LOOK_FOR_KEYS}]
ini:
- {key: look_for_keys, section: paramiko_connection}
type: boolean
proxy_command:
default: ''
description:
- Proxy information for running the connection via a jumphost
- Also this plugin will scan 'ssh_args', 'ssh_extra_args' and 'ssh_common_args' from the 'ssh' plugin settings for proxy information if set.
env: [{name: ANSIBLE_PARAMIKO_PROXY_COMMAND}]
ini:
- {key: proxy_command, section: paramiko_connection}
pty:
default: True
description: 'TODO: write it'
env:
- name: ANSIBLE_PARAMIKO_PTY
ini:
- section: paramiko_connection
key: pty
type: boolean
record_host_keys:
default: True
description: 'TODO: write it'
env: [{name: ANSIBLE_PARAMIKO_RECORD_HOST_KEYS}]
ini:
- section: paramiko_connection
key: record_host_keys
type: boolean
host_key_checking:
description: 'Set this to "False" if you want to avoid host key checking by the underlying tools Ansible uses to connect to the host'
type: boolean
default: True
env:
- name: ANSIBLE_HOST_KEY_CHECKING
- name: ANSIBLE_SSH_HOST_KEY_CHECKING
version_added: '2.5'
- name: ANSIBLE_PARAMIKO_HOST_KEY_CHECKING
version_added: '2.5'
ini:
- section: defaults
key: host_key_checking
- section: paramiko_connection
key: host_key_checking
version_added: '2.5'
vars:
- name: ansible_host_key_checking
version_added: '2.5'
- name: ansible_ssh_host_key_checking
version_added: '2.5'
- name: ansible_paramiko_host_key_checking
version_added: '2.5'
# TODO:
#getattr(self._play_context, 'ssh_extra_args', '') or '',
#getattr(self._play_context, 'ssh_common_args', '') or '',
#getattr(self._play_context, 'ssh_args', '') or '',
#C.HOST_KEY_CHECKING
#C.PARAMIKO_HOST_KEY_AUTO_ADD
#C.USE_PERSISTENT_CONNECTIONS:
# ssh.connect(
# look_for_keys=C.PARAMIKO_LOOK_FOR_KEYS,
# key_filename,
# password=self._play_context.password,
# timeout=self._play_context.timeout,
# port=port,
#proxy_command = proxy_command or C.PARAMIKO_PROXY_COMMAND
#C.PARAMIKO_PTY
#C.PARAMIKO_RECORD_HOST_KEYS
#timeout=self._play_context.timeout,
"""
import warnings
@@ -110,10 +179,11 @@ class MyAddPolicy(object):
def __init__(self, new_stdin, connection):
self._new_stdin = new_stdin
self.connection = connection
self._options = connection._options
def missing_host_key(self, client, hostname, key):
if all((C.HOST_KEY_CHECKING, not C.PARAMIKO_HOST_KEY_AUTO_ADD)):
if all((self._options['host_key_checking'], not self._options['host_key_auto_add'])):
fingerprint = hexlify(key.get_fingerprint())
ktype = key.get_name()
@@ -194,7 +264,7 @@ class Connection(ConnectionBase):
if proxy_command:
break
proxy_command = proxy_command or C.PARAMIKO_PROXY_COMMAND
proxy_command = proxy_command or self._options['proxy_command']
sock_kwarg = {}
if proxy_command:
@@ -229,7 +299,7 @@ class Connection(ConnectionBase):
self.keyfile = os.path.expanduser("~/.ssh/known_hosts")
if C.HOST_KEY_CHECKING:
if self._options['host_key_checking']:
for ssh_known_hosts in ("/etc/ssh/ssh_known_hosts", "/etc/openssh/ssh_known_hosts"):
try:
# TODO: check if we need to look at several possible locations, possible for loop
@@ -257,7 +327,7 @@ class Connection(ConnectionBase):
self._play_context.remote_addr,
username=self._play_context.remote_user,
allow_agent=allow_agent,
look_for_keys=C.PARAMIKO_LOOK_FOR_KEYS,
look_for_keys=self._options['look_for_keys'],
key_filename=key_filename,
password=self._play_context.password,
timeout=self._play_context.timeout,
@@ -301,7 +371,7 @@ class Connection(ConnectionBase):
# sudo usually requires a PTY (cf. requiretty option), therefore
# we give it one by default (pty=True in ansble.cfg), and we try
# to initialise from the calling environment when sudoable is enabled
if C.PARAMIKO_PTY and sudoable:
if self._options['pty'] and sudoable:
chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0)))
display.vvv("EXEC %s" % cmd, host=self._play_context.remote_addr)
@@ -454,7 +524,7 @@ class Connection(ConnectionBase):
if self.sftp is not None:
self.sftp.close()
if C.HOST_KEY_CHECKING and C.PARAMIKO_RECORD_HOST_KEYS and self._any_keys_added():
if self._options['host_key_checking'] and self._options['record_host_keys'] and self._any_keys_added():
# add any new SSH host keys -- warning -- this could be slow
# (This doesn't acquire the connection lock because it needs

View File

@@ -14,13 +14,11 @@ DOCUMENTATION = """
version_added: "2.3"
"""
import os
import sys
import pty
import json
import subprocess
from ansible import constants as C
from ansible.plugins.loader import connection_loader
from ansible.plugins.connection import ConnectionBase
from ansible.module_utils._text import to_text
from ansible.module_utils.six.moves import cPickle

View File

@@ -22,14 +22,23 @@ DOCUMENTATION = '''
- name: ansible_host
- name: ansible_ssh_host
host_key_checking:
#constant: HOST_KEY_CHECKING
description: Determines if ssh should check host keys
type: boolean
ini:
- section: defaults
key: 'host_key_checking'
- section: ssh_connection
key: 'host_key_checking'
version_added: '2.5'
env:
- name: ANSIBLE_HOST_KEY_CHECKING
- name: ANSIBLE_SSH_HOST_KEY_CHECKING
version_added: '2.5'
vars:
- name: ansible_host_key_checking
version_added: '2.5'
- name: ansible_ssh_host_key_checking
version_added: '2.5'
password:
description: Authentication password for the C(remote_user). Can be supplied as CLI option.
vars:

View File

@@ -11,8 +11,13 @@ DOCUMENTATION = """
short_description: Run tasks over Microsoft's WinRM
description:
- Run commands or put/fetch on a target via WinRM
- This plugin allows extra arguments to be passed that are supported by the protocol but not explicitly defined here.
They should take the form of variables declared with the following pattern `ansible_winrm_<option>`.
version_added: "2.0"
requirements:
- pywinrm (python library)
options:
# figure out more elegant 'delegation'
remote_addr:
description:
- Address of the windows machine
@@ -21,11 +26,58 @@ DOCUMENTATION = """
- name: ansible_host
- name: ansible_winrm_host
remote_user:
keywords:
- name: user
- name: remote_user
description:
- The user to log in as to the Windows machine
vars:
- name: ansible_user
- name: ansible_winrm_user
port:
description:
- port for winrm to connect on remote target
- The default is the https (5896) port, if using http it should be 5895
vars:
- name: ansible_port
- name: ansible_winrm_port
default: 5986
keywords:
- name: port
type: integer
scheme:
description:
- URI scheme to use
choices: [http, https]
default: https
vars:
- name: ansible_winrm_scheme
path:
description: URI path to connect to
default: '/wsman'
vars:
- name: ansible_winrm_path
transport:
description:
- List of winrm transports to attempt to to use (ssl, plaintext, kerberos, etc)
- If None (the default) the plugin will try to automatically guess the correct list
- The choices avialable depend on your version of pywinrm
type: list
vars:
- name: ansible_winrm_transport
kerberos_command:
description: kerberos command to use to request a authentication ticket
default: kinit
vars:
- name: ansible_winrm_kinit_cmd
kerberos_mode:
description:
- kerberos usage mode.
- The managed option means Ansible will obtain kerberos ticket.
- While the manual one means a ticket must already have been obtained by the user.
choices: [managed, manual]
vars:
- name: ansible_winrm_kinit_mode
"""
import base64
@@ -84,43 +136,40 @@ class Connection(ConnectionBase):
module_implementation_preferences = ('.ps1', '.exe', '')
become_methods = ['runas']
allow_executable = False
has_pipelining = True
allow_extras = True
def __init__(self, *args, **kwargs):
self.has_pipelining = True
self.always_pipeline_modules = True
self.has_native_async = True
self.protocol = None
self.shell_id = None
self.delegate = None
self._shell_type = 'powershell'
# FUTURE: Add runas support
super(Connection, self).__init__(*args, **kwargs)
def set_host_overrides(self, host, variables, templar):
'''
Override WinRM-specific options from host variables.
'''
def set_options(self, task_keys=None, var_options=None, direct=None):
if not HAS_WINRM:
return
hostvars = {}
for k in variables:
if k.startswith('ansible_winrm'):
hostvars[k] = templar.template(variables[k])
super(Connection, self).set_options(task_keys=None, var_options=var_options, direct=direct)
self._winrm_host = self._play_context.remote_addr
self._winrm_port = int(self._play_context.port or 5986)
self._winrm_scheme = hostvars.get('ansible_winrm_scheme', 'http' if self._winrm_port == 5985 else 'https')
self._winrm_path = hostvars.get('ansible_winrm_path', '/wsman')
self._winrm_user = self._play_context.remote_user
self._winrm_pass = self._play_context.password
self._become_method = self._play_context.become_method
self._become_user = self._play_context.become_user
self._become_pass = self._play_context.become_pass
self._kinit_cmd = hostvars.get('ansible_winrm_kinit_cmd', 'kinit')
self._winrm_port = self._options['port']
self._winrm_scheme = self._options['scheme']
self._winrm_path = self._options['path']
self._kinit_cmd = self._options['kerberos_command']
self._winrm_transport = self._options['transport']
if hasattr(winrm, 'FEATURE_SUPPORTED_AUTHTYPES'):
self._winrm_supported_authtypes = set(winrm.FEATURE_SUPPORTED_AUTHTYPES)
@@ -128,16 +177,15 @@ class Connection(ConnectionBase):
# for legacy versions of pywinrm, use the values we know are supported
self._winrm_supported_authtypes = set(['plaintext', 'ssl', 'kerberos'])
# TODO: figure out what we want to do with auto-transport selection in the face of NTLM/Kerb/CredSSP/Cert/Basic
transport_selector = 'ssl' if self._winrm_scheme == 'https' else 'plaintext'
# calculate transport if needed
if self._winrm_transport is None or self._winrm_transport[0] is None:
# TODO: figure out what we want to do with auto-transport selection in the face of NTLM/Kerb/CredSSP/Cert/Basic
transport_selector = ['ssl'] if self._winrm_scheme == 'https' else ['plaintext']
if HAVE_KERBEROS and ((self._winrm_user and '@' in self._winrm_user)):
self._winrm_transport = 'kerberos,%s' % transport_selector
else:
self._winrm_transport = transport_selector
self._winrm_transport = hostvars.get('ansible_winrm_transport', self._winrm_transport)
if isinstance(self._winrm_transport, string_types):
self._winrm_transport = [x.strip() for x in self._winrm_transport.split(',') if x.strip()]
if HAVE_KERBEROS and ((self._winrm_user and '@' in self._winrm_user)):
self._winrm_transport = ['kerberos'] + transport_selector
else:
self._winrm_transport = transport_selector
unsupported_transports = set(self._winrm_transport).difference(self._winrm_supported_authtypes)
@@ -145,16 +193,14 @@ class Connection(ConnectionBase):
raise AnsibleError('The installed version of WinRM does not support transport(s) %s' % list(unsupported_transports))
# if kerberos is among our transports and there's a password specified, we're managing the tickets
kinit_mode = to_text(hostvars.get('ansible_winrm_kinit_mode', '')).strip()
if kinit_mode == "":
kinit_mode = self._options['kerberos_mode']
if kinit_mode is None:
# HACK: ideally, remove multi-transport stuff
self._kerb_managed = "kerberos" in self._winrm_transport and self._winrm_pass
elif kinit_mode == "managed":
self._kerb_managed = True
elif kinit_mode == "manual":
self._kerb_managed = False
else:
raise AnsibleError('Unknown ansible_winrm_kinit_mode value: "%s" (must be "managed" or "manual")' % kinit_mode)
# arg names we're going passing directly
internal_kwarg_mask = set(['self', 'endpoint', 'transport', 'username', 'password', 'scheme', 'path', 'kinit_mode', 'kinit_cmd'])
@@ -163,16 +209,16 @@ class Connection(ConnectionBase):
argspec = inspect.getargspec(Protocol.__init__)
supported_winrm_args = set(argspec.args)
supported_winrm_args.update(internal_kwarg_mask)
passed_winrm_args = set([v.replace('ansible_winrm_', '') for v in hostvars if v.startswith('ansible_winrm_')])
passed_winrm_args = set([v.replace('ansible_winrm_', '') for v in self._options['_extras']])
unsupported_args = passed_winrm_args.difference(supported_winrm_args)
# warn for kwargs unsupported by the installed version of pywinrm
for arg in unsupported_args:
display.warning("ansible_winrm_{0} unsupported by pywinrm (is an up-to-date version of pywinrm installed?)".format(arg))
# pass through matching kwargs, excluding the list we want to treat specially
# pass through matching extras, excluding the list we want to treat specially
for arg in passed_winrm_args.difference(internal_kwarg_mask).intersection(supported_winrm_args):
self._winrm_kwargs[arg] = hostvars['ansible_winrm_%s' % arg]
self._winrm_kwargs[arg] = self._options['_extras']['ansible_winrm_%s' % arg]
# Until pykerberos has enough goodies to implement a rudimentary kinit/klist, simplest way is to let each connection
# auth itself with a private CCACHE.