def run(self, tmp=None, task_vars=None): """The std execution entry pt for an action plugin :param tmp: no longer used :type tmp: none :param task_vars: The vars provided when the task is run :type task_vars: dict :return: The results from the parser :rtype: dict """ valid, argspec_result, updated_params = check_argspec( DOCUMENTATION, "validate module", schema_conditionals=ARGSPEC_CONDITIONALS, **self._task.args ) if not valid: return argspec_result self._task_vars = task_vars self._playhost = ( task_vars.get("inventory_hostname") if task_vars else None ) self._validator_engine, validator_result = _load_validator( engine=updated_params["engine"], data=updated_params["data"], criteria=updated_params["criteria"], plugin_vars=task_vars, ) if validator_result.get("failed"): return validator_result try: result = self._validator_engine.validate() except AnsibleError as exc: raise AnsibleActionFail( to_text(exc, errors="surrogate_then_replace") ) except Exception as exc: raise AnsibleActionFail( "Unhandled exception from validator '{validator}'. Error: {err}".format( validator=self._validator_engine, err=to_text(exc, errors="surrogate_then_replace"), ) ) if result.get("errors"): self._result["errors"] = result["errors"] self._result.update({"failed": True}) if "msg" in result: self._result["msg"] = ( "Validation errors were found.\n" + result["msg"] ) else: self._result["msg"] = "Validation errors were found." else: self._result["msg"] = "all checks passed" return self._result
def validate(*args, **kwargs): if not len(args): raise AnsibleError( "Missing either 'data' value in test plugin input," "refer ansible.utils.validate test plugin documentation for details" ) params = {"data": args[0]} for item in ["engine", "criteria"]: if kwargs.get(item): params.update({item: kwargs[item]}) valid, argspec_result, updated_params = check_argspec( DOCUMENTATION, "validate test", schema_conditionals=ARGSPEC_CONDITIONALS, **params) if not valid: raise AnsibleError( "{argspec_result} with errors: {argspec_errors}".format( argspec_result=argspec_result.get("msg"), argspec_errors=argspec_result.get("errors"), )) validator_engine, validator_result = load_validator( engine=updated_params["engine"], data=updated_params["data"], criteria=updated_params["criteria"], kwargs=kwargs, ) if validator_result.get("failed"): raise AnsibleError("validate lookup plugin failed with errors: %s" % validator_result.get("msg")) try: result = validator_engine.validate() except AnsibleError as exc: raise AnsibleError(to_text(exc, errors="surrogate_then_replace")) except Exception as exc: raise AnsibleError( "Unhandled exception from validator '{validator}'. Error: {err}". format( validator=updated_params["engine"], err=to_text(exc, errors="surrogate_then_replace"), )) errors = to_list(result.get("errors", [])) if len(errors): return False return True
def test_fn_check_argspec_fail_no_parser_name(self): """ Confirm failed argspec no parser name """ kwargs = {"text": "anything", "parser": {"command": "show version"}} valid, result, updated_params = check_argspec( DOCUMENTATION, "cli_parse module", schema_conditionals=ARGSPEC_CONDITIONALS, **kwargs) self.assertEqual( "missing required arguments: name found in parser", result["errors"], )
def test_fn_check_argspec_pass(self): """Confirm a valid argspec passes""" kwargs = { "text": "text", "parser": { "name": "ansible.utils.textfsm", "command": "show version", }, } valid, result, updated_params = check_argspec(DOCUMENTATION, "cli_parse module", schema_conditionals={}, **kwargs) self.assertEqual(valid, True)
def test_fn_check_argspec_fail_no_parser_name(self): """Confirm failed argspec no parser name""" kwargs = {"text": "anything", "parser": {"command": "show version"}} valid, result, updated_params = check_argspec( DOCUMENTATION, "cli_parse module", schema_conditionals=ARGSPEC_CONDITIONALS, **kwargs) # NOTE: Ansible 2.11+ returns result["errors"] as a list error_msg = "missing required arguments: name found in parser" if isinstance(result["errors"], list): self.assertIn(error_msg, result["errors"]) else: self.assertEqual(error_msg, result["errors"])
def run(self, terms, variables, **kwargs): if len(terms) < 2: raise AnsibleLookupError( "missing either 'data' or 'criteria' value in lookup input," " refer ansible.utils.validate lookup plugin documentation for details" ) params = {"data": terms[0], "criteria": terms[1]} if kwargs.get("engine"): params.update({"engine": kwargs["engine"]}) valid, argspec_result, updated_params = check_argspec( DOCUMENTATION, "validate lookup", schema_conditionals=ARGSPEC_CONDITIONALS, **params) if not valid: raise AnsibleLookupError( "{argspec_result} with errors: {argspec_errors}".format( argspec_result=argspec_result.get("msg"), argspec_errors=argspec_result.get("errors"), )) validator_engine, validator_result = load_validator( engine=updated_params["engine"], data=updated_params["data"], criteria=updated_params["criteria"], plugin_vars=variables, kwargs=kwargs, ) if validator_result.get("failed"): raise AnsibleLookupError( "validate lookup plugin failed with errors: {validator_result}" .format(validator_result=validator_result.get("msg"))) try: result = validator_engine.validate() except AnsibleError as exc: raise AnsibleLookupError( to_text(exc, errors="surrogate_then_replace")) except Exception as exc: raise AnsibleLookupError( "Unhandled exception from validator '{validator}'. Error: {err}" .format( validator=updated_params["engine"], err=to_text(exc, errors="surrogate_then_replace"), )) return to_list(result.get("errors", []))
def _validate_args(plugin, doc, params): """ argspec validator utility function """ valid, argspec_result, updated_params = check_argspec( doc, plugin + " test", **params ) if not valid: raise AnsibleError( "{argspec_result} with errors: {argspec_errors}".format( argspec_result=argspec_result.get("msg"), argspec_errors=argspec_result.get("errors"), ) )
def param_list_compare(*args, **kwargs): params = ["base", "target"] data = dict(zip(params, args)) data.update(kwargs) if len(data) < 2: raise AnsibleFilterError( "Missing either 'base' or 'other value in filter input," "refer 'ansible.utils.param_list_compare' filter plugin documentation for details" ) valid, argspec_result, updated_params = check_argspec( DOCUMENTATION, "param_list_compare filter", schema_conditionals=ARGSPEC_CONDITIONALS, **data ) if not valid: raise AnsibleFilterError( "{argspec_result} with errors: {argspec_errors}".format( argspec_result=argspec_result.get("msg"), argspec_errors=argspec_result.get("errors"), ) ) base = data["base"] other = data["target"] combined = [] alls = [x for x in other if x == "all"] bangs = [x[1:] for x in other if x.startswith("!")] rbangs = [x for x in other if x.startswith("!")] remain = [ x for x in other if x not in alls and x not in rbangs and x in base ] unsupported = [ x for x in other if x not in alls and x not in rbangs and x not in base ] if alls: combined = base for entry in bangs: if entry in combined: combined.remove(entry) for entry in remain: if entry not in combined: combined.append(entry) combined.sort() output = {"actionable": combined, "unsupported": unsupported} return output
def test_fn_check_argspec_fail_no_test_or_command(self): """ Confirm failed argpsec w/o text or command """ kwargs = { "parser": { "name": "ansible.utils.textfsm", "command": "show version", } } valid, result, updated_params = check_argspec( DOCUMENTATION, "cli_parse module", schema_conditionals=ARGSPEC_CONDITIONALS, **kwargs) self.assertEqual("one of the following is required: command, text", result["errors"])
def test_fn_check_argspec_fail_no_test_or_command(self): """Confirm failed argpsec w/o text or command""" kwargs = { "parser": { "name": "ansible.utils.textfsm", "command": "show version", } } valid, result, updated_params = check_argspec( DOCUMENTATION, "cli_parse module", schema_conditionals=ARGSPEC_CONDITIONALS, **kwargs) # NOTE: Ansible 2.11+ returns result["errors"] as a list error_msg = "one of the following is required: command, text" if isinstance(result["errors"], list): self.assertIn(error_msg, result["errors"]) else: self.assertEqual(error_msg, result["errors"])
def run(self, tmp=None, task_vars=None): """ The std execution entry pt for an action plugin :param tmp: no longer used :type tmp: none :param task_vars: The vars provided when the task is run :type task_vars: dict :return: The results from the parser :rtype: dict """ msg = ( "Use 'ansible.utils.cli_parse' instead of 'ansible.netcommon.cli_parse'." " See the plugin documentation for more details." " This feature will be removed from ansible.netcommon in a release after 2023-01-01" ) display.deprecated(msg, date="2023-01-01", collection_name="ansible.netcommon") valid, argspec_result, updated_params = check_argspec( DOCUMENTATION, "cli_parse module", schema_conditionals=ARGSPEC_CONDITIONALS, **self._task.args) if not valid: return argspec_result self._extended_check_argspec() if self._result.get("failed"): return self._result self._task_vars = task_vars self._playhost = task_vars.get("inventory_hostname") self._parser_name = self._task.args.get("parser").get("name") self._run_command() if self._result.get("failed"): return self._result self._set_parser_command() self._set_text() parser = self._load_parser(task_vars) if self._result.get("failed"): self._prune_result() return self._result # Not all parsers use a template, in the case a parser provides # an extension, provide it the template path if getattr(parser, "DEFAULT_TEMPLATE_EXTENSION", False): self._update_template_path(parser.DEFAULT_TEMPLATE_EXTENSION) # Not all parsers require the template contents # when true, provide the template contents if getattr(parser, "PROVIDE_TEMPLATE_CONTENTS", False) is True: template_contents = self._get_template_contents() else: template_contents = None try: result = parser.parse(template_contents=template_contents) # ensure the response returned to the controller # contains only native types, nothing unique to the parser result = json.loads(json.dumps(result)) except Exception as exc: raise AnsibleActionFail( "Unhandled exception from parser '{parser}'. Error: {err}". format(parser=self._parser_name, err=to_native(exc))) if result.get("errors"): self._prune_result() self._result.update({ "failed": True, "msg": " ".join(result["errors"]) }) else: self._result["parsed"] = result["parsed"] set_fact = self._task.args.get("set_fact") if set_fact: self._result["ansible_facts"] = {set_fact: result["parsed"]} return self._result
def _set_sub_plugin_options(self, doc): params = {} try: argspec_obj = yaml.load(doc, SafeLoader) except Exception as exc: raise AnsibleError( "Error '{err}' while reading validate plugin {engine} documentation: '{argspec}'" .format( err=to_text(exc, errors="surrogate_or_strict"), engine=self._engine, argspec=doc, )) options = argspec_obj.get("options", {}) if not options: return None for option_name, option_value in iteritems(options): option_var_name_list = option_value.get("vars", []) option_env_name_list = option_value.get("env", []) # check if plugin configuration option passed as kwargs # valid for lookup, filter, test plugins or pass through # variables if supported by the module. if option_name in self._kwargs: params[option_name] = self._kwargs[option_name] continue # check if plugin configuration option passed in task vars eg. # vars: # - name: ansible_validate_jsonschema_draft # - name: ansible_validate_jsonschema_draft_type if option_var_name_list and (option_name not in params): for var_name_entry in to_list(option_var_name_list): if not isinstance(var_name_entry, dict): raise AnsibleError( "invalid type '{var_name_type}' for the value of '{var_name_entry}' option," " should to be type dict".format( var_name_type=type(var_name_entry), var_name_entry=var_name_entry, )) var_name = var_name_entry.get("name") if var_name and var_name in self._plugin_vars: params[option_name] = self._plugin_vars[var_name] break # check if plugin configuration option as passed as enviornment eg. # env: # - name: ANSIBLE_VALIDATE_JSONSCHEMA_DRAFT if option_env_name_list and (option_name not in params): for env_name_entry in to_list(option_env_name_list): if not isinstance(env_name_entry, dict): raise AnsibleError( "invalid type '{env_name_entry_type}' for the value of '{env_name_entry}' option," " should to be type dict".format( env_name_entry_type=type(env_name_entry), env_name_entry=env_name_entry, )) env_name = env_name_entry.get("name") if env_name in os.environ: params[option_name] = os.environ[env_name] break valid, argspec_result, updated_params = check_argspec( yaml.dump(argspec_obj), self._engine, **params) if not valid: raise AnsibleError( "{argspec_result} with errors: {argspec_errors}".format( argspec_result=argspec_result.get("msg"), argspec_errors=argspec_result.get("errors"), )) if updated_params: self._sub_plugin_options = updated_params