mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 05:42:50 +00:00
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:
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user