Started implementing become in v2
This commit is contained in:
parent
f451974efe
commit
070c7c319f
16 changed files with 236 additions and 93 deletions
|
@ -24,7 +24,6 @@ import pwd
|
|||
import sys
|
||||
|
||||
from . compat import configparser
|
||||
|
||||
from string import ascii_letters, digits
|
||||
|
||||
# copied from utils, avoid circular reference fun :)
|
||||
|
@ -143,6 +142,19 @@ DEFAULT_ASK_SU_PASS = get_config(p, DEFAULTS, 'ask_su_pass', 'ANSIBLE_ASK_
|
|||
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
|
||||
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','pfexec','runas']
|
||||
BECOME_ERROR_STRINGS = {'sudo': 'Sorry, try again.', 'su': 'Authentication failure', 'pbrun': '', 'pfexec': '', 'runas': ''}
|
||||
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')
|
||||
|
@ -168,12 +180,15 @@ DEFAULT_CALLABLE_WHITELIST = get_config(p, DEFAULTS, 'callable_whitelist', '
|
|||
COMMAND_WARNINGS = get_config(p, DEFAULTS, 'command_warnings', 'ANSIBLE_COMMAND_WARNINGS', False, boolean=True)
|
||||
DEFAULT_LOAD_CALLBACK_PLUGINS = get_config(p, DEFAULTS, 'bin_ansible_callbacks', 'ANSIBLE_LOAD_CALLBACK_PLUGINS', False, boolean=True)
|
||||
|
||||
RETRY_FILES_ENABLED = get_config(p, DEFAULTS, 'retry_files_enabled', 'ANSIBLE_RETRY_FILES_ENABLED', True, boolean=True)
|
||||
RETRY_FILES_SAVE_PATH = get_config(p, DEFAULTS, 'retry_files_save_path', 'ANSIBLE_RETRY_FILES_SAVE_PATH', '~/')
|
||||
|
||||
# CONNECTION RELATED
|
||||
ANSIBLE_SSH_ARGS = get_config(p, 'ssh_connection', 'ssh_args', 'ANSIBLE_SSH_ARGS', None)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
@ -189,6 +204,7 @@ PARAMIKO_PTY = get_config(p, 'paramiko_connection', 'pty', 'AN
|
|||
DEFAULT_PASSWORD_CHARS = ascii_letters + digits + ".,:-_"
|
||||
|
||||
# non-configurable things
|
||||
DEFAULT_BECOME_PASS = None
|
||||
DEFAULT_SUDO_PASS = None
|
||||
DEFAULT_REMOTE_PASS = None
|
||||
DEFAULT_SUBSET = None
|
||||
|
|
|
@ -48,16 +48,16 @@ class ConnectionInformation:
|
|||
self.password = ''
|
||||
self.port = 22
|
||||
self.private_key_file = None
|
||||
self.su = False
|
||||
self.su_user = ''
|
||||
self.su_pass = ''
|
||||
self.sudo = False
|
||||
self.sudo_user = ''
|
||||
self.sudo_pass = ''
|
||||
self.verbosity = 0
|
||||
self.only_tags = set()
|
||||
self.skip_tags = set()
|
||||
|
||||
# privilege escalation
|
||||
self.become = False
|
||||
self.become_method = C.DEFAULT_BECOME_METHOD
|
||||
self.become_user = ''
|
||||
self.become_pass = ''
|
||||
|
||||
self.no_log = False
|
||||
self.check_mode = False
|
||||
|
||||
|
@ -87,12 +87,10 @@ class ConnectionInformation:
|
|||
self.remote_user = play.remote_user
|
||||
self.password = ''
|
||||
self.port = int(play.port) if play.port else 22
|
||||
self.su = play.su
|
||||
self.su_user = play.su_user
|
||||
self.su_pass = play.su_pass
|
||||
self.sudo = play.sudo
|
||||
self.sudo_user = play.sudo_user
|
||||
self.sudo_pass = play.sudo_pass
|
||||
self.become = play.become
|
||||
self.become_method = play.become_method
|
||||
self.become_user = play.become_user
|
||||
self.become_pass = play.become_pass
|
||||
|
||||
# non connection related
|
||||
self.no_log = play.no_log
|
||||
|
@ -158,7 +156,7 @@ class ConnectionInformation:
|
|||
new_info = ConnectionInformation()
|
||||
new_info.copy(self)
|
||||
|
||||
for attr in ('connection', 'remote_user', 'su', 'su_user', 'su_pass', 'sudo', 'sudo_user', 'sudo_pass', 'environment', 'no_log'):
|
||||
for attr in ('connection', 'remote_user', 'become', 'become_user', 'become_pass', 'become_method', 'environment', 'no_log'):
|
||||
if hasattr(task, attr):
|
||||
attr_val = getattr(task, attr)
|
||||
if attr_val:
|
||||
|
@ -166,31 +164,58 @@ class ConnectionInformation:
|
|||
|
||||
return new_info
|
||||
|
||||
def make_sudo_cmd(self, sudo_exe, executable, cmd):
|
||||
"""
|
||||
Helper function for wrapping commands with sudo.
|
||||
def make_become_cmd(self, cmd, shell, become_settings=None):
|
||||
|
||||
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.
|
||||
"""
|
||||
helper function to create privilege escalation commands
|
||||
"""
|
||||
|
||||
# FIXME: become settings should probably be stored in the connection info itself
|
||||
if become_settings is None:
|
||||
become_settings = {}
|
||||
|
||||
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 self.become_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
|
||||
success_key = 'SUDO-SUCCESS-%s' % randbits
|
||||
exe = become_settings.get('sudo_exe', C.DEFAULT_SUDO_EXE)
|
||||
flags = become_settings.get('sudo_flags', C.DEFAULT_SUDO_FLAGS)
|
||||
becomecmd = '%s -k && %s %s -S -p "%s" -u %s %s -c "%s"' % \
|
||||
(exe, exe, flags or C.DEFAULT_SUDO_FLAGS, prompt, self.become_user, shell, 'echo %s; %s' % (success_key, cmd))
|
||||
|
||||
sudocmd = '%s -k && %s %s -S -p "%s" -u %s %s -c %s' % (
|
||||
sudo_exe, sudo_exe, C.DEFAULT_SUDO_FLAGS, prompt,
|
||||
self.sudo_user, executable or '$SHELL',
|
||||
pipes.quote('echo %s; %s' % (success_key, cmd))
|
||||
)
|
||||
elif self.become_method == 'su':
|
||||
exe = become_settings.get('su_exe', C.DEFAULT_SU_EXE)
|
||||
flags = become_settings.get('su_flags', C.DEFAULT_SU_FLAGS)
|
||||
becomecmd = '%s %s %s -c "%s -c %s"' % (exe, flags, self.become_user, shell, pipes.quote('echo %s; %s' % (success_key, cmd)))
|
||||
|
||||
# FIXME: old code, can probably be removed as it's been commented out for a while
|
||||
#return ('/bin/sh -c ' + pipes.quote(sudocmd), prompt, success_key)
|
||||
return (sudocmd, prompt, success_key)
|
||||
elif self.become_method == 'pbrun':
|
||||
exe = become_settings.get('pbrun_exe', 'pbrun')
|
||||
flags = become_settings.get('pbrun_flags', '')
|
||||
becomecmd = '%s -b -l %s -u %s "%s"' % (exe, flags, self.become_user, 'echo %s; %s' % (success_key,cmd))
|
||||
|
||||
elif self.become_method == 'pfexec':
|
||||
exe = become_settings.get('pfexec_exe', 'pbrun')
|
||||
flags = become_settings.get('pfexec_flags', '')
|
||||
# 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))
|
||||
elif self.become:
|
||||
raise errors.AnsibleError("Privilege escalation method not found: %s" % method)
|
||||
|
||||
return (('%s -c ' % shell) + pipes.quote(becomecmd), prompt, success_key)
|
||||
|
||||
def check_become_success(self, output, become_settings):
|
||||
#TODO: implement
|
||||
pass
|
||||
|
||||
def _get_fields(self):
|
||||
return [i for i in self.__dict__.keys() if i[:1] != '_']
|
||||
|
@ -204,4 +229,3 @@ class ConnectionInformation:
|
|||
for field in self._get_fields():
|
||||
value = templar.template(getattr(self, field))
|
||||
setattr(self, field, value)
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ class PlayIterator:
|
|||
self._host_states[host.name] = s
|
||||
|
||||
def get_failed_hosts(self):
|
||||
return dict((host, True) for (host, state) in self._host_states.iteritems() if state.run_state == self.ITERATING_COMPLETE and state.failed_state != self.FAILED_NONE)
|
||||
return dict((host, True) for (host, state) in self._host_states.iteritems() if state.run_state == self.ITERATING_COMPLETE and state.fail_state != self.FAILED_NONE)
|
||||
|
||||
def get_original_task(self, host, task):
|
||||
'''
|
||||
|
|
|
@ -33,6 +33,7 @@ __all__ = ['TaskExecutor']
|
|||
|
||||
import json
|
||||
import time
|
||||
import pipes
|
||||
|
||||
class TaskExecutor:
|
||||
|
||||
|
|
|
@ -72,6 +72,11 @@ class Base:
|
|||
def munge(self, ds):
|
||||
''' infrequently used method to do some pre-processing of legacy terms '''
|
||||
|
||||
for base_class in self.__class__.__bases__:
|
||||
method = getattr(self, ("_munge_%s" % base_class.__name__).lower(), None)
|
||||
if method:
|
||||
ds = method(ds)
|
||||
|
||||
return ds
|
||||
|
||||
def load_data(self, ds, variable_manager=None, loader=None):
|
||||
|
|
88
v2/ansible/playbook/become.py
Normal file
88
v2/ansible/playbook/become.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleParserError
|
||||
from ansible.playbook.attribute import Attribute, FieldAttribute
|
||||
#from ansible.utils.display import deprecated
|
||||
|
||||
class Become:
|
||||
|
||||
# Privlege escalation
|
||||
_become = FieldAttribute(isa='bool', default=False)
|
||||
_become_method = FieldAttribute(isa='string')
|
||||
_become_user = FieldAttribute(isa='string')
|
||||
_become_pass = FieldAttribute(isa='string')
|
||||
|
||||
def __init__(self):
|
||||
return super(Become, self).__init__()
|
||||
|
||||
def _detect_privilege_escalation_conflict(self, ds):
|
||||
|
||||
# Fail out if user specifies conflicting privelege escalations
|
||||
has_become = 'become' in ds or 'become_user'in ds
|
||||
has_sudo = 'sudo' in ds or 'sudo_user' in ds
|
||||
has_su = 'su' in ds or 'su_user' in ds
|
||||
|
||||
if has_become:
|
||||
msg = 'The become params ("become", "become_user") and'
|
||||
if has_sudo:
|
||||
raise errors.AnsibleParserError('%s sudo params ("sudo", "sudo_user") cannot be used together' % msg)
|
||||
elif has_su:
|
||||
raise errors.AnsibleParserError('%s su params ("su", "su_user") cannot be used together' % msg)
|
||||
elif has_sudo and has_su:
|
||||
raise errors.AnsibleParserError('sudo params ("sudo", "sudo_user") and su params ("su", "su_user") cannot be used together')
|
||||
|
||||
def _munge_become(self, ds):
|
||||
|
||||
self._detect_privilege_escalation_conflict(ds)
|
||||
|
||||
# Setting user implies setting become/sudo/su to true
|
||||
if 'become_user' in ds and not ds.get('become', False):
|
||||
ds['become'] = True
|
||||
|
||||
# Privilege escalation, backwards compatibility for sudo/su
|
||||
if 'sudo' in ds or 'sudo_user' in ds:
|
||||
ds['become_method'] = 'sudo'
|
||||
if 'sudo' in ds:
|
||||
ds['become'] = ds['sudo']
|
||||
del ds['sudo']
|
||||
else:
|
||||
ds['become'] = True
|
||||
if 'sudo_user' in ds:
|
||||
ds['become_user'] = ds['sudo_user']
|
||||
del ds['sudo_user']
|
||||
|
||||
#deprecated("Instead of sudo/sudo_user, use become/become_user and set become_method to 'sudo' (default)")
|
||||
|
||||
elif 'su' in ds or 'su_user' in ds:
|
||||
ds['become_method'] = 'su'
|
||||
if 'su' in ds:
|
||||
ds['become'] = ds['su']
|
||||
del ds['su']
|
||||
else:
|
||||
ds['become'] = True
|
||||
if 'su_user' in ds:
|
||||
ds['become_user'] = ds['su_user']
|
||||
del ds['su_user']
|
||||
|
||||
#deprecated("Instead of su/su_user, use become/become_user and set become_method to 'su' (default is sudo)")
|
||||
|
||||
return ds
|
|
@ -21,6 +21,7 @@ __metaclass__ = type
|
|||
|
||||
from ansible.playbook.attribute import Attribute, FieldAttribute
|
||||
from ansible.playbook.base import Base
|
||||
#from ansible.playbook.become import Become
|
||||
from ansible.playbook.conditional import Conditional
|
||||
from ansible.playbook.helpers import load_list_of_tasks
|
||||
from ansible.playbook.role import Role
|
||||
|
@ -80,7 +81,8 @@ class Block(Base, Conditional, Taggable):
|
|||
return dict(block=ds)
|
||||
else:
|
||||
return dict(block=[ds])
|
||||
return ds
|
||||
|
||||
return super(Block, self).munge(ds)
|
||||
|
||||
def _load_block(self, attr, ds):
|
||||
return load_list_of_tasks(
|
||||
|
|
|
@ -23,6 +23,7 @@ from ansible.errors import AnsibleError, AnsibleParserError
|
|||
|
||||
from ansible.playbook.attribute import Attribute, FieldAttribute
|
||||
from ansible.playbook.base import Base
|
||||
from ansible.playbook.become import Become
|
||||
from ansible.playbook.helpers import load_list_of_blocks, load_list_of_roles, compile_block_list
|
||||
from ansible.playbook.role import Role
|
||||
from ansible.playbook.taggable import Taggable
|
||||
|
@ -33,7 +34,7 @@ from ansible.utils.vars import combine_vars
|
|||
__all__ = ['Play']
|
||||
|
||||
|
||||
class Play(Base, Taggable):
|
||||
class Play(Base, Taggable, Become):
|
||||
|
||||
"""
|
||||
A play is a language feature that represents a list of roles and/or
|
||||
|
@ -47,21 +48,19 @@ class Play(Base, Taggable):
|
|||
|
||||
# =================================================================================
|
||||
# Connection-Related Attributes
|
||||
|
||||
# TODO: generalize connection
|
||||
_accelerate = FieldAttribute(isa='bool', default=False)
|
||||
_accelerate_ipv6 = FieldAttribute(isa='bool', default=False)
|
||||
_accelerate_port = FieldAttribute(isa='int', default=5099)
|
||||
_accelerate_port = FieldAttribute(isa='int', default=5099) # should be alias of port
|
||||
|
||||
# Connection
|
||||
_connection = FieldAttribute(isa='string', default='smart')
|
||||
_gather_facts = FieldAttribute(isa='string', default='smart')
|
||||
_hosts = FieldAttribute(isa='list', default=[], required=True)
|
||||
_name = FieldAttribute(isa='string', default='<no name specified>')
|
||||
_port = FieldAttribute(isa='int', default=22)
|
||||
_remote_user = FieldAttribute(isa='string', default='root')
|
||||
_su = FieldAttribute(isa='bool', default=False)
|
||||
_su_user = FieldAttribute(isa='string', default='root')
|
||||
_su_pass = FieldAttribute(isa='string')
|
||||
_sudo = FieldAttribute(isa='bool', default=False)
|
||||
_sudo_user = FieldAttribute(isa='string', default='root')
|
||||
_sudo_pass = FieldAttribute(isa='string')
|
||||
|
||||
# Variable Attributes
|
||||
_vars = FieldAttribute(isa='dict', default=dict())
|
||||
|
@ -101,6 +100,7 @@ class Play(Base, Taggable):
|
|||
@staticmethod
|
||||
def load(data, variable_manager=None, loader=None):
|
||||
p = Play()
|
||||
print("in play load, become is: %s" % getattr(p, 'become'))
|
||||
return p.load_data(data, variable_manager=variable_manager, loader=loader)
|
||||
|
||||
def munge(self, ds):
|
||||
|
@ -122,7 +122,7 @@ class Play(Base, Taggable):
|
|||
ds['remote_user'] = ds['user']
|
||||
del ds['user']
|
||||
|
||||
return ds
|
||||
return super(Play, self).munge(ds)
|
||||
|
||||
def _load_vars(self, attr, ds):
|
||||
'''
|
||||
|
@ -187,7 +187,7 @@ class Play(Base, Taggable):
|
|||
roles.append(Role.load(ri))
|
||||
return roles
|
||||
|
||||
# FIXME: post_validation needs to ensure that su/sudo are not both set
|
||||
# FIXME: post_validation needs to ensure that become/su/sudo have only 1 set
|
||||
|
||||
def _compile_roles(self):
|
||||
'''
|
||||
|
|
|
@ -98,7 +98,7 @@ class PlaybookInclude(Base):
|
|||
raise AnsibleParserError("vars for include statements must be specified as a dictionary", obj=ds)
|
||||
new_ds[k] = v
|
||||
|
||||
return new_ds
|
||||
return super(PlaybookInclude, self).munge(new_ds)
|
||||
|
||||
def _munge_include(self, ds, new_ds, k, v):
|
||||
'''
|
||||
|
|
|
@ -88,7 +88,7 @@ class RoleDefinition(Base, Conditional, Taggable):
|
|||
self._ds = ds
|
||||
|
||||
# and return the cleaned-up data structure
|
||||
return new_ds
|
||||
return super(RoleDefinition, self).munge(new_ds)
|
||||
|
||||
def _load_role_name(self, ds):
|
||||
'''
|
||||
|
|
|
@ -29,6 +29,7 @@ from ansible.plugins import module_loader, lookup_loader
|
|||
|
||||
from ansible.playbook.attribute import Attribute, FieldAttribute
|
||||
from ansible.playbook.base import Base
|
||||
from ansible.playbook.become import Become
|
||||
from ansible.playbook.block import Block
|
||||
from ansible.playbook.conditional import Conditional
|
||||
from ansible.playbook.role import Role
|
||||
|
@ -36,7 +37,7 @@ from ansible.playbook.taggable import Taggable
|
|||
|
||||
__all__ = ['Task']
|
||||
|
||||
class Task(Base, Conditional, Taggable):
|
||||
class Task(Base, Conditional, Taggable, Become):
|
||||
|
||||
"""
|
||||
A task is a language feature that represents a call to a module, with given arguments and other parameters.
|
||||
|
@ -86,12 +87,6 @@ class Task(Base, Conditional, Taggable):
|
|||
_remote_user = FieldAttribute(isa='string')
|
||||
_retries = FieldAttribute(isa='int', default=1)
|
||||
_run_once = FieldAttribute(isa='bool')
|
||||
_su = FieldAttribute(isa='bool')
|
||||
_su_pass = FieldAttribute(isa='string')
|
||||
_su_user = FieldAttribute(isa='string')
|
||||
_sudo = FieldAttribute(isa='bool')
|
||||
_sudo_user = FieldAttribute(isa='string')
|
||||
_sudo_pass = FieldAttribute(isa='string')
|
||||
_transport = FieldAttribute(isa='string')
|
||||
_until = FieldAttribute(isa='list') # ?
|
||||
_vars = FieldAttribute(isa='dict', default=dict())
|
||||
|
@ -172,6 +167,7 @@ class Task(Base, Conditional, Taggable):
|
|||
args_parser = ModuleArgsParser(task_ds=ds)
|
||||
(action, args, delegate_to) = args_parser.parse()
|
||||
|
||||
|
||||
new_ds['action'] = action
|
||||
new_ds['args'] = args
|
||||
new_ds['delegate_to'] = delegate_to
|
||||
|
@ -186,7 +182,7 @@ class Task(Base, Conditional, Taggable):
|
|||
else:
|
||||
new_ds[k] = v
|
||||
|
||||
return new_ds
|
||||
return super(Task, self).munge(new_ds)
|
||||
|
||||
def post_validate(self, all_vars=dict(), fail_on_undefined=True):
|
||||
'''
|
||||
|
|
|
@ -130,7 +130,7 @@ class ActionBase:
|
|||
if tmp and "tmp" in tmp:
|
||||
# tmp has already been created
|
||||
return False
|
||||
if not self._connection._has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES or self._connection_info.su:
|
||||
if not self._connection._has_pipelining or not C.ANSIBLE_SSH_PIPELINING or C.DEFAULT_KEEP_REMOTE_FILES or self._connection_info.become:
|
||||
# tmp is necessary to store module source code
|
||||
return True
|
||||
if not self._connection._has_pipelining:
|
||||
|
@ -152,12 +152,11 @@ class ActionBase:
|
|||
basefile = 'ansible-tmp-%s-%s' % (time.time(), random.randint(0, 2**48))
|
||||
use_system_tmp = False
|
||||
|
||||
if (self._connection_info.sudo and self._connection_info.sudo_user != 'root') or (self._connection_info.su and self._connection_info.su_user != 'root'):
|
||||
if self._connection_info.become and self._connection_info.become_user != 'root':
|
||||
use_system_tmp = True
|
||||
|
||||
tmp_mode = None
|
||||
if self._connection_info.remote_user != 'root' or \
|
||||
((self._connection_info.sudo and self._connection_info.sudo_user != 'root') or (self._connection_info.su and self._connection_info.su_user != 'root')):
|
||||
if self._connection_info.remote_user != 'root' or self._connection_info.become and self._connection_info.become_user != 'root':
|
||||
tmp_mode = 'a+rx'
|
||||
|
||||
cmd = self._shell.mkdtemp(basefile, use_system_tmp, tmp_mode)
|
||||
|
@ -291,10 +290,8 @@ class ActionBase:
|
|||
split_path = path.split(os.path.sep, 1)
|
||||
expand_path = split_path[0]
|
||||
if expand_path == '~':
|
||||
if self._connection_info.sudo and self._connection_info.sudo_user:
|
||||
expand_path = '~%s' % self._connection_info.sudo_user
|
||||
elif self._connection_info.su and self._connection_info.su_user:
|
||||
expand_path = '~%s' % self._connection_info.su_user
|
||||
if self._connection_info.become and self._connection_info.become_user:
|
||||
expand_path = '~%s' % self._connection_info.become_user
|
||||
|
||||
cmd = self._shell.expand_user(expand_path)
|
||||
debug("calling _low_level_execute_command to expand the remote user path")
|
||||
|
@ -373,7 +370,7 @@ class ActionBase:
|
|||
|
||||
environment_string = self._compute_environment_string()
|
||||
|
||||
if tmp and "tmp" in tmp and ((self._connection_info.sudo and self._connection_info.sudo_user != 'root') or (self._connection_info.su and self._connection_info.su_user != 'root')):
|
||||
if tmp and "tmp" in tmp and self._connection_info.become and self._connection_info.become_user != 'root':
|
||||
# deal with possible umask issues once sudo'ed to other user
|
||||
self._remote_chmod(tmp, 'a+r', remote_module_path)
|
||||
|
||||
|
@ -391,7 +388,7 @@ class ActionBase:
|
|||
|
||||
rm_tmp = None
|
||||
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||
if not self._connection_info.sudo or self._connection_info.su or self._connection_info.sudo_user == 'root' or self._connection_info.su_user == 'root':
|
||||
if not self._connection_info.become or self._connection_info.become_user == 'root':
|
||||
# not sudoing or sudoing to root, so can cleanup files in the same step
|
||||
rm_tmp = tmp
|
||||
|
||||
|
@ -409,7 +406,7 @@ class ActionBase:
|
|||
debug("_low_level_execute_command returned ok")
|
||||
|
||||
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
|
||||
if (self._connection_info.sudo and self._connection_info.sudo_user != 'root') or (self._connection_info.su and self._connection_info.su_user != 'root'):
|
||||
if self._connection_info.become and self._connection_info.become_user != 'root':
|
||||
# not sudoing to root, so maybe can't delete files as that other user
|
||||
# have to clean up temp files as original user in a second step
|
||||
cmd2 = self._shell.remove(tmp, recurse=True)
|
||||
|
@ -457,11 +454,7 @@ class ActionBase:
|
|||
success_key = None
|
||||
|
||||
if sudoable:
|
||||
if self._connection_info.su and self._connection_info.su_user:
|
||||
cmd, prompt, success_key = self._connection_info.make_su_cmd(executable, cmd)
|
||||
elif self._connection_info.sudo and self._connection_info.sudo_user:
|
||||
# FIXME: hard-coded sudo_exe here
|
||||
cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
||||
cmd, prompt, success_key = self._connection_info.make_become_cmd(executable, cmd)
|
||||
|
||||
debug("executing the command %s through the connection" % cmd)
|
||||
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data)
|
||||
|
|
|
@ -44,8 +44,8 @@ class Connection(ConnectionBase):
|
|||
|
||||
debug("in local.exec_command()")
|
||||
# su requires to be run from a terminal, and therefore isn't supported here (yet?)
|
||||
if self._connection_info.su:
|
||||
raise AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
#if self._connection_info.su:
|
||||
# raise AnsibleError("Internal Error: this module does not support running commands via su")
|
||||
|
||||
if in_data:
|
||||
raise AnsibleError("Internal Error: this module does not support optimized module pipelining")
|
||||
|
@ -57,7 +57,7 @@ class Connection(ConnectionBase):
|
|||
# else:
|
||||
# local_cmd = cmd
|
||||
#else:
|
||||
# local_cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
||||
# local_cmd, prompt, success_key = utils.make_become_cmd(self.runner.sudo_exe, sudo_user, executable, cmd)
|
||||
if executable:
|
||||
local_cmd = executable.split() + ['-c', cmd]
|
||||
else:
|
||||
|
|
|
@ -281,19 +281,19 @@ class Connection(ConnectionBase):
|
|||
# ssh_cmd += ['-6']
|
||||
ssh_cmd += [self._connection_info.remote_addr]
|
||||
|
||||
if not (self._connection_info.sudo or self._connection_info.su):
|
||||
prompt = None
|
||||
if executable:
|
||||
ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd))
|
||||
else:
|
||||
ssh_cmd.append(cmd)
|
||||
elif self._connection_info.su and self._connection_info.su_user:
|
||||
su_cmd, prompt, success_key = self._connection_info.make_su_cmd(executable, cmd)
|
||||
ssh_cmd.append(su_cmd)
|
||||
else:
|
||||
# FIXME: hard-coded sudo_exe here
|
||||
sudo_cmd, prompt, success_key = self._connection_info.make_sudo_cmd('/usr/bin/sudo', executable, cmd)
|
||||
ssh_cmd.append(sudo_cmd)
|
||||
#if not (self._connection_info.sudo or self._connection_info.su):
|
||||
# prompt = None
|
||||
# if executable:
|
||||
# ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd))
|
||||
# else:
|
||||
# ssh_cmd.append(cmd)
|
||||
#elif self._connection_info.su and self._connection_info.su_user:
|
||||
# su_cmd, prompt, success_key = self._connection_info.make_su_cmd(executable, cmd)
|
||||
# ssh_cmd.append(su_cmd)
|
||||
#else:
|
||||
# # FIXME: hard-coded sudo_exe here
|
||||
# sudo_cmd, prompt, success_key = self._connection_info.make_become_cmd('/usr/bin/sudo', executable, cmd)
|
||||
# ssh_cmd.append(sudo_cmd)
|
||||
|
||||
self._display.vvv("EXEC %s" % ' '.join(ssh_cmd), host=self._connection_info.remote_addr)
|
||||
|
||||
|
@ -369,6 +369,8 @@ class Connection(ConnectionBase):
|
|||
# no_prompt_err += sudo_errput
|
||||
|
||||
#(returncode, stdout, stderr) = self._communicate(p, stdin, in_data, su=su, sudoable=sudoable, prompt=prompt)
|
||||
# FIXME: the prompt won't be here anymore
|
||||
prompt=""
|
||||
(returncode, stdout, stderr) = self._communicate(p, stdin, in_data, prompt=prompt)
|
||||
|
||||
#if C.HOST_KEY_CHECKING and not_in_host_file:
|
||||
|
|
|
@ -59,6 +59,8 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
|
|||
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-become-pass', default=False, dest='ask_become_pass', action='store_true',
|
||||
help='ask for privlege escalation password')
|
||||
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
|
||||
help='ask for vault password')
|
||||
parser.add_option('--vault-password-file', default=C.DEFAULT_VAULT_PASSWORD_FILE,
|
||||
|
@ -84,6 +86,10 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
|
|||
help='log output to this directory')
|
||||
|
||||
if runas_opts:
|
||||
parser.add_option("-b", "--become", default=C.DEFAULT_BECOME, action="store_true",
|
||||
dest='become', help="run operations with become (nopasswd implied)")
|
||||
parser.add_option('-B', '--become-user', help='run operations with as this '
|
||||
'user (default=%s)' % C.DEFAULT_BECOME_USER)
|
||||
parser.add_option("-s", "--sudo", default=C.DEFAULT_SUDO, action="store_true",
|
||||
dest='sudo', help="run operations with sudo (nopasswd)")
|
||||
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
|
||||
|
@ -100,6 +106,9 @@ def base_parser(usage="", output_opts=False, runas_opts=False,
|
|||
parser.add_option('-c', '--connection', dest='connection',
|
||||
default=C.DEFAULT_TRANSPORT,
|
||||
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
|
||||
parser.add_option('--become-method', dest='become_method',
|
||||
default=C.DEFAULT_BECOME_METHOD,
|
||||
help="privlege escalation method to use (default=%s)" % C.DEFAULT_BECOME_METHOD)
|
||||
|
||||
if async_opts:
|
||||
parser.add_option('-P', '--poll', default=C.DEFAULT_POLL_INTERVAL, type='int',
|
||||
|
|
7
v2/samples/test_become.yml
Normal file
7
v2/samples/test_become.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
- hosts: all
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- command: whoami
|
||||
become: yes
|
||||
become_user: jamesc
|
||||
become_method: su
|
Loading…
Reference in a new issue