Adding ansible python interpreter as driver_info

This patch grants the possibility to change the ansible python
interpreter used on remote managed machines as per-node
driver_info option.
Follw-up of https://review.openstack.org/641376

Change-Id: Ic1a994970a5aef5bbbbaaa747818e7ac402912ad
Story: 2005159
Task: 29915
This commit is contained in:
Riccardo Pittau 2019-03-11 11:05:59 +01:00
parent 5614d251bb
commit 80e762e18a
5 changed files with 62 additions and 5 deletions

View File

@ -231,6 +231,13 @@ ansible_clean_steps_config
Default is taken from ``[ansible]/default_clean_steps_config`` option of the
ironic configuration file (defaults to ``clean_steps.yaml``).
ansible_python_interpreter
Absolute path to the python interpreter on the managed machine.
Default is taken from ``[ansible]/default_python_interpreter`` option of
the ironic configuration file.
Ansible uses ``/usr/bin/python`` by default.
Customizing the deployment logic
================================

View File

@ -136,8 +136,10 @@ opts = [
"'driver_info' field.")),
cfg.StrOpt('default_python_interpreter',
help=_("Absolute path to the python interpreter on the "
"managed machines. By default, ansible uses "
"/usr/bin/python")),
"managed machines. It may be overridden by per-node "
"'ansible_python_interpreter' option in node's "
"'driver_info' field. "
"By default, ansible uses /usr/bin/python")),
]

View File

@ -73,6 +73,8 @@ OPTIONAL_PROPERTIES = {
'ansible_clean_steps_config': _('Name of the file inside the '
'"ansible_playbooks_path" folder with '
'cleaning steps configuration. Optional.'),
'ansible_python_interpreter': _('Absolute path to the python interpreter '
'on the managed machines. Optional.'),
}
COMMON_PROPERTIES = OPTIONAL_PROPERTIES
@ -101,6 +103,11 @@ def _parse_ansible_driver_info(node, action='deploy'):
return os.path.basename(playbook), user, key
def _get_python_interpreter(node):
return node.driver_info.get('ansible_python_interpreter',
CONF.ansible.default_python_interpreter)
def _get_configdrive_path(basename):
return os.path.join(CONF.tempdir, basename + '.cndrive')
@ -126,9 +133,9 @@ def _run_playbook(node, name, extra_vars, key, tags=None, notags=None):
playbook = os.path.join(root, name)
inventory = os.path.join(root, 'inventory')
ironic_vars = {'ironic': extra_vars}
if CONF.ansible.default_python_interpreter:
ironic_vars['ansible_python_interpreter'] = (
CONF.ansible.default_python_interpreter)
python_interpreter = _get_python_interpreter(node)
if python_interpreter:
ironic_vars['ansible_python_interpreter'] = python_interpreter
args = [CONF.ansible.ansible_playbook_script, playbook,
'-i', inventory,
'-e', json.dumps(ironic_vars),

View File

@ -191,6 +191,35 @@ class TestAnsibleMethods(AnsibleDeployTestCaseBase):
"ironic": {"foo": "bar"}},
json.loads(all_vars))
@mock.patch.object(com_utils, 'execute', return_value=('out', 'err'),
autospec=True)
def test__run_playbook_ansible_interpreter_override(self, execute_mock):
self.config(group='ansible', playbooks_path='/path/to/playbooks')
self.config(group='ansible', config_file_path='/path/to/config')
self.config(group='ansible', verbosity=3)
self.config(group='ansible',
default_python_interpreter='/usr/bin/python3')
self.config(group='ansible', ansible_extra_args='--timeout=100')
self.node.driver_info['ansible_python_interpreter'] = (
'/usr/bin/python4')
extra_vars = {'foo': 'bar'}
ansible_deploy._run_playbook(self.node, 'deploy',
extra_vars, '/path/to/key',
tags=['spam'], notags=['ham'])
execute_mock.assert_called_once_with(
'env', 'ANSIBLE_CONFIG=/path/to/config',
'ansible-playbook', '/path/to/playbooks/deploy', '-i',
'/path/to/playbooks/inventory', '-e',
mock.ANY, '--tags=spam', '--skip-tags=ham',
'--private-key=/path/to/key', '-vvv', '--timeout=100')
all_vars = execute_mock.call_args[0][7]
self.assertEqual({"ansible_python_interpreter": "/usr/bin/python4",
"ironic": {"foo": "bar"}},
json.loads(all_vars))
@mock.patch.object(com_utils, 'execute',
side_effect=processutils.ProcessExecutionError(
description='VIKINGS!'),
@ -286,6 +315,16 @@ class TestAnsibleMethods(AnsibleDeployTestCaseBase):
self.assertEqual(2, ansible_deploy._calculate_memory_req(task))
image_mock.assert_called_once_with(task.context, 'fake-image')
def test__get_python_interpreter(self):
self.config(group='ansible',
default_python_interpreter='/usr/bin/python3')
self.node.driver_info['ansible_python_interpreter'] = (
'/usr/bin/python4')
python_interpreter = ansible_deploy._get_python_interpreter(self.node)
self.assertEqual('/usr/bin/python4', python_interpreter)
def test__get_configdrive_path(self):
self.config(tempdir='/path/to/tmpdir')
self.assertEqual('/path/to/tmpdir/spam.cndrive',

View File

@ -11,3 +11,5 @@ features:
``/usr/bin/python3``.
The same interpreter will be used in all operations that use the
ansible deploy interface.
It is also possible to override the value set in the configuration for a
node by passing ``ansible_python_interpreter`` in its ``driver_info``.