mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 12:50:22 -07:00
Become plugins (#50991)
* [WIP] become plugins Move from hardcoded method to plugins for ease of use, expansion and overrides - load into connection as it is going to be the main consumer - play_context will also use to keep backwards compat API - ensure shell is used to construct commands when needed - migrate settings remove from base config in favor of plugin specific configs - cleanup ansible-doc - add become plugin docs - remove deprecated sudo/su code and keywords - adjust become options for cli - set plugin options from context - ensure config defs are avaialbe before instance - refactored getting the shell plugin, fixed tests - changed into regex as they were string matching, which does not work with random string generation - explicitly set flags for play context tests - moved plugin loading up front - now loads for basedir also - allow pyc/o for non m modules - fixes to tests and some plugins - migrate to play objects fro play_context - simiplify gathering - added utf8 headers - moved option setting - add fail msg to dzdo - use tuple for multiple options on fail/missing - fix relative plugin paths - shift from play context to play - all tasks already inherit this from play directly - remove obsolete 'set play' - correct environment handling - add wrap_exe option to pfexec - fix runas to noop - fixed setting play context - added password configs - removed required false - remove from doc building till they are ready future development: - deal with 'enable' and 'runas' which are not 'command wrappers' but 'state flags' and currently hardcoded in diff subsystems * cleanup remove callers to removed func removed --sudo cli doc refs remove runas become_exe ensure keyerorr on plugin also fix backwards compat, missing method is attributeerror, not ansible error get remote_user consistently ignore missing system_tmpdirs on plugin load correct config precedence add deprecation fix networking imports backwards compat for plugins using BECOME_METHODS * Port become_plugins to context.CLIARGS This is a work in progress: * Stop passing options around everywhere as we can use context.CLIARGS instead * Refactor make_become_commands as asked for by alikins * Typo in comment fix * Stop loading values from the cli in more than one place Both play and play_context were saving default values from the cli arguments directly. This changes things so that the default values are loaded into the play and then play_context takes them from there. * Rename BECOME_PLUGIN_PATH to DEFAULT_BECOME_PLUGIN_PATH As alikins said, all other plugin paths are named DEFAULT_plugintype_PLUGIN_PATH. If we're going to rename these, that should be done all at one time rather than piecemeal. * One to throw away This is a set of hacks to get setting FieldAttribute defaults to command line args to work. It's not fully done yet. After talking it over with sivel and jimi-c this should be done by fixing FieldAttributeBase and _get_parent_attribute() calls to do the right thing when there is a non-None default. What we want to be able to do ideally is something like this: class Base(FieldAttributeBase): _check_mode = FieldAttribute([..] default=lambda: context.CLIARGS['check']) class Play(Base): # lambda so that we have a chance to parse the command line args # before we get here. In the future we might be able to restructure # this so that the cli parsing code runs before these classes are # defined. class Task(Base): pass And still have a playbook like this function: --- - hosts: tasks: - command: whoami check_mode: True (The check_mode test that is added as a separate commit in this PR will let you test variations on this case). There's a few separate reasons that the code doesn't let us do this or a non-ugly workaround for this as written right now. The fix that jimi-c, sivel, and I talked about may let us do this or it may still require a workaround (but less ugly) (having one class that has the FieldAttributes with default values and one class that inherits from that but just overrides the FieldAttributes which now have defaults) * Revert "One to throw away" This reverts commit 23aa883cbed11429ef1be2a2d0ed18f83a3b8064. * Set FieldAttr defaults directly from CLIARGS * Remove dead code * Move timeout directly to PlayContext, it's never needed on Play * just for backwards compat, add a static version of BECOME_METHODS to constants * Make the become attr on the connection public, since it's used outside of the connection * Logic fix * Nuke connection testing if it supports specific become methods * Remove unused vars * Address rebase issues * Fix path encoding issue * Remove unused import * Various cleanups * Restore network_cli check in _low_level_execute_command * type improvements for cliargs_deferred_get and swap shallowcopy to default to False * minor cleanups * Allow the su plugin to work, since it doesn't define a prompt the same way * Fix up ksu become plugin * Only set prompt if build_become_command was called * Add helper to assist connection plugins in knowing they need to wait for a prompt * Fix tests and code expectations * Doc updates * Various additional minor cleanups * Make doas functional * Don't change connection signature, load become plugin from TaskExecutor * Remove unused imports * Add comment about setting the become plugin on the playcontext * Fix up tests for recent changes * Support 'Password:' natively for the doas plugin * Make default prompts raw * wording cleanups. ci_complete * Remove unrelated changes * Address spelling mistake * Restore removed test, and udpate to use new functionality * Add changelog fragment * Don't hard fail in set_attributes_from_cli on missing CLI keys * Remove unrelated change to loader * Remove internal deprecated FieldAttributes now * Emit deprecation warnings now
This commit is contained in:
parent
c581fbd0be
commit
445ff39f94
73 changed files with 1849 additions and 721 deletions
|
@ -24,6 +24,8 @@ __metaclass__ = type
|
|||
from abc import ABCMeta
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.six import with_metaclass, string_types
|
||||
from ansible.utils.display import Display
|
||||
|
||||
|
@ -52,7 +54,10 @@ class AnsiblePlugin(with_metaclass(ABCMeta, object)):
|
|||
|
||||
def get_option(self, option, hostvars=None):
|
||||
if option not in self._options:
|
||||
option_value = C.config.get_config_value(option, plugin_type=get_plugin_class(self), plugin_name=self._load_name, variables=hostvars)
|
||||
try:
|
||||
option_value = C.config.get_config_value(option, plugin_type=get_plugin_class(self), plugin_name=self._load_name, variables=hostvars)
|
||||
except AnsibleError as e:
|
||||
raise KeyError(to_native(e))
|
||||
self.set_option(option, option_value)
|
||||
return self._options.get(option)
|
||||
|
||||
|
|
|
@ -108,6 +108,24 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
|
||||
return result
|
||||
|
||||
def get_plugin_option(self, plugin, option, default=None):
|
||||
"""Helper to get an option from a plugin without having to use
|
||||
the try/except dance everywhere to set a default
|
||||
"""
|
||||
try:
|
||||
return plugin.get_option(option)
|
||||
except (AttributeError, KeyError):
|
||||
return default
|
||||
|
||||
def get_become_option(self, option, default=None):
|
||||
return self.get_plugin_option(self._connection.become, option, default=default)
|
||||
|
||||
def get_connection_option(self, option, default=None):
|
||||
return self.get_plugin_option(self._connection, option, default=default)
|
||||
|
||||
def get_shell_option(self, option, default=None):
|
||||
return self.get_plugin_option(self._connection._shell, option, default=default)
|
||||
|
||||
def _remote_file_exists(self, path):
|
||||
cmd = self._connection._shell.exists(path)
|
||||
result = self._low_level_execute_command(cmd=cmd, sudoable=True)
|
||||
|
@ -241,12 +259,23 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
Returns a list of admin users that are configured for the current shell
|
||||
plugin
|
||||
'''
|
||||
|
||||
return self.get_shell_option('admin_users', ['root'])
|
||||
|
||||
def _get_remote_user(self):
|
||||
''' consistently get the 'remote_user' for the action plugin '''
|
||||
# TODO: use 'current user running ansible' as fallback when moving away from play_context
|
||||
# pwd.getpwuid(os.getuid()).pw_name
|
||||
remote_user = None
|
||||
try:
|
||||
admin_users = self._connection._shell.get_option('admin_users')
|
||||
except AnsibleError:
|
||||
# fallback for old custom plugins w/o get_option
|
||||
admin_users = ['root']
|
||||
return admin_users
|
||||
remote_user = self._connection.get_option('remote_user')
|
||||
except KeyError:
|
||||
# plugin does not have remote_user option, fallback to default and/play_context
|
||||
remote_user = getattr(self._connection, 'default_user', None) or self._play_context.remote_user
|
||||
except AttributeError:
|
||||
# plugin does not use config system, fallback to old play_context
|
||||
remote_user = self._play_context.remote_user
|
||||
return remote_user
|
||||
|
||||
def _is_become_unprivileged(self):
|
||||
'''
|
||||
|
@ -261,11 +290,8 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
# if we use become and the user is not an admin (or same user) then
|
||||
# we need to return become_unprivileged as True
|
||||
admin_users = self._get_admin_users()
|
||||
try:
|
||||
remote_user = self._connection.get_option('remote_user')
|
||||
except AnsibleError:
|
||||
remote_user = self._play_context.remote_user
|
||||
return bool(self._play_context.become_user not in admin_users + [remote_user])
|
||||
remote_user = self._get_remote_user()
|
||||
return bool(self.get_become_option('become_user') not in admin_users + [remote_user])
|
||||
|
||||
def _make_tmp_path(self, remote_user=None):
|
||||
'''
|
||||
|
@ -273,10 +299,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
'''
|
||||
|
||||
become_unprivileged = self._is_become_unprivileged()
|
||||
try:
|
||||
remote_tmp = self._connection._shell.get_option('remote_tmp')
|
||||
except AnsibleError:
|
||||
remote_tmp = '~/.ansible/tmp'
|
||||
remote_tmp = self.get_shell_option('remote_tmp', default='~/.ansible/tmp')
|
||||
|
||||
# deal with tmpdir creation
|
||||
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
|
||||
|
@ -409,7 +432,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
"allow_world_readable_tmpfiles" in the ansible.cfg
|
||||
"""
|
||||
if remote_user is None:
|
||||
remote_user = self._play_context.remote_user
|
||||
remote_user = self._get_remote_user()
|
||||
|
||||
if self._connection._shell.SHELL_FAMILY == 'powershell':
|
||||
# This won't work on Powershell as-is, so we'll just completely skip until
|
||||
|
@ -432,7 +455,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
# start to we'll have to fix this.
|
||||
setfacl_mode = 'r-X'
|
||||
|
||||
res = self._remote_set_user_facl(remote_paths, self._play_context.become_user, setfacl_mode)
|
||||
res = self._remote_set_user_facl(remote_paths, self.get_become_option('become_user'), setfacl_mode)
|
||||
if res['rc'] != 0:
|
||||
# File system acls failed; let's try to use chown next
|
||||
# Set executable bit first as on some systems an
|
||||
|
@ -442,7 +465,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
if res['rc'] != 0:
|
||||
raise AnsibleError('Failed to set file mode on remote temporary files (rc: {0}, err: {1})'.format(res['rc'], to_native(res['stderr'])))
|
||||
|
||||
res = self._remote_chown(remote_paths, self._play_context.become_user)
|
||||
res = self._remote_chown(remote_paths, self.get_become_option('become_user'))
|
||||
if res['rc'] != 0 and remote_user in self._get_admin_users():
|
||||
# chown failed even if remote_user is administrator/root
|
||||
raise AnsibleError('Failed to change ownership of the temporary files Ansible needs to create despite connecting as a privileged user. '
|
||||
|
@ -579,13 +602,14 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
# Network connection plugins (network_cli, netconf, etc.) execute on the controller, rather than the remote host.
|
||||
# As such, we want to avoid using remote_user for paths as remote_user may not line up with the local user
|
||||
# This is a hack and should be solved by more intelligent handling of remote_tmp in 2.7
|
||||
become_user = self.get_become_option('become_user')
|
||||
if getattr(self._connection, '_remote_is_local', False):
|
||||
pass
|
||||
elif sudoable and self._play_context.become and self._play_context.become_user:
|
||||
expand_path = '~%s' % self._play_context.become_user
|
||||
elif sudoable and self._play_context.become and become_user:
|
||||
expand_path = '~%s' % become_user
|
||||
else:
|
||||
# use remote user instead, if none set default to current user
|
||||
expand_path = '~%s' % (self._play_context.remote_user or self._connection.default_user or '')
|
||||
expand_path = '~%s' % (self._get_remote_user() or '')
|
||||
|
||||
# use shell to construct appropriate command and execute
|
||||
cmd = self._connection._shell.expand_user(expand_path)
|
||||
|
@ -673,26 +697,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
module_args['_ansible_tmpdir'] = self._connection._shell.tmpdir
|
||||
|
||||
# make sure the remote_tmp value is sent through in case modules needs to create their own
|
||||
try:
|
||||
module_args['_ansible_remote_tmp'] = self._connection._shell.get_option('remote_tmp')
|
||||
except KeyError:
|
||||
# here for 3rd party shell plugin compatibility in case they do not define the remote_tmp option
|
||||
module_args['_ansible_remote_tmp'] = '~/.ansible/tmp'
|
||||
|
||||
def _update_connection_options(self, options, variables=None):
|
||||
''' ensures connections have the appropriate information '''
|
||||
update = {}
|
||||
|
||||
if getattr(self.connection, 'glob_option_vars', False):
|
||||
# if the connection allows for it, pass any variables matching it.
|
||||
if variables is not None:
|
||||
for varname in variables:
|
||||
if varname.match('ansible_%s_' % self.connection._load_name):
|
||||
update[varname] = variables[varname]
|
||||
|
||||
# always override existing with options
|
||||
update.update(options)
|
||||
self.connection.set_options(update)
|
||||
module_args['_ansible_remote_tmp'] = self.get_shell_option('remote_tmp', default='~/.ansible/tmp')
|
||||
|
||||
def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=None, wrap_async=False):
|
||||
'''
|
||||
|
@ -748,11 +753,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
# ANSIBLE_ASYNC_DIR is not set on the task, we get the value
|
||||
# from the shell option and temporarily add to the environment
|
||||
# list for async_wrapper to pick up
|
||||
try:
|
||||
async_dir = self._connection._shell.get_option('async_dir')
|
||||
except KeyError:
|
||||
# in case 3rd party plugin has not set this, use the default
|
||||
async_dir = "~/.ansible_async"
|
||||
async_dir = self.get_shell_option('async_dir', default="~/.ansible_async")
|
||||
remove_async_dir = len(self._task.environment)
|
||||
self._task.environment.append({"ANSIBLE_ASYNC_DIR": async_dir})
|
||||
|
||||
|
@ -861,7 +862,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
if remote_files:
|
||||
# remove none/empty
|
||||
remote_files = [x for x in remote_files if x]
|
||||
self._fixup_perms2(remote_files, self._play_context.remote_user)
|
||||
self._fixup_perms2(remote_files, self._get_remote_user())
|
||||
|
||||
# actually execute
|
||||
res = self._low_level_execute_command(cmd, sudoable=sudoable, in_data=in_data)
|
||||
|
@ -934,6 +935,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
data['rc'] = res['rc']
|
||||
return data
|
||||
|
||||
# FIXME: move to connection base
|
||||
def _low_level_execute_command(self, cmd, sudoable=True, in_data=None, executable=None, encoding_errors='surrogate_then_replace', chdir=None):
|
||||
'''
|
||||
This is the function which executes the low level shell command, which
|
||||
|
@ -951,21 +953,20 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
'''
|
||||
|
||||
display.debug("_low_level_execute_command(): starting")
|
||||
# if not cmd:
|
||||
# # this can happen with powershell modules when there is no analog to a Windows command (like chmod)
|
||||
# display.debug("_low_level_execute_command(): no command, exiting")
|
||||
# return dict(stdout='', stderr='', rc=254)
|
||||
# if not cmd:
|
||||
# # this can happen with powershell modules when there is no analog to a Windows command (like chmod)
|
||||
# display.debug("_low_level_execute_command(): no command, exiting")
|
||||
# return dict(stdout='', stderr='', rc=254)
|
||||
|
||||
if chdir:
|
||||
display.debug("_low_level_execute_command(): changing cwd to %s for this command" % chdir)
|
||||
cmd = self._connection._shell.append_command('cd %s' % chdir, cmd)
|
||||
|
||||
allow_same_user = C.BECOME_ALLOW_SAME_USER
|
||||
same_user = self._play_context.become_user == self._play_context.remote_user
|
||||
if sudoable and self._play_context.become and (allow_same_user or not same_user):
|
||||
if (sudoable and self._connection.transport != 'network_cli' and self._connection.become and
|
||||
(C.BECOME_ALLOW_SAME_USER or
|
||||
self.get_become_option('become_user') != self._get_remote_user())):
|
||||
display.debug("_low_level_execute_command(): using become for this command")
|
||||
if self._connection.transport != 'network_cli' and self._play_context.become_method != 'enable':
|
||||
cmd = self._play_context.make_become_cmd(cmd, executable=executable)
|
||||
cmd = self._connection.become.build_become_command(cmd, self._connection._shell)
|
||||
|
||||
if self._connection.allow_executable:
|
||||
if executable is None:
|
||||
|
|
|
@ -37,12 +37,7 @@ class ActionModule(ActionBase):
|
|||
else:
|
||||
# inject the async directory based on the shell option into the
|
||||
# module args
|
||||
try:
|
||||
async_dir = self._connection._shell.get_option('async_dir')
|
||||
except KeyError:
|
||||
# here for 3rd party shell plugin compatibility in case they do
|
||||
# not define the async_dir option
|
||||
async_dir = "~/.ansible_async"
|
||||
async_dir = self.get_shell_option('async_dir', default="~/.ansible_async")
|
||||
|
||||
module_args = dict(jid=jid, mode=mode, _async_dir=async_dir)
|
||||
status = self._execute_module(task_vars=task_vars,
|
||||
|
|
|
@ -307,7 +307,7 @@ class ActionModule(ActionBase):
|
|||
# Get the connect_timeout set on the connection to compare to the original
|
||||
try:
|
||||
connect_timeout = self._connection.get_option('connection_timeout')
|
||||
except AnsibleError:
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
if original_connection_timeout != connect_timeout:
|
||||
|
@ -380,7 +380,7 @@ class ActionModule(ActionBase):
|
|||
try:
|
||||
original_connection_timeout = self._connection.get_option('connection_timeout')
|
||||
display.debug("{action}: saving original connect_timeout of {timeout}".format(action=self._task.action, timeout=original_connection_timeout))
|
||||
except AnsibleError:
|
||||
except KeyError:
|
||||
display.debug("{action}: connect_timeout connection option has not been set".format(action=self._task.action))
|
||||
# Initiate reboot
|
||||
reboot_result = self.perform_reboot(task_vars, distribution)
|
||||
|
|
89
lib/ansible/plugins/become/__init__.py
Normal file
89
lib/ansible/plugins/become/__init__.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from abc import abstractmethod
|
||||
from random import choice
|
||||
from string import ascii_lowercase
|
||||
from gettext import dgettext
|
||||
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.plugins import AnsiblePlugin
|
||||
|
||||
|
||||
def _gen_id(length=32):
|
||||
''' return random string used to identify the current privelege escalation '''
|
||||
return ''.join(choice(ascii_lowercase) for x in range(length))
|
||||
|
||||
|
||||
class BecomeBase(AnsiblePlugin):
|
||||
|
||||
name = None
|
||||
|
||||
# messages for detecting prompted password issues
|
||||
fail = tuple()
|
||||
missing = tuple()
|
||||
|
||||
# many connection plugins cannot provide tty, set to True if your become
|
||||
# plugin requires a tty, i.e su
|
||||
require_tty = False
|
||||
|
||||
# prompt to match
|
||||
prompt = ''
|
||||
|
||||
def __init__(self):
|
||||
super(BecomeBase, self).__init__()
|
||||
self._id = ''
|
||||
self.success = ''
|
||||
|
||||
def expect_prompt(self):
|
||||
"""This function assists connection plugins in determining if they need to wait for
|
||||
a prompt. Both a prompt and a password are required.
|
||||
"""
|
||||
return self.prompt and self.get_option('become_pass')
|
||||
|
||||
def _build_success_command(self, cmd, shell, noexe=False):
|
||||
if not all((cmd, shell, self.success)):
|
||||
return cmd
|
||||
|
||||
cmd = shlex_quote('%s %s %s %s' % (shell.ECHO, self.success, shell.COMMAND_SEP, cmd))
|
||||
exe = getattr(shell, 'executable', None)
|
||||
if exe and not noexe:
|
||||
cmd = '%s -c %s' % (exe, cmd)
|
||||
return cmd
|
||||
|
||||
@abstractmethod
|
||||
def build_become_command(self, cmd, shell):
|
||||
self._id = _gen_id()
|
||||
self.success = 'BECOME-SUCCESS-%s' % self._id
|
||||
|
||||
def check_success(self, b_output):
|
||||
b_success = to_bytes(self.success)
|
||||
return any(b_success in l.rstrip() for l in b_output.splitlines(True))
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
''' checks if the expected passwod prompt exists in b_output '''
|
||||
if self.prompt:
|
||||
b_prompt = to_bytes(self.prompt).strip()
|
||||
return any(l.strip().startswith(b_prompt) for l in b_output.splitlines())
|
||||
return False
|
||||
|
||||
def _check_password_error(self, b_out, msg):
|
||||
''' returns True/False if domain specific i18n version of msg is found in b_out '''
|
||||
b_fail = to_bytes(dgettext(self.name, msg))
|
||||
return b_fail and b_fail in b_out
|
||||
|
||||
def check_incorrect_password(self, b_output):
|
||||
for errstring in self.fail:
|
||||
if self._check_password_error(b_output, errstring):
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_missing_password(self, b_output):
|
||||
for errstring in self.missing:
|
||||
if self._check_password_error(b_output, errstring):
|
||||
return True
|
||||
return False
|
128
lib/ansible/plugins/become/doas.py
Normal file
128
lib/ansible/plugins/become/doas.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: doas
|
||||
short_description: Do As user
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the doas utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: doas_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_doas_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_DOAS_USER
|
||||
become_exe:
|
||||
description: Doas executable
|
||||
default: doas
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: doas_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_doas_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_DOAS_EXE
|
||||
become_flags:
|
||||
description: Options to pass to doas
|
||||
default:
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: doas_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_doas_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_DOAS_FLAGS
|
||||
become_pass:
|
||||
description: password for doas prompt
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_doas_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_DOAS_PASS
|
||||
ini:
|
||||
- section: doas_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
description:
|
||||
- List of localized strings to match for prompt detection
|
||||
- If empty we'll use the built in one
|
||||
default: []
|
||||
ini:
|
||||
- section: doas_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_doas_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_DOAS_PROMPT_L10N
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'doas'
|
||||
|
||||
# messages for detecting prompted password issues
|
||||
fail = ('Permission denied',)
|
||||
missing = ('Authorization required',)
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
''' checks if the expected passwod prompt exists in b_output '''
|
||||
|
||||
# FIXME: more accurate would be: 'doas (%s@' % remote_user
|
||||
# however become plugins don't have that information currently
|
||||
b_prompts = [to_bytes(p) for p in self.get_option('prompt_l10n')] or [br'doas \(', br'Password:']
|
||||
b_prompt = b"|".join(b_prompts)
|
||||
|
||||
return bool(re.match(b_prompt, b_output))
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
self.prompt = True
|
||||
|
||||
become_exe = self.get_option('become_exe') or self.name
|
||||
|
||||
flags = self.get_option('become_flags') or ''
|
||||
if not self.get_option('become_pass') and '-n' not in flags:
|
||||
flags += ' -n'
|
||||
|
||||
user = self.get_option('become_user') or ''
|
||||
if user:
|
||||
user = '-u %s' % (user)
|
||||
|
||||
success_cmd = self._build_success_command(cmd, shell, noexe=True)
|
||||
executable = getattr(shell, 'executable', shell.SHELL_FAMILY)
|
||||
|
||||
return '%s %s %s %s -c %s' % (become_exe, flags, user, executable, success_cmd)
|
97
lib/ansible/plugins/become/dzdo.py
Normal file
97
lib/ansible/plugins/become/dzdo.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: dzdo
|
||||
short_description: Centrify's Direct Authorize
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the dzdo utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: dzdo_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_dzdo_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_DZDO_USER
|
||||
become_exe:
|
||||
description: Sudo executable
|
||||
default: dzdo
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: dzdo_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_dzdo_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_DZDO_EXE
|
||||
become_flags:
|
||||
description: Options to pass to dzdo
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: dzdo_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_dzdo_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_DZDO_FLAGS
|
||||
become_pass:
|
||||
description: Options to pass to dzdo
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_dzdo_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_DZDO_PASS
|
||||
ini:
|
||||
- section: dzdo_become_plugin
|
||||
key: password
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'dzdo'
|
||||
|
||||
# messages for detecting prompted password issues
|
||||
fail = ('Sorry, try again.',)
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
becomecmd = self.get_option('become_exe') or self.name
|
||||
|
||||
flags = self.get_option('become_flags') or ''
|
||||
if self.get_option('become_pass'):
|
||||
self._prompt = '[dzdo via ansible, key=%s] password:' % self._id
|
||||
flags = '%s -p "%s"' % (flags.replace('-n', ''), self._prompt)
|
||||
|
||||
user = self.get_option('become_user') or ''
|
||||
if user:
|
||||
user = '-u %s' % (user)
|
||||
|
||||
return ' '.join([becomecmd, flags, user, self._build_success_command(cmd, shell)])
|
120
lib/ansible/plugins/become/ksu.py
Normal file
120
lib/ansible/plugins/become/ksu.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: ksu
|
||||
short_description: Kerberos substitute user
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the ksu utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: ksu_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_ksu_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_KSU_USER
|
||||
required: True
|
||||
become_exe:
|
||||
description: Su executable
|
||||
default: ksu
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: ksu_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_ksu_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_KSU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to ksu
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: ksu_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_ksu_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_KSU_FLAGS
|
||||
become_pass:
|
||||
description: ksu password
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_ksu_pass
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_become_password
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_KSU_PASS
|
||||
ini:
|
||||
- section: ksu_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
description:
|
||||
- List of localized strings to match for prompt detection
|
||||
- If empty we'll use the built in one
|
||||
default: []
|
||||
ini:
|
||||
- section: ksu_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_ksu_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_KSU_PROMPT_L10N
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'ksu'
|
||||
|
||||
# messages for detecting prompted password issues
|
||||
fail = ('Password incorrect',)
|
||||
missing = ('No password given',)
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
''' checks if the expected passwod prompt exists in b_output '''
|
||||
|
||||
prompts = self.get_option('prompt_l10n') or ["Kerberos password for .*@.*:"]
|
||||
b_prompt = b"|".join(to_bytes(p) for p in prompts)
|
||||
|
||||
return bool(re.match(b_prompt, b_output))
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
# Prompt handling for ``ksu`` is more complicated, this
|
||||
# is used to satisfy the connection plugin
|
||||
self.prompt = True
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
exe = self.get_option('become_exe') or self.name
|
||||
flags = self.get_option('become_flags') or ''
|
||||
user = self.get_option('become_user') or ''
|
||||
return '%s %s %s -e %s ' % (exe, user, flags, self._build_success_command(cmd, shell))
|
87
lib/ansible/plugins/become/machinectl.py
Normal file
87
lib/ansible/plugins/become/machinectl.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: machinectl
|
||||
short_description: Systemd's machinectl privilege escalation
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the machinectl utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: machinectl_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_machinectl_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_MACHINECTL_USER
|
||||
become_exe:
|
||||
description: Machinectl executable
|
||||
default: machinectl
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: machinectl_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_machinectl_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_MACHINECTL_EXE
|
||||
become_flags:
|
||||
description: Options to pass to machinectl
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: machinectl_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_machinectl_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_MACHINECTL_FLAGS
|
||||
become_pass:
|
||||
description: Password for machinectl
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_machinectl_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_MACHINECTL_PASS
|
||||
ini:
|
||||
- section: machinectl_become_plugin
|
||||
key: password
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'machinectl'
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
become = self._get_option('become_exe') or self.name
|
||||
flags = self.get_option('flags') or ''
|
||||
user = self.get_option('become_user') or ''
|
||||
return '%s shell -q %s %s@ %s' % (become, flags, user, cmd)
|
103
lib/ansible/plugins/become/pbrun.py
Normal file
103
lib/ansible/plugins/become/pbrun.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: pbrun
|
||||
short_description: PowerBroker run
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the pbrun utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: pbrun_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_pbrun_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_PBRUN_USER
|
||||
become_exe:
|
||||
description: Sudo executable
|
||||
default: pbrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pbrun_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pbrun_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PBRUN_EXE
|
||||
become_flags:
|
||||
description: Options to pass to pbrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pbrun_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pbrun_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PBRUN_FLAGS
|
||||
become_pass:
|
||||
description: Password for pbrun
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pbrun_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PBRUN_PASS
|
||||
ini:
|
||||
- section: pbrun_become_plugin
|
||||
key: password
|
||||
wrap_exe:
|
||||
description: Toggle to wrap the command pbrun calls in 'shell -c' or not
|
||||
default: False
|
||||
type: bool
|
||||
ini:
|
||||
- section: pbrun_become_plugin
|
||||
key: wrap_execution
|
||||
vars:
|
||||
- name: ansible_pbrun_wrap_execution
|
||||
env:
|
||||
- name: ANSIBLE_PBRUN_WRAP_EXECUTION
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'pbrun'
|
||||
|
||||
prompt = 'Password:'
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
become_exe = self.get_option('become_exe') or self.name
|
||||
flags = self.get_option('become_flags') or ''
|
||||
user = self.get_option('become_user')
|
||||
if user:
|
||||
user = '-u %s' % (user)
|
||||
noexe = not self.get_option('wrap_exe')
|
||||
|
||||
return ' '.join([become_exe, flags, user, self._build_success_command(cmd, shell, noexe=noexe)])
|
103
lib/ansible/plugins/become/pfexec.py
Normal file
103
lib/ansible/plugins/become/pfexec.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: pfexec
|
||||
short_description: profile based execution
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the pfexec utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description:
|
||||
- User you 'become' to execute the task
|
||||
- This plugin ignores this settingas pfexec uses it's own ``exec_attr`` to figure this out,
|
||||
but it is supplied here for Ansible to make decisions needed for the task execution, like file permissions.
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: pfexec_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_pfexec_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_PFEXEC_USER
|
||||
become_exe:
|
||||
description: Sudo executable
|
||||
default: pfexec
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pfexec_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pfexec_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PFEXEC_EXE
|
||||
become_flags:
|
||||
description: Options to pass to pfexec
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pfexec_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pfexec_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PFEXEC_FLAGS
|
||||
become_pass:
|
||||
description: pfexec password
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pfexec_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PFEXEC_PASS
|
||||
ini:
|
||||
- section: pfexec_become_plugin
|
||||
key: password
|
||||
wrap_exe:
|
||||
description: Toggle to wrap the command pfexec calls in 'shell -c' or not
|
||||
default: False
|
||||
type: bool
|
||||
ini:
|
||||
- section: pfexec_become_plugin
|
||||
key: wrap_execution
|
||||
vars:
|
||||
- name: ansible_pfexec_wrap_execution
|
||||
env:
|
||||
note:
|
||||
- This plugin ignores ``become_user`` as pfexec uses it's own ``exec_attr`` to figure this out.
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'pfexec'
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
exe = self.get_option('become_exe') or self.name
|
||||
flags = self.get_option('become_flags')
|
||||
noexe = not self.get_option('wrap_exe')
|
||||
return '%s %s "%s"' % (exe, flags, self._build_success_command(cmd, shell, noexe=noexe))
|
75
lib/ansible/plugins/become/pmrun.py
Normal file
75
lib/ansible/plugins/become/pmrun.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: pmrun
|
||||
short_description: Privilege Manager run
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the pmrun utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_exe:
|
||||
description: Sudo executable
|
||||
default: pmrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: pmrun_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_pmrun_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_PMRUN_EXE
|
||||
become_flags:
|
||||
description: Options to pass to pmrun
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: pmrun_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_pmrun_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_PMRUN_FLAGS
|
||||
become_pass:
|
||||
description: pmrun password
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_pmrun_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_PMRUN_PASS
|
||||
ini:
|
||||
- section: pmrun_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- This plugin ignores the become_user supplied and uses pmrun's own configuration to select the user.
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'pmrun'
|
||||
prompt = 'Enter UPM user password:'
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
become = self.get_option('become_exe') or self.name
|
||||
flags = self.get_option('become_flags') or ''
|
||||
return '%s %s %s' % (become, flags, self._build_success_command(cmd, shell))
|
69
lib/ansible/plugins/become/runas.py
Normal file
69
lib/ansible/plugins/become/runas.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: runas
|
||||
short_description: Run As user
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the windows runas facility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: runas_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_runas_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_RUNAS_USER
|
||||
required: True
|
||||
become_flags:
|
||||
description: Options to pass to runas, a space delimited list of k=v pairs
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: runas_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_runas_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_RUNAS_FLAGS
|
||||
become_pass:
|
||||
description: password
|
||||
ini:
|
||||
- section: runas_become_plugin
|
||||
key: password
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_runas_runas
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_RUNAS_PASS
|
||||
notes:
|
||||
- runas is really implemented in the powershell module handler and as such can only be used with winrm connections.
|
||||
- This plugin ignores the 'become_exe' setting as it uses an API and not an executable.
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'runas'
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
# runas is implemented inside the winrm connection plugin
|
||||
return cmd
|
90
lib/ansible/plugins/become/sesu.py
Normal file
90
lib/ansible/plugins/become/sesu.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: sesu
|
||||
short_description: CA Privilged Access Manager
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the sesu utility.
|
||||
author: ansible (@nekonyuu)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: sesu_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_sesu_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SESU_USER
|
||||
become_exe:
|
||||
description: sesu executable
|
||||
default: sesu
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: sesu_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_sesu_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_SESU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to sesu
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: sesu_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_sesu_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SESU_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to sesu
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_sesu_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SESU_PASS
|
||||
ini:
|
||||
- section: sesu_become_plugin
|
||||
key: password
|
||||
"""
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'sesu'
|
||||
|
||||
_prompt = 'Please enter your password:'
|
||||
fail = missing = ('Sorry, try again with sesu.',)
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
become = self.get_option('become_exe') or self.name
|
||||
flags = self.get_option('become_flags') or ''
|
||||
user = self.get_option('become_user') or ''
|
||||
return '%s %s %s -c %s' % (become, flags, user, self._build_success_command(cmd, shell))
|
158
lib/ansible/plugins/become/su.py
Normal file
158
lib/ansible/plugins/become/su.py
Normal file
|
@ -0,0 +1,158 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: su
|
||||
short_description: Substitute User
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the su utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: su_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_su_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SU_USER
|
||||
become_exe:
|
||||
description: Su executable
|
||||
default: su
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: su_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_su_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_SU_EXE
|
||||
become_flags:
|
||||
description: Options to pass to su
|
||||
default: ''
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: su_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_su_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SU_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to su
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_su_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SU_PASS
|
||||
ini:
|
||||
- section: su_become_plugin
|
||||
key: password
|
||||
prompt_l10n:
|
||||
description:
|
||||
- List of localized strings to match for prompt detection
|
||||
- If empty we'll use the built in one
|
||||
default: []
|
||||
ini:
|
||||
- section: su_become_plugin
|
||||
key: localized_prompts
|
||||
vars:
|
||||
- name: ansible_su_prompt_l10n
|
||||
env:
|
||||
- name: ANSIBLE_SU_PROMPT_L10N
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'su'
|
||||
|
||||
# messages for detecting prompted password issues
|
||||
fail = ('Authentication failure',)
|
||||
|
||||
SU_PROMPT_LOCALIZATIONS = [
|
||||
'Password',
|
||||
'암호',
|
||||
'パスワード',
|
||||
'Adgangskode',
|
||||
'Contraseña',
|
||||
'Contrasenya',
|
||||
'Hasło',
|
||||
'Heslo',
|
||||
'Jelszó',
|
||||
'Lösenord',
|
||||
'Mật khẩu',
|
||||
'Mot de passe',
|
||||
'Parola',
|
||||
'Parool',
|
||||
'Pasahitza',
|
||||
'Passord',
|
||||
'Passwort',
|
||||
'Salasana',
|
||||
'Sandi',
|
||||
'Senha',
|
||||
'Wachtwoord',
|
||||
'ססמה',
|
||||
'Лозинка',
|
||||
'Парола',
|
||||
'Пароль',
|
||||
'गुप्तशब्द',
|
||||
'शब्दकूट',
|
||||
'సంకేతపదము',
|
||||
'හස්පදය',
|
||||
'密码',
|
||||
'密碼',
|
||||
'口令',
|
||||
]
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
''' checks if the expected passwod prompt exists in b_output '''
|
||||
|
||||
prompts = self.get_option('prompt_l10n') or self.SU_PROMPT_LOCALIZATIONS
|
||||
b_password_string = b"|".join((br'(\w+\'s )?' + to_bytes(p)) for p in prompts)
|
||||
# Colon or unicode fullwidth colon
|
||||
b_password_string = b_password_string + to_bytes(u' ?(:|:) ?')
|
||||
b_su_prompt_localizations_re = re.compile(b_password_string, flags=re.IGNORECASE)
|
||||
return bool(b_su_prompt_localizations_re.match(b_output))
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
# Prompt handling for ``su`` is more complicated, this
|
||||
# is used to satisfy the connection plugin
|
||||
self.prompt = True
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
exe = self.get_option('become_exe') or self.name
|
||||
flags = self.get_option('become_flags') or ''
|
||||
user = self.get_option('become_user') or ''
|
||||
success_cmd = self._build_success_command(cmd, shell)
|
||||
|
||||
return "%s %s %s -c %s" % (exe, flags, user, shlex_quote(success_cmd))
|
104
lib/ansible/plugins/become/sudo.py
Normal file
104
lib/ansible/plugins/become/sudo.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
become: sudo
|
||||
short_description: Substitute User DO
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the sudo utility.
|
||||
author: ansible (@core)
|
||||
version_added: "2.8"
|
||||
options:
|
||||
become_user:
|
||||
description: User you 'become' to execute the task
|
||||
default: root
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_user
|
||||
- section: sudo_become_plugin
|
||||
key: user
|
||||
vars:
|
||||
- name: ansible_become_user
|
||||
- name: ansible_sudo_user
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_USER
|
||||
- name: ANSIBLE_SUDO_USER
|
||||
become_exe:
|
||||
description: Sudo executable
|
||||
default: sudo
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_exe
|
||||
- section: sudo_become_plugin
|
||||
key: executable
|
||||
vars:
|
||||
- name: ansible_become_exe
|
||||
- name: ansible_sudo_exe
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_EXE
|
||||
- name: ANSIBLE_SUDO_EXE
|
||||
become_flags:
|
||||
description: Options to pass to sudo
|
||||
default: -H -S -n
|
||||
ini:
|
||||
- section: privilege_escalation
|
||||
key: become_flags
|
||||
- section: sudo_become_plugin
|
||||
key: flags
|
||||
vars:
|
||||
- name: ansible_become_flags
|
||||
- name: ansible_sudo_flags
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_FLAGS
|
||||
- name: ANSIBLE_SUDO_FLAGS
|
||||
become_pass:
|
||||
description: Password to pass to sudo
|
||||
required: False
|
||||
vars:
|
||||
- name: ansible_become_password
|
||||
- name: ansible_become_pass
|
||||
- name: ansible_sudo_pass
|
||||
env:
|
||||
- name: ANSIBLE_BECOME_PASS
|
||||
- name: ANSIBLE_SUDO_PASS
|
||||
ini:
|
||||
- section: sudo_become_plugin
|
||||
key: password
|
||||
"""
|
||||
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'sudo'
|
||||
|
||||
# messages for detecting prompted password issues
|
||||
fail = ('Sorry, try again.',)
|
||||
missing = ('Sorry, a password is required to run sudo', 'sudo: a password is required')
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
if not cmd:
|
||||
return cmd
|
||||
|
||||
becomecmd = self.get_option('become_exe') or self.name
|
||||
|
||||
flags = self.get_option('become_flags') or ''
|
||||
prompt = ''
|
||||
if self.get_option('become_pass'):
|
||||
self.prompt = '[sudo via ansible, key=%s] password:' % self._id
|
||||
if flags: # this could be simplified, but kept as is for now for backwards string matching
|
||||
flags = flags.replace('-n', '')
|
||||
prompt = '-p "%s"' % (self.prompt)
|
||||
|
||||
user = self.get_option('become_user') or ''
|
||||
if user:
|
||||
user = '-u %s' % (user)
|
||||
|
||||
return ' '.join([becomecmd, flags, prompt, user, self._build_success_command(cmd, shell)])
|
|
@ -6,19 +6,16 @@ from __future__ import (absolute_import, division, print_function)
|
|||
__metaclass__ = type
|
||||
|
||||
import fcntl
|
||||
import gettext
|
||||
import os
|
||||
import shlex
|
||||
from abc import abstractmethod, abstractproperty
|
||||
from functools import wraps
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.plugins import AnsiblePlugin
|
||||
from ansible.plugins.loader import shell_loader, connection_loader
|
||||
from ansible.utils.display import Display
|
||||
from ansible.plugins.loader import connection_loader, get_shell_plugin
|
||||
from ansible.utils.path import unfrackpath
|
||||
|
||||
display = Display()
|
||||
|
@ -46,7 +43,7 @@ class ConnectionBase(AnsiblePlugin):
|
|||
has_pipelining = False
|
||||
has_native_async = False # eg, winrm
|
||||
always_pipeline_modules = False # eg, winrm
|
||||
become_methods = C.BECOME_METHODS
|
||||
has_tty = True # for interacting with become plugins
|
||||
# When running over this connection type, prefer modules written in a certain language
|
||||
# as discovered by the specified file extension. An empty string as the
|
||||
# language means any language.
|
||||
|
@ -66,11 +63,12 @@ class ConnectionBase(AnsiblePlugin):
|
|||
|
||||
# All these hasattrs allow subclasses to override these parameters
|
||||
if not hasattr(self, '_play_context'):
|
||||
# Backwards compat: self._play_context isn't really needed, using set_options/get_option
|
||||
self._play_context = play_context
|
||||
if not hasattr(self, '_new_stdin'):
|
||||
self._new_stdin = new_stdin
|
||||
# Backwards compat: self._display isn't really needed, just import the global display and use that.
|
||||
if not hasattr(self, '_display'):
|
||||
# Backwards compat: self._display isn't really needed, just import the global display and use that.
|
||||
self._display = display
|
||||
if not hasattr(self, '_connected'):
|
||||
self._connected = False
|
||||
|
@ -80,30 +78,17 @@ class ConnectionBase(AnsiblePlugin):
|
|||
self._connected = False
|
||||
self._socket_path = None
|
||||
|
||||
if shell is not None:
|
||||
self._shell = shell
|
||||
# helper plugins
|
||||
self._shell = shell
|
||||
|
||||
# load the shell plugin for this action/connection
|
||||
if play_context.shell:
|
||||
shell_type = play_context.shell
|
||||
elif hasattr(self, '_shell_type'):
|
||||
shell_type = getattr(self, '_shell_type')
|
||||
else:
|
||||
shell_type = 'sh'
|
||||
shell_filename = os.path.basename(self._play_context.executable)
|
||||
try:
|
||||
shell = shell_loader.get(shell_filename)
|
||||
except Exception:
|
||||
shell = None
|
||||
if shell is None:
|
||||
for shell in shell_loader.all():
|
||||
if shell_filename in shell.COMPATIBLE_SHELLS:
|
||||
break
|
||||
shell_type = shell.SHELL_FAMILY
|
||||
|
||||
self._shell = shell_loader.get(shell_type)
|
||||
# we always must have shell
|
||||
if not self._shell:
|
||||
raise AnsibleError("Invalid shell type specified (%s), or the plugin for that shell type is missing." % shell_type)
|
||||
self._shell = get_shell_plugin(shell_type=getattr(self, '_shell_type', None), executable=self._play_context.executable)
|
||||
|
||||
self.become = None
|
||||
|
||||
def set_become_plugin(self, plugin):
|
||||
self.become = plugin
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
|
@ -115,14 +100,6 @@ class ConnectionBase(AnsiblePlugin):
|
|||
'''Read-only property holding the connection socket path for this remote host'''
|
||||
return self._socket_path
|
||||
|
||||
def _become_method_supported(self):
|
||||
''' Checks if the current class supports this privilege escalation method '''
|
||||
|
||||
if self._play_context.become_method in self.become_methods:
|
||||
return True
|
||||
|
||||
raise AnsibleError("Internal Error: this connection module does not support running commands via %s" % self._play_context.become_method)
|
||||
|
||||
@staticmethod
|
||||
def _split_ssh_args(argstring):
|
||||
"""
|
||||
|
@ -151,10 +128,6 @@ class ConnectionBase(AnsiblePlugin):
|
|||
def _connect(self):
|
||||
"""Connect to the host we've been initialized with"""
|
||||
|
||||
# Check if PE is supported
|
||||
if self._play_context.become:
|
||||
self._become_method_supported()
|
||||
|
||||
@ensure_connect
|
||||
@abstractmethod
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
|
@ -240,31 +213,6 @@ class ConnectionBase(AnsiblePlugin):
|
|||
"""Terminate the connection"""
|
||||
pass
|
||||
|
||||
def check_become_success(self, b_output):
|
||||
b_success_key = to_bytes(self._play_context.success_key)
|
||||
for b_line in b_output.splitlines(True):
|
||||
if b_success_key == b_line.rstrip():
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
if self._play_context.prompt is None:
|
||||
return False
|
||||
elif isinstance(self._play_context.prompt, string_types):
|
||||
b_prompt = to_bytes(self._play_context.prompt).strip()
|
||||
b_lines = b_output.splitlines()
|
||||
return any(l.strip().startswith(b_prompt) for l in b_lines)
|
||||
else:
|
||||
return self._play_context.prompt(b_output)
|
||||
|
||||
def check_incorrect_password(self, b_output):
|
||||
b_incorrect_password = to_bytes(gettext.dgettext(self._play_context.become_method, C.BECOME_ERROR_STRINGS[self._play_context.become_method]))
|
||||
return b_incorrect_password and b_incorrect_password in b_output
|
||||
|
||||
def check_missing_password(self, b_output):
|
||||
b_missing_password = to_bytes(gettext.dgettext(self._play_context.become_method, C.BECOME_MISSING_STRINGS[self._play_context.become_method]))
|
||||
return b_missing_password and b_missing_password in b_output
|
||||
|
||||
def connection_lock(self):
|
||||
f = self._play_context.connection_lockfd
|
||||
display.vvvv('CONNECTION: pid %d waiting for lock on %d' % (os.getpid(), f), host=self._play_context.remote_addr)
|
||||
|
@ -279,6 +227,39 @@ class ConnectionBase(AnsiblePlugin):
|
|||
def reset(self):
|
||||
display.warning("Reset is not implemented for this connection")
|
||||
|
||||
# NOTE: these password functions are all become specific, the name is
|
||||
# confusing as it does not handle 'protocol passwords'
|
||||
# DEPRECATED:
|
||||
# These are kept for backwards compatiblity
|
||||
# Use the methods provided by the become plugins instead
|
||||
def check_become_success(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_become_success is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
)
|
||||
return self.become.check_success(b_output)
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_password_prompt is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
)
|
||||
return self.become.check_password_prompt(b_output)
|
||||
|
||||
def check_incorrect_password(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_incorrect_password is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
)
|
||||
return self.become.check_incorrect_password(b_output)
|
||||
|
||||
def check_missing_password(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_missing_password is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
)
|
||||
return self.become.check_missing_password(b_output)
|
||||
|
||||
|
||||
class NetworkConnectionBase(ConnectionBase):
|
||||
"""
|
||||
|
|
|
@ -63,7 +63,6 @@ class Connection(ConnectionBase):
|
|||
# String used to identify this Connection class from other classes
|
||||
transport = 'buildah'
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS)
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
|
|
@ -59,7 +59,7 @@ class Connection(ConnectionBase):
|
|||
# su currently has an undiagnosed issue with calculating the file
|
||||
# checksums (so copy, for instance, doesn't work right)
|
||||
# Have to look into that before re-enabling this
|
||||
become_methods = frozenset(C.BECOME_METHODS).difference(('su',))
|
||||
has_tty = False
|
||||
|
||||
default_user = 'root'
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'docker'
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS)
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
|
|
@ -56,8 +56,7 @@ class Connection(ConnectionBase):
|
|||
# Pipelining may work. Someone needs to test by setting this to True and
|
||||
# having pipelining=True in their ansible.cfg
|
||||
has_pipelining = True
|
||||
|
||||
become_methods = frozenset(C.BECOME_METHODS)
|
||||
has_tty = False
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
|
|
@ -198,7 +198,6 @@ class Connection(ConnectionBase):
|
|||
connection_options = CONNECTION_OPTIONS
|
||||
documentation = DOCUMENTATION
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS)
|
||||
transport_cmd = None
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
|
|
|
@ -49,8 +49,8 @@ class Connection(ConnectionBase):
|
|||
# su currently has an undiagnosed issue with calculating the file
|
||||
# checksums (so copy, for instance, doesn't work right)
|
||||
# Have to look into that before re-enabling this
|
||||
become_methods = frozenset(C.BECOME_METHODS).difference(('su',))
|
||||
default_user = 'root'
|
||||
has_tty = False
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
|
|
@ -83,7 +83,7 @@ class Connection(ConnectionBase):
|
|||
)
|
||||
display.debug("done running command with Popen()")
|
||||
|
||||
if self._play_context.prompt and sudoable:
|
||||
if self.become and self.become.expect_prompt() and sudoable:
|
||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
selector = selectors.DefaultSelector()
|
||||
|
|
|
@ -54,7 +54,6 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'lxc'
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS)
|
||||
default_user = 'root'
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
|
|
|
@ -156,10 +156,8 @@ class Connection(NetworkConnectionBase):
|
|||
|
||||
def _connect(self):
|
||||
if not HAS_NAPALM:
|
||||
raise AnsibleError(
|
||||
'Napalm is required to use the napalm connection type.\n'
|
||||
'Please run pip install napalm'
|
||||
)
|
||||
raise AnsibleError('The "napalm" python library is required to use the napalm connection type.\n')
|
||||
|
||||
super(Connection, self)._connect()
|
||||
|
||||
if not self.connected:
|
||||
|
|
|
@ -274,7 +274,7 @@ class Connection(NetworkConnectionBase):
|
|||
def _connect(self):
|
||||
if not HAS_NCCLIENT:
|
||||
raise AnsibleError(
|
||||
'ncclient is required to use the netconf connection type: %s.\n'
|
||||
'The required "ncclient" python library is required to use the netconf connection type: %s.\n'
|
||||
'Please run pip install ncclient' % to_native(NCCLIENT_IMP_ERR)
|
||||
)
|
||||
|
||||
|
|
|
@ -415,7 +415,7 @@ class Connection(ConnectionBase):
|
|||
|
||||
try:
|
||||
chan.exec_command(cmd)
|
||||
if self._play_context.prompt:
|
||||
if self.become and self.become.expect_prompt():
|
||||
passprompt = False
|
||||
become_sucess = False
|
||||
while not (become_sucess or passprompt):
|
||||
|
|
|
@ -218,7 +218,6 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'psrp'
|
||||
module_implementation_preferences = ('.ps1', '.exe', '')
|
||||
become_methods = ['runas']
|
||||
allow_executable = False
|
||||
has_pipelining = True
|
||||
allow_extras = True
|
||||
|
|
|
@ -61,7 +61,6 @@ class Connection(ConnectionBase):
|
|||
# String used to identify this Connection class from other classes
|
||||
transport = 'qubes'
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS)
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
|
|
@ -443,7 +443,6 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'ssh'
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS).difference(['runas'])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Connection, self).__init__(*args, **kwargs)
|
||||
|
@ -708,11 +707,11 @@ class Connection(ConnectionBase):
|
|||
suppress_output = False
|
||||
|
||||
# display.debug("Examining line (source=%s, state=%s): '%s'" % (source, state, display_line))
|
||||
if self._play_context.prompt and self.check_password_prompt(b_line):
|
||||
if self.become.expect_prompt() and self.check_password_prompt(b_line):
|
||||
display.debug("become_prompt: (source=%s, state=%s): '%s'" % (source, state, display_line))
|
||||
self._flags['become_prompt'] = True
|
||||
suppress_output = True
|
||||
elif self._play_context.success_key and self.check_become_success(b_line):
|
||||
elif self.become.success and self.check_become_success(b_line):
|
||||
display.debug("become_success: (source=%s, state=%s): '%s'" % (source, state, display_line))
|
||||
self._flags['become_success'] = True
|
||||
suppress_output = True
|
||||
|
@ -811,11 +810,12 @@ class Connection(ConnectionBase):
|
|||
|
||||
state = states.index('ready_to_send')
|
||||
if to_bytes(self.get_option('ssh_executable')) in cmd and sudoable:
|
||||
if self._play_context.prompt:
|
||||
prompt = getattr(self.become, 'prompt', None)
|
||||
if prompt:
|
||||
# We're requesting escalation with a password, so we have to
|
||||
# wait for a password prompt.
|
||||
state = states.index('awaiting_prompt')
|
||||
display.debug(u'Initial state: %s: %s' % (states[state], self._play_context.prompt))
|
||||
display.debug(u'Initial state: %s: %s' % (states[state], prompt))
|
||||
elif self._play_context.become and self._play_context.success_key:
|
||||
# We're requesting escalation without a password, so we have to
|
||||
# detect success/failure before sending any initial data.
|
||||
|
|
|
@ -176,7 +176,6 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'winrm'
|
||||
module_implementation_preferences = ('.ps1', '.exe', '')
|
||||
become_methods = ['runas']
|
||||
allow_executable = False
|
||||
has_pipelining = True
|
||||
allow_extras = True
|
||||
|
@ -207,10 +206,6 @@ class Connection(ConnectionBase):
|
|||
self._winrm_user = self.get_option('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._winrm_port = self.get_option('port')
|
||||
|
||||
self._winrm_scheme = self.get_option('scheme')
|
||||
|
|
|
@ -47,7 +47,7 @@ class Connection(ConnectionBase):
|
|||
|
||||
transport = 'zone'
|
||||
has_pipelining = True
|
||||
become_methods = frozenset(C.BECOME_METHODS).difference(('su',))
|
||||
has_tty = False
|
||||
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
|
|
@ -18,13 +18,15 @@ from collections import defaultdict
|
|||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||
from ansible.module_utils._text import to_bytes, to_text, to_native
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.parsing.utils.yaml import from_yaml
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.plugins import get_plugin_class, MODULE_CACHE, PATH_CACHE, PLUGIN_PATH_CACHE
|
||||
from ansible.utils.display import Display
|
||||
from ansible.utils.plugin_docs import add_fragments
|
||||
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
|
@ -32,6 +34,52 @@ def get_all_plugin_loaders():
|
|||
return [(name, obj) for (name, obj) in globals().items() if isinstance(obj, PluginLoader)]
|
||||
|
||||
|
||||
def add_all_plugin_dirs(path):
|
||||
''' add any existing plugin dirs in the path provided '''
|
||||
b_path = to_bytes(path, errors='surrogate_or_strict')
|
||||
if os.path.isdir(b_path):
|
||||
for name, obj in get_all_plugin_loaders():
|
||||
if obj.subdir:
|
||||
plugin_path = os.path.join(b_path, to_bytes(obj.subdir))
|
||||
if os.path.isdir(plugin_path):
|
||||
obj.add_directory(to_text(plugin_path))
|
||||
else:
|
||||
display.warning("Ignoring invalid path provided to plugin path: %s is not a directory" % to_native(path))
|
||||
|
||||
|
||||
def get_shell_plugin(shell_type=None, executable=None):
|
||||
|
||||
if not shell_type:
|
||||
# default to sh
|
||||
shell_type = 'sh'
|
||||
|
||||
# mostly for backwards compat
|
||||
if executable:
|
||||
if isinstance(executable, string_types):
|
||||
shell_filename = os.path.basename(executable)
|
||||
try:
|
||||
shell = shell_loader.get(shell_filename)
|
||||
except Exception:
|
||||
shell = None
|
||||
|
||||
if shell is None:
|
||||
for shell in shell_loader.all():
|
||||
if shell_filename in shell.COMPATIBLE_SHELLS:
|
||||
shell_type = shell.SHELL_FAMILY
|
||||
break
|
||||
else:
|
||||
raise AnsibleError("Either a shell type or a shell executable must be provided ")
|
||||
|
||||
shell = shell_loader.get(shell_type)
|
||||
if not shell:
|
||||
raise AnsibleError("Could not find the shell plugin required (%s)." % shell_type)
|
||||
|
||||
if executable:
|
||||
setattr(shell, 'executable', executable)
|
||||
|
||||
return shell
|
||||
|
||||
|
||||
class PluginLoader:
|
||||
'''
|
||||
PluginLoader loads plugins from the configured plugin directories.
|
||||
|
@ -394,6 +442,7 @@ class PluginLoader:
|
|||
return None
|
||||
|
||||
self._display_plugin_load(self.class_name, name, self._searched_paths, path, found_in_cache=found_in_cache, class_only=class_only)
|
||||
|
||||
if not class_only:
|
||||
try:
|
||||
obj = obj(*args, **kwargs)
|
||||
|
@ -513,6 +562,7 @@ class PluginLoader:
|
|||
continue
|
||||
|
||||
self._display_plugin_load(self.class_name, basename, self._searched_paths, path, found_in_cache=found_in_cache, class_only=class_only)
|
||||
|
||||
if not class_only:
|
||||
try:
|
||||
obj = obj(*args, **kwargs)
|
||||
|
@ -774,3 +824,10 @@ httpapi_loader = PluginLoader(
|
|||
'httpapi_plugins',
|
||||
required_base_class='HttpApiBase',
|
||||
)
|
||||
|
||||
become_loader = PluginLoader(
|
||||
'BecomeModule',
|
||||
'ansible.plugins.become',
|
||||
C.DEFAULT_BECOME_PLUGIN_PATH,
|
||||
'become_plugins'
|
||||
)
|
||||
|
|
|
@ -46,6 +46,7 @@ class ShellBase(AnsiblePlugin):
|
|||
'LC_MESSAGES': module_locale}
|
||||
|
||||
self.tmpdir = None
|
||||
self.executable = None
|
||||
|
||||
def _normalize_system_tmpdirs(self):
|
||||
# Normalize the tmp directory strings. We don't use expanduser/expandvars because those
|
||||
|
@ -65,15 +66,20 @@ class ShellBase(AnsiblePlugin):
|
|||
|
||||
super(ShellBase, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
|
||||
|
||||
# set env
|
||||
self.env.update(self.get_option('environment'))
|
||||
# set env if needed, deal with environment's 'dual nature' list of dicts or dict
|
||||
env = self.get_option('environment')
|
||||
if isinstance(env, list):
|
||||
for env_dict in env:
|
||||
self.env.update(env_dict)
|
||||
else:
|
||||
self.env.update(env)
|
||||
|
||||
# We can remove the try: except in the future when we make ShellBase a proper subset of
|
||||
# *all* shells. Right now powershell and third party shells which do not use the
|
||||
# shell_common documentation fragment (and so do not have system_tmpdirs) will fail
|
||||
try:
|
||||
self._normalize_system_tmpdirs()
|
||||
except AnsibleError:
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def env_prefix(self, **kwargs):
|
||||
|
|
|
@ -30,6 +30,10 @@ class ShellModule(ShellBase):
|
|||
# Family of shells this has. Must match the filename without extension
|
||||
SHELL_FAMILY = 'sh'
|
||||
|
||||
# commonly used
|
||||
ECHO = 'echo'
|
||||
COMMAND_SEP = ';'
|
||||
|
||||
# How to end lines in a python script one-liner
|
||||
_SHELL_EMBEDDED_PY_EOL = '\n'
|
||||
_SHELL_REDIRECT_ALLNULL = '> /dev/null 2>&1'
|
||||
|
|
|
@ -880,7 +880,7 @@ class StrategyBase:
|
|||
|
||||
host_results = []
|
||||
for host in notified_hosts:
|
||||
if not iterator.is_failed(host) or play_context.force_handlers:
|
||||
if not iterator.is_failed(host) or iterator._play.force_handlers:
|
||||
task_vars = self._variable_manager.get_vars(play=iterator._play, host=host, task=handler)
|
||||
self.add_tqm_variables(task_vars, play=iterator._play)
|
||||
self._queue_task(host, handler, task_vars, play_context)
|
||||
|
@ -1061,7 +1061,7 @@ class StrategyBase:
|
|||
del self._active_connections[target_host]
|
||||
else:
|
||||
connection = connection_loader.get(play_context.connection, play_context, os.devnull)
|
||||
play_context.set_options_from_plugin(connection)
|
||||
play_context.set_attributes_from_plugin(connection)
|
||||
|
||||
if connection:
|
||||
try:
|
||||
|
|
|
@ -235,7 +235,7 @@ class StrategyModule(StrategyBase):
|
|||
|
||||
for new_block in new_blocks:
|
||||
task_vars = self._variable_manager.get_vars(play=iterator._play, task=new_block._parent)
|
||||
final_block = new_block.filter_tagged_tasks(play_context, task_vars)
|
||||
final_block = new_block.filter_tagged_tasks(task_vars)
|
||||
for host in hosts_left:
|
||||
if host in included_file._hosts:
|
||||
all_blocks[host].append(final_block)
|
||||
|
|
|
@ -365,7 +365,7 @@ class StrategyModule(StrategyBase):
|
|||
task=new_block._parent
|
||||
)
|
||||
display.debug("filtering new block on tags")
|
||||
final_block = new_block.filter_tagged_tasks(play_context, task_vars)
|
||||
final_block = new_block.filter_tagged_tasks(task_vars)
|
||||
display.debug("done filtering new block on tags")
|
||||
|
||||
noop_block = self._prepare_and_create_noop_block_from(final_block, task._parent, iterator)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue