Commit 2372a3b7 by Michael DeHaan

Sudo support operational in both playbooks and main program. Implementation…

Sudo support operational in both playbooks and main program.   Implementation could use some cleanup.
parent 81e34960
......@@ -10,6 +10,10 @@
- hosts: all
user: root
# could have also have done:
# user: mdehaan
# sudo: True
# make these variables available inside of templates
# for when we use the 'template' action/module later on...
......
......@@ -22,6 +22,7 @@ import paramiko
import traceback
import os
import time
import random
from ansible import errors
################################################
......@@ -80,36 +81,47 @@ class ParamikoConnection(object):
self.ssh = self._get_conn()
return self
def exec_command(self, cmd, sudoable=True):
def exec_command(self, cmd, tmp_path, sudoable=False):
''' run a command on the remote host '''
if not self.runner.sudo or not sudoable:
stdin, stdout, stderr = self.ssh.exec_command(cmd)
return (stdin, stdout, stderr)
else:
# percalculated tmp_path is ONLY required for sudo usage
if tmp_path is None:
raise Exception("expecting tmp_path")
r = random.randint(0,99999)
# invoke command using a new connection over sudo
result_file = os.path.join(tmp_path, "sudo_result.%s" % r)
self.ssh.close()
ssh_sudo = self._get_conn()
sudo_chan = ssh_sudo.invoke_shell()
sudo_chan.send("sudo -s\n")
sudo_chan.send("echo 'START==>';%s;echo '<==STOP'\n" % cmd)
timeout = 60 # make configurable?
# to avoid ssh expect logic, redirect output to file and move the
# 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))
time.sleep(1)
while not sudo_chan.recv_ready():
time.sleep(1)
timeout -= 1
if timeout < 0:
return (None, json.dumps(dict(failed=True, msg="sudo timeout")), '')
out = sudo_chan.recv(2058)
sudo_chan.close()
self.ssh = self._get_conn()
out = self._expect_like(out)
return (None, out, '')
def _expect_like(self, msg):
''' hack to make invoke_shell more or less work for sudo usage '''
left = msg.rindex("START==>")
right = msg.rindex("<==STOP")
return msg[left+8:right].lstrip().rstrip()
# now load the results of the JSON execution...
# FIXME: really need some timeout logic here
sftp = self.ssh.open_sftp()
while True:
# print "waiting on %s" % result_file
time.sleep(1)
try:
stat = sftp.stat(result_file)
break
except IOError:
pass
sftp.close()
# TODO: see if there's a SFTP way to just get the file contents w/o saving
# to disk vs this hack...
stdin, stdout, stderr = self.ssh.exec_command("cat %s" % result_file)
return (stdin, stdout, stderr)
def put_file(self, in_path, out_path):
''' transfer a file from local to remote '''
......
......@@ -255,7 +255,7 @@ class PlayBook(object):
# *****************************************************
def _run_module(self, pattern, host_list, module, args, remote_user,
async_seconds, async_poll_interval, only_if):
async_seconds, async_poll_interval, only_if, sudo):
''' run a particular module step in a playbook '''
hosts = [ h for h in host_list if (h not in self.stats.failures) and (h not in self.stats.dark)]
......@@ -268,6 +268,7 @@ class PlayBook(object):
remote_port=self.remote_port,
setup_cache=SETUP_CACHE, basedir=self.basedir,
conditional=only_if, callbacks=self.runner_callbacks,
sudo=sudo
)
if async_seconds == 0:
......@@ -278,7 +279,7 @@ class PlayBook(object):
# *****************************************************
def _run_task(self, pattern=None, host_list=None, task=None,
remote_user=None, handlers=None, conditional=False):
remote_user=None, handlers=None, conditional=False, sudo=False):
''' run a single task in the playbook and recursively run any subtasks. '''
# load the module name and parameters from the task entry
......@@ -307,7 +308,7 @@ class PlayBook(object):
# run the task in parallel
results = self._run_module(pattern, host_list, module_name,
module_args, remote_user, async_seconds,
async_poll_interval, only_if)
async_poll_interval, only_if, sudo)
self.stats.compute(results)
......@@ -402,7 +403,7 @@ class PlayBook(object):
# *****************************************************
def _do_setup_step(self, pattern, vars, user, port, vars_files=None):
def _do_setup_step(self, pattern, vars, user, port, sudo, vars_files=None):
''' push variables down to the systems and get variables+facts back up '''
# this enables conditional includes like $facter_os.yml and is only done
......@@ -432,7 +433,7 @@ class PlayBook(object):
timeout=self.timeout, remote_user=user,
remote_pass=self.remote_pass, remote_port=self.remote_port,
setup_cache=SETUP_CACHE,
callbacks=self.runner_callbacks,
callbacks=self.runner_callbacks, sudo=sudo,
).run()
self.stats.compute(setup_results, setup=True)
......@@ -464,15 +465,16 @@ class PlayBook(object):
handlers = pg.get('handlers', [])
user = pg.get('user', C.DEFAULT_REMOTE_USER)
port = pg.get('port', C.DEFAULT_REMOTE_PORT)
sudo = pg.get('sudo', False)
self.callbacks.on_play_start(pattern)
# push any variables down to the system # and get facts/ohai/other data back up
self._do_setup_step(pattern, vars, user, port, None)
self._do_setup_step(pattern, vars, user, port, sudo, None)
# now with that data, handle contentional variable file imports!
if len(vars_files) > 0:
self._do_setup_step(pattern, vars, user, port, vars_files)
self._do_setup_step(pattern, vars, user, port, sudo, vars_files)
# run all the top level tasks, these get run on every node
for task in tasks:
......@@ -482,6 +484,7 @@ class PlayBook(object):
task=task,
handlers=handlers,
remote_user=user,
sudo=sudo
)
# handlers only run on certain nodes, they are flagged by _flag_handlers
......@@ -499,7 +502,8 @@ class PlayBook(object):
handlers=[],
host_list=triggered_by,
conditional=True,
remote_user=user
remote_user=user,
sudo=sudo
)
# end of execution for this particular pattern. Multiple patterns
......
......@@ -241,7 +241,7 @@ class Runner(object):
for filename in files:
if not filename.startswith('/tmp/'):
raise Exception("not going to happen")
self._exec_command(conn, "rm -rf %s" % filename)
self._exec_command(conn, "rm -rf %s" % filename, None)
# *****************************************************
......@@ -249,7 +249,7 @@ class Runner(object):
''' transfers a module file to the remote side to execute it, but does not execute it yet '''
outpath = self._copy_module(conn, tmp, module)
self._exec_command(conn, "chmod +x %s" % outpath)
self._exec_command(conn, "chmod +x %s" % outpath, tmp)
return outpath
# *****************************************************
......@@ -313,7 +313,7 @@ class Runner(object):
if self.remote_user == 'root':
args = "%s metadata=/etc/ansible/setup" % args
else:
args = "%s metadata=~/.ansible/setup" % args
args = "%s metadata=/home/%s/.ansible/setup" % (args, self.remote_user)
return args
# *****************************************************
......@@ -356,7 +356,7 @@ class Runner(object):
cmd = "%s %s" % (remote_module_path, argsfile)
else:
cmd = " ".join([str(x) for x in [remote_module_path, async_jid, async_limit, async_module, argsfile]])
return [ self._exec_command(conn, cmd, sudoable=True), client_executed_str ]
return [ self._exec_command(conn, cmd, tmp, sudoable=True), client_executed_str ]
# *****************************************************
......@@ -555,14 +555,14 @@ class Runner(object):
# *****************************************************
def _exec_command(self, conn, cmd, sudoable=False):
def _exec_command(self, conn, cmd, tmp, sudoable=False):
''' execute a command string over SSH, return the output '''
msg = '%s: %s' % (self.module_name, cmd)
# log remote command execution
conn.exec_command('/usr/bin/logger -t ansible -p auth.info "%s"' % msg)
conn.exec_command('/usr/bin/logger -t ansible -p auth.info "%s"' % msg, None)
# now run actual command
stdin, stdout, stderr = conn.exec_command(cmd, sudoable=sudoable)
stdin, stdout, stderr = conn.exec_command(cmd, tmp, sudoable=sudoable)
if type(stdout) != str:
return "\n".join(stdout.readlines())
else:
......@@ -573,7 +573,7 @@ class Runner(object):
def _get_tmp_path(self, conn):
''' gets a temporary path on a remote box '''
result = self._exec_command(conn, "mktemp -d /tmp/ansible.XXXXXX", sudoable=False)
result = self._exec_command(conn, "mktemp -d /tmp/ansible.XXXXXX", None, sudoable=False)
cleaned = result.split("\n")[0].strip() + '/'
return cleaned
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment