From db61e9be0cb4f9840156877dac455fb46809c5d5 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Fri, 11 Mar 2016 20:38:38 -0500 Subject: [PATCH] add ansible_executable inventory var also handle the overrides appropriately also new executable to set shell type --- docsite/rst/intro_configuration.rst | 2 ++ docsite/rst/intro_inventory.rst | 11 +++++++++-- lib/ansible/playbook/play_context.py | 10 +++++++++- lib/ansible/plugins/action/__init__.py | 6 ++++-- lib/ansible/plugins/connection/__init__.py | 4 ++-- test/units/plugins/action/test_action.py | 6 +++--- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index 87f43d1a91..64070e3200 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -289,6 +289,8 @@ This indicates the command to use to spawn a shell under a sudo environment. Us executable = /bin/bash +Starting in version 2.1 this can be overriden by the inventory var ``ansible_executable``. + .. _filter_plugins: filter_plugins diff --git a/docsite/rst/intro_inventory.rst b/docsite/rst/intro_inventory.rst index b52183b384..f8b303f597 100644 --- a/docsite/rst/intro_inventory.rst +++ b/docsite/rst/intro_inventory.rst @@ -231,8 +231,7 @@ SSH connection:: ansible_ssh_extra_args This setting is always appended to the default ssh command line. ansible_ssh_pipelining - Determines whether or not to use SSH pipelining. This can override the - ``pipelining`` setting in ``ansible.cfg``. + Determines whether or not to use SSH pipelining. This can override the ``pipelining`` setting in ``ansible.cfg``. Privilege escalation (see :doc:`Ansible Privilege Escalation` for further details):: @@ -261,6 +260,14 @@ Remote host environment parameters:: Works for anything such as ruby or perl and works just like ansible_python_interpreter. This replaces shebang of modules which will run on that host. +.. versionadded:: 2.1 + +:: + + ansible_executable + This sets the shell the ansible controller will use on the target machine, overrides ``executable`` in ``ansible.cfg`` which defaults to '/bin/sh'. + You should really only change it if is not possible to use '/bin/sh' (i.e. it is not in the list of allowed shells for your users). + Examples from a host file:: some_host ansible_port=2222 ansible_user=manager diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py index a48f7395b4..de6b817055 100644 --- a/lib/ansible/playbook/play_context.py +++ b/lib/ansible/playbook/play_context.py @@ -78,6 +78,7 @@ MAGIC_VARIABLE_MAPPING = dict( su_pass = ('ansible_su_password', 'ansible_su_pass'), su_exe = ('ansible_su_exe',), su_flags = ('ansible_su_flags',), + executable = ('ansible_executable',), ) SU_PROMPT_LOCALIZATIONS = [ @@ -163,6 +164,7 @@ class PlayContext(Base): _accelerate = FieldAttribute(isa='bool', default=False) _accelerate_ipv6 = FieldAttribute(isa='bool', default=False, always_post_validate=True) _accelerate_port = FieldAttribute(isa='int', default=C.ACCELERATE_PORT, always_post_validate=True) + _executable = FieldAttribute(isa='string', default=C.DEFAULT_EXECUTABLE) # privilege escalation fields _become = FieldAttribute(isa='bool') @@ -354,6 +356,12 @@ class PlayContext(Base): else: delegated_vars = dict() + # setup shell + for exe_var in MAGIC_VARIABLE_MAPPING.get('executable'): + if exe_var in variables: + setattr(new_info, 'executable', variables.get(exe_var)) + + attrs_considered = [] for (attr, variable_names) in iteritems(MAGIC_VARIABLE_MAPPING): for variable_name in variable_names: @@ -417,7 +425,7 @@ class PlayContext(Base): self.prompt = None if executable is None: - executable = C.DEFAULT_EXECUTABLE + executable = self.executable if self.become: diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 392b670406..8666329fff 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -521,7 +521,7 @@ class ActionBase(with_metaclass(ABCMeta, object)): display.debug("done with _execute_module (%s, %s)" % (module_name, module_args)) return data - def _low_level_execute_command(self, cmd, sudoable=True, in_data=None, executable=C.DEFAULT_EXECUTABLE, encoding_errors='replace'): + def _low_level_execute_command(self, cmd, sudoable=True, in_data=None, executable=None, encoding_errors='replace'): ''' This is the function which executes the low level shell command, which may be commands to create/remove directories for temporary files, or to @@ -548,7 +548,9 @@ class ActionBase(with_metaclass(ABCMeta, object)): display.debug("_low_level_execute_command(): using become for this command") cmd = self._play_context.make_become_cmd(cmd, executable=executable) - if executable is not None and self._connection.allow_executable: + if self._connection.allow_executable: + if executable is None: + executable = self._play_context.executable cmd = executable + ' -c ' + pipes.quote(cmd) display.debug("_low_level_execute_command(): executing: %s" % (cmd,)) diff --git a/lib/ansible/plugins/connection/__init__.py b/lib/ansible/plugins/connection/__init__.py index d884496368..958a3f8f0a 100644 --- a/lib/ansible/plugins/connection/__init__.py +++ b/lib/ansible/plugins/connection/__init__.py @@ -1,4 +1,4 @@ -# (c) 2012-2014, Michael DeHaan + # (c) 2015 Toshio Kuratomi # # This file is part of Ansible @@ -89,7 +89,7 @@ class ConnectionBase(with_metaclass(ABCMeta, object)): shell_type = getattr(self, '_shell_type') else: shell_type = 'sh' - shell_filename = os.path.basename(C.DEFAULT_EXECUTABLE) + shell_filename = os.path.basename(self.play_context.executable) for shell in shell_loader.all(): if shell_filename in shell.COMPATIBLE_SHELLS: shell_type = shell.SHELL_FAMILY diff --git a/test/units/plugins/action/test_action.py b/test/units/plugins/action/test_action.py index ea44e31564..4bb151f090 100644 --- a/test/units/plugins/action/test_action.py +++ b/test/units/plugins/action/test_action.py @@ -617,8 +617,8 @@ class TestActionBase(unittest.TestCase): play_context.make_become_cmd.assert_not_called() play_context.remote_user = 'apo' - action_base._low_level_execute_command('ECHO', sudoable=True) - play_context.make_become_cmd.assert_called_once_with("ECHO", executable='/bin/sh') + action_base._low_level_execute_command('ECHO', sudoable=True, executable='/bin/csh') + play_context.make_become_cmd.assert_called_once_with("ECHO", executable='/bin/csh') play_context.make_become_cmd.reset_mock() @@ -627,6 +627,6 @@ class TestActionBase(unittest.TestCase): try: play_context.remote_user = 'root' action_base._low_level_execute_command('ECHO SAME', sudoable=True) - play_context.make_become_cmd.assert_called_once_with("ECHO SAME", executable='/bin/sh') + play_context.make_become_cmd.assert_called_once_with("ECHO SAME", executable=None) finally: C.BECOME_ALLOW_SAME_USER = become_allow_same_user