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
pull/11259/merge
Brian Coca 10 years ago
parent bac35ae773
commit 580993fef7

@ -24,7 +24,6 @@ __metaclass__ = type
import pipes import pipes
import random import random
import re import re
import gettext
from ansible import constants as C from ansible import constants as C
from ansible.template import Templar from ansible.template import Templar
@ -298,7 +297,7 @@ class ConnectionInformation:
return new_info 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 """ """ helper function to create privilege escalation commands """
prompt = None prompt = None
@ -356,19 +355,6 @@ class ConnectionInformation:
return (cmd, prompt, success_key) 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): def _get_fields(self):
return [i for i in self.__dict__.keys() if i[:1] != '_'] return [i for i in self.__dict__.keys() if i[:1] != '_']

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

@ -20,6 +20,7 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import gettext
from abc import ABCMeta, abstractmethod, abstractproperty from abc import ABCMeta, abstractmethod, abstractproperty
from functools import wraps from functools import wraps
@ -97,7 +98,7 @@ class ConnectionBase(with_metaclass(ABCMeta, object)):
@ensure_connect @ensure_connect
@abstractmethod @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""" """Run a command on the remote host"""
pass pass
@ -117,3 +118,17 @@ class ConnectionBase(with_metaclass(ABCMeta, object)):
def close(self): def close(self):
"""Terminate the connection""" """Terminate the connection"""
pass 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)

@ -46,10 +46,10 @@ class Connection(ConnectionBase):
self._connected = True self._connected = True
return self 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 ''' ''' 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()") debug("in local.exec_command()")
# su requires to be run from a terminal, and therefore isn't supported here (yet?) # 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: if in_data:
raise AnsibleError("Internal Error: this module does not support optimized module pipelining") 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)) 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 # FIXME: cwd= needs to be set to the basedir of the playbook

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

Loading…
Cancel
Save