mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Merge pull request #10411 from bcoca/add_pbrun
preliminary privlege escalation unification + pbrun
This commit is contained in:
commit
ab45e5ee59
45 changed files with 841 additions and 472 deletions
53
bin/ansible
53
bin/ansible
|
@ -58,12 +58,12 @@ class Cli(object):
|
||||||
''' create an options parser for bin/ansible '''
|
''' create an options parser for bin/ansible '''
|
||||||
|
|
||||||
parser = utils.base_parser(
|
parser = utils.base_parser(
|
||||||
constants=C,
|
constants=C,
|
||||||
runas_opts=True,
|
runas_opts=True,
|
||||||
subset_opts=True,
|
subset_opts=True,
|
||||||
async_opts=True,
|
async_opts=True,
|
||||||
output_opts=True,
|
output_opts=True,
|
||||||
connect_opts=True,
|
connect_opts=True,
|
||||||
check_opts=True,
|
check_opts=True,
|
||||||
diff_opts=False,
|
diff_opts=False,
|
||||||
usage='%prog <host-pattern> [options]'
|
usage='%prog <host-pattern> [options]'
|
||||||
|
@ -82,12 +82,8 @@ class Cli(object):
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# su and sudo command line arguments need to be mutually exclusive
|
# privlege escalation command line arguments need to be mutually exclusive
|
||||||
if (options.su or options.su_user or options.ask_su_pass) and \
|
utils.check_mutually_exclusive_privilege(options, parser)
|
||||||
(options.sudo or options.sudo_user or options.ask_sudo_pass):
|
|
||||||
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
|
||||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
|
|
||||||
"mutually exclusive")
|
|
||||||
|
|
||||||
if (options.ask_vault_pass and options.vault_password_file):
|
if (options.ask_vault_pass and options.vault_password_file):
|
||||||
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||||
|
@ -101,20 +97,20 @@ class Cli(object):
|
||||||
|
|
||||||
pattern = args[0]
|
pattern = args[0]
|
||||||
|
|
||||||
sshpass = None
|
sshpass = becomepass = vault_pass = become_method = None
|
||||||
sudopass = None
|
|
||||||
su_pass = None
|
|
||||||
vault_pass = None
|
|
||||||
|
|
||||||
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS
|
|
||||||
# Never ask for an SSH password when we run with local connection
|
# Never ask for an SSH password when we run with local connection
|
||||||
if options.connection == "local":
|
if options.connection == "local":
|
||||||
options.ask_pass = False
|
options.ask_pass = False
|
||||||
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS
|
else:
|
||||||
options.ask_su_pass = options.ask_su_pass or C.DEFAULT_ASK_SU_PASS
|
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS
|
||||||
|
|
||||||
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
|
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
|
||||||
|
|
||||||
(sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass)
|
# become
|
||||||
|
utils.normalize_become_options(options)
|
||||||
|
prompt_method = utils.choose_pass_prompt(options)
|
||||||
|
(sshpass, becomepass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, become_ask_pass=options.become_ask_pass, ask_vault_pass=options.ask_vault_pass, become_method=prompt_method)
|
||||||
|
|
||||||
# read vault_pass from a file
|
# read vault_pass from a file
|
||||||
if not options.ask_vault_pass and options.vault_password_file:
|
if not options.ask_vault_pass and options.vault_password_file:
|
||||||
|
@ -126,6 +122,7 @@ class Cli(object):
|
||||||
if options.subset:
|
if options.subset:
|
||||||
inventory_manager.subset(options.subset)
|
inventory_manager.subset(options.subset)
|
||||||
hosts = inventory_manager.list_hosts(pattern)
|
hosts = inventory_manager.list_hosts(pattern)
|
||||||
|
|
||||||
if len(hosts) == 0:
|
if len(hosts) == 0:
|
||||||
callbacks.display("No hosts matched", stderr=True)
|
callbacks.display("No hosts matched", stderr=True)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -135,16 +132,10 @@ class Cli(object):
|
||||||
callbacks.display(' %s' % host)
|
callbacks.display(' %s' % host)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if ((options.module_name == 'command' or options.module_name == 'shell')
|
if options.module_name in ['command','shell'] and not options.module_args:
|
||||||
and not options.module_args):
|
|
||||||
callbacks.display("No argument passed to %s module" % options.module_name, color='red', stderr=True)
|
callbacks.display("No argument passed to %s module" % options.module_name, color='red', stderr=True)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if options.su_user or options.ask_su_pass:
|
|
||||||
options.su = True
|
|
||||||
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
|
|
||||||
options.su_user = options.su_user or C.DEFAULT_SU_USER
|
|
||||||
if options.tree:
|
if options.tree:
|
||||||
utils.prepare_writeable_dir(options.tree)
|
utils.prepare_writeable_dir(options.tree)
|
||||||
|
|
||||||
|
@ -160,17 +151,15 @@ class Cli(object):
|
||||||
forks=options.forks,
|
forks=options.forks,
|
||||||
pattern=pattern,
|
pattern=pattern,
|
||||||
callbacks=self.callbacks,
|
callbacks=self.callbacks,
|
||||||
sudo=options.sudo,
|
|
||||||
sudo_pass=sudopass,
|
|
||||||
sudo_user=options.sudo_user,
|
|
||||||
transport=options.connection,
|
transport=options.connection,
|
||||||
subset=options.subset,
|
subset=options.subset,
|
||||||
check=options.check,
|
check=options.check,
|
||||||
diff=options.check,
|
diff=options.check,
|
||||||
su=options.su,
|
|
||||||
su_pass=su_pass,
|
|
||||||
su_user=options.su_user,
|
|
||||||
vault_pass=vault_pass,
|
vault_pass=vault_pass,
|
||||||
|
become=options.become,
|
||||||
|
become_method=options.become_method,
|
||||||
|
become_pass=becomepass,
|
||||||
|
become_user=options.become_user,
|
||||||
extra_vars=extra_vars,
|
extra_vars=extra_vars,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -108,19 +108,14 @@ def main(args):
|
||||||
parser.print_help(file=sys.stderr)
|
parser.print_help(file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# su and sudo command line arguments need to be mutually exclusive
|
# privlege escalation command line arguments need to be mutually exclusive
|
||||||
if (options.su or options.su_user or options.ask_su_pass) and \
|
utils.check_mutually_exclusive_privilege(options, parser)
|
||||||
(options.sudo or options.sudo_user or options.ask_sudo_pass):
|
|
||||||
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
|
||||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
|
|
||||||
"mutually exclusive")
|
|
||||||
|
|
||||||
if (options.ask_vault_pass and options.vault_password_file):
|
if (options.ask_vault_pass and options.vault_password_file):
|
||||||
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||||
|
|
||||||
sshpass = None
|
sshpass = None
|
||||||
sudopass = None
|
becomepass = None
|
||||||
su_pass = None
|
|
||||||
vault_pass = None
|
vault_pass = None
|
||||||
|
|
||||||
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
|
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
|
||||||
|
@ -132,11 +127,14 @@ def main(args):
|
||||||
# Never ask for an SSH password when we run with local connection
|
# Never ask for an SSH password when we run with local connection
|
||||||
if options.connection == "local":
|
if options.connection == "local":
|
||||||
options.ask_pass = False
|
options.ask_pass = False
|
||||||
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS
|
|
||||||
options.ask_su_pass = options.ask_su_pass or C.DEFAULT_ASK_SU_PASS
|
# set pe options
|
||||||
(sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass)
|
utils.normalize_become_options(options)
|
||||||
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
|
prompt_method = utils.choose_pass_prompt(options)
|
||||||
options.su_user = options.su_user or C.DEFAULT_SU_USER
|
(sshpass, becomepass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass,
|
||||||
|
become_ask_pass=options.become_ask_pass,
|
||||||
|
ask_vault_pass=options.ask_vault_pass,
|
||||||
|
become_method=prompt_method)
|
||||||
|
|
||||||
# read vault_pass from a file
|
# read vault_pass from a file
|
||||||
if not options.ask_vault_pass and options.vault_password_file:
|
if not options.ask_vault_pass and options.vault_password_file:
|
||||||
|
@ -197,20 +195,18 @@ def main(args):
|
||||||
stats=stats,
|
stats=stats,
|
||||||
timeout=options.timeout,
|
timeout=options.timeout,
|
||||||
transport=options.connection,
|
transport=options.connection,
|
||||||
sudo=options.sudo,
|
become=options.become,
|
||||||
sudo_user=options.sudo_user,
|
become_method=options.become_method,
|
||||||
sudo_pass=sudopass,
|
become_user=options.become_user,
|
||||||
|
become_pass=becomepass,
|
||||||
extra_vars=extra_vars,
|
extra_vars=extra_vars,
|
||||||
private_key_file=options.private_key_file,
|
private_key_file=options.private_key_file,
|
||||||
only_tags=only_tags,
|
only_tags=only_tags,
|
||||||
skip_tags=skip_tags,
|
skip_tags=skip_tags,
|
||||||
check=options.check,
|
check=options.check,
|
||||||
diff=options.diff,
|
diff=options.diff,
|
||||||
su=options.su,
|
|
||||||
su_pass=su_pass,
|
|
||||||
su_user=options.su_user,
|
|
||||||
vault_password=vault_pass,
|
vault_password=vault_pass,
|
||||||
force_handlers=options.force_handlers
|
force_handlers=options.force_handlers,
|
||||||
)
|
)
|
||||||
|
|
||||||
if options.flush_cache:
|
if options.flush_cache:
|
||||||
|
|
83
docsite/rst/become.rst
Normal file
83
docsite/rst/become.rst
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
Ansible Privilege Escalation
|
||||||
|
++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
Ansible can use existing privilege escalation systems to allow a user to execute tasks as another.
|
||||||
|
|
||||||
|
.. contents:: Topics
|
||||||
|
|
||||||
|
Become
|
||||||
|
``````
|
||||||
|
Before 1.9 Ansible mostly allowed the use of sudo and a limited use of su to allow a login/remote user to become a different user
|
||||||
|
and execute tasks, create resources with the 2nd user's permissions. As of 1.9 'become' supersedes the old sudo/su, while still
|
||||||
|
being backwards compatible. This new system also makes it easier to add other privilege escalation tools like pbrun (Powerbroker),
|
||||||
|
pfexec and others.
|
||||||
|
|
||||||
|
|
||||||
|
New directives
|
||||||
|
--------------
|
||||||
|
|
||||||
|
become
|
||||||
|
equivalent to adding sudo: or su: to a play or task, set to true/yes to activate privilege escalation
|
||||||
|
|
||||||
|
become_user
|
||||||
|
equivalent to adding sudo_user: or su_user: to a play or task
|
||||||
|
|
||||||
|
become_method
|
||||||
|
at play or task level overrides the default method set in ansibile.cfg
|
||||||
|
|
||||||
|
|
||||||
|
New ansible_ variables
|
||||||
|
----------------------
|
||||||
|
Each allows you to set an option per group and/or host
|
||||||
|
|
||||||
|
ansible_become
|
||||||
|
equivalent to ansible_sudo or ansbile_su, allows to force privilege escalation
|
||||||
|
|
||||||
|
ansible_become_method
|
||||||
|
allows to set privilege escalation method
|
||||||
|
|
||||||
|
ansible_become_user
|
||||||
|
equivalent to ansible_sudo_user or ansbile_su_user, allows to set the user you become through privilege escalation
|
||||||
|
|
||||||
|
ansible_become_pass
|
||||||
|
equivalent to ansible_sudo_pass or ansbile_su_pass, allows you to set the privilege escalation password
|
||||||
|
|
||||||
|
|
||||||
|
New command line options
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
--ask-become-pass
|
||||||
|
ask for privilege escalation password
|
||||||
|
|
||||||
|
-b, --become
|
||||||
|
run operations with become (no passorwd implied)
|
||||||
|
|
||||||
|
--become-method=BECOME_METHOD
|
||||||
|
privilege escalation method to use (default=sudo),
|
||||||
|
valid choices: [ sudo | su | pbrun | pfexec ]
|
||||||
|
|
||||||
|
--become-user=BECOME_USER
|
||||||
|
run operations as this user (default=root)
|
||||||
|
|
||||||
|
|
||||||
|
sudo and su still work!
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Old playbooks will not need to be changed, even though they are deprecated, sudo and su directives will continue to work though it
|
||||||
|
is recommended to move to become as they may be retired at one point. You cannot mix directives on the same object though, ansible
|
||||||
|
will complain if you try to.
|
||||||
|
|
||||||
|
Become will default to using the old sudo/su configs and variables if they exist, but will override them if you specify any of the
|
||||||
|
new ones.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. note:: Privilege escalation methods must also be supported by the connection plugin used, most will warn if they do not, some will just ignore it as they always run as root (jail, chroot, etc).
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
`Mailing List <http://groups.google.com/group/ansible-project>`_
|
||||||
|
Questions? Help? Ideas? Stop by the list on Google Groups
|
||||||
|
`irc.freenode.net <http://irc.freenode.net>`_
|
||||||
|
#ansible IRC chat channel
|
||||||
|
|
|
@ -159,6 +159,12 @@ fact_caching = memory
|
||||||
#retry_files_enabled = False
|
#retry_files_enabled = False
|
||||||
#retry_files_save_path = ~/.ansible-retry
|
#retry_files_save_path = ~/.ansible-retry
|
||||||
|
|
||||||
|
[privilege_escalation]
|
||||||
|
#become=True
|
||||||
|
#become_method='sudo'
|
||||||
|
#become_user='root'
|
||||||
|
#become_ask_pass=False
|
||||||
|
|
||||||
[paramiko_connection]
|
[paramiko_connection]
|
||||||
|
|
||||||
# uncomment this line to cause the paramiko connection plugin to not record new host
|
# uncomment this line to cause the paramiko connection plugin to not record new host
|
||||||
|
|
|
@ -86,9 +86,6 @@ def shell_expand_path(path):
|
||||||
path = os.path.expanduser(os.path.expandvars(path))
|
path = os.path.expanduser(os.path.expandvars(path))
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def get_plugin_paths(path):
|
|
||||||
return ':'.join([os.path.join(x, path) for x in [os.path.expanduser('~/.ansible/plugins/'), '/usr/share/ansible_plugins/']])
|
|
||||||
|
|
||||||
p = load_config_file()
|
p = load_config_file()
|
||||||
|
|
||||||
active_user = pwd.getpwuid(os.geteuid())[0]
|
active_user = pwd.getpwuid(os.geteuid())[0]
|
||||||
|
@ -137,16 +134,28 @@ DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAG
|
||||||
DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root')
|
DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root')
|
||||||
DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True)
|
DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True)
|
||||||
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
|
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
|
||||||
|
|
||||||
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', get_plugin_paths('action_plugins'))
|
|
||||||
DEFAULT_CACHE_PLUGIN_PATH = get_config(p, DEFAULTS, 'cache_plugins', 'ANSIBLE_CACHE_PLUGINS', get_plugin_paths('cache_plugins'))
|
|
||||||
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', get_plugin_paths('callback_plugins'))
|
|
||||||
DEFAULT_CONNECTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'connection_plugins', 'ANSIBLE_CONNECTION_PLUGINS', get_plugin_paths('connection_plugins'))
|
|
||||||
DEFAULT_LOOKUP_PLUGIN_PATH = get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', get_plugin_paths('lookup_plugins'))
|
|
||||||
DEFAULT_VARS_PLUGIN_PATH = get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', get_plugin_paths('vars_plugins'))
|
|
||||||
DEFAULT_FILTER_PLUGIN_PATH = get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', get_plugin_paths('filter_plugins'))
|
|
||||||
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
|
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
|
||||||
|
|
||||||
|
#TODO: get rid of ternary chain mess
|
||||||
|
BECOME_METHODS = ['sudo','su','pbrun','runas','pfexec']
|
||||||
|
DEFAULT_BECOME = get_config(p, 'privilege_escalation', 'become', 'ANSIBLE_BECOME',True if DEFAULT_SUDO or DEFAULT_SU else False, boolean=True)
|
||||||
|
DEFAULT_BECOME_METHOD = get_config(p, 'privilege_escalation', 'become_method', 'ANSIBLE_BECOME_METHOD','sudo' if DEFAULT_SUDO else 'su' if DEFAULT_SU else 'sudo' ).lower()
|
||||||
|
DEFAULT_BECOME_USER = get_config(p, 'privilege_escalation', 'become_user', 'ANSIBLE_BECOME_USER',DEFAULT_SUDO_USER if DEFAULT_SUDO else DEFAULT_SU_USER if DEFAULT_SU else 'root')
|
||||||
|
DEFAULT_BECOME_ASK_PASS = get_config(p, 'privilege_escalation', 'become_ask_pass', 'ANSIBLE_BECOME_ASK_PASS',True if DEFAULT_ASK_SUDO_PASS else False, boolean=True)
|
||||||
|
# need to rethink impementing these 2
|
||||||
|
DEFAULT_BECOME_EXE = None
|
||||||
|
#DEFAULT_BECOME_EXE = get_config(p, DEFAULTS, 'become_exe', 'ANSIBLE_BECOME_EXE','sudo' if DEFAULT_SUDO else 'su' if DEFAULT_SU else 'sudo')
|
||||||
|
#DEFAULT_BECOME_FLAGS = get_config(p, DEFAULTS, 'become_flags', 'ANSIBLE_BECOME_FLAGS',DEFAULT_SUDO_FLAGS if DEFAULT_SUDO else DEFAULT_SU_FLAGS if DEFAULT_SU else '-H')
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '~/.ansible/plugins/action_plugins:/usr/share/ansible_plugins/action_plugins')
|
||||||
|
DEFAULT_CACHE_PLUGIN_PATH = get_config(p, DEFAULTS, 'cache_plugins', 'ANSIBLE_CACHE_PLUGINS', '~/.ansible/plugins/cache_plugins:/usr/share/ansible_plugins/cache_plugins')
|
||||||
|
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '~/.ansible/plugins/callback_plugins:/usr/share/ansible_plugins/callback_plugins')
|
||||||
|
DEFAULT_CONNECTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'connection_plugins', 'ANSIBLE_CONNECTION_PLUGINS', '~/.ansible/plugins/connection_plugins:/usr/share/ansible_plugins/connection_plugins')
|
||||||
|
DEFAULT_LOOKUP_PLUGIN_PATH = get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', '~/.ansible/plugins/lookup_plugins:/usr/share/ansible_plugins/lookup_plugins')
|
||||||
|
DEFAULT_VARS_PLUGIN_PATH = get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', '~/.ansible/plugins/vars_plugins:/usr/share/ansible_plugins/vars_plugins')
|
||||||
|
DEFAULT_FILTER_PLUGIN_PATH = get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', '~/.ansible/plugins/filter_plugins:/usr/share/ansible_plugins/filter_plugins')
|
||||||
|
|
||||||
CACHE_PLUGIN = get_config(p, DEFAULTS, 'fact_caching', 'ANSIBLE_CACHE_PLUGIN', 'memory')
|
CACHE_PLUGIN = get_config(p, DEFAULTS, 'fact_caching', 'ANSIBLE_CACHE_PLUGIN', 'memory')
|
||||||
CACHE_PLUGIN_CONNECTION = get_config(p, DEFAULTS, 'fact_caching_connection', 'ANSIBLE_CACHE_PLUGIN_CONNECTION', None)
|
CACHE_PLUGIN_CONNECTION = get_config(p, DEFAULTS, 'fact_caching_connection', 'ANSIBLE_CACHE_PLUGIN_CONNECTION', None)
|
||||||
CACHE_PLUGIN_PREFIX = get_config(p, DEFAULTS, 'fact_caching_prefix', 'ANSIBLE_CACHE_PLUGIN_PREFIX', 'ansible_facts')
|
CACHE_PLUGIN_PREFIX = get_config(p, DEFAULTS, 'fact_caching_prefix', 'ANSIBLE_CACHE_PLUGIN_PREFIX', 'ansible_facts')
|
||||||
|
@ -172,7 +181,7 @@ ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'AN
|
||||||
ANSIBLE_SSH_CONTROL_PATH = get_config(p, 'ssh_connection', 'control_path', 'ANSIBLE_SSH_CONTROL_PATH', "%(directory)s/ansible-ssh-%%h-%%p-%%r")
|
ANSIBLE_SSH_CONTROL_PATH = get_config(p, 'ssh_connection', 'control_path', 'ANSIBLE_SSH_CONTROL_PATH', "%(directory)s/ansible-ssh-%%h-%%p-%%r")
|
||||||
ANSIBLE_SSH_PIPELINING = get_config(p, 'ssh_connection', 'pipelining', 'ANSIBLE_SSH_PIPELINING', False, boolean=True)
|
ANSIBLE_SSH_PIPELINING = get_config(p, 'ssh_connection', 'pipelining', 'ANSIBLE_SSH_PIPELINING', False, boolean=True)
|
||||||
PARAMIKO_RECORD_HOST_KEYS = get_config(p, 'paramiko_connection', 'record_host_keys', 'ANSIBLE_PARAMIKO_RECORD_HOST_KEYS', True, boolean=True)
|
PARAMIKO_RECORD_HOST_KEYS = get_config(p, 'paramiko_connection', 'record_host_keys', 'ANSIBLE_PARAMIKO_RECORD_HOST_KEYS', True, boolean=True)
|
||||||
# obsolete -- will be formally removed in 1.6
|
# obsolete -- will be formally removed
|
||||||
ZEROMQ_PORT = get_config(p, 'fireball_connection', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099, integer=True)
|
ZEROMQ_PORT = get_config(p, 'fireball_connection', 'zeromq_port', 'ANSIBLE_ZEROMQ_PORT', 5099, integer=True)
|
||||||
ACCELERATE_PORT = get_config(p, 'accelerate', 'accelerate_port', 'ACCELERATE_PORT', 5099, integer=True)
|
ACCELERATE_PORT = get_config(p, 'accelerate', 'accelerate_port', 'ACCELERATE_PORT', 5099, integer=True)
|
||||||
ACCELERATE_TIMEOUT = get_config(p, 'accelerate', 'accelerate_timeout', 'ACCELERATE_TIMEOUT', 30, integer=True)
|
ACCELERATE_TIMEOUT = get_config(p, 'accelerate', 'accelerate_timeout', 'ACCELERATE_TIMEOUT', 30, integer=True)
|
||||||
|
@ -188,6 +197,7 @@ PARAMIKO_PTY = get_config(p, 'paramiko_connection', 'pty', 'AN
|
||||||
DEFAULT_PASSWORD_CHARS = ascii_letters + digits + ".,:-_"
|
DEFAULT_PASSWORD_CHARS = ascii_letters + digits + ".,:-_"
|
||||||
|
|
||||||
# non-configurable things
|
# non-configurable things
|
||||||
|
DEFAULT_BECOME_PASS = None
|
||||||
DEFAULT_SUDO_PASS = None
|
DEFAULT_SUDO_PASS = None
|
||||||
DEFAULT_REMOTE_PASS = None
|
DEFAULT_REMOTE_PASS = None
|
||||||
DEFAULT_SUBSET = None
|
DEFAULT_SUBSET = None
|
||||||
|
|
|
@ -60,15 +60,12 @@ class PlayBook(object):
|
||||||
timeout = C.DEFAULT_TIMEOUT,
|
timeout = C.DEFAULT_TIMEOUT,
|
||||||
remote_user = C.DEFAULT_REMOTE_USER,
|
remote_user = C.DEFAULT_REMOTE_USER,
|
||||||
remote_pass = C.DEFAULT_REMOTE_PASS,
|
remote_pass = C.DEFAULT_REMOTE_PASS,
|
||||||
sudo_pass = C.DEFAULT_SUDO_PASS,
|
|
||||||
remote_port = None,
|
remote_port = None,
|
||||||
transport = C.DEFAULT_TRANSPORT,
|
transport = C.DEFAULT_TRANSPORT,
|
||||||
private_key_file = C.DEFAULT_PRIVATE_KEY_FILE,
|
private_key_file = C.DEFAULT_PRIVATE_KEY_FILE,
|
||||||
callbacks = None,
|
callbacks = None,
|
||||||
runner_callbacks = None,
|
runner_callbacks = None,
|
||||||
stats = None,
|
stats = None,
|
||||||
sudo = False,
|
|
||||||
sudo_user = C.DEFAULT_SUDO_USER,
|
|
||||||
extra_vars = None,
|
extra_vars = None,
|
||||||
only_tags = None,
|
only_tags = None,
|
||||||
skip_tags = None,
|
skip_tags = None,
|
||||||
|
@ -77,11 +74,13 @@ class PlayBook(object):
|
||||||
check = False,
|
check = False,
|
||||||
diff = False,
|
diff = False,
|
||||||
any_errors_fatal = False,
|
any_errors_fatal = False,
|
||||||
su = False,
|
|
||||||
su_user = False,
|
|
||||||
su_pass = False,
|
|
||||||
vault_password = False,
|
vault_password = False,
|
||||||
force_handlers = False,
|
force_handlers = False,
|
||||||
|
# privelege escalation
|
||||||
|
become = C.DEFAULT_BECOME,
|
||||||
|
become_method = C.DEFAULT_BECOME_METHOD,
|
||||||
|
become_user = C.DEFAULT_BECOME_USER,
|
||||||
|
become_pass = None,
|
||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -92,13 +91,11 @@ class PlayBook(object):
|
||||||
timeout: connection timeout
|
timeout: connection timeout
|
||||||
remote_user: run as this user if not specified in a particular play
|
remote_user: run as this user if not specified in a particular play
|
||||||
remote_pass: use this remote password (for all plays) vs using SSH keys
|
remote_pass: use this remote password (for all plays) vs using SSH keys
|
||||||
sudo_pass: if sudo==True, and a password is required, this is the sudo password
|
|
||||||
remote_port: default remote port to use if not specified with the host or play
|
remote_port: default remote port to use if not specified with the host or play
|
||||||
transport: how to connect to hosts that don't specify a transport (local, paramiko, etc)
|
transport: how to connect to hosts that don't specify a transport (local, paramiko, etc)
|
||||||
callbacks output callbacks for the playbook
|
callbacks output callbacks for the playbook
|
||||||
runner_callbacks: more callbacks, this time for the runner API
|
runner_callbacks: more callbacks, this time for the runner API
|
||||||
stats: holds aggregrate data about events occurring to each host
|
stats: holds aggregrate data about events occurring to each host
|
||||||
sudo: if not specified per play, requests all plays use sudo mode
|
|
||||||
inventory: can be specified instead of host_list to use a pre-existing inventory object
|
inventory: can be specified instead of host_list to use a pre-existing inventory object
|
||||||
check: don't change anything, just try to detect some potential changes
|
check: don't change anything, just try to detect some potential changes
|
||||||
any_errors_fatal: terminate the entire execution immediately when one of the hosts has failed
|
any_errors_fatal: terminate the entire execution immediately when one of the hosts has failed
|
||||||
|
@ -139,21 +136,20 @@ class PlayBook(object):
|
||||||
self.callbacks = callbacks
|
self.callbacks = callbacks
|
||||||
self.runner_callbacks = runner_callbacks
|
self.runner_callbacks = runner_callbacks
|
||||||
self.stats = stats
|
self.stats = stats
|
||||||
self.sudo = sudo
|
|
||||||
self.sudo_pass = sudo_pass
|
|
||||||
self.sudo_user = sudo_user
|
|
||||||
self.extra_vars = extra_vars
|
self.extra_vars = extra_vars
|
||||||
self.global_vars = {}
|
self.global_vars = {}
|
||||||
self.private_key_file = private_key_file
|
self.private_key_file = private_key_file
|
||||||
self.only_tags = only_tags
|
self.only_tags = only_tags
|
||||||
self.skip_tags = skip_tags
|
self.skip_tags = skip_tags
|
||||||
self.any_errors_fatal = any_errors_fatal
|
self.any_errors_fatal = any_errors_fatal
|
||||||
self.su = su
|
|
||||||
self.su_user = su_user
|
|
||||||
self.su_pass = su_pass
|
|
||||||
self.vault_password = vault_password
|
self.vault_password = vault_password
|
||||||
self.force_handlers = force_handlers
|
self.force_handlers = force_handlers
|
||||||
|
|
||||||
|
self.become = become
|
||||||
|
self.become_method = become_method
|
||||||
|
self.become_user = become_user
|
||||||
|
self.become_pass = become_pass
|
||||||
|
|
||||||
self.callbacks.playbook = self
|
self.callbacks.playbook = self
|
||||||
self.runner_callbacks.playbook = self
|
self.runner_callbacks.playbook = self
|
||||||
|
|
||||||
|
@ -416,10 +412,7 @@ class PlayBook(object):
|
||||||
basedir=task.play.basedir,
|
basedir=task.play.basedir,
|
||||||
conditional=task.when,
|
conditional=task.when,
|
||||||
callbacks=self.runner_callbacks,
|
callbacks=self.runner_callbacks,
|
||||||
sudo=task.sudo,
|
|
||||||
sudo_user=task.sudo_user,
|
|
||||||
transport=task.transport,
|
transport=task.transport,
|
||||||
sudo_pass=task.sudo_pass,
|
|
||||||
is_playbook=True,
|
is_playbook=True,
|
||||||
check=self.check,
|
check=self.check,
|
||||||
diff=self.diff,
|
diff=self.diff,
|
||||||
|
@ -429,13 +422,14 @@ class PlayBook(object):
|
||||||
accelerate_port=task.play.accelerate_port,
|
accelerate_port=task.play.accelerate_port,
|
||||||
accelerate_ipv6=task.play.accelerate_ipv6,
|
accelerate_ipv6=task.play.accelerate_ipv6,
|
||||||
error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR,
|
error_on_undefined_vars=C.DEFAULT_UNDEFINED_VAR_BEHAVIOR,
|
||||||
su=task.su,
|
|
||||||
su_user=task.su_user,
|
|
||||||
su_pass=task.su_pass,
|
|
||||||
vault_pass = self.vault_password,
|
vault_pass = self.vault_password,
|
||||||
run_hosts=hosts,
|
run_hosts=hosts,
|
||||||
no_log=task.no_log,
|
no_log=task.no_log,
|
||||||
run_once=task.run_once,
|
run_once=task.run_once,
|
||||||
|
become=task.become,
|
||||||
|
become_method=task.become_method,
|
||||||
|
become_user=task.become_user,
|
||||||
|
become_pass=task.become_pass,
|
||||||
)
|
)
|
||||||
|
|
||||||
runner.module_vars.update({'play_hosts': hosts})
|
runner.module_vars.update({'play_hosts': hosts})
|
||||||
|
@ -616,12 +610,10 @@ class PlayBook(object):
|
||||||
setup_cache=self.SETUP_CACHE,
|
setup_cache=self.SETUP_CACHE,
|
||||||
vars_cache=self.VARS_CACHE,
|
vars_cache=self.VARS_CACHE,
|
||||||
callbacks=self.runner_callbacks,
|
callbacks=self.runner_callbacks,
|
||||||
sudo=play.sudo,
|
become=play.become,
|
||||||
sudo_user=play.sudo_user,
|
become_method=play.become_method,
|
||||||
sudo_pass=self.sudo_pass,
|
become_user=play.become_user,
|
||||||
su=play.su,
|
become_pass=self.become_pass,
|
||||||
su_user=play.su_user,
|
|
||||||
su_pass=self.su_pass,
|
|
||||||
vault_pass=self.vault_password,
|
vault_pass=self.vault_password,
|
||||||
transport=play.transport,
|
transport=play.transport,
|
||||||
is_playbook=True,
|
is_playbook=True,
|
||||||
|
|
|
@ -32,24 +32,25 @@ import uuid
|
||||||
|
|
||||||
class Play(object):
|
class Play(object):
|
||||||
|
|
||||||
__slots__ = [
|
_pb_common = [
|
||||||
'hosts', 'name', 'vars', 'vars_file_vars', 'role_vars', 'default_vars', 'vars_prompt', 'vars_files',
|
'accelerate', 'accelerate_ipv6', 'accelerate_port', 'any_errors_fatal', 'become',
|
||||||
'handlers', 'remote_user', 'remote_port', 'included_roles', 'accelerate',
|
'become_method', 'become_user', 'environment', 'gather_facts', 'handlers', 'hosts',
|
||||||
'accelerate_port', 'accelerate_ipv6', 'sudo', 'sudo_user', 'transport', 'playbook',
|
'name', 'no_log', 'remote_user', 'roles', 'serial', 'su', 'su_user', 'sudo',
|
||||||
'tags', 'gather_facts', 'serial', '_ds', '_handlers', '_tasks',
|
'sudo_user', 'tags', 'vars', 'vars_files', 'vars_prompt', 'vault_password',
|
||||||
'basedir', 'any_errors_fatal', 'roles', 'max_fail_pct', '_play_hosts', 'su', 'su_user',
|
]
|
||||||
'vault_password', 'no_log', 'environment',
|
|
||||||
|
__slots__ = _pb_common + [
|
||||||
|
'_ds', '_handlers', '_play_hosts', '_tasks', 'any_errors_fatal', 'basedir',
|
||||||
|
'default_vars', 'included_roles', 'max_fail_pct', 'playbook', 'remote_port',
|
||||||
|
'role_vars', 'transport', 'vars_file_vars',
|
||||||
]
|
]
|
||||||
|
|
||||||
# to catch typos and so forth -- these are userland names
|
# to catch typos and so forth -- these are userland names
|
||||||
# and don't line up 1:1 with how they are stored
|
# and don't line up 1:1 with how they are stored
|
||||||
VALID_KEYS = frozenset((
|
VALID_KEYS = frozenset(_pb_common + [
|
||||||
'hosts', 'name', 'vars', 'vars_prompt', 'vars_files',
|
'connection', 'include', 'max_fail_percentage', 'port', 'post_tasks',
|
||||||
'tasks', 'handlers', 'remote_user', 'user', 'port', 'include', 'accelerate', 'accelerate_port', 'accelerate_ipv6',
|
'pre_tasks', 'role_names', 'tasks', 'user',
|
||||||
'sudo', 'sudo_user', 'connection', 'tags', 'gather_facts', 'serial',
|
])
|
||||||
'any_errors_fatal', 'roles', 'role_names', 'pre_tasks', 'post_tasks', 'max_fail_percentage',
|
|
||||||
'su', 'su_user', 'vault_password', 'no_log', 'environment',
|
|
||||||
))
|
|
||||||
|
|
||||||
# *************************************************
|
# *************************************************
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ class Play(object):
|
||||||
|
|
||||||
for x in ds.keys():
|
for x in ds.keys():
|
||||||
if not x in Play.VALID_KEYS:
|
if not x in Play.VALID_KEYS:
|
||||||
raise errors.AnsibleError("%s is not a legal parameter at this level in an Ansible Playbook" % x)
|
raise errors.AnsibleError("%s is not a legal parameter of an Ansible Play" % x)
|
||||||
|
|
||||||
# allow all playbook keys to be set by --extra-vars
|
# allow all playbook keys to be set by --extra-vars
|
||||||
self.vars = ds.get('vars', {})
|
self.vars = ds.get('vars', {})
|
||||||
|
@ -140,8 +141,6 @@ class Play(object):
|
||||||
self._handlers = ds.get('handlers', [])
|
self._handlers = ds.get('handlers', [])
|
||||||
self.remote_user = ds.get('remote_user', ds.get('user', self.playbook.remote_user))
|
self.remote_user = ds.get('remote_user', ds.get('user', self.playbook.remote_user))
|
||||||
self.remote_port = ds.get('port', self.playbook.remote_port)
|
self.remote_port = ds.get('port', self.playbook.remote_port)
|
||||||
self.sudo = ds.get('sudo', self.playbook.sudo)
|
|
||||||
self.sudo_user = ds.get('sudo_user', self.playbook.sudo_user)
|
|
||||||
self.transport = ds.get('connection', self.playbook.transport)
|
self.transport = ds.get('connection', self.playbook.transport)
|
||||||
self.remote_port = self.remote_port
|
self.remote_port = self.remote_port
|
||||||
self.any_errors_fatal = utils.boolean(ds.get('any_errors_fatal', 'false'))
|
self.any_errors_fatal = utils.boolean(ds.get('any_errors_fatal', 'false'))
|
||||||
|
@ -149,22 +148,40 @@ class Play(object):
|
||||||
self.accelerate_port = ds.get('accelerate_port', None)
|
self.accelerate_port = ds.get('accelerate_port', None)
|
||||||
self.accelerate_ipv6 = ds.get('accelerate_ipv6', False)
|
self.accelerate_ipv6 = ds.get('accelerate_ipv6', False)
|
||||||
self.max_fail_pct = int(ds.get('max_fail_percentage', 100))
|
self.max_fail_pct = int(ds.get('max_fail_percentage', 100))
|
||||||
self.su = ds.get('su', self.playbook.su)
|
|
||||||
self.su_user = ds.get('su_user', self.playbook.su_user)
|
|
||||||
self.no_log = utils.boolean(ds.get('no_log', 'false'))
|
self.no_log = utils.boolean(ds.get('no_log', 'false'))
|
||||||
|
|
||||||
|
# Fail out if user specifies conflicting privelege escalations
|
||||||
|
if (ds.get('become') or ds.get('become_user')) and (ds.get('sudo') or ds.get('sudo_user')):
|
||||||
|
raise errors.AnsibleError('sudo params ("become", "become_user") and su params ("sudo", "sudo_user") cannot be used together')
|
||||||
|
if (ds.get('become') or ds.get('become_user')) and (ds.get('su') or ds.get('su_user')):
|
||||||
|
raise errors.AnsibleError('sudo params ("become", "become_user") and su params ("su", "su_user") cannot be used together')
|
||||||
|
if (ds.get('sudo') or ds.get('sudo_user')) and (ds.get('su') or ds.get('su_user')):
|
||||||
|
raise errors.AnsibleError('sudo params ("sudo", "sudo_user") and su params ("su", "su_user") cannot be used together')
|
||||||
|
|
||||||
|
# become settings are inherited and updated normally
|
||||||
|
self.become = ds.get('become', self.playbook.become)
|
||||||
|
self.become_method = ds.get('become_method', self.playbook.become_method)
|
||||||
|
self.become_user = ds.get('become_user', self.playbook.become_user)
|
||||||
|
|
||||||
|
# Make sure current play settings are reflected in become fields
|
||||||
|
if 'sudo' in ds:
|
||||||
|
self.become=ds['sudo']
|
||||||
|
self.become_method='sudo'
|
||||||
|
if 'sudo_user' in ds:
|
||||||
|
self.become_user=ds['sudo_user']
|
||||||
|
elif 'su' in ds:
|
||||||
|
self.become=True
|
||||||
|
self.become=ds['su']
|
||||||
|
if 'su_user' in ds:
|
||||||
|
self.become_user=ds['su_user']
|
||||||
|
|
||||||
# gather_facts is not a simple boolean, as None means that a 'smart'
|
# gather_facts is not a simple boolean, as None means that a 'smart'
|
||||||
# fact gathering mode will be used, so we need to be careful here as
|
# fact gathering mode will be used, so we need to be careful here as
|
||||||
# calling utils.boolean(None) returns False
|
# calling utils.boolean(None) returns False
|
||||||
self.gather_facts = ds.get('gather_facts', None)
|
self.gather_facts = ds.get('gather_facts', None)
|
||||||
if self.gather_facts:
|
if self.gather_facts is not None:
|
||||||
self.gather_facts = utils.boolean(self.gather_facts)
|
self.gather_facts = utils.boolean(self.gather_facts)
|
||||||
|
|
||||||
# Fail out if user specifies a sudo param with a su param in a given play
|
|
||||||
if (ds.get('sudo') or ds.get('sudo_user')) and (ds.get('su') or ds.get('su_user')):
|
|
||||||
raise errors.AnsibleError('sudo params ("sudo", "sudo_user") and su params '
|
|
||||||
'("su", "su_user") cannot be used together')
|
|
||||||
|
|
||||||
load_vars['role_names'] = ds.get('role_names', [])
|
load_vars['role_names'] = ds.get('role_names', [])
|
||||||
|
|
||||||
self._tasks = self._load_tasks(self._ds.get('tasks', []), load_vars)
|
self._tasks = self._load_tasks(self._ds.get('tasks', []), load_vars)
|
||||||
|
@ -173,9 +190,6 @@ class Play(object):
|
||||||
# apply any missing tags to role tasks
|
# apply any missing tags to role tasks
|
||||||
self._late_merge_role_tags()
|
self._late_merge_role_tags()
|
||||||
|
|
||||||
if self.sudo_user != 'root':
|
|
||||||
self.sudo = True
|
|
||||||
|
|
||||||
# place holder for the discovered hosts to be used in this play
|
# place holder for the discovered hosts to be used in this play
|
||||||
self._play_hosts = None
|
self._play_hosts = None
|
||||||
|
|
||||||
|
@ -429,7 +443,7 @@ class Play(object):
|
||||||
|
|
||||||
for (role, role_path, role_vars, role_params, default_vars) in roles:
|
for (role, role_path, role_vars, role_params, default_vars) in roles:
|
||||||
# special vars must be extracted from the dict to the included tasks
|
# special vars must be extracted from the dict to the included tasks
|
||||||
special_keys = [ "sudo", "sudo_user", "when", "with_items" ]
|
special_keys = [ "sudo", "sudo_user", "when", "with_items", "su", "su_user", "become", "become_user" ]
|
||||||
special_vars = {}
|
special_vars = {}
|
||||||
for k in special_keys:
|
for k in special_keys:
|
||||||
if k in role_vars:
|
if k in role_vars:
|
||||||
|
@ -531,7 +545,7 @@ class Play(object):
|
||||||
|
|
||||||
# *************************************************
|
# *************************************************
|
||||||
|
|
||||||
def _load_tasks(self, tasks, vars=None, role_params=None, default_vars=None, sudo_vars=None,
|
def _load_tasks(self, tasks, vars=None, role_params=None, default_vars=None, become_vars=None,
|
||||||
additional_conditions=None, original_file=None, role_name=None):
|
additional_conditions=None, original_file=None, role_name=None):
|
||||||
''' handle task and handler include statements '''
|
''' handle task and handler include statements '''
|
||||||
|
|
||||||
|
@ -547,8 +561,8 @@ class Play(object):
|
||||||
role_params = {}
|
role_params = {}
|
||||||
if default_vars is None:
|
if default_vars is None:
|
||||||
default_vars = {}
|
default_vars = {}
|
||||||
if sudo_vars is None:
|
if become_vars is None:
|
||||||
sudo_vars = {}
|
become_vars = {}
|
||||||
|
|
||||||
old_conditions = list(additional_conditions)
|
old_conditions = list(additional_conditions)
|
||||||
|
|
||||||
|
@ -560,14 +574,37 @@ class Play(object):
|
||||||
if not isinstance(x, dict):
|
if not isinstance(x, dict):
|
||||||
raise errors.AnsibleError("expecting dict; got: %s, error in %s" % (x, original_file))
|
raise errors.AnsibleError("expecting dict; got: %s, error in %s" % (x, original_file))
|
||||||
|
|
||||||
# evaluate sudo vars for current and child tasks
|
# evaluate privilege escalation vars for current and child tasks
|
||||||
included_sudo_vars = {}
|
included_become_vars = {}
|
||||||
for k in ["sudo", "sudo_user"]:
|
for k in ["become", "become_user", "become_method", "become_exe"]:
|
||||||
if k in x:
|
if k in x:
|
||||||
included_sudo_vars[k] = x[k]
|
included_become_vars[k] = x[k]
|
||||||
elif k in sudo_vars:
|
elif k in become_vars:
|
||||||
included_sudo_vars[k] = sudo_vars[k]
|
included_become_vars[k] = become_vars[k]
|
||||||
x[k] = sudo_vars[k]
|
x[k] = become_vars[k]
|
||||||
|
|
||||||
|
## backwards compat with old sudo/su directives
|
||||||
|
if 'sudo' in x or 'sudo_user' in x:
|
||||||
|
included_become_vars['become'] = x['sudo']
|
||||||
|
x['become'] = x['sudo']
|
||||||
|
x['become_method'] = 'sudo'
|
||||||
|
del x['sudo']
|
||||||
|
|
||||||
|
if x.get('sudo_user', False):
|
||||||
|
included_become_vars['become_user'] = x['sudo_user']
|
||||||
|
x['become_user'] = x['sudo_user']
|
||||||
|
del x['sudo_user']
|
||||||
|
|
||||||
|
elif 'su' in x or 'su_user' in x:
|
||||||
|
included_become_vars['become'] = x['su']
|
||||||
|
x['become'] = x['su']
|
||||||
|
x['become_method'] = 'su'
|
||||||
|
del x['su']
|
||||||
|
|
||||||
|
if x.get('su_user', False):
|
||||||
|
included_become_vars['become_user'] = x['su_user']
|
||||||
|
x['become_user'] = x['su_user']
|
||||||
|
del x['su_user']
|
||||||
|
|
||||||
if 'meta' in x:
|
if 'meta' in x:
|
||||||
if x['meta'] == 'flush_handlers':
|
if x['meta'] == 'flush_handlers':
|
||||||
|
@ -596,7 +633,7 @@ class Play(object):
|
||||||
included_additional_conditions.append(x[k])
|
included_additional_conditions.append(x[k])
|
||||||
elif type(x[k]) is list:
|
elif type(x[k]) is list:
|
||||||
included_additional_conditions.extend(x[k])
|
included_additional_conditions.extend(x[k])
|
||||||
elif k in ("include", "vars", "role_params", "default_vars", "sudo", "sudo_user", "role_name", "no_log"):
|
elif k in ("include", "vars", "role_params", "default_vars", "sudo", "sudo_user", "role_name", "no_log", "become", "become_user", "su", "su_user"):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
include_vars[k] = x[k]
|
include_vars[k] = x[k]
|
||||||
|
@ -643,7 +680,7 @@ class Play(object):
|
||||||
for y in data:
|
for y in data:
|
||||||
if isinstance(y, dict) and 'include' in y:
|
if isinstance(y, dict) and 'include' in y:
|
||||||
y['role_name'] = new_role
|
y['role_name'] = new_role
|
||||||
loaded = self._load_tasks(data, mv, role_params, default_vars, included_sudo_vars, list(included_additional_conditions), original_file=include_filename, role_name=new_role)
|
loaded = self._load_tasks(data, mv, role_params, default_vars, included_become_vars, list(included_additional_conditions), original_file=include_filename, role_name=new_role)
|
||||||
results += loaded
|
results += loaded
|
||||||
elif type(x) == dict:
|
elif type(x) == dict:
|
||||||
task = Task(
|
task = Task(
|
||||||
|
|
|
@ -24,26 +24,24 @@ import sys
|
||||||
|
|
||||||
class Task(object):
|
class Task(object):
|
||||||
|
|
||||||
__slots__ = [
|
_t_common = [
|
||||||
'name', 'meta', 'action', 'when', 'async_seconds', 'async_poll_interval',
|
'action', 'always_run', 'any_errors_fatal', 'args', 'become', 'become_method', 'become_pass',
|
||||||
'notify', 'module_name', 'module_args', 'module_vars', 'play_vars', 'play_file_vars', 'role_vars', 'role_params', 'default_vars',
|
'become_user', 'changed_when', 'delay', 'delegate_to', 'environment', 'failed_when',
|
||||||
'play', 'notified_by', 'tags', 'register', 'role_name',
|
'first_available_file', 'ignore_errors', 'local_action', 'meta', 'name', 'no_log',
|
||||||
'delegate_to', 'first_available_file', 'ignore_errors',
|
'notify', 'register', 'remote_user', 'retries', 'run_once', 'su', 'su_pass', 'su_user',
|
||||||
'local_action', 'transport', 'sudo', 'remote_user', 'sudo_user', 'sudo_pass',
|
'sudo', 'sudo_pass', 'sudo_user', 'tags', 'transport', 'until', 'when',
|
||||||
'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args',
|
|
||||||
'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until',
|
|
||||||
'su', 'su_user', 'su_pass', 'no_log', 'run_once',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'async_poll_interval', 'async_seconds', 'default_vars', 'first_available_file',
|
||||||
|
'items_lookup_plugin', 'items_lookup_terms', 'module_args', 'module_name', 'module_vars',
|
||||||
|
'notified_by', 'play', 'play_file_vars', 'play_vars', 'role_name', 'role_params', 'role_vars',
|
||||||
|
] + _t_common
|
||||||
|
|
||||||
# to prevent typos and such
|
# to prevent typos and such
|
||||||
VALID_KEYS = frozenset((
|
VALID_KEYS = frozenset([
|
||||||
'name', 'meta', 'action', 'when', 'async', 'poll', 'notify',
|
'async', 'connection', 'include', 'poll',
|
||||||
'first_available_file', 'include', 'tags', 'register', 'ignore_errors',
|
] + _t_common)
|
||||||
'delegate_to', 'local_action', 'transport', 'remote_user', 'sudo', 'sudo_user',
|
|
||||||
'sudo_pass', 'when', 'connection', 'environment', 'args',
|
|
||||||
'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until',
|
|
||||||
'su', 'su_user', 'su_pass', 'no_log', 'run_once',
|
|
||||||
))
|
|
||||||
|
|
||||||
def __init__(self, play, ds, module_vars=None, play_vars=None, play_file_vars=None, role_vars=None, role_params=None, default_vars=None, additional_conditions=None, role_name=None):
|
def __init__(self, play, ds, module_vars=None, play_vars=None, play_file_vars=None, role_vars=None, role_params=None, default_vars=None, additional_conditions=None, role_name=None):
|
||||||
''' constructor loads from a task or handler datastructure '''
|
''' constructor loads from a task or handler datastructure '''
|
||||||
|
@ -131,14 +129,12 @@ class Task(object):
|
||||||
self.name = ds.get('name', None)
|
self.name = ds.get('name', None)
|
||||||
self.tags = [ 'untagged' ]
|
self.tags = [ 'untagged' ]
|
||||||
self.register = ds.get('register', None)
|
self.register = ds.get('register', None)
|
||||||
self.sudo = utils.boolean(ds.get('sudo', play.sudo))
|
|
||||||
self.su = utils.boolean(ds.get('su', play.su))
|
|
||||||
self.environment = ds.get('environment', play.environment)
|
self.environment = ds.get('environment', play.environment)
|
||||||
self.role_name = role_name
|
self.role_name = role_name
|
||||||
self.no_log = utils.boolean(ds.get('no_log', "false")) or self.play.no_log
|
self.no_log = utils.boolean(ds.get('no_log', "false")) or self.play.no_log
|
||||||
self.run_once = utils.boolean(ds.get('run_once', 'false'))
|
self.run_once = utils.boolean(ds.get('run_once', 'false'))
|
||||||
|
|
||||||
#Code to allow do until feature in a Task
|
#Code to allow do until feature in a Task
|
||||||
if 'until' in ds:
|
if 'until' in ds:
|
||||||
if not ds.get('register'):
|
if not ds.get('register'):
|
||||||
raise errors.AnsibleError("register keyword is mandatory when using do until feature")
|
raise errors.AnsibleError("register keyword is mandatory when using do until feature")
|
||||||
|
@ -160,24 +156,36 @@ class Task(object):
|
||||||
else:
|
else:
|
||||||
self.remote_user = ds.get('remote_user', play.playbook.remote_user)
|
self.remote_user = ds.get('remote_user', play.playbook.remote_user)
|
||||||
|
|
||||||
self.sudo_user = None
|
# Fail out if user specifies privilege escalation params in conflict
|
||||||
self.sudo_pass = None
|
if (ds.get('become') or ds.get('become_user') or ds.get('become_pass')) and (ds.get('sudo') or ds.get('sudo_user') or ds.get('sudo_pass')):
|
||||||
self.su_user = None
|
raise errors.AnsibleError('incompatible parameters ("become", "become_user", "become_pass") and sudo params "sudo", "sudo_user", "sudo_pass" in task: %s' % self.name)
|
||||||
self.su_pass = None
|
|
||||||
|
|
||||||
if self.sudo:
|
if (ds.get('become') or ds.get('become_user') or ds.get('become_pass')) and (ds.get('su') or ds.get('su_user') or ds.get('su_pass')):
|
||||||
self.sudo_user = ds.get('sudo_user', play.sudo_user)
|
raise errors.AnsibleError('incompatible parameters ("become", "become_user", "become_pass") and su params "su", "su_user", "sudo_pass" in task: %s' % self.name)
|
||||||
self.sudo_pass = ds.get('sudo_pass', play.playbook.sudo_pass)
|
|
||||||
elif self.su:
|
|
||||||
self.su_user = ds.get('su_user', play.su_user)
|
|
||||||
self.su_pass = ds.get('su_pass', play.playbook.su_pass)
|
|
||||||
|
|
||||||
# Fail out if user specifies a sudo param with a su param in a given play
|
if (ds.get('sudo') or ds.get('sudo_user') or ds.get('sudo_pass')) and (ds.get('su') or ds.get('su_user') or ds.get('su_pass')):
|
||||||
if (ds.get('sudo') or ds.get('sudo_user') or ds.get('sudo_pass')) and \
|
raise errors.AnsibleError('incompatible parameters ("su", "su_user", "su_pass") and sudo params "sudo", "sudo_user", "sudo_pass" in task: %s' % self.name)
|
||||||
(ds.get('su') or ds.get('su_user') or ds.get('su_pass')):
|
|
||||||
raise errors.AnsibleError('sudo params ("sudo", "sudo_user", "sudo_pass") '
|
self.become = utils.boolean(ds.get('become', play.become))
|
||||||
'and su params "su", "su_user", "su_pass") '
|
self.become_method = ds.get('become_method', play.become_method)
|
||||||
'cannot be used together')
|
self.become_user = ds.get('become_user', play.become_user)
|
||||||
|
self.become_pass = ds.get('become_pass', play.playbook.become_pass)
|
||||||
|
|
||||||
|
# set only if passed in current task data
|
||||||
|
if 'sudo' in ds or 'sudo_user' in ds:
|
||||||
|
self.become=ds['sudo']
|
||||||
|
self.become_method='sudo'
|
||||||
|
if 'sudo_user' in ds:
|
||||||
|
self.become_user = ds['sudo_user']
|
||||||
|
if 'sudo_pass' in ds:
|
||||||
|
self.become_pass = ds['sudo_pass']
|
||||||
|
if 'su' in ds or 'su_user' in ds:
|
||||||
|
self.become=ds['su']
|
||||||
|
self.become_method='su'
|
||||||
|
if 'su_user' in ds:
|
||||||
|
self.become_user = ds['su_user']
|
||||||
|
if 'su_pass' in ds:
|
||||||
|
self.become_pass = ds['su_pass']
|
||||||
|
|
||||||
# Both are defined
|
# Both are defined
|
||||||
if ('action' in ds) and ('local_action' in ds):
|
if ('action' in ds) and ('local_action' in ds):
|
||||||
|
|
|
@ -123,7 +123,6 @@ class Runner(object):
|
||||||
remote_pass=C.DEFAULT_REMOTE_PASS, # ex: 'password123' or None if using key
|
remote_pass=C.DEFAULT_REMOTE_PASS, # ex: 'password123' or None if using key
|
||||||
remote_port=None, # if SSH on different ports
|
remote_port=None, # if SSH on different ports
|
||||||
private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords
|
private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords
|
||||||
sudo_pass=C.DEFAULT_SUDO_PASS, # ex: 'password123' or None
|
|
||||||
background=0, # async poll every X seconds, else 0 for non-async
|
background=0, # async poll every X seconds, else 0 for non-async
|
||||||
basedir=None, # directory of playbook, if applicable
|
basedir=None, # directory of playbook, if applicable
|
||||||
setup_cache=None, # used to share fact data w/ other tasks
|
setup_cache=None, # used to share fact data w/ other tasks
|
||||||
|
@ -131,8 +130,6 @@ class Runner(object):
|
||||||
transport=C.DEFAULT_TRANSPORT, # 'ssh', 'paramiko', 'local'
|
transport=C.DEFAULT_TRANSPORT, # 'ssh', 'paramiko', 'local'
|
||||||
conditional='True', # run only if this fact expression evals to true
|
conditional='True', # run only if this fact expression evals to true
|
||||||
callbacks=None, # used for output
|
callbacks=None, # used for output
|
||||||
sudo=False, # whether to run sudo or not
|
|
||||||
sudo_user=C.DEFAULT_SUDO_USER, # ex: 'root'
|
|
||||||
module_vars=None, # a playbooks internals thing
|
module_vars=None, # a playbooks internals thing
|
||||||
play_vars=None, #
|
play_vars=None, #
|
||||||
play_file_vars=None, #
|
play_file_vars=None, #
|
||||||
|
@ -151,14 +148,15 @@ class Runner(object):
|
||||||
accelerate=False, # use accelerated connection
|
accelerate=False, # use accelerated connection
|
||||||
accelerate_ipv6=False, # accelerated connection w/ IPv6
|
accelerate_ipv6=False, # accelerated connection w/ IPv6
|
||||||
accelerate_port=None, # port to use with accelerated connection
|
accelerate_port=None, # port to use with accelerated connection
|
||||||
su=False, # Are we running our command via su?
|
|
||||||
su_user=None, # User to su to when running command, ex: 'root'
|
|
||||||
su_pass=C.DEFAULT_SU_PASS,
|
|
||||||
vault_pass=None,
|
vault_pass=None,
|
||||||
run_hosts=None, # an optional list of pre-calculated hosts to run on
|
run_hosts=None, # an optional list of pre-calculated hosts to run on
|
||||||
no_log=False, # option to enable/disable logging for a given task
|
no_log=False, # option to enable/disable logging for a given task
|
||||||
run_once=False, # option to enable/disable host bypass loop for a given task
|
run_once=False, # option to enable/disable host bypass loop for a given task
|
||||||
sudo_exe=C.DEFAULT_SUDO_EXE, # ex: /usr/local/bin/sudo
|
become=False, # whether to run privelege escalation or not
|
||||||
|
become_method=C.DEFAULT_BECOME_METHOD,
|
||||||
|
become_user=C.DEFAULT_BECOME_USER, # ex: 'root'
|
||||||
|
become_pass=C.DEFAULT_BECOME_PASS, # ex: 'password123' or None
|
||||||
|
become_exe=C.DEFAULT_BECOME_EXE, # ex: /usr/local/bin/sudo
|
||||||
):
|
):
|
||||||
|
|
||||||
# used to lock multiprocess inputs and outputs at various levels
|
# used to lock multiprocess inputs and outputs at various levels
|
||||||
|
@ -201,10 +199,12 @@ class Runner(object):
|
||||||
self.remote_port = remote_port
|
self.remote_port = remote_port
|
||||||
self.private_key_file = private_key_file
|
self.private_key_file = private_key_file
|
||||||
self.background = background
|
self.background = background
|
||||||
self.sudo = sudo
|
self.become = become
|
||||||
self.sudo_user_var = sudo_user
|
self.become_method = become_method
|
||||||
self.sudo_user = None
|
self.become_user_var = become_user
|
||||||
self.sudo_pass = sudo_pass
|
self.become_user = None
|
||||||
|
self.become_pass = become_pass
|
||||||
|
self.become_exe = become_exe
|
||||||
self.is_playbook = is_playbook
|
self.is_playbook = is_playbook
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
self.complex_args = complex_args
|
self.complex_args = complex_args
|
||||||
|
@ -213,15 +213,10 @@ class Runner(object):
|
||||||
self.accelerate_port = accelerate_port
|
self.accelerate_port = accelerate_port
|
||||||
self.accelerate_ipv6 = accelerate_ipv6
|
self.accelerate_ipv6 = accelerate_ipv6
|
||||||
self.callbacks.runner = self
|
self.callbacks.runner = self
|
||||||
self.su = su
|
|
||||||
self.su_user_var = su_user
|
|
||||||
self.su_user = None
|
|
||||||
self.su_pass = su_pass
|
|
||||||
self.omit_token = '__omit_place_holder__%s' % sha1(os.urandom(64)).hexdigest()
|
self.omit_token = '__omit_place_holder__%s' % sha1(os.urandom(64)).hexdigest()
|
||||||
self.vault_pass = vault_pass
|
self.vault_pass = vault_pass
|
||||||
self.no_log = no_log
|
self.no_log = no_log
|
||||||
self.run_once = run_once
|
self.run_once = run_once
|
||||||
self.sudo_exe = sudo_exe
|
|
||||||
|
|
||||||
if self.transport == 'smart':
|
if self.transport == 'smart':
|
||||||
# If the transport is 'smart', check to see if certain conditions
|
# If the transport is 'smart', check to see if certain conditions
|
||||||
|
@ -369,7 +364,7 @@ class Runner(object):
|
||||||
delegate['pass'] = this_info.get('ansible_ssh_pass', password)
|
delegate['pass'] = this_info.get('ansible_ssh_pass', password)
|
||||||
delegate['private_key_file'] = this_info.get('ansible_ssh_private_key_file', self.private_key_file)
|
delegate['private_key_file'] = this_info.get('ansible_ssh_private_key_file', self.private_key_file)
|
||||||
delegate['transport'] = this_info.get('ansible_connection', self.transport)
|
delegate['transport'] = this_info.get('ansible_connection', self.transport)
|
||||||
delegate['sudo_pass'] = this_info.get('ansible_sudo_pass', self.sudo_pass)
|
delegate['become_pass'] = this_info.get('ansible_become_pass', this_info.get('ansible_ssh_pass', self.become_pass))
|
||||||
|
|
||||||
# Last chance to get private_key_file from global variables.
|
# Last chance to get private_key_file from global variables.
|
||||||
# this is useful if delegated host is not defined in the inventory
|
# this is useful if delegated host is not defined in the inventory
|
||||||
|
@ -481,13 +476,13 @@ class Runner(object):
|
||||||
or not conn.has_pipelining
|
or not conn.has_pipelining
|
||||||
or not C.ANSIBLE_SSH_PIPELINING
|
or not C.ANSIBLE_SSH_PIPELINING
|
||||||
or C.DEFAULT_KEEP_REMOTE_FILES
|
or C.DEFAULT_KEEP_REMOTE_FILES
|
||||||
or self.su):
|
or self.become_method == 'su'):
|
||||||
self._transfer_str(conn, tmp, module_name, module_data)
|
self._transfer_str(conn, tmp, module_name, module_data)
|
||||||
|
|
||||||
environment_string = self._compute_environment_string(conn, inject)
|
environment_string = self._compute_environment_string(conn, inject)
|
||||||
|
|
||||||
if "tmp" in tmp and ((self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root')):
|
if "tmp" in tmp and (self.become and self.become_user != 'root'):
|
||||||
# deal with possible umask issues once sudo'ed to other user
|
# deal with possible umask issues once you become another user
|
||||||
self._remote_chmod(conn, 'a+r', remote_module_path, tmp)
|
self._remote_chmod(conn, 'a+r', remote_module_path, tmp)
|
||||||
|
|
||||||
cmd = ""
|
cmd = ""
|
||||||
|
@ -514,8 +509,8 @@ class Runner(object):
|
||||||
else:
|
else:
|
||||||
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
|
argsfile = self._transfer_str(conn, tmp, 'arguments', args)
|
||||||
|
|
||||||
if (self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root'):
|
if self.become and self.become_user != 'root':
|
||||||
# deal with possible umask issues once sudo'ed to other user
|
# deal with possible umask issues once become another user
|
||||||
self._remote_chmod(conn, 'a+r', argsfile, tmp)
|
self._remote_chmod(conn, 'a+r', argsfile, tmp)
|
||||||
|
|
||||||
if async_jid is None:
|
if async_jid is None:
|
||||||
|
@ -524,7 +519,7 @@ class Runner(object):
|
||||||
cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]])
|
cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]])
|
||||||
else:
|
else:
|
||||||
if async_jid is None:
|
if async_jid is None:
|
||||||
if conn.has_pipelining and C.ANSIBLE_SSH_PIPELINING and not C.DEFAULT_KEEP_REMOTE_FILES and not self.su:
|
if conn.has_pipelining and C.ANSIBLE_SSH_PIPELINING and not C.DEFAULT_KEEP_REMOTE_FILES and not self.become_method == 'su':
|
||||||
in_data = module_data
|
in_data = module_data
|
||||||
else:
|
else:
|
||||||
cmd = "%s" % (remote_module_path)
|
cmd = "%s" % (remote_module_path)
|
||||||
|
@ -536,7 +531,7 @@ class Runner(object):
|
||||||
|
|
||||||
rm_tmp = None
|
rm_tmp = None
|
||||||
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||||
if not self.sudo or self.su or self.sudo_user == 'root' or self.su_user == 'root':
|
if not self.become or self.become_user == 'root':
|
||||||
# not sudoing or sudoing to root, so can cleanup files in the same step
|
# not sudoing or sudoing to root, so can cleanup files in the same step
|
||||||
rm_tmp = tmp
|
rm_tmp = tmp
|
||||||
|
|
||||||
|
@ -546,17 +541,14 @@ class Runner(object):
|
||||||
sudoable = True
|
sudoable = True
|
||||||
if module_name == "accelerate":
|
if module_name == "accelerate":
|
||||||
# always run the accelerate module as the user
|
# always run the accelerate module as the user
|
||||||
# specified in the play, not the sudo_user
|
# specified in the play, not the become_user
|
||||||
sudoable = False
|
sudoable = False
|
||||||
|
|
||||||
if self.su:
|
res = self._low_level_exec_command(conn, cmd, tmp, become=self.become, sudoable=sudoable, in_data=in_data)
|
||||||
res = self._low_level_exec_command(conn, cmd, tmp, su=True, in_data=in_data)
|
|
||||||
else:
|
|
||||||
res = self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable, in_data=in_data)
|
|
||||||
|
|
||||||
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
if "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||||
if (self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root'):
|
if self.become and self.become_user != 'root':
|
||||||
# not sudoing to root, so maybe can't delete files as that other user
|
# not becoming root, so maybe can't delete files as that other user
|
||||||
# have to clean up temp files as original user in a second step
|
# have to clean up temp files as original user in a second step
|
||||||
cmd2 = conn.shell.remove(tmp, recurse=True)
|
cmd2 = conn.shell.remove(tmp, recurse=True)
|
||||||
self._low_level_exec_command(conn, cmd2, tmp, sudoable=False)
|
self._low_level_exec_command(conn, cmd2, tmp, sudoable=False)
|
||||||
|
@ -849,11 +841,9 @@ class Runner(object):
|
||||||
def _executor_internal_inner(self, host, module_name, module_args, inject, port, is_chained=False, complex_args=None):
|
def _executor_internal_inner(self, host, module_name, module_args, inject, port, is_chained=False, complex_args=None):
|
||||||
''' decides how to invoke a module '''
|
''' decides how to invoke a module '''
|
||||||
|
|
||||||
# late processing of parameterized sudo_user (with_items,..)
|
# late processing of parameterized become_user (with_items,..)
|
||||||
if self.sudo_user_var is not None:
|
if self.become_user_var is not None:
|
||||||
self.sudo_user = template.template(self.basedir, self.sudo_user_var, inject)
|
self.become_user = template.template(self.basedir, self.become_user_var, inject)
|
||||||
if self.su_user_var is not None:
|
|
||||||
self.su_user = template.template(self.basedir, self.su_user_var, inject)
|
|
||||||
|
|
||||||
# module_name may be dynamic (but cannot contain {{ ansible_ssh_user }})
|
# module_name may be dynamic (but cannot contain {{ ansible_ssh_user }})
|
||||||
module_name = template.template(self.basedir, module_name, inject)
|
module_name = template.template(self.basedir, module_name, inject)
|
||||||
|
@ -893,18 +883,16 @@ class Runner(object):
|
||||||
actual_transport = inject.get('ansible_connection', self.transport)
|
actual_transport = inject.get('ansible_connection', self.transport)
|
||||||
actual_private_key_file = inject.get('ansible_ssh_private_key_file', self.private_key_file)
|
actual_private_key_file = inject.get('ansible_ssh_private_key_file', self.private_key_file)
|
||||||
actual_private_key_file = template.template(self.basedir, actual_private_key_file, inject, fail_on_undefined=True)
|
actual_private_key_file = template.template(self.basedir, actual_private_key_file, inject, fail_on_undefined=True)
|
||||||
self.sudo = utils.boolean(inject.get('ansible_sudo', self.sudo))
|
self.become = utils.boolean(inject.get('ansible_become', inject.get('ansible_sudo', inject.get('ansible_su', self.become))))
|
||||||
self.sudo_user = inject.get('ansible_sudo_user', self.sudo_user)
|
self.become_user = inject.get('ansible_become_user', inject.get('ansible_sudo_user', inject.get('ansible_su_user',self.become_user)))
|
||||||
self.sudo_pass = inject.get('ansible_sudo_pass', self.sudo_pass)
|
self.become_pass = inject.get('ansible_become_pass', inject.get('ansible_sudo_pass', inject.get('ansible_su_pass', self.become_pass)))
|
||||||
self.su = inject.get('ansible_su', self.su)
|
self.become_exe = inject.get('ansible_become_exe', inject.get('ansible_sudo_exe', self.become_exe))
|
||||||
self.su_pass = inject.get('ansible_su_pass', self.su_pass)
|
|
||||||
self.sudo_exe = inject.get('ansible_sudo_exe', self.sudo_exe)
|
|
||||||
|
|
||||||
# select default root user in case self.sudo requested
|
# select default root user in case self.become requested
|
||||||
# but no user specified; happens e.g. in host vars when
|
# but no user specified; happens e.g. in host vars when
|
||||||
# just ansible_sudo=True is specified
|
# just ansible_become=True is specified
|
||||||
if self.sudo and self.sudo_user is None:
|
if self.become and self.become_user is None:
|
||||||
self.sudo_user = 'root'
|
self.become_user = 'root'
|
||||||
|
|
||||||
if actual_private_key_file is not None:
|
if actual_private_key_file is not None:
|
||||||
actual_private_key_file = os.path.expanduser(actual_private_key_file)
|
actual_private_key_file = os.path.expanduser(actual_private_key_file)
|
||||||
|
@ -937,7 +925,7 @@ class Runner(object):
|
||||||
actual_user = delegate['user']
|
actual_user = delegate['user']
|
||||||
actual_pass = delegate['pass']
|
actual_pass = delegate['pass']
|
||||||
actual_private_key_file = delegate['private_key_file']
|
actual_private_key_file = delegate['private_key_file']
|
||||||
self.sudo_pass = delegate['sudo_pass']
|
self.become_pass = delegate.get('become_pass',delegate.get('sudo_pass'))
|
||||||
inject = delegate['inject']
|
inject = delegate['inject']
|
||||||
# set resolved delegate_to into inject so modules can call _remote_checksum
|
# set resolved delegate_to into inject so modules can call _remote_checksum
|
||||||
inject['delegate_to'] = self.delegate_to
|
inject['delegate_to'] = self.delegate_to
|
||||||
|
@ -945,7 +933,7 @@ class Runner(object):
|
||||||
# user/pass may still contain variables at this stage
|
# user/pass may still contain variables at this stage
|
||||||
actual_user = template.template(self.basedir, actual_user, inject)
|
actual_user = template.template(self.basedir, actual_user, inject)
|
||||||
actual_pass = template.template(self.basedir, actual_pass, inject)
|
actual_pass = template.template(self.basedir, actual_pass, inject)
|
||||||
self.sudo_pass = template.template(self.basedir, self.sudo_pass, inject)
|
self.become_pass = template.template(self.basedir, self.become_pass, inject)
|
||||||
|
|
||||||
# make actual_user available as __magic__ ansible_ssh_user variable
|
# make actual_user available as __magic__ ansible_ssh_user variable
|
||||||
inject['ansible_ssh_user'] = actual_user
|
inject['ansible_ssh_user'] = actual_user
|
||||||
|
@ -1134,7 +1122,7 @@ class Runner(object):
|
||||||
if "tmp" in tmp:
|
if "tmp" in tmp:
|
||||||
# tmp has already been created
|
# tmp has already been created
|
||||||
return False
|
return False
|
||||||
if not conn.has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES or self.su:
|
if not conn.has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES or self.become_method == 'su':
|
||||||
# tmp is necessary to store module source code
|
# tmp is necessary to store module source code
|
||||||
return True
|
return True
|
||||||
if not conn.has_pipelining:
|
if not conn.has_pipelining:
|
||||||
|
@ -1150,62 +1138,54 @@ class Runner(object):
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _low_level_exec_command(self, conn, cmd, tmp, sudoable=False,
|
def _low_level_exec_command(self, conn, cmd, tmp, sudoable=False,
|
||||||
executable=None, su=False, in_data=None):
|
executable=None, become=False, in_data=None):
|
||||||
''' execute a command string over SSH, return the output '''
|
''' execute a command string over SSH, return the output '''
|
||||||
|
# this can be skipped with powershell modules when there is no analog to a Windows command (like chmod)
|
||||||
|
if cmd:
|
||||||
|
|
||||||
if not cmd:
|
if executable is None:
|
||||||
# this can happen with powershell modules when there is no analog to a Windows command (like chmod)
|
executable = C.DEFAULT_EXECUTABLE
|
||||||
return dict(stdout='', stderr='')
|
|
||||||
|
|
||||||
if executable is None:
|
become_user = self.become_user
|
||||||
executable = C.DEFAULT_EXECUTABLE
|
|
||||||
|
|
||||||
sudo_user = self.sudo_user
|
# compare connection user to (su|sudo)_user and disable if the same
|
||||||
su_user = self.su_user
|
# assume connection type is local if no user attribute
|
||||||
|
this_user = getattr(conn, 'user', getpass.getuser())
|
||||||
|
if (not become and this_user == become_user):
|
||||||
|
sudoable = False
|
||||||
|
become = False
|
||||||
|
|
||||||
# compare connection user to (su|sudo)_user and disable if the same
|
|
||||||
# assume connection type is local if no user attribute
|
|
||||||
this_user = getattr(conn, 'user', getpass.getuser())
|
|
||||||
if (not su and this_user == sudo_user) or (su and this_user == su_user):
|
|
||||||
sudoable = False
|
|
||||||
su = False
|
|
||||||
|
|
||||||
if su:
|
|
||||||
rc, stdin, stdout, stderr = conn.exec_command(cmd,
|
rc, stdin, stdout, stderr = conn.exec_command(cmd,
|
||||||
tmp,
|
tmp,
|
||||||
su=su,
|
become_user=become_user,
|
||||||
su_user=su_user,
|
|
||||||
executable=executable,
|
|
||||||
in_data=in_data)
|
|
||||||
else:
|
|
||||||
rc, stdin, stdout, stderr = conn.exec_command(cmd,
|
|
||||||
tmp,
|
|
||||||
sudo_user,
|
|
||||||
sudoable=sudoable,
|
sudoable=sudoable,
|
||||||
executable=executable,
|
executable=executable,
|
||||||
in_data=in_data)
|
in_data=in_data)
|
||||||
|
|
||||||
if type(stdout) not in [ str, unicode ]:
|
if type(stdout) not in [ str, unicode ]:
|
||||||
out = ''.join(stdout.readlines())
|
out = ''.join(stdout.readlines())
|
||||||
else:
|
else:
|
||||||
out = stdout
|
out = stdout
|
||||||
|
|
||||||
if type(stderr) not in [ str, unicode ]:
|
if type(stderr) not in [ str, unicode ]:
|
||||||
err = ''.join(stderr.readlines())
|
err = ''.join(stderr.readlines())
|
||||||
else:
|
else:
|
||||||
err = stderr
|
err = stderr
|
||||||
|
|
||||||
|
if rc is not None:
|
||||||
|
return dict(rc=rc, stdout=out, stderr=err)
|
||||||
|
else:
|
||||||
|
return dict(stdout=out, stderr=err)
|
||||||
|
|
||||||
|
return dict(rc=None, stdout='', stderr='')
|
||||||
|
|
||||||
if rc is not None:
|
|
||||||
return dict(rc=rc, stdout=out, stderr=err)
|
|
||||||
else:
|
|
||||||
return dict(stdout=out, stderr=err)
|
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
def _remote_chmod(self, conn, mode, path, tmp, sudoable=False, su=False):
|
def _remote_chmod(self, conn, mode, path, tmp, sudoable=False, become=False):
|
||||||
''' issue a remote chmod command '''
|
''' issue a remote chmod command '''
|
||||||
cmd = conn.shell.chmod(mode, path)
|
cmd = conn.shell.chmod(mode, path)
|
||||||
return self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable, su=su)
|
return self._low_level_exec_command(conn, cmd, tmp, sudoable=sudoable, become=become)
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
|
@ -1217,13 +1197,11 @@ class Runner(object):
|
||||||
split_path = path.split(os.path.sep, 1)
|
split_path = path.split(os.path.sep, 1)
|
||||||
expand_path = split_path[0]
|
expand_path = split_path[0]
|
||||||
if expand_path == '~':
|
if expand_path == '~':
|
||||||
if self.sudo and self.sudo_user:
|
if self.become and self.become_user:
|
||||||
expand_path = '~%s' % self.sudo_user
|
expand_path = '~%s' % self.become_user
|
||||||
elif self.su and self.su_user:
|
|
||||||
expand_path = '~%s' % self.su_user
|
|
||||||
|
|
||||||
cmd = conn.shell.expand_user(expand_path)
|
cmd = conn.shell.expand_user(expand_path)
|
||||||
data = self._low_level_exec_command(conn, cmd, tmp, sudoable=False, su=False)
|
data = self._low_level_exec_command(conn, cmd, tmp, sudoable=False, become=False)
|
||||||
initial_fragment = utils.last_non_blank_line(data['stdout'])
|
initial_fragment = utils.last_non_blank_line(data['stdout'])
|
||||||
|
|
||||||
if not initial_fragment:
|
if not initial_fragment:
|
||||||
|
@ -1287,11 +1265,11 @@ class Runner(object):
|
||||||
''' make and return a temporary path on a remote box '''
|
''' make and return a temporary path on a remote box '''
|
||||||
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
|
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
|
||||||
use_system_tmp = False
|
use_system_tmp = False
|
||||||
if (self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root'):
|
if self.become and self.become_user != 'root':
|
||||||
use_system_tmp = True
|
use_system_tmp = True
|
||||||
|
|
||||||
tmp_mode = None
|
tmp_mode = None
|
||||||
if self.remote_user != 'root' or ((self.sudo and self.sudo_user != 'root') or (self.su and self.su_user != 'root')):
|
if self.remote_user != 'root' or (self.become and self.become_user != 'root'):
|
||||||
tmp_mode = 'a+rx'
|
tmp_mode = 'a+rx'
|
||||||
|
|
||||||
cmd = conn.shell.mkdtemp(basefile, use_system_tmp, tmp_mode)
|
cmd = conn.shell.mkdtemp(basefile, use_system_tmp, tmp_mode)
|
||||||
|
|
|
@ -125,7 +125,7 @@ class ActionModule(object):
|
||||||
xfered = self.runner._transfer_str(conn, tmp, 'src', resultant)
|
xfered = self.runner._transfer_str(conn, tmp, 'src', resultant)
|
||||||
|
|
||||||
# fix file permissions when the copy is done as a different user
|
# fix file permissions when the copy is done as a different user
|
||||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
if self.runner.become and self.runner.become_user != 'root':
|
||||||
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
||||||
|
|
||||||
# run the copy module
|
# run the copy module
|
||||||
|
|
|
@ -234,7 +234,7 @@ class ActionModule(object):
|
||||||
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
||||||
|
|
||||||
# fix file permissions when the copy is done as a different user
|
# fix file permissions when the copy is done as a different user
|
||||||
if (self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root') and not raw:
|
if self.runner.become and self.runner.become_user != 'root' and not raw:
|
||||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
|
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
|
||||||
|
|
||||||
if raw:
|
if raw:
|
||||||
|
|
|
@ -78,7 +78,7 @@ class ActionModule(object):
|
||||||
|
|
||||||
# use slurp if sudo and permissions are lacking
|
# use slurp if sudo and permissions are lacking
|
||||||
remote_data = None
|
remote_data = None
|
||||||
if remote_checksum in ('1', '2') or self.runner.sudo:
|
if remote_checksum in ('1', '2') or self.runner.become:
|
||||||
slurpres = self.runner._execute_module(conn, tmp, 'slurp', 'src=%s' % source, inject=inject)
|
slurpres = self.runner._execute_module(conn, tmp, 'slurp', 'src=%s' % source, inject=inject)
|
||||||
if slurpres.is_successful():
|
if slurpres.is_successful():
|
||||||
if slurpres.result['encoding'] == 'base64':
|
if slurpres.result['encoding'] == 'base64':
|
||||||
|
|
|
@ -50,7 +50,7 @@ class ActionModule(object):
|
||||||
tmp_src = tmp + src
|
tmp_src = tmp + src
|
||||||
conn.put_file(src, tmp_src)
|
conn.put_file(src, tmp_src)
|
||||||
|
|
||||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
if self.runner.become and self.runner.become_user != 'root':
|
||||||
if not self.runner.noop_on_check(inject):
|
if not self.runner.noop_on_check(inject):
|
||||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
||||||
|
|
||||||
|
|
|
@ -113,8 +113,7 @@ class ActionModule(object):
|
||||||
|
|
||||||
sudoable = True
|
sudoable = True
|
||||||
# set file permissions, more permissive when the copy is done as a different user
|
# set file permissions, more permissive when the copy is done as a different user
|
||||||
if ((self.runner.sudo and self.runner.sudo_user != 'root') or
|
if self.runner.become and self.runner.become_user != 'root':
|
||||||
(self.runner.su and self.runner.su_user != 'root')):
|
|
||||||
chmod_mode = 'a+rx'
|
chmod_mode = 'a+rx'
|
||||||
sudoable = False
|
sudoable = False
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -78,7 +78,7 @@ class ActionModule(object):
|
||||||
|
|
||||||
# Store original transport and sudo values.
|
# Store original transport and sudo values.
|
||||||
self.original_transport = inject.get('ansible_connection', self.runner.transport)
|
self.original_transport = inject.get('ansible_connection', self.runner.transport)
|
||||||
self.original_sudo = self.runner.sudo
|
self.original_become = self.runner.become
|
||||||
self.transport_overridden = False
|
self.transport_overridden = False
|
||||||
|
|
||||||
if inject.get('delegate_to') is None:
|
if inject.get('delegate_to') is None:
|
||||||
|
@ -87,7 +87,7 @@ class ActionModule(object):
|
||||||
if self.original_transport != 'local':
|
if self.original_transport != 'local':
|
||||||
inject['ansible_connection'] = 'local'
|
inject['ansible_connection'] = 'local'
|
||||||
self.transport_overridden = True
|
self.transport_overridden = True
|
||||||
self.runner.sudo = False
|
self.runner.become = False
|
||||||
|
|
||||||
def run(self, conn, tmp, module_name, module_args,
|
def run(self, conn, tmp, module_name, module_args,
|
||||||
inject, complex_args=None, **kwargs):
|
inject, complex_args=None, **kwargs):
|
||||||
|
@ -143,7 +143,7 @@ class ActionModule(object):
|
||||||
# use a delegate host instead of localhost
|
# use a delegate host instead of localhost
|
||||||
use_delegate = True
|
use_delegate = True
|
||||||
|
|
||||||
# COMPARE DELEGATE, HOST AND TRANSPORT
|
# COMPARE DELEGATE, HOST AND TRANSPORT
|
||||||
process_args = False
|
process_args = False
|
||||||
if not dest_host is src_host and self.original_transport != 'local':
|
if not dest_host is src_host and self.original_transport != 'local':
|
||||||
# interpret and inject remote host info into src or dest
|
# interpret and inject remote host info into src or dest
|
||||||
|
@ -160,7 +160,7 @@ class ActionModule(object):
|
||||||
if not use_delegate or not user:
|
if not use_delegate or not user:
|
||||||
user = inject.get('ansible_ssh_user',
|
user = inject.get('ansible_ssh_user',
|
||||||
self.runner.remote_user)
|
self.runner.remote_user)
|
||||||
|
|
||||||
if use_delegate:
|
if use_delegate:
|
||||||
# FIXME
|
# FIXME
|
||||||
private_key = inject.get('ansible_ssh_private_key_file', self.runner.private_key_file)
|
private_key = inject.get('ansible_ssh_private_key_file', self.runner.private_key_file)
|
||||||
|
@ -172,7 +172,7 @@ class ActionModule(object):
|
||||||
if not private_key is None:
|
if not private_key is None:
|
||||||
private_key = os.path.expanduser(private_key)
|
private_key = os.path.expanduser(private_key)
|
||||||
options['private_key'] = private_key
|
options['private_key'] = private_key
|
||||||
|
|
||||||
# use the mode to define src and dest's url
|
# use the mode to define src and dest's url
|
||||||
if options.get('mode', 'push') == 'pull':
|
if options.get('mode', 'push') == 'pull':
|
||||||
# src is a remote path: <user>@<host>, dest is a local path
|
# src is a remote path: <user>@<host>, dest is a local path
|
||||||
|
@ -192,7 +192,7 @@ class ActionModule(object):
|
||||||
rsync_path = options.get('rsync_path', None)
|
rsync_path = options.get('rsync_path', None)
|
||||||
|
|
||||||
# If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument.
|
# If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument.
|
||||||
if not rsync_path and self.transport_overridden and self.original_sudo and not dest_is_local:
|
if not rsync_path and self.transport_overridden and self.original_become and not dest_is_local and self.runner.become_method == 'sudo':
|
||||||
rsync_path = 'sudo rsync'
|
rsync_path = 'sudo rsync'
|
||||||
|
|
||||||
# make sure rsync path is quoted.
|
# make sure rsync path is quoted.
|
||||||
|
@ -206,8 +206,8 @@ class ActionModule(object):
|
||||||
# run the module and store the result
|
# run the module and store the result
|
||||||
result = self.runner._execute_module(conn, tmp, 'synchronize', module_args, complex_args=options, inject=inject)
|
result = self.runner._execute_module(conn, tmp, 'synchronize', module_args, complex_args=options, inject=inject)
|
||||||
|
|
||||||
# reset the sudo property
|
# reset the sudo property
|
||||||
self.runner.sudo = self.original_sudo
|
self.runner.become = self.original_become
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ class ActionModule(object):
|
||||||
xfered = self.runner._transfer_str(conn, tmp, 'source', resultant)
|
xfered = self.runner._transfer_str(conn, tmp, 'source', resultant)
|
||||||
|
|
||||||
# fix file permissions when the copy is done as a different user
|
# fix file permissions when the copy is done as a different user
|
||||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
if self.runner.become and self.runner.become_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
||||||
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
||||||
|
|
||||||
# run the copy module
|
# run the copy module
|
||||||
|
|
|
@ -99,7 +99,7 @@ class ActionModule(object):
|
||||||
# handle check mode client side
|
# handle check mode client side
|
||||||
# fix file permissions when the copy is done as a different user
|
# fix file permissions when the copy is done as a different user
|
||||||
if copy:
|
if copy:
|
||||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
if self.runner.become and self.runner.become_user != 'root':
|
||||||
if not self.runner.noop_on_check(inject):
|
if not self.runner.noop_on_check(inject):
|
||||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp)
|
||||||
# Build temporary module_args.
|
# Build temporary module_args.
|
||||||
|
|
|
@ -230,7 +230,7 @@ class ActionModule(object):
|
||||||
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
self._remove_tempfile_if_content_defined(content, content_tempfile)
|
||||||
|
|
||||||
# fix file permissions when the copy is done as a different user
|
# fix file permissions when the copy is done as a different user
|
||||||
if (self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root') and not raw:
|
if self.runner.become and self.runner.become_user != 'root' and not raw:
|
||||||
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
|
self.runner._remote_chmod(conn, 'a+r', tmp_src, tmp_path)
|
||||||
|
|
||||||
if raw:
|
if raw:
|
||||||
|
|
|
@ -109,7 +109,7 @@ class ActionModule(object):
|
||||||
xfered = self.runner._transfer_str(conn, tmp, 'source', resultant)
|
xfered = self.runner._transfer_str(conn, tmp, 'source', resultant)
|
||||||
|
|
||||||
# fix file permissions when the copy is done as a different user
|
# fix file permissions when the copy is done as a different user
|
||||||
if self.runner.sudo and self.runner.sudo_user != 'root' or self.runner.su and self.runner.su_user != 'root':
|
if self.runner.become and self.runner.become_user != 'root':
|
||||||
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
self.runner._remote_chmod(conn, 'a+r', xfered, tmp)
|
||||||
|
|
||||||
# run the copy module
|
# run the copy module
|
||||||
|
|
|
@ -50,6 +50,7 @@ class Connection(object):
|
||||||
self.accport = port[1]
|
self.accport = port[1]
|
||||||
self.is_connected = False
|
self.is_connected = False
|
||||||
self.has_pipelining = False
|
self.has_pipelining = False
|
||||||
|
self.become_methods_supported=['sudo']
|
||||||
|
|
||||||
if not self.port:
|
if not self.port:
|
||||||
self.port = constants.DEFAULT_REMOTE_PORT
|
self.port = constants.DEFAULT_REMOTE_PORT
|
||||||
|
@ -226,11 +227,11 @@ class Connection(object):
|
||||||
else:
|
else:
|
||||||
return response.get('rc') == 0
|
return response.get('rc') == 0
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the remote host '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
if su or su_user:
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
raise AnsibleError("Internal Error: this module does not support running commands via su")
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
@ -238,8 +239,8 @@ class Connection(object):
|
||||||
if executable == "":
|
if executable == "":
|
||||||
executable = constants.DEFAULT_EXECUTABLE
|
executable = constants.DEFAULT_EXECUTABLE
|
||||||
|
|
||||||
if self.runner.sudo and sudoable and sudo_user:
|
if self.runner.become and sudoable:
|
||||||
cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe)
|
||||||
|
|
||||||
vvv("EXEC COMMAND %s" % cmd)
|
vvv("EXEC COMMAND %s" % cmd)
|
||||||
|
|
||||||
|
@ -292,8 +293,8 @@ class Connection(object):
|
||||||
if fd.tell() >= fstat.st_size:
|
if fd.tell() >= fstat.st_size:
|
||||||
last = True
|
last = True
|
||||||
data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last)
|
data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last)
|
||||||
if self.runner.sudo:
|
if self.runner.become:
|
||||||
data['user'] = self.runner.sudo_user
|
data['user'] = self.runner.become_user
|
||||||
data = utils.jsonify(data)
|
data = utils.jsonify(data)
|
||||||
data = utils.encrypt(self.key, data)
|
data = utils.encrypt(self.key, data)
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import subprocess
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
from ansible import utils
|
from ansible import utils
|
||||||
from ansible.callbacks import vvv
|
from ansible.callbacks import vvv
|
||||||
|
import ansible.constants as C
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' Local chroot based connections '''
|
''' Local chroot based connections '''
|
||||||
|
@ -31,6 +32,7 @@ class Connection(object):
|
||||||
def __init__(self, runner, host, port, *args, **kwargs):
|
def __init__(self, runner, host, port, *args, **kwargs):
|
||||||
self.chroot = host
|
self.chroot = host
|
||||||
self.has_pipelining = False
|
self.has_pipelining = False
|
||||||
|
self.become_methods_supported=C.BECOME_METHODS
|
||||||
|
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0:
|
||||||
raise errors.AnsibleError("chroot connection requires running as root")
|
raise errors.AnsibleError("chroot connection requires running as root")
|
||||||
|
@ -60,16 +62,16 @@ class Connection(object):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the chroot '''
|
''' run a command on the chroot '''
|
||||||
|
|
||||||
if su or su_user:
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
# We enter chroot as root so sudo stuff can be ignored
|
# We enter chroot as root so we ignore privlege escalation?
|
||||||
|
|
||||||
if executable:
|
if executable:
|
||||||
local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
|
local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
|
||||||
|
|
|
@ -53,6 +53,8 @@ class Connection(object):
|
||||||
else:
|
else:
|
||||||
self.port = port
|
self.port = port
|
||||||
|
|
||||||
|
self.become_methods_supported=[]
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
''' activates the connection object '''
|
''' activates the connection object '''
|
||||||
|
|
||||||
|
@ -64,11 +66,11 @@ class Connection(object):
|
||||||
socket = self.context.socket(zmq.REQ)
|
socket = self.context.socket(zmq.REQ)
|
||||||
addr = "tcp://%s:%s" % (self.host, self.port)
|
addr = "tcp://%s:%s" % (self.host, self.port)
|
||||||
socket.connect(addr)
|
socket.connect(addr)
|
||||||
self.socket = socket
|
self.socket = socket
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=None):
|
def exec_command(self, cmd, tmp_path, become_user, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the remote host '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
|
@ -76,7 +78,7 @@ class Connection(object):
|
||||||
|
|
||||||
vvv("EXEC COMMAND %s" % cmd)
|
vvv("EXEC COMMAND %s" % cmd)
|
||||||
|
|
||||||
if (self.runner.sudo and sudoable) or (self.runner.su and su):
|
if self.runner.become and sudoable:
|
||||||
raise errors.AnsibleError(
|
raise errors.AnsibleError(
|
||||||
"When using fireball, do not specify sudo or su to run your tasks. " +
|
"When using fireball, do not specify sudo or su to run your tasks. " +
|
||||||
"Instead sudo the fireball action with sudo. " +
|
"Instead sudo the fireball action with sudo. " +
|
||||||
|
|
|
@ -53,16 +53,14 @@ class Connection(object):
|
||||||
self.client = fc.Client(self.host)
|
self.client = fc.Client(self.host)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False,
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False,
|
||||||
executable='/bin/sh', in_data=None, su=None, su_user=None):
|
executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the remote minion '''
|
''' run a command on the remote minion '''
|
||||||
|
|
||||||
if su or su_user:
|
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
|
# totally ignores privlege escalation
|
||||||
vvv("EXEC %s" % (cmd), host=self.host)
|
vvv("EXEC %s" % (cmd), host=self.host)
|
||||||
p = self.client.command.run(cmd)[self.host]
|
p = self.client.command.run(cmd)[self.host]
|
||||||
return (p[0], '', p[1], p[2])
|
return (p[0], '', p[1], p[2])
|
||||||
|
|
|
@ -24,6 +24,7 @@ import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
from ansible.callbacks import vvv
|
from ansible.callbacks import vvv
|
||||||
|
import ansible.constants as C
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' Local chroot based connections '''
|
''' Local chroot based connections '''
|
||||||
|
@ -61,6 +62,7 @@ class Connection(object):
|
||||||
self.runner = runner
|
self.runner = runner
|
||||||
self.host = host
|
self.host = host
|
||||||
self.has_pipelining = False
|
self.has_pipelining = False
|
||||||
|
self.become_methods_supported=C.BECOME_METHODS
|
||||||
|
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0:
|
||||||
raise errors.AnsibleError("jail connection requires running as root")
|
raise errors.AnsibleError("jail connection requires running as root")
|
||||||
|
@ -91,16 +93,16 @@ class Connection(object):
|
||||||
local_cmd = '%s "%s" %s' % (self.jexec_cmd, self.jail, cmd)
|
local_cmd = '%s "%s" %s' % (self.jexec_cmd, self.jail, cmd)
|
||||||
return local_cmd
|
return local_cmd
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the chroot '''
|
''' run a command on the chroot '''
|
||||||
|
|
||||||
if su or su_user:
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
# We enter chroot as root so sudo stuff can be ignored
|
# Ignores privilege escalation
|
||||||
local_cmd = self._generate_cmd(executable, cmd)
|
local_cmd = self._generate_cmd(executable, cmd)
|
||||||
|
|
||||||
vvv("EXEC %s" % (local_cmd), host=self.jail)
|
vvv("EXEC %s" % (local_cmd), host=self.jail)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
from ansible.callbacks import vvv
|
from ansible.callbacks import vvv
|
||||||
|
import ansible.constants as C
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' Local lxc based connections '''
|
''' Local lxc based connections '''
|
||||||
|
@ -50,6 +51,7 @@ class Connection(object):
|
||||||
self.host = host
|
self.host = host
|
||||||
# port is unused, since this is local
|
# port is unused, since this is local
|
||||||
self.port = port
|
self.port = port
|
||||||
|
self.become_methods_supported=C.BECOME_METHODS
|
||||||
|
|
||||||
def connect(self, port=None):
|
def connect(self, port=None):
|
||||||
''' connect to the lxc; nothing to do here '''
|
''' connect to the lxc; nothing to do here '''
|
||||||
|
@ -65,16 +67,16 @@ class Connection(object):
|
||||||
local_cmd = '%s -q -c lxc:/// lxc-enter-namespace %s -- %s' % (self.cmd, self.lxc, cmd)
|
local_cmd = '%s -q -c lxc:/// lxc-enter-namespace %s -- %s' % (self.cmd, self.lxc, cmd)
|
||||||
return local_cmd
|
return local_cmd
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the chroot '''
|
''' run a command on the chroot '''
|
||||||
|
|
||||||
if su or su_user:
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
# We enter lxc as root so sudo stuff can be ignored
|
# We ignore privelege escalation!
|
||||||
local_cmd = self._generate_cmd(executable, cmd)
|
local_cmd = self._generate_cmd(executable, cmd)
|
||||||
|
|
||||||
vvv("EXEC %s" % (local_cmd), host=self.lxc)
|
vvv("EXEC %s" % (local_cmd), host=self.lxc)
|
||||||
|
|
|
@ -26,6 +26,7 @@ from ansible import errors
|
||||||
from ansible import utils
|
from ansible import utils
|
||||||
from ansible.callbacks import vvv
|
from ansible.callbacks import vvv
|
||||||
|
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' Local based connections '''
|
''' Local based connections '''
|
||||||
|
|
||||||
|
@ -33,31 +34,34 @@ class Connection(object):
|
||||||
self.runner = runner
|
self.runner = runner
|
||||||
self.host = host
|
self.host = host
|
||||||
# port is unused, since this is local
|
# port is unused, since this is local
|
||||||
self.port = port
|
self.port = port
|
||||||
self.has_pipelining = False
|
self.has_pipelining = False
|
||||||
|
|
||||||
|
# TODO: add su(needs tty), pbrun, pfexec
|
||||||
|
self.become_methods_supported=['sudo']
|
||||||
|
|
||||||
def connect(self, port=None):
|
def connect(self, port=None):
|
||||||
''' connect to the local host; nothing to do here '''
|
''' connect to the local host; nothing to do here '''
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the local host '''
|
''' run a command on the local host '''
|
||||||
|
|
||||||
# su requires to be run from a terminal, and therefore isn't supported here (yet?)
|
# su requires to be run from a terminal, and therefore isn't supported here (yet?)
|
||||||
if su or su_user:
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
if not self.runner.sudo or not sudoable:
|
if self.runner.become and sudoable:
|
||||||
|
local_cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '-H', self.runner.become_exe)
|
||||||
|
else:
|
||||||
if executable:
|
if executable:
|
||||||
local_cmd = executable.split() + ['-c', cmd]
|
local_cmd = executable.split() + ['-c', cmd]
|
||||||
else:
|
else:
|
||||||
local_cmd = cmd
|
local_cmd = cmd
|
||||||
else:
|
|
||||||
local_cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
|
||||||
executable = executable.split()[0] if executable else None
|
executable = executable.split()[0] if executable else None
|
||||||
|
|
||||||
vvv("EXEC %s" % (local_cmd), host=self.host)
|
vvv("EXEC %s" % (local_cmd), host=self.host)
|
||||||
|
@ -66,13 +70,19 @@ class Connection(object):
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
if self.runner.sudo and sudoable and self.runner.sudo_pass:
|
if self.runner.become and sudoable and self.runner.become_pass:
|
||||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL,
|
fcntl.fcntl(p.stdout, fcntl.F_SETFL,
|
||||||
fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
||||||
fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||||
sudo_output = ''
|
become_output = ''
|
||||||
while not sudo_output.endswith(prompt) and success_key not in sudo_output:
|
while success_key not in become_output:
|
||||||
|
|
||||||
|
if prompt and become_output.endswith(prompt):
|
||||||
|
break
|
||||||
|
if utils.su_prompts.check_su_prompt(become_output):
|
||||||
|
break
|
||||||
|
|
||||||
rfd, wfd, efd = select.select([p.stdout, p.stderr], [],
|
rfd, wfd, efd = select.select([p.stdout, p.stderr], [],
|
||||||
[p.stdout, p.stderr], self.runner.timeout)
|
[p.stdout, p.stderr], self.runner.timeout)
|
||||||
if p.stdout in rfd:
|
if p.stdout in rfd:
|
||||||
|
@ -81,13 +91,13 @@ class Connection(object):
|
||||||
chunk = p.stderr.read()
|
chunk = p.stderr.read()
|
||||||
else:
|
else:
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
raise errors.AnsibleError('timeout waiting for sudo password prompt:\n' + sudo_output)
|
raise errors.AnsibleError('timeout waiting for %s password prompt:\n' % self.runner.become_method + become_output)
|
||||||
if not chunk:
|
if not chunk:
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
raise errors.AnsibleError('sudo output closed while waiting for password prompt:\n' + sudo_output)
|
raise errors.AnsibleError('%s output closed while waiting for password prompt:\n' % self.runner.become_method + become_output)
|
||||||
sudo_output += chunk
|
become_output += chunk
|
||||||
if success_key not in sudo_output:
|
if success_key not in become_output:
|
||||||
p.stdin.write(self.runner.sudo_pass + '\n')
|
p.stdin.write(self.runner.become_pass + '\n')
|
||||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
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)
|
fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,9 @@ class Connection(object):
|
||||||
self.private_key_file = private_key_file
|
self.private_key_file = private_key_file
|
||||||
self.has_pipelining = False
|
self.has_pipelining = False
|
||||||
|
|
||||||
|
# TODO: add pbrun, pfexec
|
||||||
|
self.become_methods_supported=['sudo', 'su', 'pbrun']
|
||||||
|
|
||||||
def _cache_key(self):
|
def _cache_key(self):
|
||||||
return "%s__%s__" % (self.host, self.user)
|
return "%s__%s__" % (self.host, self.user)
|
||||||
|
|
||||||
|
@ -184,9 +187,12 @@ class Connection(object):
|
||||||
|
|
||||||
return ssh
|
return ssh
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the remote host '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
|
if self.runner.become and sudoable and self.runner.become_method not in self.become_methods_supported:
|
||||||
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
|
@ -206,7 +212,7 @@ class Connection(object):
|
||||||
|
|
||||||
no_prompt_out = ''
|
no_prompt_out = ''
|
||||||
no_prompt_err = ''
|
no_prompt_err = ''
|
||||||
if not (self.runner.sudo and sudoable) and not (self.runner.su and su):
|
if not (self.runner.become and sudoable):
|
||||||
|
|
||||||
if executable:
|
if executable:
|
||||||
quoted_command = executable + ' -c ' + pipes.quote(cmd)
|
quoted_command = executable + ' -c ' + pipes.quote(cmd)
|
||||||
|
@ -224,50 +230,46 @@ class Connection(object):
|
||||||
chan.get_pty(term=os.getenv('TERM', 'vt100'),
|
chan.get_pty(term=os.getenv('TERM', 'vt100'),
|
||||||
width=int(os.getenv('COLUMNS', 0)),
|
width=int(os.getenv('COLUMNS', 0)),
|
||||||
height=int(os.getenv('LINES', 0)))
|
height=int(os.getenv('LINES', 0)))
|
||||||
if self.runner.sudo or sudoable:
|
if self.runner.become and sudoable:
|
||||||
shcmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
shcmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe)
|
||||||
elif self.runner.su or su:
|
|
||||||
shcmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd)
|
|
||||||
|
|
||||||
vvv("EXEC %s" % shcmd, host=self.host)
|
vvv("EXEC %s" % shcmd, host=self.host)
|
||||||
sudo_output = ''
|
become_output = ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
chan.exec_command(shcmd)
|
chan.exec_command(shcmd)
|
||||||
|
|
||||||
if self.runner.sudo_pass or self.runner.su_pass:
|
if self.runner.become_pass:
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
if success_key in sudo_output or \
|
if success_key in become_output or \
|
||||||
(self.runner.sudo_pass and sudo_output.endswith(prompt)) or \
|
(prompt and become_output.endswith(prompt)) or \
|
||||||
(self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)):
|
utils.su_prompts.check_su_prompt(become_output)):
|
||||||
break
|
break
|
||||||
chunk = chan.recv(bufsize)
|
chunk = chan.recv(bufsize)
|
||||||
|
|
||||||
if not chunk:
|
if not chunk:
|
||||||
if 'unknown user' in sudo_output:
|
if 'unknown user' in become_output:
|
||||||
raise errors.AnsibleError(
|
raise errors.AnsibleError(
|
||||||
'user %s does not exist' % sudo_user)
|
'user %s does not exist' % become_user)
|
||||||
else:
|
else:
|
||||||
raise errors.AnsibleError('ssh connection ' +
|
raise errors.AnsibleError('ssh connection ' +
|
||||||
'closed waiting for password prompt')
|
'closed waiting for password prompt')
|
||||||
sudo_output += chunk
|
become_output += chunk
|
||||||
|
|
||||||
if success_key not in sudo_output:
|
if success_key not in become_output:
|
||||||
|
|
||||||
if sudoable:
|
if sudoable:
|
||||||
chan.sendall(self.runner.sudo_pass + '\n')
|
chan.sendall(self.runner.become_pass + '\n')
|
||||||
elif su:
|
|
||||||
chan.sendall(self.runner.su_pass + '\n')
|
|
||||||
else:
|
else:
|
||||||
no_prompt_out += sudo_output
|
no_prompt_out += become_output
|
||||||
no_prompt_err += sudo_output
|
no_prompt_err += become_output
|
||||||
|
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
|
|
||||||
raise errors.AnsibleError('ssh timed out waiting for sudo.\n' + sudo_output)
|
raise errors.AnsibleError('ssh timed out waiting for privilege escalation.\n' + become_output)
|
||||||
|
|
||||||
stdout = ''.join(chan.makefile('rb', bufsize))
|
stdout = ''.join(chan.makefile('rb', bufsize))
|
||||||
stderr = ''.join(chan.makefile_stderr('rb', bufsize))
|
stderr = ''.join(chan.makefile_stderr('rb', bufsize))
|
||||||
|
|
|
@ -34,6 +34,7 @@ from ansible.callbacks import vvv
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
from ansible import utils
|
from ansible import utils
|
||||||
|
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' ssh based connections '''
|
''' ssh based connections '''
|
||||||
|
|
||||||
|
@ -48,6 +49,9 @@ class Connection(object):
|
||||||
self.HASHED_KEY_MAGIC = "|1|"
|
self.HASHED_KEY_MAGIC = "|1|"
|
||||||
self.has_pipelining = True
|
self.has_pipelining = True
|
||||||
|
|
||||||
|
# TODO: add pbrun, pfexec
|
||||||
|
self.become_methods_supported=['sudo', 'su', 'pbrun']
|
||||||
|
|
||||||
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
||||||
self.cp_dir = utils.prepare_writeable_dir('$HOME/.ansible/cp',mode=0700)
|
self.cp_dir = utils.prepare_writeable_dir('$HOME/.ansible/cp',mode=0700)
|
||||||
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
|
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN)
|
||||||
|
@ -140,7 +144,7 @@ class Connection(object):
|
||||||
os.write(self.wfd, "%s\n" % self.password)
|
os.write(self.wfd, "%s\n" % self.password)
|
||||||
os.close(self.wfd)
|
os.close(self.wfd)
|
||||||
|
|
||||||
def _communicate(self, p, stdin, indata, su=False, sudoable=False, prompt=None):
|
def _communicate(self, p, stdin, indata, sudoable=False, prompt=None):
|
||||||
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
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)
|
fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK)
|
||||||
# We can't use p.communicate here because the ControlMaster may have stdout open as well
|
# We can't use p.communicate here because the ControlMaster may have stdout open as well
|
||||||
|
@ -157,23 +161,20 @@ class Connection(object):
|
||||||
while True:
|
while True:
|
||||||
rfd, wfd, efd = select.select(rpipes, [], rpipes, 1)
|
rfd, wfd, efd = select.select(rpipes, [], rpipes, 1)
|
||||||
|
|
||||||
# fail early if the sudo/su password is wrong
|
# fail early if the become password is wrong
|
||||||
if self.runner.sudo and sudoable:
|
if self.runner.become and sudoable:
|
||||||
if self.runner.sudo_pass:
|
if self.runner.become_pass:
|
||||||
incorrect_password = gettext.dgettext(
|
incorrect_password = gettext.dgettext(
|
||||||
"sudo", "Sorry, try again.")
|
"Privilege Escalation", "Sorry, try again.")
|
||||||
if stdout.endswith("%s\r\n%s" % (incorrect_password,
|
if stdout.endswith("%s\r\n%s" % (incorrect_password,
|
||||||
prompt)):
|
prompt)):
|
||||||
raise errors.AnsibleError('Incorrect sudo password')
|
raise errors.AnsibleError('Incorrect become password')
|
||||||
|
|
||||||
if stdout.endswith(prompt):
|
if prompt:
|
||||||
raise errors.AnsibleError('Missing sudo password')
|
if stdout.endswith(prompt):
|
||||||
|
raise errors.AnsibleError('Missing become password')
|
||||||
if self.runner.su and su and self.runner.su_pass:
|
elif stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)):
|
||||||
incorrect_password = gettext.dgettext(
|
raise errors.AnsibleError('Incorrect becom password')
|
||||||
"su", "Sorry")
|
|
||||||
if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)):
|
|
||||||
raise errors.AnsibleError('Incorrect su password')
|
|
||||||
|
|
||||||
if p.stdout in rfd:
|
if p.stdout in rfd:
|
||||||
dat = os.read(p.stdout.fileno(), 9000)
|
dat = os.read(p.stdout.fileno(), 9000)
|
||||||
|
@ -256,9 +257,12 @@ class Connection(object):
|
||||||
vvv("EXEC previous known host file not found for %s" % host)
|
vvv("EXEC previous known host file not found for %s" % host)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=False):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None):
|
||||||
''' run a command on the remote host '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
ssh_cmd = self._password_cmd()
|
ssh_cmd = self._password_cmd()
|
||||||
ssh_cmd += ["ssh", "-C"]
|
ssh_cmd += ["ssh", "-C"]
|
||||||
if not in_data:
|
if not in_data:
|
||||||
|
@ -276,25 +280,22 @@ class Connection(object):
|
||||||
ssh_cmd += ['-6']
|
ssh_cmd += ['-6']
|
||||||
ssh_cmd += [self.host]
|
ssh_cmd += [self.host]
|
||||||
|
|
||||||
if su and su_user:
|
if self.runner.become and sudoable:
|
||||||
sudocmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd)
|
becomecmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe)
|
||||||
ssh_cmd.append(sudocmd)
|
ssh_cmd.append(becomecmd)
|
||||||
elif not self.runner.sudo or not sudoable:
|
else:
|
||||||
prompt = None
|
prompt = None
|
||||||
if executable:
|
if executable:
|
||||||
ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd))
|
ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd))
|
||||||
else:
|
else:
|
||||||
ssh_cmd.append(cmd)
|
ssh_cmd.append(cmd)
|
||||||
else:
|
|
||||||
sudocmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
|
||||||
ssh_cmd.append(sudocmd)
|
|
||||||
|
|
||||||
vvv("EXEC %s" % ' '.join(ssh_cmd), host=self.host)
|
vvv("EXEC %s" % ' '.join(ssh_cmd), host=self.host)
|
||||||
|
|
||||||
not_in_host_file = self.not_in_host_file(self.host)
|
not_in_host_file = self.not_in_host_file(self.host)
|
||||||
|
|
||||||
if C.HOST_KEY_CHECKING and not_in_host_file:
|
if C.HOST_KEY_CHECKING and not_in_host_file:
|
||||||
# lock around the initial SSH connectivity so the user prompt about whether to add
|
# lock around the initial SSH connectivity so the user prompt about whether to add
|
||||||
# the host to known hosts is not intermingled with multiprocess output.
|
# the host to known hosts is not intermingled with multiprocess output.
|
||||||
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
|
||||||
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)
|
fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)
|
||||||
|
@ -306,9 +307,8 @@ class Connection(object):
|
||||||
|
|
||||||
no_prompt_out = ''
|
no_prompt_out = ''
|
||||||
no_prompt_err = ''
|
no_prompt_err = ''
|
||||||
if (self.runner.sudo and sudoable and self.runner.sudo_pass) or \
|
if self.runner.become and sudoable and self.runner.become_pass:
|
||||||
(self.runner.su and su and self.runner.su_pass):
|
# several cases are handled for escalated privileges with password
|
||||||
# several cases are handled for sudo privileges with password
|
|
||||||
# * NOPASSWD (tty & no-tty): detect success_key on stdout
|
# * NOPASSWD (tty & no-tty): detect success_key on stdout
|
||||||
# * without NOPASSWD:
|
# * without NOPASSWD:
|
||||||
# * detect prompt on stdout (tty)
|
# * detect prompt on stdout (tty)
|
||||||
|
@ -317,13 +317,14 @@ class Connection(object):
|
||||||
fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||||
fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
fcntl.fcntl(p.stderr, fcntl.F_SETFL,
|
||||||
fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||||
sudo_output = ''
|
become_output = ''
|
||||||
sudo_errput = ''
|
become_errput = ''
|
||||||
|
|
||||||
while True:
|
while success_key not in become_output:
|
||||||
if success_key in sudo_output or \
|
|
||||||
(self.runner.sudo_pass and sudo_output.endswith(prompt)) or \
|
if prompt and become_output.endswith(prompt):
|
||||||
(self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)):
|
break
|
||||||
|
if utils.su_prompts.check_su_prompt(become_output):
|
||||||
break
|
break
|
||||||
|
|
||||||
rfd, wfd, efd = select.select([p.stdout, p.stderr], [],
|
rfd, wfd, efd = select.select([p.stdout, p.stderr], [],
|
||||||
|
@ -331,36 +332,34 @@ class Connection(object):
|
||||||
if p.stderr in rfd:
|
if p.stderr in rfd:
|
||||||
chunk = p.stderr.read()
|
chunk = p.stderr.read()
|
||||||
if not chunk:
|
if not chunk:
|
||||||
raise errors.AnsibleError('ssh connection closed waiting for sudo or su password prompt')
|
raise errors.AnsibleError('ssh connection closed waiting for a privilege escalation password prompt')
|
||||||
sudo_errput += chunk
|
become_errput += chunk
|
||||||
incorrect_password = gettext.dgettext(
|
incorrect_password = gettext.dgettext(
|
||||||
"sudo", "Sorry, try again.")
|
"become", "Sorry, try again.")
|
||||||
if sudo_errput.strip().endswith("%s%s" % (prompt, incorrect_password)):
|
if become_errput.strip().endswith("%s%s" % (prompt, incorrect_password)):
|
||||||
raise errors.AnsibleError('Incorrect sudo password')
|
raise errors.AnsibleError('Incorrect become password')
|
||||||
elif prompt and sudo_errput.endswith(prompt):
|
elif prompt and become_errput.endswith(prompt):
|
||||||
stdin.write(self.runner.sudo_pass + '\n')
|
stdin.write(self.runner.become_pass + '\n')
|
||||||
|
|
||||||
if p.stdout in rfd:
|
if p.stdout in rfd:
|
||||||
chunk = p.stdout.read()
|
chunk = p.stdout.read()
|
||||||
if not chunk:
|
if not chunk:
|
||||||
raise errors.AnsibleError('ssh connection closed waiting for sudo or su password prompt')
|
raise errors.AnsibleError('ssh connection closed waiting for %s password prompt' % self.runner.become_method)
|
||||||
sudo_output += chunk
|
become_output += chunk
|
||||||
|
|
||||||
if not rfd:
|
if not rfd:
|
||||||
# timeout. wrap up process communication
|
# timeout. wrap up process communication
|
||||||
stdout = p.communicate()
|
stdout = p.communicate()
|
||||||
raise errors.AnsibleError('ssh connection error waiting for sudo or su password prompt')
|
raise errors.AnsibleError('ssh connection error while waiting for %s password prompt' % self.runner.become_method)
|
||||||
|
|
||||||
if success_key not in sudo_output:
|
if success_key not in become_output:
|
||||||
if sudoable:
|
if sudoable:
|
||||||
stdin.write(self.runner.sudo_pass + '\n')
|
stdin.write(self.runner.become_pass + '\n')
|
||||||
elif su:
|
|
||||||
stdin.write(self.runner.su_pass + '\n')
|
|
||||||
else:
|
else:
|
||||||
no_prompt_out += sudo_output
|
no_prompt_out += become_output
|
||||||
no_prompt_err += sudo_errput
|
no_prompt_err += become_errput
|
||||||
|
|
||||||
(returncode, stdout, stderr) = self._communicate(p, stdin, in_data, su=su, sudoable=sudoable, prompt=prompt)
|
(returncode, stdout, stderr) = self._communicate(p, stdin, in_data, sudoable=sudoable, prompt=prompt)
|
||||||
|
|
||||||
if C.HOST_KEY_CHECKING and not_in_host_file:
|
if C.HOST_KEY_CHECKING and not_in_host_file:
|
||||||
# lock around the initial SSH connectivity so the user prompt about whether to add
|
# lock around the initial SSH connectivity so the user prompt about whether to add
|
||||||
|
|
|
@ -72,6 +72,10 @@ class Connection(object):
|
||||||
self.shell_id = None
|
self.shell_id = None
|
||||||
self.delegate = None
|
self.delegate = None
|
||||||
|
|
||||||
|
# Add runas support
|
||||||
|
#self.become_methods_supported=['runas']
|
||||||
|
self.become_methods_supported=[]
|
||||||
|
|
||||||
def _winrm_connect(self):
|
def _winrm_connect(self):
|
||||||
'''
|
'''
|
||||||
Establish a WinRM connection over HTTP/HTTPS.
|
Establish a WinRM connection over HTTP/HTTPS.
|
||||||
|
@ -143,7 +147,11 @@ class Connection(object):
|
||||||
self.protocol = self._winrm_connect()
|
self.protocol = self._winrm_connect()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable=None, in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None):
|
||||||
|
|
||||||
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
cmd = cmd.encode('utf-8')
|
cmd = cmd.encode('utf-8')
|
||||||
cmd_parts = shlex.split(cmd, posix=False)
|
cmd_parts = shlex.split(cmd, posix=False)
|
||||||
if '-EncodedCommand' in cmd_parts:
|
if '-EncodedCommand' in cmd_parts:
|
||||||
|
|
|
@ -26,6 +26,7 @@ import subprocess
|
||||||
from subprocess import Popen,PIPE
|
from subprocess import Popen,PIPE
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
from ansible.callbacks import vvv
|
from ansible.callbacks import vvv
|
||||||
|
import ansible.constants as C
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' Local zone based connections '''
|
''' Local zone based connections '''
|
||||||
|
@ -68,6 +69,7 @@ class Connection(object):
|
||||||
self.runner = runner
|
self.runner = runner
|
||||||
self.host = host
|
self.host = host
|
||||||
self.has_pipelining = False
|
self.has_pipelining = False
|
||||||
|
self.become_methods_supported=C.BECOME_METHODS
|
||||||
|
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0:
|
||||||
raise errors.AnsibleError("zone connection requires running as root")
|
raise errors.AnsibleError("zone connection requires running as root")
|
||||||
|
@ -98,17 +100,16 @@ class Connection(object):
|
||||||
local_cmd = '%s "%s" %s' % (self.zlogin_cmd, self.zone, cmd)
|
local_cmd = '%s "%s" %s' % (self.zlogin_cmd, self.zone, cmd)
|
||||||
return local_cmd
|
return local_cmd
|
||||||
|
|
||||||
#def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None):
|
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable=None, in_data=None):
|
||||||
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable=None, in_data=None, su=None, su_user=None):
|
|
||||||
''' run a command on the zone '''
|
''' run a command on the zone '''
|
||||||
|
|
||||||
if su or su_user:
|
if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support running commands via su")
|
raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method)
|
||||||
|
|
||||||
if in_data:
|
if in_data:
|
||||||
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||||
|
|
||||||
# We enter zone as root so sudo stuff can be ignored
|
# We happily ignore privelege escalation
|
||||||
if executable == '/bin/sh':
|
if executable == '/bin/sh':
|
||||||
executable = None
|
executable = None
|
||||||
local_cmd = self._generate_cmd(executable, cmd)
|
local_cmd = self._generate_cmd(executable, cmd)
|
||||||
|
|
|
@ -992,14 +992,12 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
|
||||||
default=constants.DEFAULT_HOST_LIST)
|
default=constants.DEFAULT_HOST_LIST)
|
||||||
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
||||||
help="set additional variables as key=value or YAML/JSON", default=[])
|
help="set additional variables as key=value or YAML/JSON", default=[])
|
||||||
|
parser.add_option('-u', '--user', default=constants.DEFAULT_REMOTE_USER, dest='remote_user',
|
||||||
|
help='connect as this user (default=%s)' % constants.DEFAULT_REMOTE_USER)
|
||||||
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
|
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
|
||||||
help='ask for SSH password')
|
help='ask for SSH password')
|
||||||
parser.add_option('--private-key', default=C.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
|
parser.add_option('--private-key', default=constants.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
|
||||||
help='use this file to authenticate the connection')
|
help='use this file to authenticate the connection')
|
||||||
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
|
||||||
help='ask for sudo password')
|
|
||||||
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
|
|
||||||
help='ask for su password')
|
|
||||||
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
|
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
|
||||||
help='ask for vault password')
|
help='ask for vault password')
|
||||||
parser.add_option('--vault-password-file', default=constants.DEFAULT_VAULT_PASSWORD_FILE,
|
parser.add_option('--vault-password-file', default=constants.DEFAULT_VAULT_PASSWORD_FILE,
|
||||||
|
@ -1025,22 +1023,35 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
|
||||||
help='log output to this directory')
|
help='log output to this directory')
|
||||||
|
|
||||||
if runas_opts:
|
if runas_opts:
|
||||||
parser.add_option("-s", "--sudo", default=constants.DEFAULT_SUDO, action="store_true",
|
# priv user defaults to root later on to enable detecting when this option was given here
|
||||||
dest='sudo', help="run operations with sudo (nopasswd)")
|
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
||||||
|
help='ask for sudo password (deprecated, use become)')
|
||||||
|
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
|
||||||
|
help='ask for su password (deprecated, use become)')
|
||||||
|
parser.add_option("-s", "--sudo", default=constants.DEFAULT_SUDO, action="store_true", dest='sudo',
|
||||||
|
help="run operations with sudo (nopasswd) (deprecated, use become)")
|
||||||
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
|
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
|
||||||
help='desired sudo user (default=root)') # Can't default to root because we need to detect when this option was given
|
help='desired sudo user (default=root) (deprecated, use become)')
|
||||||
parser.add_option('-u', '--user', default=constants.DEFAULT_REMOTE_USER,
|
parser.add_option('-S', '--su', default=constants.DEFAULT_SU, action='store_true',
|
||||||
dest='remote_user', help='connect as this user (default=%s)' % constants.DEFAULT_REMOTE_USER)
|
help='run operations with su (deprecated, use become)')
|
||||||
|
parser.add_option('-R', '--su-user', default=None,
|
||||||
|
help='run operations with su as this user (default=%s) (deprecated, use become)' % constants.DEFAULT_SU_USER)
|
||||||
|
|
||||||
|
# consolidated privilege escalation (become)
|
||||||
|
parser.add_option("-b", "--become", default=constants.DEFAULT_BECOME, action="store_true", dest='become',
|
||||||
|
help="run operations with become (nopasswd implied)")
|
||||||
|
parser.add_option('--become-method', dest='become_method', default=constants.DEFAULT_BECOME_METHOD, type='string',
|
||||||
|
help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (constants.DEFAULT_BECOME_METHOD, ' | '.join(constants.BECOME_METHODS)))
|
||||||
|
parser.add_option('--become-user', default=None, dest='become_user', type='string',
|
||||||
|
help='run operations as this user (default=%s)' % constants.DEFAULT_BECOME_USER)
|
||||||
|
parser.add_option('--ask-become-pass', default=False, dest='become_ask_pass', action='store_true',
|
||||||
|
help='ask for privilege escalation password')
|
||||||
|
|
||||||
parser.add_option('-S', '--su', default=constants.DEFAULT_SU,
|
|
||||||
action='store_true', help='run operations with su')
|
|
||||||
parser.add_option('-R', '--su-user', help='run operations with su as this '
|
|
||||||
'user (default=%s)' % constants.DEFAULT_SU_USER)
|
|
||||||
|
|
||||||
if connect_opts:
|
if connect_opts:
|
||||||
parser.add_option('-c', '--connection', dest='connection',
|
parser.add_option('-c', '--connection', dest='connection',
|
||||||
default=C.DEFAULT_TRANSPORT,
|
default=constants.DEFAULT_TRANSPORT,
|
||||||
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
|
help="connection type to use (default=%s)" % constants.DEFAULT_TRANSPORT)
|
||||||
|
|
||||||
if async_opts:
|
if async_opts:
|
||||||
parser.add_option('-P', '--poll', default=constants.DEFAULT_POLL_INTERVAL, type='int',
|
parser.add_option('-P', '--poll', default=constants.DEFAULT_POLL_INTERVAL, type='int',
|
||||||
|
@ -1059,7 +1070,6 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
|
||||||
help="when changing (small) files and templates, show the differences in those files; works great with --check"
|
help="when changing (small) files and templates, show the differences in those files; works great with --check"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def parse_extra_vars(extra_vars_opts, vault_pass):
|
def parse_extra_vars(extra_vars_opts, vault_pass):
|
||||||
|
@ -1106,41 +1116,58 @@ def ask_vault_passwords(ask_vault_pass=False, ask_new_vault_pass=False, confirm_
|
||||||
|
|
||||||
return vault_pass, new_vault_pass
|
return vault_pass, new_vault_pass
|
||||||
|
|
||||||
def ask_passwords(ask_pass=False, ask_sudo_pass=False, ask_su_pass=False, ask_vault_pass=False):
|
def ask_passwords(ask_pass=False, become_ask_pass=False, ask_vault_pass=False, become_method=C.DEFAULT_BECOME_METHOD):
|
||||||
sshpass = None
|
sshpass = None
|
||||||
sudopass = None
|
becomepass = None
|
||||||
supass = None
|
|
||||||
vaultpass = None
|
vaultpass = None
|
||||||
sudo_prompt = "sudo password: "
|
become_prompt = ''
|
||||||
su_prompt = "su password: "
|
|
||||||
|
|
||||||
if ask_pass:
|
if ask_pass:
|
||||||
sshpass = getpass.getpass(prompt="SSH password: ")
|
sshpass = getpass.getpass(prompt="SSH password: ")
|
||||||
|
become_prompt = "%s password[defaults to SSH password]: " % become_method.upper()
|
||||||
if sshpass:
|
if sshpass:
|
||||||
sshpass = to_bytes(sshpass, errors='strict', nonstring='simplerepr')
|
sshpass = to_bytes(sshpass, errors='strict', nonstring='simplerepr')
|
||||||
sudo_prompt = "sudo password [defaults to SSH password]: "
|
else:
|
||||||
su_prompt = "su password [defaults to SSH password]: "
|
become_prompt = "%s password: " % become_method.upper()
|
||||||
|
|
||||||
if ask_sudo_pass:
|
if become_ask_pass:
|
||||||
sudopass = getpass.getpass(prompt=sudo_prompt)
|
becomepass = getpass.getpass(prompt=become_prompt)
|
||||||
if ask_pass and sudopass == '':
|
if ask_pass and becomepass == '':
|
||||||
sudopass = sshpass
|
becomepass = sshpass
|
||||||
if sudopass:
|
if becomepass:
|
||||||
sudopass = to_bytes(sudopass, errors='strict', nonstring='simplerepr')
|
becomepass = to_bytes(becomepass)
|
||||||
|
|
||||||
if ask_su_pass:
|
|
||||||
supass = getpass.getpass(prompt=su_prompt)
|
|
||||||
if ask_pass and supass == '':
|
|
||||||
supass = sshpass
|
|
||||||
if supass:
|
|
||||||
supass = to_bytes(supass, errors='strict', nonstring='simplerepr')
|
|
||||||
|
|
||||||
if ask_vault_pass:
|
if ask_vault_pass:
|
||||||
vaultpass = getpass.getpass(prompt="Vault password: ")
|
vaultpass = getpass.getpass(prompt="Vault password: ")
|
||||||
if vaultpass:
|
if vaultpass:
|
||||||
vaultpass = to_bytes(vaultpass, errors='strict', nonstring='simplerepr').strip()
|
vaultpass = to_bytes(vaultpass, errors='strict', nonstring='simplerepr').strip()
|
||||||
|
|
||||||
return (sshpass, sudopass, supass, vaultpass)
|
return (sshpass, becomepass, vaultpass)
|
||||||
|
|
||||||
|
|
||||||
|
def choose_pass_prompt(options):
|
||||||
|
|
||||||
|
if options.ask_su_pass:
|
||||||
|
return 'su'
|
||||||
|
elif options.ask_sudo_pass:
|
||||||
|
return 'sudo'
|
||||||
|
|
||||||
|
return options.become_method
|
||||||
|
|
||||||
|
def normalize_become_options(options):
|
||||||
|
|
||||||
|
options.become_ask_pass = options.become_ask_pass or options.ask_sudo_pass or options.ask_su_pass or C.DEFAULT_BECOME_ASK_PASS
|
||||||
|
options.become_user = options.become_user or options.sudo_user or options.su_user or C.DEFAULT_BECOME_USER
|
||||||
|
|
||||||
|
if options.become:
|
||||||
|
pass
|
||||||
|
elif options.sudo:
|
||||||
|
options.become = True
|
||||||
|
options.become_method = 'sudo'
|
||||||
|
elif options.su:
|
||||||
|
options.become = True
|
||||||
|
options.become_method = 'su'
|
||||||
|
|
||||||
|
|
||||||
def do_encrypt(result, encrypt, salt_size=None, salt=None):
|
def do_encrypt(result, encrypt, salt_size=None, salt=None):
|
||||||
if PASSLIB_AVAILABLE:
|
if PASSLIB_AVAILABLE:
|
||||||
|
@ -1194,38 +1221,63 @@ def boolean(value):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def make_become_cmd(cmd, user, shell, method, flags=None, exe=None):
|
||||||
|
"""
|
||||||
|
helper function for connection plugins to create privilege escalation commands
|
||||||
|
"""
|
||||||
|
|
||||||
|
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||||
|
success_key = 'BECOME-SUCCESS-%s' % randbits
|
||||||
|
prompt = None
|
||||||
|
becomecmd = None
|
||||||
|
|
||||||
|
shell = shell or '$SHELL'
|
||||||
|
|
||||||
|
if method == 'sudo':
|
||||||
|
# Rather than detect if sudo wants a password this time, -k makes sudo always ask for
|
||||||
|
# a password if one is required. Passing a quoted compound command to sudo (or sudo -s)
|
||||||
|
# directly doesn't work, so we shellquote it with pipes.quote() and pass the quoted
|
||||||
|
# string to the user's shell. We loop reading output until we see the randomly-generated
|
||||||
|
# sudo prompt set with the -p option.
|
||||||
|
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
||||||
|
exe = exe or C.DEFAULT_SUDO_EXE
|
||||||
|
becomecmd = '%s -k && %s %s -S -p "%s" -u %s %s -c "%s"' % \
|
||||||
|
(exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, user, shell, 'echo %s; %s' % (success_key, cmd))
|
||||||
|
|
||||||
|
elif method == 'su':
|
||||||
|
exe = exe or C.DEFAULT_SU_EXE
|
||||||
|
flags = flags or C.DEFAULT_SU_FLAGS
|
||||||
|
becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, user, shell, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||||
|
|
||||||
|
elif method == 'pbrun':
|
||||||
|
exe = exe or 'pbrun'
|
||||||
|
flags = flags or ''
|
||||||
|
becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, user, 'echo %s; %s' % (success_key,cmd))
|
||||||
|
|
||||||
|
elif method == 'pfexec':
|
||||||
|
exe = exe or 'pfexec'
|
||||||
|
flags = flags or ''
|
||||||
|
# No user as it uses it's own exec_attr to figure it out
|
||||||
|
becomecmd = '%s %s "%s"' % (exe, flags, 'echo %s; %s' % (success_key,cmd))
|
||||||
|
|
||||||
|
if becomecmd is None:
|
||||||
|
raise errors.AnsibleError("Privilege escalation method not found: %s" % method)
|
||||||
|
|
||||||
|
return (('%s -c ' % shell) + pipes.quote(becomecmd), prompt, success_key)
|
||||||
|
|
||||||
|
|
||||||
def make_sudo_cmd(sudo_exe, sudo_user, executable, cmd):
|
def make_sudo_cmd(sudo_exe, sudo_user, executable, cmd):
|
||||||
"""
|
"""
|
||||||
helper function for connection plugins to create sudo commands
|
helper function for connection plugins to create sudo commands
|
||||||
"""
|
"""
|
||||||
# Rather than detect if sudo wants a password this time, -k makes
|
return make_become_cmd(cmd, sudo_user, executable, 'sudo', C.DEFAULT_SUDO_FLAGS, sudo_exe)
|
||||||
# sudo always ask for a password if one is required.
|
|
||||||
# Passing a quoted compound command to sudo (or sudo -s)
|
|
||||||
# directly doesn't work, so we shellquote it with pipes.quote()
|
|
||||||
# and pass the quoted string to the user's shell. We loop reading
|
|
||||||
# output until we see the randomly-generated sudo prompt set with
|
|
||||||
# the -p option.
|
|
||||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
|
||||||
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
|
||||||
success_key = 'SUDO-SUCCESS-%s' % randbits
|
|
||||||
sudocmd = '%s -k && %s %s -S -p "%s" -u %s %s -c %s' % (
|
|
||||||
sudo_exe, sudo_exe, C.DEFAULT_SUDO_FLAGS,
|
|
||||||
prompt, sudo_user, executable or '$SHELL', pipes.quote('echo %s; %s' % (success_key, cmd)))
|
|
||||||
return ('/bin/sh -c ' + pipes.quote(sudocmd), prompt, success_key)
|
|
||||||
|
|
||||||
|
|
||||||
def make_su_cmd(su_user, executable, cmd):
|
def make_su_cmd(su_user, executable, cmd):
|
||||||
"""
|
"""
|
||||||
Helper function for connection plugins to create direct su commands
|
Helper function for connection plugins to create direct su commands
|
||||||
"""
|
"""
|
||||||
# TODO: work on this function
|
return make_become_cmd(cmd, su_user, executable, 'su', C.DEFAULT_SU_FLAGS, C.DEFAULT_SU_EXE)
|
||||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
|
||||||
success_key = 'SUDO-SUCCESS-%s' % randbits
|
|
||||||
sudocmd = '%s %s %s -c "%s -c %s"' % (
|
|
||||||
C.DEFAULT_SU_EXE, C.DEFAULT_SU_FLAGS, su_user, executable or '$SHELL',
|
|
||||||
pipes.quote('echo %s; %s' % (success_key, cmd))
|
|
||||||
)
|
|
||||||
return ('/bin/sh -c ' + pipes.quote(sudocmd), None, success_key)
|
|
||||||
|
|
||||||
def get_diff(diff):
|
def get_diff(diff):
|
||||||
# called by --diff usage in playbook and runner via callbacks
|
# called by --diff usage in playbook and runner via callbacks
|
||||||
|
@ -1577,9 +1629,9 @@ def update_hash(hash, key, new_value):
|
||||||
hash[key] = value
|
hash[key] = value
|
||||||
|
|
||||||
def censor_unlogged_data(data):
|
def censor_unlogged_data(data):
|
||||||
'''
|
'''
|
||||||
used when the no_log: True attribute is passed to a task to keep data from a callback.
|
used when the no_log: True attribute is passed to a task to keep data from a callback.
|
||||||
NOT intended to prevent variable registration, but only things from showing up on
|
NOT intended to prevent variable registration, but only things from showing up on
|
||||||
screen
|
screen
|
||||||
'''
|
'''
|
||||||
new_data = {}
|
new_data = {}
|
||||||
|
@ -1589,5 +1641,19 @@ def censor_unlogged_data(data):
|
||||||
new_data['censored'] = 'results hidden due to no_log parameter'
|
new_data['censored'] = 'results hidden due to no_log parameter'
|
||||||
return new_data
|
return new_data
|
||||||
|
|
||||||
|
def check_mutually_exclusive_privilege(options, parser):
|
||||||
|
|
||||||
|
# privilege escalation command line arguments need to be mutually exclusive
|
||||||
|
if (options.su or options.su_user or options.ask_su_pass) and \
|
||||||
|
(options.sudo or options.sudo_user or options.ask_sudo_pass) or \
|
||||||
|
(options.su or options.su_user or options.ask_su_pass) and \
|
||||||
|
(options.become or options.become_user or options.become_ask_pass) or \
|
||||||
|
(options.sudo or options.sudo_user or options.ask_sudo_pass) and \
|
||||||
|
(options.become or options.become_user or options.become_ask_pass):
|
||||||
|
|
||||||
|
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
||||||
|
"and su arguments ('-su', '--su-user', and '--ask-su-pass') "
|
||||||
|
"and become arguments ('--become', '--become-user', and '--ask-become-pass')"
|
||||||
|
" are exclusive of each other")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
roles:
|
roles:
|
||||||
# In destructive because it creates and removes a user
|
# In destructive because it creates and removes a user
|
||||||
- { role: test_sudo, tags: test_sudo}
|
- { role: test_sudo, tags: test_sudo}
|
||||||
|
#- { role: test_su, tags: test_su} # wait till su support is added to local connection, needs tty
|
||||||
|
- { role: test_become, tags: test_become}
|
||||||
- { role: test_service, tags: test_service }
|
- { role: test_service, tags: test_service }
|
||||||
# Current pip unconditionally uses md5. We can re-enable if pip switches
|
# Current pip unconditionally uses md5. We can re-enable if pip switches
|
||||||
# to a different hash or allows us to not check md5
|
# to a different hash or allows us to not check md5
|
||||||
|
|
1
test/integration/roles/test_become/files/baz.txt
Normal file
1
test/integration/roles/test_become/files/baz.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
testing tilde expansion with become
|
77
test/integration/roles/test_become/tasks/main.yml
Normal file
77
test/integration/roles/test_become/tasks/main.yml
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
- include_vars: default.yml
|
||||||
|
|
||||||
|
- name: Create test user
|
||||||
|
become: True
|
||||||
|
become_user: root
|
||||||
|
user:
|
||||||
|
name: "{{ become_test_user }}"
|
||||||
|
|
||||||
|
- name: test becoming user
|
||||||
|
shell: whoami
|
||||||
|
become: True
|
||||||
|
become_user: "{{ become_test_user }}"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stdout == '{{ become_test_user }}'"
|
||||||
|
|
||||||
|
- name: tilde expansion honors become in file
|
||||||
|
become: True
|
||||||
|
become_user: "{{ become_test_user }}"
|
||||||
|
file:
|
||||||
|
path: "~/foo.txt"
|
||||||
|
state: touch
|
||||||
|
|
||||||
|
- name: check that the path in the user's home dir was created
|
||||||
|
stat:
|
||||||
|
path: "~{{ become_test_user }}/foo.txt"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stat.exists == True"
|
||||||
|
- "results.stat.path|dirname|basename == '{{ become_test_user }}'"
|
||||||
|
|
||||||
|
- name: tilde expansion honors become in template
|
||||||
|
become: True
|
||||||
|
become_user: "{{ become_test_user }}"
|
||||||
|
template:
|
||||||
|
src: "bar.j2"
|
||||||
|
dest: "~/bar.txt"
|
||||||
|
|
||||||
|
- name: check that the path in the user's home dir was created
|
||||||
|
stat:
|
||||||
|
path: "~{{ become_test_user }}/bar.txt"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stat.exists == True"
|
||||||
|
- "results.stat.path|dirname|basename == '{{ become_test_user }}'"
|
||||||
|
|
||||||
|
- name: tilde expansion honors become in copy
|
||||||
|
become: True
|
||||||
|
become_user: "{{ become_test_user }}"
|
||||||
|
copy:
|
||||||
|
src: baz.txt
|
||||||
|
dest: "~/baz.txt"
|
||||||
|
|
||||||
|
- name: check that the path in the user's home dir was created
|
||||||
|
stat:
|
||||||
|
path: "~{{ become_test_user }}/baz.txt"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stat.exists == True"
|
||||||
|
- "results.stat.path|dirname|basename == '{{ become_test_user }}'"
|
||||||
|
|
||||||
|
- name: Remove test user and their home dir
|
||||||
|
become: True
|
||||||
|
become_user: root
|
||||||
|
user:
|
||||||
|
name: "{{ become_test_user }}"
|
||||||
|
state: "absent"
|
||||||
|
remove: "yes"
|
||||||
|
|
1
test/integration/roles/test_become/templates/bar.j2
Normal file
1
test/integration/roles/test_become/templates/bar.j2
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ become_test_user }}
|
1
test/integration/roles/test_become/vars/default.yml
Normal file
1
test/integration/roles/test_become/vars/default.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
become_test_user: ansibletest1
|
1
test/integration/roles/test_su/files/baz.txt
Normal file
1
test/integration/roles/test_su/files/baz.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
testing tilde expansion with su
|
75
test/integration/roles/test_su/tasks/main.yml
Normal file
75
test/integration/roles/test_su/tasks/main.yml
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
- include_vars: default.yml
|
||||||
|
|
||||||
|
- name: Create test user
|
||||||
|
su: True
|
||||||
|
user:
|
||||||
|
name: "{{ su_test_user }}"
|
||||||
|
|
||||||
|
- name: test becoming user
|
||||||
|
shell: whoami
|
||||||
|
su: True
|
||||||
|
su_user: "{{ su_test_user }}"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stdout == '{{ su_test_user }}'"
|
||||||
|
|
||||||
|
- name: tilde expansion honors su in file
|
||||||
|
su: True
|
||||||
|
su_user: "{{ su_test_user }}"
|
||||||
|
file:
|
||||||
|
path: "~/foo.txt"
|
||||||
|
state: touch
|
||||||
|
|
||||||
|
- name: check that the path in the user's home dir was created
|
||||||
|
stat:
|
||||||
|
path: "~{{ su_test_user }}/foo.txt"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stat.exists == True"
|
||||||
|
- "results.stat.path|dirname|basename == '{{ su_test_user }}'"
|
||||||
|
|
||||||
|
- name: tilde expansion honors su in template
|
||||||
|
su: True
|
||||||
|
su_user: "{{ su_test_user }}"
|
||||||
|
template:
|
||||||
|
src: "bar.j2"
|
||||||
|
dest: "~/bar.txt"
|
||||||
|
|
||||||
|
- name: check that the path in the user's home dir was created
|
||||||
|
stat:
|
||||||
|
path: "~{{ su_test_user }}/bar.txt"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stat.exists == True"
|
||||||
|
- "results.stat.path|dirname|basename == '{{ su_test_user }}'"
|
||||||
|
|
||||||
|
- name: tilde expansion honors su in copy
|
||||||
|
su: True
|
||||||
|
su_user: "{{ su_test_user }}"
|
||||||
|
copy:
|
||||||
|
src: baz.txt
|
||||||
|
dest: "~/baz.txt"
|
||||||
|
|
||||||
|
- name: check that the path in the user's home dir was created
|
||||||
|
stat:
|
||||||
|
path: "~{{ su_test_user }}/baz.txt"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stat.exists == True"
|
||||||
|
- "results.stat.path|dirname|basename == '{{ su_test_user }}'"
|
||||||
|
|
||||||
|
- name: Remove test user and their home dir
|
||||||
|
su: True
|
||||||
|
user:
|
||||||
|
name: "{{ su_test_user }}"
|
||||||
|
state: "absent"
|
||||||
|
remove: "yes"
|
||||||
|
|
1
test/integration/roles/test_su/templates/bar.j2
Normal file
1
test/integration/roles/test_su/templates/bar.j2
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{{ su_test_user }}
|
1
test/integration/roles/test_su/vars/default.yml
Normal file
1
test/integration/roles/test_su/vars/default.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
su_test_user: ansibletest1
|
|
@ -1,9 +1,20 @@
|
||||||
- include_vars: default.yml
|
- include_vars: default.yml
|
||||||
|
|
||||||
- name: Create test user
|
- name: Create test user
|
||||||
|
sudo: true
|
||||||
user:
|
user:
|
||||||
name: "{{ sudo_test_user }}"
|
name: "{{ sudo_test_user }}"
|
||||||
|
|
||||||
|
- name: test becoming user
|
||||||
|
shell: whoami
|
||||||
|
sudo: True
|
||||||
|
sudo_user: "{{ sudo_test_user }}"
|
||||||
|
register: results
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "results.stdout == '{{ sudo_test_user }}'"
|
||||||
|
|
||||||
- name: tilde expansion honors sudo in file
|
- name: tilde expansion honors sudo in file
|
||||||
sudo: True
|
sudo: True
|
||||||
sudo_user: "{{ sudo_test_user }}"
|
sudo_user: "{{ sudo_test_user }}"
|
||||||
|
@ -56,6 +67,7 @@
|
||||||
- "results.stat.path|dirname|basename == '{{ sudo_test_user }}'"
|
- "results.stat.path|dirname|basename == '{{ sudo_test_user }}'"
|
||||||
|
|
||||||
- name: Remove test user and their home dir
|
- name: Remove test user and their home dir
|
||||||
|
sudo: true
|
||||||
user:
|
user:
|
||||||
name: "{{ sudo_test_user }}"
|
name: "{{ sudo_test_user }}"
|
||||||
state: "absent"
|
state: "absent"
|
||||||
|
|
|
@ -41,6 +41,9 @@ class FakePlayBook(object):
|
||||||
self.sudo_user = None
|
self.sudo_user = None
|
||||||
self.su = None
|
self.su = None
|
||||||
self.su_user = None
|
self.su_user = None
|
||||||
|
self.become = None
|
||||||
|
self.become_method = None
|
||||||
|
self.become_user = None
|
||||||
self.transport = None
|
self.transport = None
|
||||||
self.only_tags = None
|
self.only_tags = None
|
||||||
self.skip_tags = None
|
self.skip_tags = None
|
||||||
|
|
|
@ -18,6 +18,9 @@ class FakeRunner(object):
|
||||||
self.remote_user = None
|
self.remote_user = None
|
||||||
self.private_key_file = None
|
self.private_key_file = None
|
||||||
self.check = False
|
self.check = False
|
||||||
|
self.become = False
|
||||||
|
self.become_method = False
|
||||||
|
self.become_user = False
|
||||||
|
|
||||||
def _execute_module(self, conn, tmp, module_name, args,
|
def _execute_module(self, conn, tmp, module_name, args,
|
||||||
async_jid=None, async_module=None, async_limit=None, inject=None,
|
async_jid=None, async_module=None, async_limit=None, inject=None,
|
||||||
|
@ -76,7 +79,7 @@ class TestSynchronize(unittest.TestCase):
|
||||||
""" verify the synchronize action plugin unsets and then sets sudo """
|
""" verify the synchronize action plugin unsets and then sets sudo """
|
||||||
|
|
||||||
runner = FakeRunner()
|
runner = FakeRunner()
|
||||||
runner.sudo = True
|
runner.become = True
|
||||||
runner.remote_user = "root"
|
runner.remote_user = "root"
|
||||||
runner.transport = "ssh"
|
runner.transport = "ssh"
|
||||||
conn = FakeConn()
|
conn = FakeConn()
|
||||||
|
@ -97,7 +100,7 @@ class TestSynchronize(unittest.TestCase):
|
||||||
assert runner.executed_complex_args == {'dest':'root@el6.lab.net:/tmp/bar',
|
assert runner.executed_complex_args == {'dest':'root@el6.lab.net:/tmp/bar',
|
||||||
'src':'/tmp/foo',
|
'src':'/tmp/foo',
|
||||||
'rsync_path':'"sudo rsync"'}, "wrong args used"
|
'rsync_path':'"sudo rsync"'}, "wrong args used"
|
||||||
assert runner.sudo == True, "sudo was not reset to True"
|
assert runner.become == True, "sudo was not reset to True"
|
||||||
|
|
||||||
|
|
||||||
def test_synchronize_action_local(self):
|
def test_synchronize_action_local(self):
|
||||||
|
|
|
@ -498,7 +498,7 @@ class TestUtils(unittest.TestCase):
|
||||||
self.assertEqual(len(cmd), 3)
|
self.assertEqual(len(cmd), 3)
|
||||||
self.assertTrue('-u root' in cmd[0])
|
self.assertTrue('-u root' in cmd[0])
|
||||||
self.assertTrue('-p "[sudo via ansible, key=' in cmd[0] and cmd[1].startswith('[sudo via ansible, key'))
|
self.assertTrue('-p "[sudo via ansible, key=' in cmd[0] and cmd[1].startswith('[sudo via ansible, key'))
|
||||||
self.assertTrue('echo SUDO-SUCCESS-' in cmd[0] and cmd[2].startswith('SUDO-SUCCESS-'))
|
self.assertTrue('echo BECOME-SUCCESS-' in cmd[0] and cmd[2].startswith('BECOME-SUCCESS-'))
|
||||||
self.assertTrue('sudo -k' in cmd[0])
|
self.assertTrue('sudo -k' in cmd[0])
|
||||||
|
|
||||||
def test_make_su_cmd(self):
|
def test_make_su_cmd(self):
|
||||||
|
@ -506,7 +506,7 @@ class TestUtils(unittest.TestCase):
|
||||||
self.assertTrue(isinstance(cmd, tuple))
|
self.assertTrue(isinstance(cmd, tuple))
|
||||||
self.assertEqual(len(cmd), 3)
|
self.assertEqual(len(cmd), 3)
|
||||||
self.assertTrue('root -c "/bin/sh' in cmd[0] or ' root -c /bin/sh' in cmd[0])
|
self.assertTrue('root -c "/bin/sh' in cmd[0] or ' root -c /bin/sh' in cmd[0])
|
||||||
self.assertTrue('echo SUDO-SUCCESS-' in cmd[0] and cmd[2].startswith('SUDO-SUCCESS-'))
|
self.assertTrue('echo BECOME-SUCCESS-' in cmd[0] and cmd[2].startswith('BECOME-SUCCESS-'))
|
||||||
|
|
||||||
def test_to_unicode(self):
|
def test_to_unicode(self):
|
||||||
uni = ansible.utils.unicode.to_unicode(u'ansible')
|
uni = ansible.utils.unicode.to_unicode(u'ansible')
|
||||||
|
|
|
@ -141,16 +141,16 @@ DEFAULT_SU_FLAGS = get_config(p, DEFAULTS, 'su_flags', 'ANSIBLE_SU_FLAG
|
||||||
DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root')
|
DEFAULT_SU_USER = get_config(p, DEFAULTS, 'su_user', 'ANSIBLE_SU_USER', 'root')
|
||||||
DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True)
|
DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_SU_PASS', False, boolean=True)
|
||||||
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
|
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
|
||||||
|
|
||||||
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '/usr/share/ansible_plugins/action_plugins')
|
|
||||||
DEFAULT_CACHE_PLUGIN_PATH = get_config(p, DEFAULTS, 'cache_plugins', 'ANSIBLE_CACHE_PLUGINS', '/usr/share/ansible_plugins/cache_plugins')
|
|
||||||
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '/usr/share/ansible_plugins/callback_plugins')
|
|
||||||
DEFAULT_CONNECTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'connection_plugins', 'ANSIBLE_CONNECTION_PLUGINS', '/usr/share/ansible_plugins/connection_plugins')
|
|
||||||
DEFAULT_LOOKUP_PLUGIN_PATH = get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', '/usr/share/ansible_plugins/lookup_plugins')
|
|
||||||
DEFAULT_VARS_PLUGIN_PATH = get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', '/usr/share/ansible_plugins/vars_plugins')
|
|
||||||
DEFAULT_FILTER_PLUGIN_PATH = get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', '/usr/share/ansible_plugins/filter_plugins')
|
|
||||||
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
|
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
|
||||||
|
|
||||||
|
DEFAULT_ACTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'action_plugins', 'ANSIBLE_ACTION_PLUGINS', '~/.ansible/plugins/action_plugins:/usr/share/ansible_plugins/action_plugins')
|
||||||
|
DEFAULT_CACHE_PLUGIN_PATH = get_config(p, DEFAULTS, 'cache_plugins', 'ANSIBLE_CACHE_PLUGINS', '~/.ansible/plugins/cache_plugins:/usr/share/ansible_plugins/cache_plugins')
|
||||||
|
DEFAULT_CALLBACK_PLUGIN_PATH = get_config(p, DEFAULTS, 'callback_plugins', 'ANSIBLE_CALLBACK_PLUGINS', '~/.ansible/plugins/callback_plugins:/usr/share/ansible_plugins/callback_plugins')
|
||||||
|
DEFAULT_CONNECTION_PLUGIN_PATH = get_config(p, DEFAULTS, 'connection_plugins', 'ANSIBLE_CONNECTION_PLUGINS', '~/.ansible/plugins/connection_plugins:/usr/share/ansible_plugins/connection_plugins')
|
||||||
|
DEFAULT_LOOKUP_PLUGIN_PATH = get_config(p, DEFAULTS, 'lookup_plugins', 'ANSIBLE_LOOKUP_PLUGINS', '~/.ansible/plugins/lookup_plugins:/usr/share/ansible_plugins/lookup_plugins')
|
||||||
|
DEFAULT_VARS_PLUGIN_PATH = get_config(p, DEFAULTS, 'vars_plugins', 'ANSIBLE_VARS_PLUGINS', '~/.ansible/plugins/vars_plugins:/usr/share/ansible_plugins/vars_plugins')
|
||||||
|
DEFAULT_FILTER_PLUGIN_PATH = get_config(p, DEFAULTS, 'filter_plugins', 'ANSIBLE_FILTER_PLUGINS', '~/.ansible/plugins/filter_plugins:/usr/share/ansible_plugins/filter_plugins')
|
||||||
|
|
||||||
CACHE_PLUGIN = get_config(p, DEFAULTS, 'fact_caching', 'ANSIBLE_CACHE_PLUGIN', 'memory')
|
CACHE_PLUGIN = get_config(p, DEFAULTS, 'fact_caching', 'ANSIBLE_CACHE_PLUGIN', 'memory')
|
||||||
CACHE_PLUGIN_CONNECTION = get_config(p, DEFAULTS, 'fact_caching_connection', 'ANSIBLE_CACHE_PLUGIN_CONNECTION', None)
|
CACHE_PLUGIN_CONNECTION = get_config(p, DEFAULTS, 'fact_caching_connection', 'ANSIBLE_CACHE_PLUGIN_CONNECTION', None)
|
||||||
CACHE_PLUGIN_PREFIX = get_config(p, DEFAULTS, 'fact_caching_prefix', 'ANSIBLE_CACHE_PLUGIN_PREFIX', 'ansible_facts')
|
CACHE_PLUGIN_PREFIX = get_config(p, DEFAULTS, 'fact_caching_prefix', 'ANSIBLE_CACHE_PLUGIN_PREFIX', 'ansible_facts')
|
||||||
|
|
Loading…
Reference in a new issue