From 09d0a03d3dc6ff0b8108d1e415beeb508490f3b1 Mon Sep 17 00:00:00 2001 From: Mathieu Bultel Date: Tue, 12 Feb 2019 09:48:44 +0100 Subject: [PATCH] Allow run_update_ansible_action run with Mistral or Ansible We extend run_update_ansible_action to allow running the Ansible playbooks with Mistral or executing directly thru Ansible. This is needed in case we need to run exceptionally a task depending on Mistral but Mistral is broken. For example, retry an upgrade operation after having Mistral broken. Change-Id: I15511b4f36260292e0ea4100b15b8e65a701b38b (cherry picked from commit 5249fbb2623165ef40098631fad18104579450c9) --- tripleoclient/tests/test_utils.py | 36 ++++-- tripleoclient/utils.py | 116 ++++++++++++++++-- tripleoclient/v1/overcloud_external_update.py | 2 +- .../v1/overcloud_external_upgrade.py | 2 +- tripleoclient/v1/overcloud_ffwd_upgrade.py | 4 +- tripleoclient/v1/overcloud_update.py | 2 +- tripleoclient/v1/overcloud_upgrade.py | 2 +- 7 files changed, 139 insertions(+), 25 deletions(-) diff --git a/tripleoclient/tests/test_utils.py b/tripleoclient/tests/test_utils.py index 4a2a71790..79e9047c0 100644 --- a/tripleoclient/tests/test_utils.py +++ b/tripleoclient/tests/test_utils.py @@ -92,6 +92,9 @@ class TestRunAnsiblePlaybook(TestCase): '/etc/ansible/roles:' '/usr/share/openstack-tripleo-validations/roles') env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg' + env['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log' + self.assertRaises(RuntimeError, utils.run_ansible_playbook, self.mock_log, @@ -100,8 +103,10 @@ class TestRunAnsiblePlaybook(TestCase): 'localhost,' ) mock_run.assert_called_once_with(self.mock_log, - [self.ansible_playbook_cmd, '-i', - 'localhost,', '-c', 'smart', + [self.ansible_playbook_cmd, + '-u', 'root', + '-i', 'localhost,', '-v', + '-c', 'smart', '/tmp/existing.yaml'], env=env, retcode_only=False) @@ -152,9 +157,14 @@ class TestRunAnsiblePlaybook(TestCase): '/etc/ansible/roles:' '/usr/share/openstack-tripleo-validations/roles') env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg' + env['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log' + mock_run.assert_called_once_with(self.mock_log, - [self.ansible_playbook_cmd, '-i', - 'localhost,', '-c', 'smart', + [self.ansible_playbook_cmd, + '-u', 'root', + '-i', 'localhost,', '-v', + '-c', 'smart', '/tmp/existing.yaml'], env=env, retcode_only=False) @@ -196,9 +206,14 @@ class TestRunAnsiblePlaybook(TestCase): '/etc/ansible/roles:' '/usr/share/openstack-tripleo-validations/roles') env['ANSIBLE_CONFIG'] = '/tmp/foo.cfg' + env['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log' + mock_run.assert_called_once_with(self.mock_log, - [self.ansible_playbook_cmd, '-i', - 'localhost,', '-c', 'smart', + [self.ansible_playbook_cmd, + '-u', 'root', + '-i', 'localhost,', '-v', + '-c', 'smart', '/tmp/existing.yaml'], env=env, retcode_only=False) @@ -236,9 +251,14 @@ class TestRunAnsiblePlaybook(TestCase): '/etc/ansible/roles:' '/usr/share/openstack-tripleo-validations/roles') env['ANSIBLE_CONFIG'] = '/tmp/fooBar.cfg' + env['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + env['ANSIBLE_LOG_PATH'] = '/tmp/ansible.log' + mock_run.assert_called_once_with(self.mock_log, - [self.ansible_playbook_cmd, '-i', - 'localhost,', '-c', 'local', + [self.ansible_playbook_cmd, + '-u', 'root', + '-i', 'localhost,', '-v', + '-c', 'local', '/tmp/existing.yaml'], env=env, retcode_only=False) diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index ae59769d8..b70218703 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -50,6 +50,7 @@ from heatclient import exc as hc_exc from six.moves.urllib import error as url_error from six.moves.urllib import request +from tripleo_common.utils import config from tripleoclient import constants from tripleoclient import exceptions @@ -66,7 +67,14 @@ def run_ansible_playbook(logger, retries=True, connection='smart', output_callback='json', - python_interpreter=None): + python_interpreter=None, + ssh_user='root', + key=None, + module_path=None, + limit_hosts=None, + tags='', + skip_tags='', + verbosity=1): """Simple wrapper for ansible-playbook :param logger: logger instance @@ -88,8 +96,8 @@ def run_ansible_playbook(logger, :param retries: do you want to get a retry_file? :type retries: Boolean - :param connect: connection type (local, smart, etc) - :type connect: String + :param connection: connection type (local, smart, etc) + :type connection: String :param output_callback: Callback for output format. Defaults to "json" :type output_callback: String @@ -97,6 +105,27 @@ def run_ansible_playbook(logger, :param python_interpreter: Absolute path for the Python interpreter on the host where Ansible is run. :type python_interpreter: String + + :param ssh_user: user for the ssh connection + :type ssh_user: String + + :param key: private key to use for the ssh connection + :type key: String + + :param module_path: location of the ansible module and library + :type module_path: String + + :param limit_hosts: limit the execution to the hosts + :type limit_hosts: String + + :param tags: run specific tags + :type tags: String + + :param skip_tags: skip specific tags + :type skip_tags: String + + :param verbosity: verbosity level for Ansible execution + :type verbosity: Interger """ env = os.environ.copy() @@ -117,6 +146,8 @@ def run_ansible_playbook(logger, '/usr/share/ansible/roles:' '/etc/ansible/roles:' '%s/roles' % constants.DEFAULT_VALIDATIONS_BASEDIR) + env['ANSIBLE_LOG_PATH'] = os.path.join(workdir, 'ansible.log') + env['ANSIBLE_HOST_KEY_CHECKING'] = 'False' cleanup = False if ansible_config is None: @@ -142,8 +173,28 @@ def run_ansible_playbook(logger, if os.path.exists(play): cmd = ["ansible-playbook-{}".format(sys.version_info[0]), - '-i', inventory, + '-u', ssh_user, + '-i', inventory ] + + if 0 < verbosity < 6: + cmd.extend(['-' + ('v' * verbosity)]) + + if key is not None: + cmd.extend(['--private-key=%s' % key]) + + if module_path is not None: + cmd.extend(['--module-path=%s' % module_path]) + + if limit_hosts is not None: + cmd.extend(['-l %s' % limit_hosts]) + + if tags is not '': + cmd.extend(['-t %s' % tags]) + + if skip_tags is not '': + cmd.extend(['--skip_tags %s' % skip_tags]) + if python_interpreter is not None: cmd.extend(['-e', 'ansible_python_interpreter=%s' % python_interpreter]) @@ -161,6 +212,20 @@ def run_ansible_playbook(logger, raise RuntimeError('No such playbook: %s' % play) +def download_ansible_playbooks(client, stack_name, output_dir='/tmp'): + + log = logging.getLogger(__name__ + ".download_ansible_playbooks") + stack_config = config.Config(client) + tmp_ansible_dir = tempfile.mkdtemp(prefix='tripleo-ansible-', + dir=output_dir) + + log.warning(_('Downloading {0} ansible playbooks...').format(stack_name)) + stack_config.write_config(stack_config.fetch_config(stack_name), + stack_name, + tmp_ansible_dir) + return tmp_ansible_dir + + def bracket_ipv6(address): """Put a bracket around address if it is valid IPv6 @@ -1168,18 +1233,47 @@ def process_multiple_environments(created_env_files, tht_root, return env_files, localenv -def run_update_ansible_action(log, clients, nodes, inventory, playbook, - all_playbooks, action, ssh_user, tags='', - skip_tags='', verbosity='1', extra_vars=None): +def run_update_ansible_action(log, clients, nodes, inventory, + playbook, all_playbooks, ssh_user, + action=None, tags='', skip_tags='', + verbosity='1', extra_vars=None, + workdir='', priv_key=''): + playbooks = [playbook] if playbook == "all": playbooks = all_playbooks for book in playbooks: log.debug("Running ansible playbook %s " % book) - action.update_ansible(clients, nodes=nodes, inventory_file=inventory, - playbook=book, node_user=ssh_user, tags=tags, - skip_tags=skip_tags, verbosity=verbosity, - extra_vars=extra_vars) + if action: + action.update_ansible(clients, nodes=nodes, + inventory_file=inventory, + playbook=book, node_user=ssh_user, + tags=tags, skip_tags=skip_tags, + verbosity=verbosity, extra_vars=extra_vars) + else: + run_ansible_playbook(logger=LOG, + workdir=workdir, + playbook=book, + inventory=inventory, + ssh_user=ssh_user, + key=ssh_private_key(workdir, priv_key), + module_path='/usr/share/ansible-modules', + limit_hosts=nodes, tags=tags, + skip_tags=skip_tags) + + +def ssh_private_key(workdir, key): + if not key: + return None + if (isinstance(key, six.string_types) and + os.path.exists(key)): + return key + + path = os.path.join(workdir, 'ssh_private_key') + with open(path, 'w') as ssh_key: + ssh_key.write(key) + os.chmod(path, 0o600) + return path def parse_extra_vars(extra_var_strings): diff --git a/tripleoclient/v1/overcloud_external_update.py b/tripleoclient/v1/overcloud_external_update.py index a7770d1f0..046e977b8 100644 --- a/tripleoclient/v1/overcloud_external_update.py +++ b/tripleoclient/v1/overcloud_external_update.py @@ -102,7 +102,7 @@ class ExternalUpdateRun(command.Command): oooutils.run_update_ansible_action( self.log, clients, limit_hosts, inventory, playbook, constants.EXTERNAL_UPDATE_PLAYBOOKS, - package_update, parsed_args.ssh_user, + parsed_args.ssh_user, package_update, tags=parsed_args.tags, skip_tags=parsed_args.skip_tags, verbosity=verbosity, extra_vars=extra_vars) diff --git a/tripleoclient/v1/overcloud_external_upgrade.py b/tripleoclient/v1/overcloud_external_upgrade.py index fbd24dba0..dcf3ed0d1 100644 --- a/tripleoclient/v1/overcloud_external_upgrade.py +++ b/tripleoclient/v1/overcloud_external_upgrade.py @@ -102,7 +102,7 @@ class ExternalUpgradeRun(command.Command): oooutils.run_update_ansible_action( self.log, clients, limit_hosts, inventory, playbook, constants.EXTERNAL_UPGRADE_PLAYBOOKS, - package_update, parsed_args.ssh_user, + parsed_args.ssh_user, package_update, tags=parsed_args.tags, skip_tags=parsed_args.skip_tags, verbosity=verbosity, extra_vars=extra_vars) diff --git a/tripleoclient/v1/overcloud_ffwd_upgrade.py b/tripleoclient/v1/overcloud_ffwd_upgrade.py index 708d3aa32..1bc25ec25 100644 --- a/tripleoclient/v1/overcloud_ffwd_upgrade.py +++ b/tripleoclient/v1/overcloud_ffwd_upgrade.py @@ -155,8 +155,8 @@ class FFWDUpgradeRun(command.Command): limit_hosts = '' oooutils.run_update_ansible_action( self.log, clients, limit_hosts, inventory, - constants.FFWD_UPGRADE_PLAYBOOK, [], package_update, - parsed_args.ssh_user, verbosity=verbosity) + constants.FFWD_UPGRADE_PLAYBOOK, [], parsed_args.ssh_user, + package_update, verbosity=verbosity) class FFWDUpgradeConverge(DeployOvercloud): diff --git a/tripleoclient/v1/overcloud_update.py b/tripleoclient/v1/overcloud_update.py index 71aa292eb..1fae70f79 100644 --- a/tripleoclient/v1/overcloud_update.py +++ b/tripleoclient/v1/overcloud_update.py @@ -181,8 +181,8 @@ class UpdateRun(command.Command): oooutils.run_update_ansible_action(self.log, clients, limit_hosts, inventory, playbook, constants.MINOR_UPDATE_PLAYBOOKS, - package_update, parsed_args.ssh_user, + package_update, verbosity=verbosity) diff --git a/tripleoclient/v1/overcloud_upgrade.py b/tripleoclient/v1/overcloud_upgrade.py index 28d81325d..659b1a87e 100644 --- a/tripleoclient/v1/overcloud_upgrade.py +++ b/tripleoclient/v1/overcloud_upgrade.py @@ -236,8 +236,8 @@ class UpgradeRun(command.Command): oooutils.run_update_ansible_action(self.log, clients, limit_hosts, inventory, playbook, constants.MAJOR_UPGRADE_PLAYBOOKS, - package_update, parsed_args.ssh_user, + package_update, parsed_args.tags, skip_tags, verbosity)