Merge pull request #1798 from dagwieers/raw-executable
Allow to change executable (shell/interpreter) when using raw
This commit is contained in:
commit
4955587d8c
8 changed files with 51 additions and 24 deletions
|
@ -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 '''
|
''' execute a command string over SSH, return the output '''
|
||||||
|
|
||||||
|
if not executable:
|
||||||
|
executable = '/bin/sh'
|
||||||
|
|
||||||
sudo_user = self.sudo_user
|
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 ]:
|
if type(stdout) not in [ str, unicode ]:
|
||||||
out = ''.join(stdout.readlines())
|
out = ''.join(stdout.readlines())
|
||||||
|
|
|
@ -34,7 +34,15 @@ class ActionModule(object):
|
||||||
self.runner = runner
|
self.runner = runner
|
||||||
|
|
||||||
def run(self, conn, tmp, module_name, module_args, inject):
|
def run(self, conn, tmp, module_name, module_args, inject):
|
||||||
return ReturnData(conn=conn,
|
executable = None
|
||||||
result=self.runner._low_level_exec_command(conn, module_args.encode('utf-8'), tmp, sudoable=True)
|
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)
|
||||||
|
)
|
||||||
|
|
|
@ -67,7 +67,7 @@ class Connection(object):
|
||||||
|
|
||||||
return self
|
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 '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
vvv("EXEC COMMAND %s" % cmd)
|
vvv("EXEC COMMAND %s" % cmd)
|
||||||
|
@ -79,6 +79,7 @@ class Connection(object):
|
||||||
mode='command',
|
mode='command',
|
||||||
cmd=cmd,
|
cmd=cmd,
|
||||||
tmp_path=tmp_path,
|
tmp_path=tmp_path,
|
||||||
|
executable=executable,
|
||||||
)
|
)
|
||||||
data = utils.jsonify(data)
|
data = utils.jsonify(data)
|
||||||
data = utils.encrypt(self.key, data)
|
data = utils.encrypt(self.key, data)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
import os
|
import os
|
||||||
|
import pipes
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
|
@ -36,20 +37,22 @@ class Connection(object):
|
||||||
|
|
||||||
return self
|
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 '''
|
''' 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:
|
if self.runner.sudo_pass:
|
||||||
# NOTE: if someone wants to add sudo w/ password to the local connection type, they are welcome
|
# 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
|
# 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
|
# 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")
|
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)
|
vvv("EXEC %s" % local_cmd, host=self.host)
|
||||||
basedir = self.runner.basedir
|
p = subprocess.Popen(local_cmd, cwd=self.runner.basedir, executable=executable,
|
||||||
p = subprocess.Popen(cmd, cwd=basedir, shell=True, stdin=None,
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
stdout, stderr = p.communicate()
|
stdout, stderr = p.communicate()
|
||||||
return (p.returncode, '', stdout, stderr)
|
return (p.returncode, '', stdout, stderr)
|
||||||
|
|
|
@ -96,7 +96,7 @@ class Connection(object):
|
||||||
|
|
||||||
return ssh
|
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 '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
bufsize = 4096
|
bufsize = 4096
|
||||||
|
@ -110,7 +110,7 @@ class Connection(object):
|
||||||
chan.get_pty()
|
chan.get_pty()
|
||||||
|
|
||||||
if not self.runner.sudo or not sudoable:
|
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)
|
vvv("EXEC %s" % quoted_command, host=self.host)
|
||||||
chan.exec_command(quoted_command)
|
chan.exec_command(quoted_command)
|
||||||
else:
|
else:
|
||||||
|
@ -123,8 +123,8 @@ class Connection(object):
|
||||||
# the -p option.
|
# the -p option.
|
||||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||||
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
||||||
sudocmd = 'sudo -k && sudo -p "%s" -u %s /bin/sh -c %s' % (
|
sudocmd = 'sudo -k && sudo -p "%s" -u %s %s -c %s' % (
|
||||||
prompt, sudo_user, pipes.quote(cmd))
|
prompt, sudo_user, executable, pipes.quote(cmd))
|
||||||
shcmd = '/bin/sh -c ' + pipes.quote(sudocmd)
|
shcmd = '/bin/sh -c ' + pipes.quote(sudocmd)
|
||||||
vvv("EXEC %s" % shcmd, host=self.host)
|
vvv("EXEC %s" % shcmd, host=self.host)
|
||||||
sudo_output = ''
|
sudo_output = ''
|
||||||
|
|
|
@ -81,13 +81,15 @@ class Connection(object):
|
||||||
os.write(self.wfd, "%s\n" % self.runner.remote_pass)
|
os.write(self.wfd, "%s\n" % self.runner.remote_pass)
|
||||||
os.close(self.wfd)
|
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 '''
|
''' run a command on the remote host '''
|
||||||
|
|
||||||
ssh_cmd = self._password_cmd()
|
ssh_cmd = self._password_cmd()
|
||||||
ssh_cmd += ["ssh", "-tt", "-q"] + self.common_args + [self.host]
|
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
|
# Rather than detect if sudo wants a password this time, -k makes
|
||||||
# sudo always ask for a password if one is required.
|
# sudo always ask for a password if one is required.
|
||||||
# Passing a quoted compound command to sudo (or sudo -s)
|
# Passing a quoted compound command to sudo (or sudo -s)
|
||||||
|
@ -97,10 +99,9 @@ class Connection(object):
|
||||||
# the -p option.
|
# the -p option.
|
||||||
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||||
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
||||||
sudocmd = 'sudo -k && sudo -p "%s" -u %s /bin/sh -c %s' % (
|
sudocmd = 'sudo -k && sudo -p "%s" -u %s %s -c %s' % (
|
||||||
prompt, sudo_user, pipes.quote(cmd))
|
prompt, sudo_user, executable, pipes.quote(cmd))
|
||||||
cmd = sudocmd
|
ssh_cmd.append('/bin/sh -c ' + pipes.quote(sudocmd))
|
||||||
ssh_cmd.append('/bin/sh -c ' + pipes.quote(cmd))
|
|
||||||
|
|
||||||
vvv("EXEC %s" % ssh_cmd, host=self.host)
|
vvv("EXEC %s" % ssh_cmd, host=self.host)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -149,9 +149,11 @@ def command(data):
|
||||||
return dict(failed=True, msg='internal error: cmd is required')
|
return dict(failed=True, msg='internal error: cmd is required')
|
||||||
if 'tmp_path' not in data:
|
if 'tmp_path' not in data:
|
||||||
return dict(failed=True, msg='internal error: tmp_path is required')
|
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'])
|
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()
|
(stdout, stderr) = p.communicate()
|
||||||
if stdout is None:
|
if stdout is None:
|
||||||
stdout = ''
|
stdout = ''
|
||||||
|
|
11
library/raw
11
library/raw
|
@ -4,7 +4,16 @@ DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
module: raw
|
module: raw
|
||||||
short_description: Executes a low-down and dirty SSH command
|
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:
|
description:
|
||||||
- Executes a low-down and dirty SSH command, not going through the module
|
- 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
|
subsystem. This is useful and should only be done in two cases. The
|
||||||
|
|
Loading…
Reference in a new issue