enabled initial support for password prompt on become

- moved check prompt/password functions to connection, make more senes there
- TODO: consider moving make_become to connection from connection_info
- removed executable param that was never overriden outside of connection info
This commit is contained in:
Brian Coca 2015-06-15 00:09:25 -04:00
parent bac35ae773
commit 580993fef7
5 changed files with 37 additions and 38 deletions

View file

@ -24,7 +24,6 @@ __metaclass__ = type
import pipes
import random
import re
import gettext
from ansible import constants as C
from ansible.template import Templar
@ -298,7 +297,7 @@ class ConnectionInformation:
return new_info
def make_become_cmd(self, cmd, executable ):
def make_become_cmd(self, cmd, executable='/bin/sh'):
""" helper function to create privilege escalation commands """
prompt = None
@ -356,19 +355,6 @@ class ConnectionInformation:
return (cmd, prompt, success_key)
def check_become_success(self, output, success_key):
return success_key in output
def check_password_prompt(self, output, prompt):
if isinstance(prompt, basestring):
return output.endswith(prompt)
else:
return prompt(output)
def check_incorrect_password(self, output, prompt):
incorrect_password = gettext.dgettext(self.become_method, "Sorry, try again.")
return output.endswith(incorrect_password)
def _get_fields(self):
return [i for i in self.__dict__.keys() if i[:1] != '_']

View file

@ -425,7 +425,7 @@ class ActionBase:
debug("done with _execute_module (%s, %s)" % (module_name, module_args))
return data
def _low_level_execute_command(self, cmd, tmp, executable=None, sudoable=True, in_data=None):
def _low_level_execute_command(self, cmd, tmp, sudoable=True, in_data=None):
'''
This is the function which executes the low level shell command, which
may be commands to create/remove directories for temporary files, or to
@ -438,17 +438,15 @@ class ActionBase:
debug("no command, exiting _low_level_execute_command()")
return dict(stdout='', stderr='')
if executable is None:
executable = C.DEFAULT_EXECUTABLE
prompt = None
success_key = None
if sudoable:
cmd, prompt, success_key = self._connection_info.make_become_cmd(cmd, executable)
#FIXME: disabled as this should happen in the connection plugin, verify before removing
#prompt = None
#success_key = None
#
#if sudoable:
# cmd, prompt, success_key = self._connection_info.make_become_cmd(cmd)
debug("executing the command %s through the connection" % cmd)
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, executable=executable, in_data=in_data)
rc, stdin, stdout, stderr = self._connection.exec_command(cmd, tmp, in_data=in_data, sudoable=sudoable)
debug("command execution done")
if not isinstance(stdout, basestring):

View file

@ -20,6 +20,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import gettext
from abc import ABCMeta, abstractmethod, abstractproperty
from functools import wraps
@ -97,7 +98,7 @@ class ConnectionBase(with_metaclass(ABCMeta, object)):
@ensure_connect
@abstractmethod
def exec_command(self, cmd, tmp_path, executable=None, in_data=None, sudoable=True):
def exec_command(self, cmd, tmp_path, in_data=None, sudoable=True):
"""Run a command on the remote host"""
pass
@ -117,3 +118,17 @@ class ConnectionBase(with_metaclass(ABCMeta, object)):
def close(self):
"""Terminate the connection"""
pass
def check_become_success(self, output, success_key):
return success_key in output
def check_password_prompt(self, output, prompt):
if isinstance(prompt, basestring):
return output.endswith(prompt)
else:
return prompt(output)
def check_incorrect_password(self, output, prompt):
incorrect_password = gettext.dgettext(self._connection_info.become_method, "Sorry, try again.")
return output.endswith(incorrect_password)

View file

@ -46,10 +46,10 @@ class Connection(ConnectionBase):
self._connected = True
return self
def exec_command(self, cmd, tmp_path, executable='/bin/sh', in_data=None):
def exec_command(self, cmd, tmp_path, in_data=None):
''' run a command on the local host '''
super(Connection, self).exec_command(cmd, tmp_path, executable=executable, in_data=in_data)
super(Connection, self).exec_command(cmd, tmp_path, in_data=in_data)
debug("in local.exec_command()")
# su requires to be run from a terminal, and therefore isn't supported here (yet?)
@ -59,7 +59,7 @@ class Connection(ConnectionBase):
if in_data:
raise AnsibleError("Internal Error: this module does not support optimized module pipelining")
executable = executable.split()[0] if executable else None
executable = self._connection_info.executable.split()[0] if self._connection_info.executable else None
self._display.vvv("{0} EXEC {1}".format(self._connection_info.remote_addr, cmd))
# FIXME: cwd= needs to be set to the basedir of the playbook

View file

@ -174,10 +174,10 @@ class Connection(ConnectionBase):
# fail early if the become password is wrong
if self._connection_info.become and sudoable:
if self._connection_info.become_pass:
if self._connection_info.check_incorrect_password(stdout, prompt):
if self.check_incorrect_password(stdout, prompt):
raise AnsibleError('Incorrect %s password', self._connection_info.become_method)
elif self._connection_info.check_password_prompt(stdout, prompt):
elif self.check_password_prompt(stdout, prompt):
raise AnsibleError('Missing %s password', self._connection_info.become_method)
if p.stdout in rfd:
@ -260,10 +260,10 @@ class Connection(ConnectionBase):
self._display.vvv("EXEC previous known host file not found for {0}".format(host))
return True
def exec_command(self, cmd, tmp_path, executable='/bin/sh', in_data=None, sudoable=True):
def exec_command(self, cmd, tmp_path, in_data=None, sudoable=True):
''' run a command on the remote host '''
super(Connection, self).exec_command(cmd, tmp_path, executable=executable, in_data=in_data, sudoable=sudoable)
super(Connection, self).exec_command(cmd, tmp_path, in_data=in_data, sudoable=sudoable)
host = self._connection_info.remote_addr
@ -287,7 +287,7 @@ class Connection(ConnectionBase):
prompt = None
success_key = ''
if sudoable:
cmd, prompt, success_key = self._connection_info.make_become_cmd(cmd, executable)
cmd, prompt, success_key = self._connection_info.make_become_cmd(cmd)
ssh_cmd.append(cmd)
self._display.vvv("EXEC {0}".format(' '.join(ssh_cmd)), host=host)
@ -323,8 +323,8 @@ class Connection(ConnectionBase):
become_errput = ''
while True:
if self._connection_info.check_become_success(become_output, success_key) or \
self._connection_info.check_password_prompt(become_output, prompt ):
if self.check_become_success(become_output, success_key) or \
self.check_password_prompt(become_output, prompt ):
break
rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout], self._connection_info.timeout)
if p.stderr in rfd:
@ -333,7 +333,7 @@ class Connection(ConnectionBase):
raise AnsibleError('ssh connection closed waiting for privilege escalation password prompt')
become_errput += chunk
if self._connection_info.check_incorrect_password(become_errput, prompt):
if self.check_incorrect_password(become_errput, prompt):
raise AnsibleError('Incorrect %s password', self._connection_info.become_method)
if p.stdout in rfd:
@ -347,7 +347,7 @@ class Connection(ConnectionBase):
stdout = p.communicate()
raise AnsibleError('ssh connection error waiting for sudo or su password prompt')
if not self._connection_info.check_become_success(become_output, success_key):
if not self.check_become_success(become_output, success_key):
if sudoable:
stdin.write(self._connection_info.become_pass + '\n')
else: