Merge pull request #1798 from dagwieers/raw-executable

Allow to change executable (shell/interpreter) when using raw
This commit is contained in:
Daniel Hokka Zakrisson 2013-01-08 08:35:08 -08:00
commit 4955587d8c
8 changed files with 51 additions and 24 deletions

View file

@ -438,11 +438,14 @@ 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):
''' execute a command string over SSH, return the output '''
if not executable:
executable = '/bin/sh'
sudo_user = self.sudo_user
rc, stdin, stdout, stderr = conn.exec_command(cmd, tmp, sudo_user, sudoable=sudoable)
rc, stdin, stdout, stderr = conn.exec_command(cmd, tmp, sudo_user, sudoable=sudoable, executable=executable)
if type(stdout) not in [ str, unicode ]:
out = ''.join(stdout.readlines())

View file

@ -34,7 +34,15 @@ class ActionModule(object):
self.runner = runner
def run(self, conn, tmp, module_name, module_args, inject):
return ReturnData(conn=conn,
result=self.runner._low_level_exec_command(conn, module_args.encode('utf-8'), tmp, sudoable=True)
)
executable = None
args = []
for arg in module_args.split(' '):
if arg.startswith('executable='):
executable = '='.join(arg.split('=')[1:])
else:
args.append(arg)
module_args = ' '.join(args).encode('utf-8')
return ReturnData(conn=conn,
result=self.runner._low_level_exec_command(conn, module_args, tmp, sudoable=True, executable=executable)
)

View file

@ -67,7 +67,7 @@ class Connection(object):
return self
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False):
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'):
''' run a command on the remote host '''
vvv("EXEC COMMAND %s" % cmd)
@ -79,6 +79,7 @@ class Connection(object):
mode='command',
cmd=cmd,
tmp_path=tmp_path,
executable=executable,
)
data = utils.jsonify(data)
data = utils.encrypt(self.key, data)

View file

@ -17,6 +17,7 @@
import traceback
import os
import pipes
import shutil
import subprocess
from ansible import errors
@ -36,20 +37,22 @@ class Connection(object):
return self
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False):
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'):
''' run a command on the local host '''
if self.runner.sudo and sudoable:
if not self.runner.sudo or not sudoable:
local_cmd = [ executable, '-c', cmd]
else:
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) and native 'ssh' connection types")
cmd = "sudo -u {0} -s {1}".format(sudo_user, cmd)
sudocmd = "sudo -u %s -s %s -c %s" % (sudo_user, executable, cmd)
local_cmd = ['/bin/sh', '-c', sudocmd]
vvv("EXEC %s" % cmd, host=self.host)
basedir = self.runner.basedir
p = subprocess.Popen(cmd, cwd=basedir, shell=True, stdin=None,
vvv("EXEC %s" % local_cmd, host=self.host)
p = subprocess.Popen(local_cmd, cwd=self.runner.basedir, executable=executable,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return (p.returncode, '', stdout, stderr)

View file

@ -96,7 +96,7 @@ class Connection(object):
return ssh
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False):
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'):
''' run a command on the remote host '''
bufsize = 4096
@ -110,7 +110,7 @@ class Connection(object):
chan.get_pty()
if not self.runner.sudo or not sudoable:
quoted_command = '/bin/sh -c ' + pipes.quote(cmd)
quoted_command = executable + ' -c ' + pipes.quote(cmd)
vvv("EXEC %s" % quoted_command, host=self.host)
chan.exec_command(quoted_command)
else:
@ -123,8 +123,8 @@ class Connection(object):
# 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
sudocmd = 'sudo -k && sudo -p "%s" -u %s /bin/sh -c %s' % (
prompt, sudo_user, pipes.quote(cmd))
sudocmd = 'sudo -k && sudo -p "%s" -u %s %s -c %s' % (
prompt, sudo_user, executable, pipes.quote(cmd))
shcmd = '/bin/sh -c ' + pipes.quote(sudocmd)
vvv("EXEC %s" % shcmd, host=self.host)
sudo_output = ''

View file

@ -81,13 +81,15 @@ class Connection(object):
os.write(self.wfd, "%s\n" % self.runner.remote_pass)
os.close(self.wfd)
def exec_command(self, cmd, tmp_path, sudo_user,sudoable=False):
def exec_command(self, cmd, tmp_path, sudo_user,sudoable=False, executable='/bin/sh'):
''' run a command on the remote host '''
ssh_cmd = self._password_cmd()
ssh_cmd += ["ssh", "-tt", "-q"] + self.common_args + [self.host]
if self.runner.sudo and sudoable:
if not self.runner.sudo or not sudoable:
ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd))
else:
# 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)
@ -97,10 +99,9 @@ class Connection(object):
# 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
sudocmd = 'sudo -k && sudo -p "%s" -u %s /bin/sh -c %s' % (
prompt, sudo_user, pipes.quote(cmd))
cmd = sudocmd
ssh_cmd.append('/bin/sh -c ' + pipes.quote(cmd))
sudocmd = 'sudo -k && sudo -p "%s" -u %s %s -c %s' % (
prompt, sudo_user, executable, pipes.quote(cmd))
ssh_cmd.append('/bin/sh -c ' + pipes.quote(sudocmd))
vvv("EXEC %s" % ssh_cmd, host=self.host)
try:

View file

@ -149,9 +149,11 @@ def command(data):
return dict(failed=True, msg='internal error: cmd is required')
if 'tmp_path' not in data:
return dict(failed=True, msg='internal error: tmp_path is required')
if 'executable' not in data:
return dict(failed=True, msg='internal error: executable is required')
log("executing: %s" % data['cmd'])
p = subprocess.Popen(data['cmd'], shell=True, stdout=subprocess.PIPE, close_fds=True)
p = subprocess.Popen(data['cmd'], executable=data['executable'], shell=True, stdout=subprocess.PIPE, close_fds=True)
(stdout, stderr) = p.communicate()
if stdout is None:
stdout = ''

View file

@ -4,7 +4,16 @@ DOCUMENTATION = '''
---
module: raw
short_description: Executes a low-down and dirty SSH command
options: {}
options:
free_form:
description:
- the raw module takes a free form command to run
required: true
executable:
description:
- change the shell used to execute the command. Should be an absolute path to the executable.
required: false
version_added: "1.0"
description:
- Executes a low-down and dirty SSH command, not going through the module
subsystem. This is useful and should only be done in two cases. The