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 Default is taken from ``[ansible]/default_clean_steps_config`` option of the
ironic configuration file (defaults to ``clean_steps.yaml``). 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 Customizing the deployment logic
================================ ================================

View File

@ -136,8 +136,10 @@ opts = [
"'driver_info' field.")), "'driver_info' field.")),
cfg.StrOpt('default_python_interpreter', cfg.StrOpt('default_python_interpreter',
help=_("Absolute path to the python interpreter on the " help=_("Absolute path to the python interpreter on the "
"managed machines. By default, ansible uses " "managed machines. It may be overridden by per-node "
"/usr/bin/python")), "'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_clean_steps_config': _('Name of the file inside the '
'"ansible_playbooks_path" folder with ' '"ansible_playbooks_path" folder with '
'cleaning steps configuration. Optional.'), 'cleaning steps configuration. Optional.'),
'ansible_python_interpreter': _('Absolute path to the python interpreter '
'on the managed machines. Optional.'),
} }
COMMON_PROPERTIES = OPTIONAL_PROPERTIES COMMON_PROPERTIES = OPTIONAL_PROPERTIES
@ -101,6 +103,11 @@ def _parse_ansible_driver_info(node, action='deploy'):
return os.path.basename(playbook), user, key 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): def _get_configdrive_path(basename):
return os.path.join(CONF.tempdir, basename + '.cndrive') 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) playbook = os.path.join(root, name)
inventory = os.path.join(root, 'inventory') inventory = os.path.join(root, 'inventory')
ironic_vars = {'ironic': extra_vars} ironic_vars = {'ironic': extra_vars}
if CONF.ansible.default_python_interpreter: python_interpreter = _get_python_interpreter(node)
ironic_vars['ansible_python_interpreter'] = ( if python_interpreter:
CONF.ansible.default_python_interpreter) ironic_vars['ansible_python_interpreter'] = python_interpreter
args = [CONF.ansible.ansible_playbook_script, playbook, args = [CONF.ansible.ansible_playbook_script, playbook,
'-i', inventory, '-i', inventory,
'-e', json.dumps(ironic_vars), '-e', json.dumps(ironic_vars),

View File

@ -191,6 +191,35 @@ class TestAnsibleMethods(AnsibleDeployTestCaseBase):
"ironic": {"foo": "bar"}}, "ironic": {"foo": "bar"}},
json.loads(all_vars)) 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', @mock.patch.object(com_utils, 'execute',
side_effect=processutils.ProcessExecutionError( side_effect=processutils.ProcessExecutionError(
description='VIKINGS!'), description='VIKINGS!'),
@ -286,6 +315,16 @@ class TestAnsibleMethods(AnsibleDeployTestCaseBase):
self.assertEqual(2, ansible_deploy._calculate_memory_req(task)) self.assertEqual(2, ansible_deploy._calculate_memory_req(task))
image_mock.assert_called_once_with(task.context, 'fake-image') 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): def test__get_configdrive_path(self):
self.config(tempdir='/path/to/tmpdir') self.config(tempdir='/path/to/tmpdir')
self.assertEqual('/path/to/tmpdir/spam.cndrive', self.assertEqual('/path/to/tmpdir/spam.cndrive',

View File

@ -11,3 +11,5 @@ features:
``/usr/bin/python3``. ``/usr/bin/python3``.
The same interpreter will be used in all operations that use the The same interpreter will be used in all operations that use the
ansible deploy interface. 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``.