mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Because paramiko using a pty can't distinguish stderr and stdout, remove handling that
treated -D as a way to show stderr, and make sure modules don't include things on stderr. Update docs and test module script to come into line.
This commit is contained in:
parent
076f1bc169
commit
36e454c52f
10 changed files with 47 additions and 54 deletions
|
@ -36,8 +36,7 @@ OPTIONS
|
||||||
|
|
||||||
*-D*, *--debug*
|
*-D*, *--debug*
|
||||||
|
|
||||||
Print any messages the remote module sends to standard error to the console
|
Debug mode
|
||||||
|
|
||||||
|
|
||||||
*-i* 'PATH', *--inventory=*'PATH'::
|
*-i* 'PATH', *--inventory=*'PATH'::
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ The 'ARGUMENTS' to pass to the module.
|
||||||
|
|
||||||
*-D*, *--debug*::
|
*-D*, *--debug*::
|
||||||
|
|
||||||
Print any messages the remote module sends to standard error to the console
|
Debug mode
|
||||||
|
|
||||||
*-k*, *--ask-pass*::
|
*-k*, *--ask-pass*::
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ cmd = subprocess.Popen("%s %s" % (modfile, argspath),
|
||||||
|
|
||||||
if err and err != '':
|
if err and err != '':
|
||||||
print "***********************************"
|
print "***********************************"
|
||||||
print "RECIEVED DATA ON STDOUT, WILL IGNORE THIS:"
|
print "RECIEVED DATA ON STDERR, THIS WILL BREAK YOUR MODULE:"
|
||||||
print err
|
print err
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -84,6 +84,9 @@ print "PARSED OUTPUT"
|
||||||
|
|
||||||
print utils.bigjson(results)
|
print utils.bigjson(results)
|
||||||
|
|
||||||
|
if err and err != '':
|
||||||
|
sys.exit(1)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_error(self, host, err):
|
def on_error(self, host, err):
|
||||||
print >>sys.stderr, "stderr: [%s] => %s\n" % (host, err)
|
print >>sys.stderr, "err: [%s] => %s\n" % (host, err)
|
||||||
|
|
||||||
def on_no_hosts(self):
|
def on_no_hosts(self):
|
||||||
print >>sys.stderr, "no hosts matched\n"
|
print >>sys.stderr, "no hosts matched\n"
|
||||||
|
@ -141,7 +141,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
|
||||||
self.stats = stats
|
self.stats = stats
|
||||||
|
|
||||||
def on_unreachable(self, host, msg):
|
def on_unreachable(self, host, msg):
|
||||||
print "unreachable: [%s] => %s" % (host, msg)
|
print "fatal: [%s] => %s" % (host, msg)
|
||||||
|
|
||||||
def on_failed(self, host, results):
|
def on_failed(self, host, results):
|
||||||
invocation = results.get('invocation',None)
|
invocation = results.get('invocation',None)
|
||||||
|
@ -160,7 +160,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
|
||||||
print "ok: [%s] => %s\n" % (host, invocation)
|
print "ok: [%s] => %s\n" % (host, invocation)
|
||||||
|
|
||||||
def on_error(self, host, err):
|
def on_error(self, host, err):
|
||||||
print >>sys.stderr, "stderr: [%s] => %s\n" % (host, err)
|
print >>sys.stderr, "err: [%s] => %s\n" % (host, err)
|
||||||
|
|
||||||
def on_skipped(self, host):
|
def on_skipped(self, host):
|
||||||
print "skipping: [%s]\n" % host
|
print "skipping: [%s]\n" % host
|
||||||
|
|
|
@ -39,9 +39,6 @@ with warnings.catch_warnings():
|
||||||
|
|
||||||
################################################
|
################################################
|
||||||
|
|
||||||
RANDOM_PROMPT_LEN = 32 # 32 random chars in [a-z] gives > 128 bits of entropy
|
|
||||||
|
|
||||||
|
|
||||||
class Connection(object):
|
class Connection(object):
|
||||||
''' Handles abstract connections to remote hosts '''
|
''' Handles abstract connections to remote hosts '''
|
||||||
|
|
||||||
|
@ -96,12 +93,8 @@ class ParamikoConnection(object):
|
||||||
except IOError,e:
|
except IOError,e:
|
||||||
raise errors.AnsibleConnectionFailed(str(e))
|
raise errors.AnsibleConnectionFailed(str(e))
|
||||||
|
|
||||||
#if 'hostname' in credentials:
|
|
||||||
# self.host = credentials['hostname']
|
|
||||||
if 'port' in credentials:
|
if 'port' in credentials:
|
||||||
self.port = int(credentials['port'])
|
self.port = int(credentials['port'])
|
||||||
#if 'user' in credentials:
|
|
||||||
# user = credentials['user']
|
|
||||||
if 'identityfile' in credentials:
|
if 'identityfile' in credentials:
|
||||||
keypair = os.path.expanduser(credentials['identityfile'])
|
keypair = os.path.expanduser(credentials['identityfile'])
|
||||||
|
|
||||||
|
@ -133,13 +126,21 @@ class ParamikoConnection(object):
|
||||||
self.ssh = self._get_conn()
|
self.ssh = self._get_conn()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exec_command(self, cmd, tmp_path, sudoable=False): # pylint: disable-msg=W0613
|
def exec_command(self, cmd, tmp_path, sudoable=False):
|
||||||
|
|
||||||
''' run a command on the remote host '''
|
''' run a command on the remote host '''
|
||||||
bufsize = 4096 # Could make this a Runner param if needed
|
|
||||||
timeout_secs = self.runner.timeout # Reusing runner's TCP connect timeout as command progress timeout
|
bufsize = 4096
|
||||||
|
# reusing runner's TCP connect timeout as command progress timeout (correct?)
|
||||||
|
timeout_secs = self.runner.timeout
|
||||||
chan = self.ssh.get_transport().open_session()
|
chan = self.ssh.get_transport().open_session()
|
||||||
chan.settimeout(timeout_secs)
|
chan.settimeout(timeout_secs)
|
||||||
chan.get_pty() # Many sudo setups require a terminal; use in both cases for consistency
|
stdin = chan.makefile('wb', bufsize)
|
||||||
|
stdout = chan.makefile('rb', bufsize)
|
||||||
|
stderr = chan.makefile_stderr('rb', bufsize)
|
||||||
|
chan.get_pty()
|
||||||
|
chan.set_combine_stderr(False)
|
||||||
|
|
||||||
|
|
||||||
if not self.runner.sudo or not sudoable:
|
if not self.runner.sudo or not sudoable:
|
||||||
quoted_command = '"$SHELL" -c ' + pipes.quote(cmd)
|
quoted_command = '"$SHELL" -c ' + pipes.quote(cmd)
|
||||||
|
@ -151,13 +152,22 @@ class ParamikoConnection(object):
|
||||||
# follows. Passing a quoted compound command to sudo (or sudo -s)
|
# follows. Passing a quoted compound command to sudo (or sudo -s)
|
||||||
# directly doesn't work, so we shellquote it with pipes.quote()
|
# directly doesn't work, so we shellquote it with pipes.quote()
|
||||||
# and pass the quoted string to the user's shell.
|
# and pass the quoted string to the user's shell.
|
||||||
sudocmd = 'sudo -k -- "$SHELL" -c ' + pipes.quote(cmd)
|
|
||||||
chan.exec_command(sudocmd)
|
randbits = ''.join(chr(random.randint(ord('a'), ord('z'))) for x in xrange(32))
|
||||||
if self.runner.sudo_pass:
|
prompt = '[sudo via ansible, key=%s] password: ' % randbits
|
||||||
while not chan.recv_ready():
|
sudocmd = 'sudo -k -p "%s" -- "$SHELL" -c %s' % (prompt, pipes.quote(cmd))
|
||||||
time.sleep(0.25)
|
sudo_output = ''
|
||||||
sudo_output = chan.recv(bufsize) # Pull prompt, catch errors, eat sudo output
|
try:
|
||||||
chan.sendall(self.runner.sudo_pass + '\n')
|
chan.exec_command(sudocmd)
|
||||||
|
if self.runner.sudo_pass:
|
||||||
|
while not sudo_output.endswith(prompt):
|
||||||
|
chunk = chan.recv(bufsize)
|
||||||
|
if not chunk:
|
||||||
|
raise errors.AnsibleError('ssh connection closed waiting for sudo password prompt')
|
||||||
|
sudo_output += chunk
|
||||||
|
chan.sendall(self.runner.sudo_pass + '\n')
|
||||||
|
except socket.timeout:
|
||||||
|
raise errors.AnsibleError('ssh timed out waiting for sudo.\n' + sudo_output)
|
||||||
|
|
||||||
stdin = chan.makefile('wb', bufsize)
|
stdin = chan.makefile('wb', bufsize)
|
||||||
stdout = chan.makefile('rb', bufsize)
|
stdout = chan.makefile('rb', bufsize)
|
||||||
|
|
|
@ -637,16 +637,18 @@ class Runner(object):
|
||||||
''' execute a command string over SSH, return the output '''
|
''' execute a command string over SSH, return the output '''
|
||||||
|
|
||||||
stdin, stdout, stderr = conn.exec_command(cmd, tmp, sudoable=sudoable)
|
stdin, stdout, stderr = conn.exec_command(cmd, tmp, sudoable=sudoable)
|
||||||
|
err=None
|
||||||
|
out=None
|
||||||
if type(stderr) != str:
|
if type(stderr) != str:
|
||||||
err="\n".join(stderr.readlines())
|
err="\n".join(stderr.readlines())
|
||||||
else:
|
else:
|
||||||
err=stderr
|
err=stderr
|
||||||
|
|
||||||
if type(stdout) != str:
|
if type(stdout) != str:
|
||||||
return "\n".join(stdout.readlines()), err
|
out="\n".join(stdout.readlines())
|
||||||
else:
|
else:
|
||||||
return stdout, err
|
out=stdout
|
||||||
|
return (out,err)
|
||||||
|
|
||||||
|
|
||||||
# *****************************************************
|
# *****************************************************
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,7 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, asyn
|
||||||
|
|
||||||
parser = SortedOptParser(usage)
|
parser = SortedOptParser(usage)
|
||||||
parser.add_option('-D','--debug', default=False, action="store_true",
|
parser.add_option('-D','--debug', default=False, action="store_true",
|
||||||
help='debug standard error output of remote modules')
|
help='debug mode')
|
||||||
parser.add_option('-f','--forks', dest='forks', default=constants.DEFAULT_FORKS, type='int',
|
parser.add_option('-f','--forks', dest='forks', default=constants.DEFAULT_FORKS, type='int',
|
||||||
help="specify number of parallel processes to use (default=%s)" % constants.DEFAULT_FORKS)
|
help="specify number of parallel processes to use (default=%s)" % constants.DEFAULT_FORKS)
|
||||||
parser.add_option('-i', '--inventory-file', dest='inventory',
|
parser.add_option('-i', '--inventory-file', dest='inventory',
|
||||||
|
|
|
@ -30,9 +30,6 @@ import traceback
|
||||||
APT_PATH = "/usr/bin/apt-get"
|
APT_PATH = "/usr/bin/apt-get"
|
||||||
APT = "DEBIAN_PRIORITY=critical %s" % APT_PATH
|
APT = "DEBIAN_PRIORITY=critical %s" % APT_PATH
|
||||||
|
|
||||||
def debug(msg):
|
|
||||||
print >>sys.stderr, msg
|
|
||||||
|
|
||||||
def exit_json(rc=0, **kwargs):
|
def exit_json(rc=0, **kwargs):
|
||||||
print json.dumps(kwargs)
|
print json.dumps(kwargs)
|
||||||
sys.exit(rc)
|
sys.exit(rc)
|
||||||
|
|
|
@ -31,14 +31,8 @@ GROUPADD = "/usr/sbin/groupadd"
|
||||||
GROUPDEL = "/usr/sbin/groupdel"
|
GROUPDEL = "/usr/sbin/groupdel"
|
||||||
GROUPMOD = "/usr/sbin/groupmod"
|
GROUPMOD = "/usr/sbin/groupmod"
|
||||||
|
|
||||||
def debug(msg):
|
|
||||||
# ansible ignores stderr, so it's safe to use for debug
|
|
||||||
print >>sys.stderr, msg
|
|
||||||
#pass
|
|
||||||
|
|
||||||
def exit_json(rc=0, **kwargs):
|
def exit_json(rc=0, **kwargs):
|
||||||
if 'name' in kwargs:
|
if 'name' in kwargs:
|
||||||
debug("add group info to exit_json")
|
|
||||||
add_group_info(kwargs)
|
add_group_info(kwargs)
|
||||||
print json.dumps(kwargs)
|
print json.dumps(kwargs)
|
||||||
sys.exit(rc)
|
sys.exit(rc)
|
||||||
|
@ -59,7 +53,6 @@ def add_group_info(kwargs):
|
||||||
|
|
||||||
def group_del(group):
|
def group_del(group):
|
||||||
cmd = [GROUPDEL, group]
|
cmd = [GROUPDEL, group]
|
||||||
debug("Arguments to groupdel: %s" % (" ".join(cmd)))
|
|
||||||
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return True
|
return True
|
||||||
|
@ -73,7 +66,6 @@ def group_add(group, **kwargs):
|
||||||
cmd.append('-g')
|
cmd.append('-g')
|
||||||
cmd.append(kwargs[key])
|
cmd.append(kwargs[key])
|
||||||
cmd.append(group)
|
cmd.append(group)
|
||||||
debug("Arguments to groupadd: %s" % (" ".join(cmd)))
|
|
||||||
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return True
|
return True
|
||||||
|
@ -91,7 +83,6 @@ def group_mod(group, **kwargs):
|
||||||
if len(cmd) == 1:
|
if len(cmd) == 1:
|
||||||
return False
|
return False
|
||||||
cmd.append(group)
|
cmd.append(group)
|
||||||
debug("Arguments to groupmod: %s" % (" ".join(cmd)))
|
|
||||||
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -33,14 +33,8 @@ USERADD = "/usr/sbin/useradd"
|
||||||
USERMOD = "/usr/sbin/usermod"
|
USERMOD = "/usr/sbin/usermod"
|
||||||
USERDEL = "/usr/sbin/userdel"
|
USERDEL = "/usr/sbin/userdel"
|
||||||
|
|
||||||
def debug(msg):
|
|
||||||
# ansible ignores stderr, so it's safe to use for debug
|
|
||||||
print >>sys.stderr, msg
|
|
||||||
#pass
|
|
||||||
|
|
||||||
def exit_json(rc=0, **kwargs):
|
def exit_json(rc=0, **kwargs):
|
||||||
if 'name' in kwargs:
|
if 'name' in kwargs:
|
||||||
debug("add user info to exit_json")
|
|
||||||
add_user_info(kwargs)
|
add_user_info(kwargs)
|
||||||
print json.dumps(kwargs)
|
print json.dumps(kwargs)
|
||||||
sys.exit(rc)
|
sys.exit(rc)
|
||||||
|
@ -75,7 +69,6 @@ def user_del(user, **kwargs):
|
||||||
elif key == 'remove' and kwargs[key]:
|
elif key == 'remove' and kwargs[key]:
|
||||||
cmd.append('-r')
|
cmd.append('-r')
|
||||||
cmd.append(user)
|
cmd.append(user)
|
||||||
debug("Arguments to userdel: %s" % (" ".join(cmd)))
|
|
||||||
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return True
|
return True
|
||||||
|
@ -118,7 +111,6 @@ def user_add(user, **kwargs):
|
||||||
else:
|
else:
|
||||||
cmd.append('-M')
|
cmd.append('-M')
|
||||||
cmd.append(user)
|
cmd.append(user)
|
||||||
debug("Arguments to useradd: %s" % (" ".join(cmd)))
|
|
||||||
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return True
|
return True
|
||||||
|
@ -172,7 +164,6 @@ def user_mod(user, **kwargs):
|
||||||
if len(cmd) == 1:
|
if len(cmd) == 1:
|
||||||
return False
|
return False
|
||||||
cmd.append(user)
|
cmd.append(user)
|
||||||
debug("Arguments to usermod: %s" % (" ".join(cmd)))
|
|
||||||
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
return True
|
return True
|
||||||
|
|
Loading…
Reference in a new issue