Add support for specifying sudo passwords to both ansible & playbook. Nopasswd sudo is no longer required.

This commit is contained in:
Michael DeHaan 2012-04-13 19:06:11 -04:00
parent 788d2bf8d1
commit f2465e0571
7 changed files with 45 additions and 13 deletions

View file

@ -72,6 +72,8 @@ class Cli(object):
sshpass = None sshpass = None
if options.ask_pass: if options.ask_pass:
sshpass = getpass.getpass(prompt="SSH password: ") sshpass = getpass.getpass(prompt="SSH password: ")
if options.ask_sudo_pass:
sudopass = getpass.getpass(prompt="sudo password: ")
if options.tree: if options.tree:
utils.prepare_writeable_dir(options.tree) utils.prepare_writeable_dir(options.tree)
@ -86,7 +88,8 @@ class Cli(object):
host_list=options.inventory, timeout=options.timeout, host_list=options.inventory, timeout=options.timeout,
remote_port=options.remote_port, forks=options.forks, remote_port=options.remote_port, forks=options.forks,
background=options.seconds, pattern=pattern, background=options.seconds, pattern=pattern,
callbacks=self.callbacks, sudo=options.sudo, verbose=True, callbacks=self.callbacks, sudo=options.sudo,
sudo_pass=sudopass, verbose=True,
transport=options.connection, debug=options.debug transport=options.connection, debug=options.debug
) )
return (runner, runner.run()) return (runner, runner.run())

View file

@ -46,8 +46,11 @@ def main(args):
return 1 return 1
sshpass = None sshpass = None
sudopass = None
if options.ask_pass: if options.ask_pass:
sshpass = getpass.getpass(prompt="SSH password: ") sshpass = getpass.getpass(prompt="SSH password: ")
if options.ask_sudo_pass:
sudopass = getpass.getpass(prompt="sudo password: ")
override_hosts = None override_hosts = None
if options.override_hosts: if options.override_hosts:
override_hosts = options.override_hosts.split(",") override_hosts = options.override_hosts.split(",")
@ -66,7 +69,8 @@ def main(args):
forks=options.forks, debug=options.debug, verbose=True, forks=options.forks, debug=options.debug, verbose=True,
remote_pass=sshpass, remote_port=options.remote_port, remote_pass=sshpass, remote_port=options.remote_port,
callbacks=playbook_cb, runner_callbacks=runner_cb, stats=stats, callbacks=playbook_cb, runner_callbacks=runner_cb, stats=stats,
timeout=options.timeout, transport=options.connection timeout=options.timeout, transport=options.connection,
sudo_pass=sudopass
) )
try: try:

View file

@ -112,15 +112,26 @@ class ParamikoConnection(object):
sudo_chan = ssh_sudo.invoke_shell() sudo_chan = ssh_sudo.invoke_shell()
sudo_chan.send("sudo -s\n") sudo_chan.send("sudo -s\n")
# FIXME: using sudo with a password adds more delay, someone may wish
# to optimize to see when the channel is actually ready
if self.runner.sudo_pass:
time.sleep(0.1) # this is conservative
sudo_chan.send("%s\n" % self.runner.sudo_pass)
time.sleep(0.1)
# to avoid ssh expect logic, redirect output to file and move the # to avoid ssh expect logic, redirect output to file and move the
# file when we are done with it... # file when we are done with it...
sudo_chan.send("(%s >%s_pre 2>/dev/null ; mv %s_pre %s) &\n" % (cmd, result_file, result_file, result_file)) sudo_chan.send("(%s >%s_pre 2>/dev/null ; mv %s_pre %s) &\n" % (cmd, result_file, result_file, result_file))
# FIXME: someone may wish to optimize to not background the launch, and tell when the command
# returns, removing the time.sleep(1) here
time.sleep(1) time.sleep(1)
sudo_chan.close() sudo_chan.close()
self.ssh = self._get_conn() self.ssh = self._get_conn()
# now load the results of the JSON execution... # now load the results of the JSON execution...
# FIXME: really need some timeout logic here # FIXME: really need some timeout logic here
# though it doesn't make since to use the SSH timeout or impose any particular
# limit. Upgrades welcome.
sftp = self.ssh.open_sftp() sftp = self.ssh.open_sftp()
while True: while True:
# print "waiting on %s" % result_file # print "waiting on %s" % result_file
@ -181,6 +192,12 @@ class LocalConnection(object):
''' run a command on the local host ''' ''' run a command on the local host '''
if self.runner.sudo and sudoable: if self.runner.sudo and sudoable:
cmd = "sudo -s %s" % cmd cmd = "sudo -s %s" % cmd
if self.runner.sudo_pass:
# NOTE: if someone wants to add sudo w/ password to the local connection type, they are welcome
# to do so. The primary usage of the local connection is for crontab and kickstart usage however
# so this doesn't seem to be a huge priority
raise errors.AnsibleError("sudo with password is presently only supported on the paramiko (SSH) connection type")
p = subprocess.Popen(cmd, shell=True, stdin=None, p = subprocess.Popen(cmd, shell=True, stdin=None,
stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate() stdout, stderr = p.communicate()

View file

@ -19,11 +19,11 @@
import os import os
# control side (aka 'overlord')
DEFAULT_HOST_LIST = os.environ.get('ANSIBLE_HOSTS', DEFAULT_HOST_LIST = os.environ.get('ANSIBLE_HOSTS',
'/etc/ansible/hosts') '/etc/ansible/hosts')
DEFAULT_MODULE_PATH = os.environ.get('ANSIBLE_LIBRARY', DEFAULT_MODULE_PATH = os.environ.get('ANSIBLE_LIBRARY',
'/usr/share/ansible') '/usr/share/ansible')
DEFAULT_MODULE_NAME = 'command' DEFAULT_MODULE_NAME = 'command'
DEFAULT_PATTERN = '*' DEFAULT_PATTERN = '*'
DEFAULT_FORKS = 5 DEFAULT_FORKS = 5
@ -32,6 +32,8 @@ DEFAULT_TIMEOUT = 10
DEFAULT_POLL_INTERVAL = 15 DEFAULT_POLL_INTERVAL = 15
DEFAULT_REMOTE_USER = 'root' DEFAULT_REMOTE_USER = 'root'
DEFAULT_REMOTE_PASS = None DEFAULT_REMOTE_PASS = None
DEFAULT_SUDO_PASS = None
DEFAULT_REMOTE_PORT = 22 DEFAULT_REMOTE_PORT = 22
DEFAULT_TRANSPORT = 'paramiko' DEFAULT_TRANSPORT = 'paramiko'
DEFAULT_TRANSPORT_OPTS = ['local', 'paramiko'] DEFAULT_TRANSPORT_OPTS = ['local', 'paramiko']

View file

@ -54,6 +54,7 @@ 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 = C.DEFAULT_REMOTE_PORT, remote_port = C.DEFAULT_REMOTE_PORT,
transport = C.DEFAULT_TRANSPORT, transport = C.DEFAULT_TRANSPORT,
override_hosts = None, override_hosts = None,
@ -82,6 +83,7 @@ class PlayBook(object):
self.override_hosts = override_hosts self.override_hosts = override_hosts
self.extra_vars = extra_vars self.extra_vars = extra_vars
self.stats = stats self.stats = stats
self.sudo_pass = sudo_pass
self.basedir = os.path.dirname(playbook) self.basedir = os.path.dirname(playbook)
self.playbook = self._parse_playbook(playbook) self.playbook = self._parse_playbook(playbook)
@ -288,7 +290,7 @@ class PlayBook(object):
setup_cache=SETUP_CACHE, basedir=self.basedir, setup_cache=SETUP_CACHE, basedir=self.basedir,
conditional=only_if, callbacks=self.runner_callbacks, conditional=only_if, callbacks=self.runner_callbacks,
extra_vars=self.extra_vars, debug=self.debug, sudo=sudo, extra_vars=self.extra_vars, debug=self.debug, sudo=sudo,
transport=transport transport=transport, sudo_pass=self.sudo_pass
) )
if async_seconds == 0: if async_seconds == 0:
@ -450,7 +452,7 @@ class PlayBook(object):
remote_pass=self.remote_pass, remote_port=self.remote_port, remote_pass=self.remote_pass, remote_port=self.remote_port,
setup_cache=SETUP_CACHE, setup_cache=SETUP_CACHE,
callbacks=self.runner_callbacks, sudo=sudo, debug=self.debug, callbacks=self.runner_callbacks, sudo=sudo, debug=self.debug,
transport=transport, transport=transport, sudo_pass=self.sudo_pass
).run() ).run()
self.stats.compute(setup_results, setup=True) self.stats.compute(setup_results, setup=True)

View file

@ -73,8 +73,9 @@ class Runner(object):
module_name=C.DEFAULT_MODULE_NAME, module_args=C.DEFAULT_MODULE_ARGS, module_name=C.DEFAULT_MODULE_NAME, module_args=C.DEFAULT_MODULE_ARGS,
forks=C.DEFAULT_FORKS, timeout=C.DEFAULT_TIMEOUT, pattern=C.DEFAULT_PATTERN, forks=C.DEFAULT_FORKS, timeout=C.DEFAULT_TIMEOUT, pattern=C.DEFAULT_PATTERN,
remote_user=C.DEFAULT_REMOTE_USER, remote_pass=C.DEFAULT_REMOTE_PASS, remote_user=C.DEFAULT_REMOTE_USER, remote_pass=C.DEFAULT_REMOTE_PASS,
remote_port=C.DEFAULT_REMOTE_PORT, background=0, basedir=None, setup_cache=None, sudo_pass=C.DEFAULT_SUDO_PASS, remote_port=C.DEFAULT_REMOTE_PORT, background=0,
transport=C.DEFAULT_TRANSPORT, conditional='True', groups={}, callbacks=None, verbose=False, basedir=None, setup_cache=None, transport=C.DEFAULT_TRANSPORT,
conditional='True', groups={}, callbacks=None, verbose=False,
debug=False, sudo=False, extra_vars=None, module_vars=None): debug=False, sudo=False, extra_vars=None, module_vars=None):
if setup_cache is None: if setup_cache is None:
@ -115,6 +116,7 @@ class Runner(object):
self.background = background self.background = background
self.basedir = basedir self.basedir = basedir
self.sudo = sudo self.sudo = sudo
self.sudo_pass = sudo_pass
euid = pwd.getpwuid(os.geteuid())[0] euid = pwd.getpwuid(os.geteuid())[0]
if self.transport == 'local' and self.remote_user != euid: if self.transport == 'local' and self.remote_user != euid:

View file

@ -290,8 +290,10 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, asyn
parser.add_option('-i', '--inventory-file', dest='inventory', parser.add_option('-i', '--inventory-file', dest='inventory',
help="specify inventory host file (default=%s)" % constants.DEFAULT_HOST_LIST, help="specify inventory host file (default=%s)" % constants.DEFAULT_HOST_LIST,
default=constants.DEFAULT_HOST_LIST) default=constants.DEFAULT_HOST_LIST)
parser.add_option('-k', '--ask-pass', default=False, 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('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
help='ask for sudo password')
parser.add_option('-M', '--module-path', dest='module_path', parser.add_option('-M', '--module-path', dest='module_path',
help="specify path to module library (default=%s)" % constants.DEFAULT_MODULE_PATH, help="specify path to module library (default=%s)" % constants.DEFAULT_MODULE_PATH,
default=constants.DEFAULT_MODULE_PATH) default=constants.DEFAULT_MODULE_PATH)