From 580993fef7f3b18c194c315ba928723970fd5649 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Mon, 15 Jun 2015 00:09:25 -0400 Subject: [PATCH] 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 --- lib/ansible/executor/connection_info.py | 16 +--------------- lib/ansible/plugins/action/__init__.py | 18 ++++++++---------- lib/ansible/plugins/connections/__init__.py | 17 ++++++++++++++++- lib/ansible/plugins/connections/local.py | 6 +++--- lib/ansible/plugins/connections/ssh.py | 18 +++++++++--------- 5 files changed, 37 insertions(+), 38 deletions(-) diff --git a/lib/ansible/executor/connection_info.py b/lib/ansible/executor/connection_info.py index 3e7586e2ca9..24e42a97014 100644 --- a/lib/ansible/executor/connection_info.py +++ b/lib/ansible/executor/connection_info.py @@ -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] != '_'] diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 4b2d7abe27a..f941d1304ca 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -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): diff --git a/lib/ansible/plugins/connections/__init__.py b/lib/ansible/plugins/connections/__init__.py index 921c4e38825..45a07a9c307 100644 --- a/lib/ansible/plugins/connections/__init__.py +++ b/lib/ansible/plugins/connections/__init__.py @@ -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) + diff --git a/lib/ansible/plugins/connections/local.py b/lib/ansible/plugins/connections/local.py index 85bc51de0ae..5915569b024 100644 --- a/lib/ansible/plugins/connections/local.py +++ b/lib/ansible/plugins/connections/local.py @@ -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 diff --git a/lib/ansible/plugins/connections/ssh.py b/lib/ansible/plugins/connections/ssh.py index 471b4143e22..b29418c9962 100644 --- a/lib/ansible/plugins/connections/ssh.py +++ b/lib/ansible/plugins/connections/ssh.py @@ -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: