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:
parent
5614d251bb
commit
80e762e18a
@ -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
|
||||||
================================
|
================================
|
||||||
|
@ -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")),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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',
|
||||||
|
@ -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``.
|
Loading…
Reference in New Issue
Block a user