From d13a7d501eba4b25091f16cb9edc374b40ceb8ce Mon Sep 17 00:00:00 2001 From: Gael Chamoulaud Date: Wed, 10 Jul 2019 17:15:56 +0200 Subject: [PATCH] Allow running validations with custom extra variables This patch adds the ability to pass extra variables to the validations execution through Mistral and 'ansible-playbook'. By default, the CLI is running the validations through 'ansible-playbook', then the operator will have two possible ways to process it; Either passing a Dict with the new --extra-vars argument ``` $ openstack tripleo validator run \ --extra-vars '{"min_undercloud_ram_gb": 2}' --validation-name undercloud-ram ``` or passing the absolute path of a JSON/YAML file using --extra-vars-file argument. ``` $ cat /home/stack/vars.json { "volumes": [ {"min_size": 10, "mount": "/var/lib/docker"}, {"min_size": 3, "mount": "/var/lib/config-data"}, {"min_size": 3, "mount": "/var/log"}, {"min_size": 5, "mount": "/usr"}, {"min_size": 20, "mount": "/var"}, {"min_size": 25, "mount": "/"} ] } $ openstack tripleo validator run \ --extra-vars-file /home/stack/vars.json --validation-name undercloud-disk-space ``` If the operator wants to use Mistral, she/he will just need to add the --use-mistral argument to the command line in the previous examples. Note that Mistral will only support a valid JSON file as input. Change-Id: Idff6bcebc041db483d64396249b040e6bdd778bd Signed-off-by: Gael Chamoulaud --- ...stom-extra-variables-12c7277b30eb791d.yaml | 8 + tripleoclient/tests/v1/test_validator.py | 9 +- tripleoclient/v1/tripleo_validator.py | 209 +++++++++++------- 3 files changed, 142 insertions(+), 84 deletions(-) create mode 100644 releasenotes/notes/allow-running-validations-with-custom-extra-variables-12c7277b30eb791d.yaml diff --git a/releasenotes/notes/allow-running-validations-with-custom-extra-variables-12c7277b30eb791d.yaml b/releasenotes/notes/allow-running-validations-with-custom-extra-variables-12c7277b30eb791d.yaml new file mode 100644 index 000000000..ad191a8ba --- /dev/null +++ b/releasenotes/notes/allow-running-validations-with-custom-extra-variables-12c7277b30eb791d.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + The operator is now able to pass extra variables while executing validations + through the command line. The command line will accept either a Dict with + the new --extra-vars argument or the absolute path of a file (JSON or YAML + when using ansible and JSON only when using Mistral) with the new + --extra-vars-file argument. diff --git a/tripleoclient/tests/v1/test_validator.py b/tripleoclient/tests/v1/test_validator.py index 8155c54bf..495ecaa71 100644 --- a/tripleoclient/tests/v1/test_validator.py +++ b/tripleoclient/tests/v1/test_validator.py @@ -67,9 +67,11 @@ class TestValidatorRun(utils.TestCommand): self.cmd.take_action(parsed_args) plan_mock.assert_called_once_with( - mock.ANY, - {'plan': 'overcloud', - 'validation_names': ['check-ftype']}) + mock.ANY, { + 'plan': 'overcloud', + 'validation_names': ['check-ftype'], + 'validation_inputs': {} + }) @mock.patch('logging.getLogger') @mock.patch('pwd.getpwuid') @@ -104,5 +106,6 @@ class TestValidatorRun(utils.TestCommand): playbook='check-ftype.yaml', retries=False, output_callback='validation_output', + extra_vars={}, python_interpreter='/usr/bin/python{}'.format(sys.version_info[0]) ) diff --git a/tripleoclient/v1/tripleo_validator.py b/tripleoclient/v1/tripleo_validator.py index de79ed717..bccffd8b7 100644 --- a/tripleoclient/v1/tripleo_validator.py +++ b/tripleoclient/v1/tripleo_validator.py @@ -14,6 +14,7 @@ # import argparse +import json import logging import os import pwd @@ -53,6 +54,7 @@ class TripleOValidatorList(command.Command): parser = argparse.ArgumentParser( description=self.get_description(), prog=prog_name, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False ) @@ -61,10 +63,8 @@ class TripleOValidatorList(command.Command): action='store', default='table', choices=['table', 'json', 'yaml'], - help=_("Change the default output. " - "Defaults to: json " - "i.e. --output json" - " --output yaml") + help=_("Change the default output: " + "--output json|yaml") ) parser.add_argument( @@ -74,10 +74,9 @@ class TripleOValidatorList(command.Command): default=[], help=_("List specific group validations, " "if more than one group is required " - "separate the group names with commas" - "Defaults to: [] " - "i.e. --group pre-upgrade,prep " - " --group openshift-on-openstack") + "separate the group names with commas: " + "--group pre-upgrade,prep | " + "--group openshift-on-openstack") ) return parser @@ -114,6 +113,7 @@ class TripleOValidatorRun(command.Command): parser = argparse.ArgumentParser( description=self.get_description(), prog=prog_name, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False ) @@ -121,18 +121,39 @@ class TripleOValidatorRun(command.Command): '--plan', action='store', default='overcloud', - help=_("Execute the validations using a " - "custom plan name. " - "Defaults to: overcloud") + help=_("Execute the validations using a custom plan name") ) parser.add_argument( '--use-mistral', action='store_true', default=False, - help=_("Execute the validations using " - "Mistral. " - "Defaults to: false") + help=_("Execute the validations using Mistral") + ) + + extra_vars_group = parser.add_mutually_exclusive_group(required=False) + + extra_vars_group.add_argument( + '--extra-vars', + action='store', + default={}, + type=json.loads, + help=_( + "Add a dictionary as extra variable to a validation: " + "--extra-vars '{\"min_undercloud_ram_gb\": 24}'") + ) + + extra_vars_group.add_argument( + '--extra-vars-file', + action='store', + default='', + help=_( + "Add a JSON/YAML file containing extra variable " + "to a validation: " + "--extra-vars-file /home/stack/vars.[json|yaml] " + "If using Mistral, only a valid JSON file will be " + "supported." + ) ) ex_group = parser.add_mutually_exclusive_group(required=True) @@ -144,10 +165,9 @@ class TripleOValidatorRun(command.Command): default=[], help=_("Run specific validations, " "if more than one validation is required " - "separate the names with commas" - "Defaults to: [] " - "i.e. --validation-name check-ftype,512e " - " --validation-name 512e") + "separate the names with commas: " + "--validation-name check-ftype,512e | " + "--validation-name 512e") ) ex_group.add_argument( @@ -157,80 +177,107 @@ class TripleOValidatorRun(command.Command): default=[], help=_("Run specific group validations, " "if more than one group is required " - "separate the group names with commas" - "Defaults to: ['pre-deployment'] " - "i.e. --group pre-upgrade,prep " - " --group openshift-on-openstack") + "separate the group names with commas: " + "--group pre-upgrade,prep | " + "--group openshift-on-openstack") ) return parser + def _run_validation_run_with_mistral(self, parsed_args): + clients = self.app.client_manager + LOG = logging.getLogger(__name__ + ".ValidationsRunWithMistral") + extra_vars_input = {} + + if parsed_args.extra_vars: + extra_vars_input = parsed_args.extra_vars + + if parsed_args.extra_vars_file: + try: + with open(parsed_args.extra_vars_file, 'r') as vars_file: + extra_vars_input = json.load(vars_file) + except ValueError as e: + raise RuntimeError( + 'Error occured while decoding extra vars JSON file: %s' % + e) + + if not parsed_args.validation_name: + workflow_input = { + "plan": parsed_args.plan, + "group_names": parsed_args.group + } + else: + workflow_input = { + "plan": parsed_args.plan, + "validation_names": parsed_args.validation_name, + "validation_inputs": extra_vars_input + } + + LOG.debug(_('Running the validations with Mistral')) + output = validations.run_validations(clients, workflow_input) + for out in output: + print('[{}] - {}\n{}'.format( + out.get('status'), + out.get('validation_name'), + oooutils.indent(out.get('stdout')))) + def _run_validator_run(self, parsed_args): clients = self.app.client_manager - LOG = logging.getLogger(__name__ + ".ValidationsRun") + LOG = logging.getLogger(__name__ + ".ValidationsRunAnsible") playbooks = [] + extra_vars_input = {} - if parsed_args.use_mistral: - if not parsed_args.validation_name: - workflow_input = { - "plan": parsed_args.plan, - "group_names": parsed_args.group - } - else: - workflow_input = { - "plan": parsed_args.plan, - "validation_names": parsed_args.validation_name - } + if parsed_args.extra_vars: + extra_vars_input = parsed_args.extra_vars + + if parsed_args.extra_vars_file: + extra_vars_input = parsed_args.extra_vars_file + + if parsed_args.group: + workflow_input = { + "group_names": parsed_args.group + } + + LOG.debug(_('Getting the validations list by group')) + try: + output = validations.list_validations( + clients, workflow_input) + for val in output: + playbooks.append(val.get('id') + '.yaml') + except Exception as e: + print( + _("Validations listing by group finished with errors")) + print('Output: {}'.format(e)) - LOG.debug(_('Running the validations with Mistral')) - output = validations.run_validations(clients, workflow_input) - for out in output: - print('[{}] - {}\n{}'.format( - out.get('status'), - out.get('validation_name'), - oooutils.indent(out.get('stdout')))) else: - if parsed_args.group: - workflow_input = { - "group_names": parsed_args.group - } + for pb in parsed_args.validation_name: + playbooks.append(pb + '.yaml') - LOG.debug(_('Getting the validations list by group')) - try: - output = validations.list_validations( - clients, workflow_input) - for val in output: - playbooks.append(val.get('id') + '.yaml') - except Exception as e: - print( - _("Validations listing by group finished with errors")) - print('Output: {}'.format(e)) + python_interpreter = \ + "/usr/bin/python{}".format(sys.version_info[0]) - else: - for pb in parsed_args.validation_name: - playbooks.append(pb + '.yaml') - - python_interpreter = \ - "/usr/bin/python{}".format(sys.version_info[0]) - - for playbook in playbooks: - try: - LOG.debug(_('Running the validations with Ansible')) - rc, output = oooutils.run_ansible_playbook( - logger=LOG, - workdir=constants.ANSIBLE_VALIDATION_DIR, - log_path_dir=pwd.getpwuid(os.getuid()).pw_dir, - playbook=playbook, - inventory='/usr/bin/tripleo-ansible-inventory', - retries=False, - connection='local', - output_callback='validation_output', - python_interpreter=python_interpreter) - print('[SUCCESS] - {}\n{}'.format( - playbook, oooutils.indent(output))) - except Exception as e: - print('[FAILED] - {}\n{}'.format( - playbook, oooutils.indent(e.args[0]))) + for playbook in playbooks: + try: + LOG.debug(_('Running the validations with Ansible')) + rc, output = oooutils.run_ansible_playbook( + logger=LOG, + workdir=constants.ANSIBLE_VALIDATION_DIR, + log_path_dir=pwd.getpwuid(os.getuid()).pw_dir, + playbook=playbook, + inventory='/usr/bin/tripleo-ansible-inventory', + retries=False, + connection='local', + output_callback='validation_output', + extra_vars=extra_vars_input, + python_interpreter=python_interpreter) + print('[SUCCESS] - {}\n{}'.format( + playbook, oooutils.indent(output))) + except Exception as e: + print('[FAILED] - {}\n{}'.format( + playbook, oooutils.indent(e.args[0]))) def take_action(self, parsed_args): - self._run_validator_run(parsed_args) + if parsed_args.use_mistral: + self._run_validation_run_with_mistral(parsed_args) + else: + self._run_validator_run(parsed_args)