Skip to content
Snippets Groups Projects
Commit 917b3b62 authored by Thomas Woerner's avatar Thomas Woerner
Browse files

IPAAnsibleModule: New staticethod member_error_handler

The staticmethod member_error_handler is handing the default member
related failures that can occur for modules with member support.
This can simply be enabled with fail_on_member_errors=True for
execute_ipa_commands.

An exception handler is also now usable with execute_ipa_commands. In
addition to the the exception it is also getting the same user defined
arguments that the result_handler is getting.

handle_result has been renamed in result_handler and handle_result_user_args
has been renamed to handlers_user_args.

Additionally the errors list does not need to be defined in the module.
The method execute_ipa_commands is doing this internally and is also
adding error: error to handlers_user_args if the handler is having errors
in the argspec and errors is not yet set in handlers_user_args.

Tests have been added to make sure that no user args for the handler
have been set without an own result or exception handler. Also the use of
fail_on_member_errors together with a result_andler is leading to an
error.
parent 29fb281b
No related branches found
No related tags found
No related merge requests found
......@@ -45,6 +45,7 @@ else:
import gssapi
from datetime import datetime
from contextlib import contextmanager
import inspect
# ansible-freeipa requires locale to be C, IPA requires utf-8.
os.environ["LANGUAGE"] = "C"
......@@ -716,8 +717,21 @@ else:
"""
return api_check_ipa_version(oper, requested_version)
def execute_ipa_commands(self, commands, handle_result=None,
**handle_result_user_args):
# pylint: disable=unused-argument
@staticmethod
def member_error_handler(module, result, command, name, args, errors):
# Get all errors
for failed_item in result.get("failed", []):
failed = result["failed"][failed_item]
for member_type in failed:
for member, failure in failed[member_type]:
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
def execute_ipa_commands(self, commands, result_handler=None,
exception_handler=None,
fail_on_member_errors=False,
**handlers_user_args):
"""
Execute IPA API commands from command list.
......@@ -727,30 +741,56 @@ else:
The list of commands in the form (name, command and args)
For commands that do not require a 'name', None needs be
used.
handle_result: function
result_handler: function
The user function to handle results of the single commands
handle_result_user_args: dict (user args mapping)
The user args to pass to handle_result function
exception_handler: function
The user function to handle exceptions of the single commands
Returns True to continue to next command, else False
fail_on_member_errors: bool
Use default member error handler handler member_error_handler
handlers_user_args: dict (user args mapping)
The user args to pass to result_handler and exception_handler
functions
Example (ipauser module):
def handle_result(module, result, command, name, args, exit_args):
def result_handler(module, result, command, name, args, exit_args,
one_name):
if "random" in args and command in ["user_add", "user_mod"] \
and "randompassword" in result["result"]:
exit_args.setdefault(name, {})["randompassword"] = \
result["result"]["randompassword"]
if one_name:
exit_args["randompassword"] = \
result["result"]["randompassword"]
else:
exit_args.setdefault(name, {})["randompassword"] = \
result["result"]["randompassword"]
def exception_handler(module, ex, exit_args, one_name):
if ex.exception == ipalib_errors.EmptyModlist:
result = {}
return False
exit_args = {}
changed = module.execute_ipa_commands(commands, handle_result,
exit_args=exit_args)
changed = module.execute_ipa_commands(commands, result_handler,
exception_handler,
exit_args=exit_args,
one_name=len(names)==1)
if len(names) == 1:
ansible_module.exit_json(changed=changed,
user=exit_args[names[0]])
else:
ansible_module.exit_json(changed=changed, user=exit_args)
ansible_module.exit_json(changed=changed, user=exit_args)
"""
# Fail on given handlers_user_args without result or exception
# handler
if result_handler is None and exception_handler is None and \
len(handlers_user_args) > 0:
self.fail_json(msg="Args without result and exception hander: "
"%s" % repr(handlers_user_args))
# Fail on given result_handler and fail_on_member_errors
if result_handler is not None and fail_on_member_errors:
self.fail_json(
msg="result_handler given and fail_on_member_errors set")
# No commands, report no changes
if commands is None:
return False
......@@ -759,6 +799,24 @@ else:
if self.check_mode:
return len(commands) > 0
# Error list for result_handler and error_handler
_errors = []
# Handle fail_on_member_errors, set result_handler to
# member_error_handler
# Add internal _errors for result_hendler if the module is not
# adding it. This also activates the final fail_json if
# errors are found.
if fail_on_member_errors:
result_handler = IPAAnsibleModule.member_error_handler
handlers_user_args["errors"] = _errors
elif result_handler is not None:
if "errors" not in handlers_user_args:
# pylint: disable=deprecated-method
argspec = inspect.getargspec(result_handler)
if "errors" in argspec.args:
handlers_user_args["errors"] = _errors
changed = False
for name, command, args in commands:
try:
......@@ -773,13 +831,22 @@ else:
else:
changed = True
if handle_result is not None:
handle_result(self, result, command, name, args,
**handle_result_user_args)
# If result_handler is not None, call it with user args
# defined in **handlers_user_args
if result_handler is not None:
result_handler(self, result, command, name, args,
**handlers_user_args)
except Exception as e:
if exception_handler is not None and \
exception_handler(self, e, **handlers_user_args):
continue
self.fail_json(msg="%s: %s: %s" % (command, name, str(e)))
# Fail on errors from result_handler and exception_handler
if len(_errors) > 0:
self.fail_json(msg=", ".join(_errors))
return changed
class FreeIPABaseModule(IPAAnsibleModule):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment