From d01c42d70cf74f43725422fe461cee23073faeac Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 19:14:55 +0200 Subject: [PATCH 01/35] ansible_freeipa_module: New IPAAnsibleModule class This class is an extended version of the Ansible Module that provides IPA specific methods to simplify module generation. Simple example: from ansible.module_utils.ansible_freeipa_module import \ IPAAnsibleModule def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( name=dict(type="str", aliases=["cn"], default=None), state=dict(type="str", default="present", choices=["present", "absent"]), ), ) # Get parameters name = ansible_module.params_get("name") state = ansible_module.params_get("state") # Connect to IPA API with ansible_module.ipa_connect(): # Execute command if state == "present": ansible_module.ipa_command(["command_add", name, {}]) else: ansible_module.ipa_command(["command_del", name, {}]) # Done ansible_module.exit_json(changed=True) if __name__ == "__main__": main() --- .../module_utils/ansible_freeipa_module.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index e8d62e28..7c60eb28 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -45,6 +45,7 @@ else: import gssapi from datetime import datetime from pprint import pformat + from contextlib import contextmanager # ansible-freeipa requires locale to be C, IPA requires utf-8. os.environ["LANGUAGE"] = "C" @@ -859,3 +860,109 @@ else: self.check_ipa_params() self.define_ipa_commands() self._run_ipa_commands() + + class IPAAnsibleModule(AnsibleModule): + """ + IPA Ansible Module. + + This class is an extended version of the Ansible Module that provides + IPA specific methods to simplify module generation. + + Simple example: + + from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule + + def main(): + ansible_module = IPAAnsibleModule( + argument_spec=dict( + name=dict(type="str", aliases=["cn"], default=None), + state=dict(type="str", default="present", + choices=["present", "absent"]), + ), + ) + + # Get parameters + name = ansible_module.params_get("name") + state = ansible_module.params_get("state") + + # Connect to IPA API + with ansible_module.ipa_connect(): + + # Execute command + if state == "present": + ansible_module.ipa_command(["command_add", name, {}]) + else: + ansible_module.ipa_command(["command_del", name, {}]) + + # Done + + ansible_module.exit_json(changed=True) + + if __name__ == "__main__": + main() + + """ + + def __init__(self, *args, **kwargs): + # Extend argument_spec with ipamodule_base_spec + if "argument_spec" in kwargs: + _spec = kwargs["argument_spec"] + _spec.update(ipamodule_base_spec) + kwargs["argument_spec"] = _spec + + # pylint: disable=super-with-arguments + super(IPAAnsibleModule, self).__init__(*args, **kwargs) + + # ipaadmin vars + self.ipaadmin_principal = self.params_get("ipaadmin_principal") + self.ipaadmin_password = self.params_get("ipaadmin_password") + + # Attributes to store kerberos credentials (if needed) + self.ccache_dir = None + self.ccache_name = None + + @contextmanager + def ipa_connect(self, context=None): + ccache_dir = None + ccache_name = None + try: + if not valid_creds(self, self.ipaadmin_principal): + ccache_dir, ccache_name = temp_kinit( + self.ipaadmin_principal, self.ipaadmin_password) + api_connect(context) + except Exception as e: + self.fail_json(msg=str(e)) + else: + try: + yield ccache_name + except Exception as e: + self.fail_json(msg=str(e)) + finally: + temp_kdestroy(ccache_dir, ccache_name) + + def params_get(self, name): + return module_params_get(self, name) + + def ipa_command(self, command, name, args): + return api_command(self, command, name, args) + + def ipa_command_no_name(self, command, args): + return api_command_no_name(self, command, args) + + def ipa_get_domain(self): # pylint: disable=R0201 + return api_get_domain() + + def ipa_get_realm(self): # pylint: disable=R0201 + return api_get_realm() + + def ipa_command_exists(self, command): # pylint: disable=R0201 + return api_check_command(command) + + # pylint: disable=R0201 + def ipa_command_param_exists(self, command, name): + return api_check_param(command, name) + + # pylint: disable=R0201 + def ipa_check_version(self, oper, requested_version): + return api_check_ipa_version(oper, requested_version) From b4d39dfd8b12f4c941a14f5c08c56fef7fd4c609 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 19:18:11 +0200 Subject: [PATCH 02/35] ipamodule[+member].py.in: Use IPAAnsibleModule class, reduce calls Use IPAAnsibleModule and ipamodule_base_docs in the templates of utils/new_module. ipaadmin_password lines in the examples have been added, ipaadmin_ variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. ipamodule+member.py.in is additionally using gen_add_list and gen_intersection_list to reduce the command calls to the changes only. --- utils/templates/ipamodule+member.py.in | 91 ++++++++++++-------------- utils/templates/ipamodule.py.in | 52 +++++---------- 2 files changed, 58 insertions(+), 85 deletions(-) diff --git a/utils/templates/ipamodule+member.py.in b/utils/templates/ipamodule+member.py.in index bd637398..c3e37727 100644 --- a/utils/templates/ipamodule+member.py.in +++ b/utils/templates/ipamodule+member.py.in @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipa$name short description: Manage FreeIPA $name description: Manage FreeIPA $name and $name members +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The list of $name name strings. required: true @@ -65,17 +61,20 @@ options: EXAMPLES = """ # Ensure $name NAME is present - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME PARAMETERS # Ensure $name "NAME" member PARAMETER2 VALUE is present - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME PARAMETER2: VALUE action: member # Ensure $name "NAME" member PARAMETER2 VALUE is absent - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME PARAMETER2: VALUE action: member @@ -83,11 +82,13 @@ EXAMPLES = """ # Ensure $name NAME is absent - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME state: absent # Ensure $name NAME ... - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME CHANGE PARAMETERS """ @@ -96,10 +97,10 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get, gen_add_del_lists + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \ + gen_intersection_list + import six if six.PY3: @@ -109,7 +110,7 @@ if six.PY3: def find_$name(module, name): """Find if a $name with the given name already exist.""" try: - _result = api_command(module, "$name_show", name, {"all": True}) + _result = module.ipa_command("$name_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if $name name is not found. return None @@ -132,12 +133,9 @@ def gen_member_args(PARAMETER2): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["API_PARAMETER_NAME"], default=None, required=True), # present @@ -159,18 +157,15 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - PARAMETER1 = module_params_get(ansible_module, "PARAMETER1") - PARAMETER2 = module_params_get(ansible_module, "PARAMETER2") - action = module_params_get(ansible_module, "action") + PARAMETER1 = ansible_module.params_get("PARAMETER1") + PARAMETER2 = ansible_module.params_get("PARAMETER2") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -200,13 +195,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -257,13 +248,18 @@ def main(): ansible_module.fail_json( msg="No $name '%s'" % name) - if PARAMETER2 is None: - ansible_module.fail_json(msg="No PARAMETER2 given") + # Reduce add lists for PARAMETER2 + # to new entries only that are not in res_find. + if PARAMETER2 is not None and \ + "API_PARAMETER2_NAME" in res_find: + PARAMETER2 = gen_add_list( + PARAMETER2, res_find["API_PARAMETER2_NAME"]) - commands.append([name, "$name_add_member", - { - "PARAMETER2": PARAMETER2, - }]) + if PARAMETER2 is not None: + commands.append([name, "$name_add_member", + { + "PARAMETER2": PARAMETER2, + }]) elif state == "absent": if action == "$name": @@ -275,13 +271,17 @@ def main(): ansible_module.fail_json( msg="No $name '%s'" % name) - if PARAMETER2 is None: - ansible_module.fail_json(msg="No PARAMETER2 given") + # Reduce del lists of member_host and member_hostgroup, + # to the entries only that are in res_find. + if PARAMETER2 is not None: + PARAMETER2 = gen_intersection_list( + PARAMETER2, res_find.get("API_PARAMETER2_NAME")) - commands.append([name, "$name_remove_member", - { - "PARAMETER2": PARAMETER2, - }]) + if PARAMETER2 is not None: + commands.append([name, "$name_remove_member", + { + "PARAMETER2": PARAMETER2, + }]) else: ansible_module.fail_json(msg="Unkown state '%s'" % state) @@ -294,8 +294,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -320,12 +319,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) diff --git a/utils/templates/ipamodule.py.in b/utils/templates/ipamodule.py.in index 233c5985..f7d1e538 100644 --- a/utils/templates/ipamodule.py.in +++ b/utils/templates/ipamodule.py.in @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipa$name short description: Manage FreeIPA $name description: Manage FreeIPA $name +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The list of $name name strings. required: true @@ -60,16 +56,19 @@ options: EXAMPLES = """ # Ensure $name NAME is present - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME PARAMETERS # Ensure $name NAME is absent - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME state: absent # Ensure $name NAME ... - ipa$name: + ipaadmin_password: SomeADMINpassword name: NAME CHANGE PARAMETERS """ @@ -78,10 +77,8 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get + IPAAnsibleModule, compare_args_ipa import six if six.PY3: @@ -91,7 +88,7 @@ if six.PY3: def find_$name(module, name): """Find if a $name with the given name already exist.""" try: - _result = api_command(module, "$name_show", name, {"all": True}) + _result = module.ipa_command("$name_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if $name name is not found. return None @@ -109,12 +106,9 @@ def gen_args(PARAMETER1, PARAMETER2): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["API_PARAMETER_NAME"], default=None, required=True), # present @@ -134,17 +128,14 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - PARAMETER1 = module_params_get(ansible_module, "PARAMETER1") - PARAMETER2 = module_params_get(ansible_module, "PARAMETER2") + PARAMETER1 = ansible_module.params_get("PARAMETER1") + PARAMETER2 = ansible_module.params_get("PARAMETER2") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -170,13 +161,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -215,8 +202,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -226,12 +212,6 @@ def main(): ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 5636742bdbf729b3f30fb72091370e4f967130a6 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 19:27:18 +0200 Subject: [PATCH 03/35] location: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipalocation.py | 61 ++++++++++------------------------ 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/plugins/modules/ipalocation.py b/plugins/modules/ipalocation.py index 6c757944..48d5b487 100644 --- a/plugins/modules/ipalocation.py +++ b/plugins/modules/ipalocation.py @@ -66,21 +66,14 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get, ipamodule_base_spec, \ - get_ipamodule_base_vars -import six - -if six.PY3: - unicode = str + IPAAnsibleModule, compare_args_ipa def find_location(module, name): """Find if a location with the given name already exist.""" try: - _result = api_command(module, "location_show", name, {"all": True}) + _result = module.ipa_command("location_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if location name is not found. return None @@ -96,20 +89,16 @@ def gen_args(description): def main(): - # Arguments - argument_spec = dict( - name=dict(type="list", aliases=["idnsname"], - default=None, required=True), - # present - description=dict(required=False, type='str', default=None), - # state - state=dict(type="str", default="present", - choices=["present", "absent"]), - ) - argument_spec.update(ipamodule_base_spec) - - ansible_module = AnsibleModule( - argument_spec=argument_spec, + ansible_module = IPAAnsibleModule( + argument_spec=dict( + name=dict(type="list", aliases=["idnsname"], + default=None, required=True), + # present + description=dict(required=False, type='str', default=None), + # state + state=dict(type="str", default="present", + choices=["present", "absent"]), + ), supports_check_mode=True, ) @@ -118,14 +107,13 @@ def main(): # Get parameters # general - base_vars = get_ipamodule_base_vars(ansible_module) - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - description = module_params_get(ansible_module, "description") + description = ansible_module.params_get("description") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -148,15 +136,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, base_vars["ipaadmin_principal"]): - ccache_dir, ccache_name = temp_kinit( - base_vars["ipaadmin_principal"], - base_vars["ipaadmin_password"]) - api_connect() + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: # Make sure location exists @@ -194,8 +176,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -205,12 +186,6 @@ def main(): ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 7ff492655b9f06ba392733022ba4acecb3a577ea Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 20:05:02 +0200 Subject: [PATCH 04/35] automember: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipaautomember.py | 101 +++++++++++-------------------- 1 file changed, 36 insertions(+), 65 deletions(-) diff --git a/plugins/modules/ipaautomember.py b/plugins/modules/ipaautomember.py index aeaf7173..49c8d9a2 100644 --- a/plugins/modules/ipaautomember.py +++ b/plugins/modules/ipaautomember.py @@ -22,14 +22,6 @@ # along with this program. If not, see . -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import ( - api_command, api_command_no_name, api_connect, compare_args_ipa, - gen_add_del_lists, temp_kdestroy, temp_kinit, valid_creds, - ipalib_errors -) -from ansible.module_utils.basic import AnsibleModule - ANSIBLE_METADATA = { "metadata_version": "1.0", "supported_by": "community", @@ -42,13 +34,9 @@ DOCUMENTATION = """ module: ipaautomember short description: Add and delete FreeIPA Auto Membership Rules. description: Add, modify and delete an IPA Auto Membership Rules. +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The automember rule required: true @@ -138,14 +126,19 @@ RETURN = """ """ +from ansible.module_utils.ansible_freeipa_module import ( + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors +) + + def find_automember(module, name, grouping): _args = { "all": True, - "type": to_text(grouping) + "type": grouping } try: - _result = api_command(module, "automember_show", to_text(name), _args) + _result = module.ipa_command("automember_show", name, _args) except ipalib_errors.NotFound: return None return _result["result"] @@ -157,13 +150,13 @@ def gen_condition_args(grouping, exclusiveregex=None): _args = {} if grouping is not None: - _args['type'] = to_text(grouping) + _args['type'] = grouping if key is not None: - _args['key'] = to_text(key) + _args['key'] = key if inclusiveregex is not None: - _args['automemberinclusiveregex'] = to_text(inclusiveregex) + _args['automemberinclusiveregex'] = inclusiveregex if exclusiveregex is not None: - _args['automemberexclusiveregex'] = to_text(exclusiveregex) + _args['automemberexclusiveregex'] = exclusiveregex return _args @@ -171,9 +164,9 @@ def gen_condition_args(grouping, def gen_args(description, grouping): _args = {} if description is not None: - _args["description"] = to_text(description) + _args["description"] = description if grouping is not None: - _args['type'] = to_text(grouping) + _args['type'] = grouping return _args @@ -195,12 +188,9 @@ def check_condition_keys(ansible_module, conditions, aciattrs): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - inclusive=dict(type="list", aliases=["automemberinclusiveregex"], default=None, options=dict( @@ -235,27 +225,25 @@ def main(): # Get parameters # general - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - names = ansible_module.params.get("name") + names = ansible_module.params_get("name") # present - description = ansible_module.params.get("description") + description = ansible_module.params_get("description") # conditions - inclusive = ansible_module.params.get("inclusive") - exclusive = ansible_module.params.get("exclusive") + inclusive = ansible_module.params_get("inclusive") + exclusive = ansible_module.params_get("exclusive") # action - action = ansible_module.params.get("action") + action = ansible_module.params_get("action") # state - state = ansible_module.params.get("state") + state = ansible_module.params_get("state") # grouping/type - automember_type = ansible_module.params.get("automember_type") + automember_type = ansible_module.params_get("automember_type") - rebuild_users = ansible_module.params.get("users") - rebuild_hosts = ansible_module.params.get("hosts") + rebuild_users = ansible_module.params_get("users") + rebuild_hosts = ansible_module.params_get("hosts") if (rebuild_hosts or rebuild_users) and state != "rebuild": ansible_module.fail_json( @@ -267,15 +255,9 @@ def main(): # Init changed = False exit_args = {} - ccache_dir = None - ccache_name = None res_find = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + with ansible_module.ipa_connect(): commands = [] @@ -287,16 +269,16 @@ def main(): if inclusive is not None or exclusive is not None: # automember_type is either "group" or "hostgorup" if automember_type == "group": - _type = "user" + _type = u"user" elif automember_type == "hostgroup": - _type = "host" + _type = u"host" else: ansible_module.fail_json( msg="Bad automember type '%s'" % automember_type) try: - aciattrs = api_command( - ansible_module, "json_metadata", to_text(_type), {} + aciattrs = ansible_module.ipa_command( + "json_metadata", _type, {} )['objects'][_type]['aciattrs'] except Exception as ex: ansible_module.fail_json( @@ -372,7 +354,7 @@ def main(): if action == "automember": if res_find is not None: commands.append([name, 'automember_del', - {'type': to_text(automember_type)}]) + {'type': automember_type}]) elif action == "member": if res_find is None: @@ -400,17 +382,13 @@ def main(): elif state == "rebuild": if automember_type: commands.append([None, 'automember_rebuild', - {"type": to_text(automember_type)}]) + {"type": automember_type}]) if rebuild_users: commands.append([None, 'automember_rebuild', - {"users": [ - to_text(_u) - for _u in rebuild_users]}]) + {"users": rebuild_users}]) if rebuild_hosts: commands.append([None, 'automember_rebuild', - {"hosts": [ - to_text(_h) - for _h in rebuild_hosts]}]) + {"hosts": rebuild_hosts}]) # Check mode exit if ansible_module.check_mode: @@ -419,10 +397,9 @@ def main(): for name, command, args in commands: try: if name is None: - result = api_command_no_name(ansible_module, command, args) + result = ansible_module.ipa_command_no_name(command, args) else: - result = api_command(ansible_module, command, - to_text(name), args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: @@ -440,12 +417,6 @@ def main(): # as exceptions. Therefore the error section is not here as # in other modules. - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 683d1c979fbf062b35b576e60c2a0ffb308cf589 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 20:06:54 +0200 Subject: [PATCH 05/35] pwpolicy: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipapwpolicy.py | 58 ++++++++++++---------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/plugins/modules/ipapwpolicy.py b/plugins/modules/ipapwpolicy.py index c9509f4d..758ee6db 100644 --- a/plugins/modules/ipapwpolicy.py +++ b/plugins/modules/ipapwpolicy.py @@ -111,19 +111,17 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa def find_pwpolicy(module, name): _args = { "all": True, - "cn": to_text(name), + "cn": name, } - _result = api_command(module, "pwpolicy_find", to_text(name), _args) + _result = module.ipa_command("pwpolicy_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -160,12 +158,9 @@ def gen_args(maxlife, minlife, history, minclasses, minlength, priority, def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=False), # present @@ -198,28 +193,26 @@ def main(): # Get parameters # general - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - names = ansible_module.params.get("name") + names = ansible_module.params_get("name") # present - maxlife = ansible_module.params.get("maxlife") - minlife = ansible_module.params.get("minlife") - history = ansible_module.params.get("history") - minclasses = ansible_module.params.get("minclasses") - minlength = ansible_module.params.get("minlength") - priority = ansible_module.params.get("priority") - maxfail = ansible_module.params.get("maxfail") - failinterval = ansible_module.params.get("failinterval") - lockouttime = ansible_module.params.get("lockouttime") + maxlife = ansible_module.params_get("maxlife") + minlife = ansible_module.params_get("minlife") + history = ansible_module.params_get("history") + minclasses = ansible_module.params_get("minclasses") + minlength = ansible_module.params_get("minlength") + priority = ansible_module.params_get("priority") + maxfail = ansible_module.params_get("maxfail") + failinterval = ansible_module.params_get("failinterval") + lockouttime = ansible_module.params_get("lockouttime") # state - state = ansible_module.params.get("state") + state = ansible_module.params_get("state") # Check parameters if names is None: - names = ["global_policy"] + names = [u"global_policy"] if state == "present": if len(names) != 1: @@ -245,13 +238,8 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + with ansible_module.ipa_connect(): commands = [] @@ -292,18 +280,12 @@ def main(): for name, command, args in commands: try: - api_command(ansible_module, command, to_text(name), args) + ansible_module.ipa_command(command, name, args) changed = True except Exception as e: ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 5986de855846c4cbdbce66250f7ae1b68434da7c Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 20:07:41 +0200 Subject: [PATCH 06/35] topologysegment: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipatopologysegment.py | 79 +++++++++++---------------- 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/plugins/modules/ipatopologysegment.py b/plugins/modules/ipatopologysegment.py index d2909025..e8ef2b0a 100644 --- a/plugins/modules/ipatopologysegment.py +++ b/plugins/modules/ipatopologysegment.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipatopologysegment short description: Manage FreeIPA topology segments description: Manage FreeIPA topology segments +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false suffix: description: Topology suffix required: true @@ -67,35 +63,41 @@ author: EXAMPLES = """ - ipatopologysegment: + ipaadmin_password: SomeADMINpassword suffix: domain left: ipaserver.test.local right: ipareplica1.test.local state: present - ipatopologysegment: + ipaadmin_password: SomeADMINpassword suffix: domain name: ipaserver.test.local-to-replica1.test.local state: absent - ipatopologysegment: + ipaadmin_password: SomeADMINpassword suffix: domain left: ipaserver.test.local right: ipareplica1.test.local state: absent - ipatopologysegment: + ipaadmin_password: SomeADMINpassword suffix: ca name: ipaserver.test.local-to-replica1.test.local direction: left-to-right state: reinitialized - ipatopologysegment: + ipaadmin_password: SomeADMINpassword suffix: domain+ca left: ipaserver.test.local right: ipareplica1.test.local state: absent - ipatopologysegment: + ipaadmin_password: SomeADMINpassword suffix: domain+ca left: ipaserver.test.local right: ipareplica1.test.local @@ -113,19 +115,16 @@ not-found: type: list """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command +from ansible.module_utils.ansible_freeipa_module import IPAAnsibleModule def find_left_right(module, suffix, left, right): _args = { - "iparepltoposegmentleftnode": to_text(left), - "iparepltoposegmentrightnode": to_text(right), + "iparepltoposegmentleftnode": left, + "iparepltoposegmentrightnode": right, } - _result = api_command(module, "topologysegment_find", - to_text(suffix), _args) + _result = module.ipa_command("topologysegment_find", + suffix, _args) if len(_result["result"]) > 1: module.fail_json( msg="Combination of left node '%s' and right node '%s' is " @@ -138,10 +137,10 @@ def find_left_right(module, suffix, left, right): def find_cn(module, suffix, name): _args = { - "cn": to_text(name), + "cn": name, } - _result = api_command(module, "topologysegment_find", - to_text(suffix), _args) + _result = module.ipa_command("topologysegment_find", + suffix, _args) if len(_result["result"]) > 1: module.fail_json( msg="CN '%s' is not unique for suffix '%s'" % (name, suffix)) @@ -156,7 +155,7 @@ def find_left_right_cn(module, suffix, left, right, name): left_right = find_left_right(module, suffix, left, right) if left_right is not None: if name is not None and \ - left_right["cn"][0] != to_text(name): + left_right["cn"][0] != name: module.fail_json( msg="Left and right nodes do not match " "given name name (cn) '%s'" % name) @@ -174,10 +173,8 @@ def find_left_right_cn(module, suffix, left, right, name): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), suffix=dict(choices=["domain", "ca", "domain+ca"], required=True), name=dict(type="str", aliases=["cn"], default=None), left=dict(type="str", aliases=["leftnode"], default=None), @@ -195,14 +192,12 @@ def main(): # Get parameters - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - suffixes = ansible_module.params.get("suffix") - name = ansible_module.params.get("name") - left = ansible_module.params.get("left") - right = ansible_module.params.get("right") - direction = ansible_module.params.get("direction") - state = ansible_module.params.get("state") + suffixes = ansible_module.params_get("suffix") + name = ansible_module.params_get("name") + left = ansible_module.params_get("left") + right = ansible_module.params_get("right") + direction = ansible_module.params_get("direction") + state = ansible_module.params_get("state") # Check parameters @@ -214,14 +209,8 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + with ansible_module.ipa_connect(): commands = [] for suffix in suffixes.split("+"): @@ -233,17 +222,17 @@ def main(): ansible_module.fail_json( msg="Left and right need to be set.") args = { - "iparepltoposegmentleftnode": to_text(left), - "iparepltoposegmentrightnode": to_text(right), + "iparepltoposegmentleftnode": left, + "iparepltoposegmentrightnode": right, } if name is not None: - args["cn"] = to_text(name) + args["cn"] = name res_left_right = find_left_right(ansible_module, suffix, left, right) if res_left_right is not None: if name is not None and \ - res_left_right["cn"][0] != to_text(name): + res_left_right["cn"][0] != name: ansible_module.fail_json( msg="Left and right nodes already used with " "different name (cn) '%s'" % res_left_right["cn"]) @@ -260,7 +249,7 @@ def main(): # else: Nothing to change else: if name is None: - args["cn"] = to_text("%s-to-%s" % (left, right)) + args["cn"] = "%s-to-%s" % (left, right) commands.append(["topologysegment_add", args, suffix]) elif state in ["absent", "disabled"]: @@ -333,15 +322,9 @@ def main(): # Execute command for command, args, _suffix in commands: - api_command(ansible_module, command, to_text(_suffix), args) + ansible_module.ipa_command(command, _suffix, args) changed = True - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From af0b782f73f7ae60354cd0bc42183b7640495dc5 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 20:08:39 +0200 Subject: [PATCH 07/35] topologysuffix: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipatopologysuffix.py | 34 ++++++++++------------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/plugins/modules/ipatopologysuffix.py b/plugins/modules/ipatopologysuffix.py index ab1e4135..47119264 100644 --- a/plugins/modules/ipatopologysuffix.py +++ b/plugins/modules/ipatopologysuffix.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipatopologysuffix short description: Verify FreeIPA topology suffix description: Verify FreeIPA topology suffix +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false suffix: description: Topology suffix required: true @@ -52,6 +48,7 @@ author: EXAMPLES = """ - ipatopologysuffix: + ipaadmin_password: SomeADMINpassword suffix: domain state: verified """ @@ -59,16 +56,12 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import execute_api_command +from ansible.module_utils.ansible_freeipa_module import IPAAnsibleModule def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), suffix=dict(choices=["domain", "ca"], required=True), state=dict(type="str", default="verified", choices=["verified"]), @@ -80,10 +73,8 @@ def main(): # Get parameters - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - suffix = ansible_module.params.get("suffix") - state = ansible_module.params.get("state") + suffix = ansible_module.params_get("suffix") + state = ansible_module.params_get("state") # Check parameters @@ -91,16 +82,15 @@ def main(): # Create command - if state in ["verified"]: - command = "topologysuffix_verify" - args = {} - else: + if state not in ["verified"]: ansible_module.fail_json(msg="Unkown state '%s'" % state) # Execute command - execute_api_command(ansible_module, ipaadmin_principal, ipaadmin_password, - command, to_text(suffix), args) + with ansible_module.ipa_connect(): + # Execute command + ansible_module.ipa_command(["topologysuffix_verify", suffix, + {}]) # Done From fc218878686e7cc52e8401ccc8a03ab2e06a22f2 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 26 Aug 2021 20:45:38 +0200 Subject: [PATCH 08/35] vault: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipavault.py | 102 +++++++++++++----------------------- 1 file changed, 37 insertions(+), 65 deletions(-) diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py index 5b8a5484..7af6c353 100644 --- a/plugins/modules/ipavault.py +++ b/plugins/modules/ipavault.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipavault short description: Manage vaults and secret vaults. description: Manage vaults and secret vaults. KRA service must be enabled. +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The vault name required: true @@ -317,12 +313,9 @@ vault: import os from base64 import b64decode -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, \ - gen_add_del_lists, compare_args_ipa, module_params_get, exit_raw_json, \ - ipalib_errors +from ansible.module_utils.ansible_freeipa_module import IPAAnsibleModule, \ + gen_add_del_lists, compare_args_ipa, exit_raw_json, ipalib_errors def find_vault(module, name, username, service, shared): @@ -338,7 +331,7 @@ def find_vault(module, name, username, service, shared): else: _args['shared'] = shared - _result = api_command(module, "vault_find", name, _args) + _result = module.ipa_command("vault_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -579,7 +572,7 @@ def get_stored_data(module, res_find, args): # retrieve vault stored data try: - result = api_command(module, 'vault_retrieve', name, pwdargs) + result = module.ipa_command('vault_retrieve', name, pwdargs) except ipalib_errors.NotFound: return None @@ -587,12 +580,9 @@ def get_stored_data(module, res_find, args): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # generalgroups - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), @@ -663,45 +653,40 @@ def main(): ansible_module._ansible_debug = True # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - description = module_params_get(ansible_module, "description") + description = ansible_module.params_get("description") - username = module_params_get(ansible_module, "username") - service = module_params_get(ansible_module, "service") - shared = module_params_get(ansible_module, "shared") + username = ansible_module.params_get("username") + service = ansible_module.params_get("service") + shared = ansible_module.params_get("shared") - users = module_params_get(ansible_module, "users") - groups = module_params_get(ansible_module, "groups") - services = module_params_get(ansible_module, "services") - owners = module_params_get(ansible_module, "owners") - ownergroups = module_params_get(ansible_module, "ownergroups") - ownerservices = module_params_get(ansible_module, "ownerservices") + users = ansible_module.params_get("users") + groups = ansible_module.params_get("groups") + services = ansible_module.params_get("services") + owners = ansible_module.params_get("owners") + ownergroups = ansible_module.params_get("ownergroups") + ownerservices = ansible_module.params_get("ownerservices") - vault_type = module_params_get(ansible_module, "vault_type") - salt = module_params_get(ansible_module, "vault_salt") - password = module_params_get(ansible_module, "vault_password") - password_file = module_params_get(ansible_module, "vault_password_file") - new_password = module_params_get(ansible_module, "new_password") - new_password_file = module_params_get(ansible_module, "new_password_file") - public_key = module_params_get(ansible_module, "vault_public_key") - public_key_file = module_params_get(ansible_module, - "vault_public_key_file") - private_key = module_params_get(ansible_module, "vault_private_key") - private_key_file = module_params_get(ansible_module, - "vault_private_key_file") + vault_type = ansible_module.params_get("vault_type") + salt = ansible_module.params_get("vault_salt") + password = ansible_module.params_get("vault_password") + password_file = ansible_module.params_get("vault_password_file") + new_password = ansible_module.params_get("new_password") + new_password_file = ansible_module.params_get("new_password_file") + public_key = ansible_module.params_get("vault_public_key") + public_key_file = ansible_module.params_get("vault_public_key_file") + private_key = ansible_module.params_get("vault_private_key") + private_key_file = ansible_module.params_get("vault_private_key_file") - vault_data = module_params_get(ansible_module, "vault_data") + vault_data = ansible_module.params_get("vault_data") - datafile_in = module_params_get(ansible_module, "datafile_in") - datafile_out = module_params_get(ansible_module, "datafile_out") + datafile_in = ansible_module.params_get("datafile_in") + datafile_out = ansible_module.params_get("datafile_out") - action = module_params_get(ansible_module, "action") - state = module_params_get(ansible_module, "state") + action = ansible_module.params_get("action") + state = ansible_module.params_get("state") # Check parameters @@ -732,17 +717,10 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - # Need to set krb5 ccache name, due to context='ansible-freeipa' - if ccache_name is not None: - os.environ["KRB5CCNAME"] = ccache_name - api_connect(context='ansible-freeipa') + with ansible_module.ipa_connect(context='ansible-freeipa') as ccache_name: + if ccache_name is not None: + os.environ["KRB5CCNAME"] = ccache_name commands = [] @@ -970,7 +948,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, name, args) + result = ansible_module.ipa_command(command, name, args) if command == 'vault_archive': changed = 'Archived data into' in result['summary'] @@ -1012,12 +990,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as exception: - ansible_module.fail_json(msg=str(exception)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done # exit_raw_json is a replacement for ansible_module.exit_json that From c634cfd5cdbf612a41d7b129c15c480cda59ad9b Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:06:46 +0200 Subject: [PATCH 09/35] delegation: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipadelegation.py | 60 +++++++++----------------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/plugins/modules/ipadelegation.py b/plugins/modules/ipadelegation.py index a5f54699..846e1277 100644 --- a/plugins/modules/ipadelegation.py +++ b/plugins/modules/ipadelegation.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipadelegation short description: Manage FreeIPA delegations description: Manage FreeIPA delegations and delegation attributes +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The list of delegation name strings. required: true @@ -112,21 +108,14 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get -import six - - -if six.PY3: - unicode = str + IPAAnsibleModule, compare_args_ipa def find_delegation(module, name): """Find if a delegation with the given name already exist.""" try: - _result = api_command(module, "delegation_show", name, {"all": True}) + _result = module.ipa_command("delegation_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if delegation name is not found. return None @@ -148,12 +137,9 @@ def gen_args(permission, attribute, membergroup, group): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["aciname"], default=None, required=True), # present @@ -177,19 +163,16 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - permission = module_params_get(ansible_module, "permission") - attribute = module_params_get(ansible_module, "attribute") - membergroup = module_params_get(ansible_module, "membergroup") - group = module_params_get(ansible_module, "group") - action = module_params_get(ansible_module, "action") + permission = ansible_module.params_get("permission") + attribute = ansible_module.params_get("attribute") + membergroup = ansible_module.params_get("membergroup") + group = ansible_module.params_get("group") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -236,13 +219,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -318,8 +297,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -329,12 +307,6 @@ def main(): ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 97b47cc8bef658f267676b91a6c39ede906c594e Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:07:31 +0200 Subject: [PATCH 10/35] dnsconfig: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipadnsconfig.py | 61 ++++++++++----------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/plugins/modules/ipadnsconfig.py b/plugins/modules/ipadnsconfig.py index 8d705807..843237f0 100644 --- a/plugins/modules/ipadnsconfig.py +++ b/plugins/modules/ipadnsconfig.py @@ -32,14 +32,9 @@ DOCUMENTATION = """ module: ipadnsconfig short description: Manage FreeIPA dnsconfig description: Manage FreeIPA dnsconfig +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false - forwarders: description: The list of global DNS forwarders. required: false @@ -70,6 +65,7 @@ options: EXAMPLES = """ # Ensure global DNS forward configuration, allowing PTR record synchronization. - ipadnsconfig: + ipaadmin_password: SomeADMINpassword forwarders: - ip_address: 8.8.4.4 - ip_address: 2001:4860:4860::8888 @@ -79,6 +75,7 @@ EXAMPLES = """ # Ensure forwarder is absent. - ipadnsconfig: + ipaadmin_password: SomeADMINpassword forwarders: - ip_address: 2001:4860:4860::8888 port: 53 @@ -86,21 +83,20 @@ EXAMPLES = """ # Disable PTR record synchronization. - ipadnsconfig: + ipaadmin_password: SomeADMINpassword allow_sync_ptr: no # Disable global forwarders. - ipadnsconfig: + ipaadmin_password: SomeADMINpassword forward_policy: none """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, \ - api_command_no_name, compare_args_ipa, module_params_get, \ - is_ipv4_addr, is_ipv6_addr +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, is_ipv4_addr, is_ipv6_addr def find_dnsconfig(module): @@ -108,7 +104,7 @@ def find_dnsconfig(module): "all": True, } - _result = api_command_no_name(module, "dnsconfig_show", _args) + _result = module.ipa_command_no_name("dnsconfig_show", _args) if "result" in _result: if _result["result"].get('idnsforwarders', None) is None: @@ -170,12 +166,8 @@ def main(): port=dict(type=int, required=False, default=None) ) - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( - # general - ipaadmin_principal=dict(type='str', default='admin'), - ipaadmin_password=dict(type='str', no_log=True), - # dnsconfig forwarders=dict(type='list', default=None, required=False, options=dict(**forwarder_spec)), @@ -192,17 +184,12 @@ def main(): ansible_module._ansible_debug = True - # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, - "ipaadmin_password") + # dnsconfig + forwarders = ansible_module.params_get('forwarders') or [] + forward_policy = ansible_module.params_get('forward_policy') + allow_sync_ptr = ansible_module.params_get('allow_sync_ptr') - forwarders = module_params_get(ansible_module, 'forwarders') or [] - forward_policy = module_params_get(ansible_module, 'forward_policy') - allow_sync_ptr = module_params_get(ansible_module, 'allow_sync_ptr') - - state = module_params_get(ansible_module, 'state') + state = ansible_module.params_get('state') # Check parameters. invalid = [] @@ -218,13 +205,9 @@ def main(): # Init changed = False - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): res_find = find_dnsconfig(ansible_module) args = gen_args(ansible_module, state, res_find, forwarders, @@ -234,7 +217,7 @@ def main(): if not compare_args_ipa(ansible_module, args, res_find): try: if not ansible_module.check_mode: - api_command_no_name(ansible_module, 'dnsconfig_mod', args) + ansible_module.ipa_command_no_name('dnsconfig_mod', args) # If command did not fail, something changed. changed = True @@ -242,12 +225,6 @@ def main(): msg = str(e) ansible_module.fail_json(msg="dnsconfig_mod: %s" % msg) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed) From a8045551930b752abd22a1d7ef156b53bb20f9f7 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:07:49 +0200 Subject: [PATCH 11/35] forwardzone: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipadnsforwardzone.py | 65 +++++++++------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py index 1c2a22e5..f6d4a24d 100644 --- a/plugins/modules/ipadnsforwardzone.py +++ b/plugins/modules/ipadnsforwardzone.py @@ -34,13 +34,9 @@ author: chris procter short_description: Manage FreeIPA DNS Forwarder Zones description: - Add and delete an IPA DNS Forwarder Zones using IPA API +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: - The DNS zone name which needs to be managed. @@ -85,7 +81,7 @@ options: EXAMPLES = ''' # Ensure dns zone is present - ipadnsforwardzone: - ipaadmin_password: MyPassword123 + ipaadmin_password: SomeADMINpassword state: present name: example.com forwarders: @@ -96,7 +92,7 @@ EXAMPLES = ''' # Ensure dns zone is present, with forwarder on non-default port - ipadnsforwardzone: - ipaadmin_password: MyPassword123 + ipaadmin_password: SomeADMINpassword state: present name: example.com forwarders: @@ -107,7 +103,7 @@ EXAMPLES = ''' # Ensure that dns zone is removed - ipadnsforwardzone: - ipaadmin_password: MyPassword123 + ipaadmin_password: SomeADMINpassword name: example.com state: absent ''' @@ -116,11 +112,9 @@ RETURN = ''' ''' -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - module_params_get +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa def find_dnsforwardzone(module, name): @@ -128,7 +122,7 @@ def find_dnsforwardzone(module, name): "all": True, "idnsname": name } - _result = api_command(module, "dnsforwardzone_find", name, _args) + _result = module.ipa_command("dnsforwardzone_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -167,11 +161,9 @@ def forwarder_list(forwarders): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), name=dict(type="list", aliases=["cn"], default=None, required=True), forwarders=dict(type="list", default=None, required=False, @@ -199,19 +191,14 @@ def main(): ansible_module._ansible_debug = True # Get parameters - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, - "ipaadmin_password") - names = module_params_get(ansible_module, "name") - action = module_params_get(ansible_module, "action") + names = ansible_module.params_get("name") + action = ansible_module.params_get("action") forwarders = forwarder_list( - module_params_get(ansible_module, "forwarders")) - forwardpolicy = module_params_get(ansible_module, "forwardpolicy") - skip_overlap_check = module_params_get(ansible_module, - "skip_overlap_check") - permission = module_params_get(ansible_module, "permission") - state = module_params_get(ansible_module, "state") + ansible_module.params_get("forwarders")) + forwardpolicy = ansible_module.params_get("forwardpolicy") + skip_overlap_check = ansible_module.params_get("skip_overlap_check") + permission = ansible_module.params_get("permission") + state = ansible_module.params_get("state") if state == 'present' and len(names) != 1: ansible_module.fail_json( @@ -257,21 +244,17 @@ def main(): changed = False exit_args = {} args = {} - ccache_dir = None - ccache_name = None is_enabled = "IGNORE" - try: + + # Connect to IPA API + with ansible_module.ipa_connect(): + # we need to determine 3 variables # args = the values we want to change/set # command = the ipa api command to call del, add, or mod # is_enabled = is the current resource enabled (True) # disabled (False) and do we care (IGNORE) - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() - for name in names: commands = [] command = None @@ -387,15 +370,9 @@ def main(): # Execute commands for _name, command, args in commands: - api_command(ansible_module, command, _name, args) + ansible_module.ipa_command(command, _name, args) changed = True - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, dnsforwardzone=exit_args) From 7318302f88732b244d005e427371f848594b4920 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:10:30 +0200 Subject: [PATCH 12/35] dnsrecord: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipadnsrecord.py | 66 +++++++++------------------------ 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/plugins/modules/ipadnsrecord.py b/plugins/modules/ipadnsrecord.py index 1741a7a7..3950b313 100644 --- a/plugins/modules/ipadnsrecord.py +++ b/plugins/modules/ipadnsrecord.py @@ -33,13 +33,9 @@ DOCUMENTATION = """ module: ipadnsrecord short description: Manage FreeIPA DNS records description: Manage FreeIPA DNS records +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false records: description: The list of user dns records dicts required: false @@ -864,11 +860,9 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, module_params_get, \ - is_ipv4_addr, is_ipv6_addr, ipalib_errors +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, is_ipv4_addr, is_ipv6_addr, ipalib_errors import dns.reversename import dns.resolver @@ -1106,12 +1100,9 @@ def configure_module(): uri_weight=dict(type='int', required=False), ) - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", no_log=True), - name=dict(type="list", aliases=["record_name"], default=None, required=False), @@ -1148,8 +1139,8 @@ def find_dnsrecord(module, dnszone, name): } try: - _result = api_command( - module, "dnsrecord_show", to_text(dnszone), _args) + _result = module.ipa_command( + "dnsrecord_show", to_text(dnszone), _args) except ipalib_errors.NotFound: return None @@ -1217,21 +1208,6 @@ def check_parameters(module, state, zone_name, record): (x, state)) -def connect_to_api(module): - """Connect to the IPA API.""" - ipaadmin_principal = module_params_get(module, "ipaadmin_principal") - ipaadmin_password = module_params_get(module, "ipaadmin_password") - - ccache_dir = None - ccache_name = None - if not valid_creds(module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() - - return ccache_dir, ccache_name - - def get_entry_from_module(module, name): """Create an entry dict from attributes in module.""" attrs = [ @@ -1243,9 +1219,9 @@ def get_entry_from_module(module, name): for key_set in [_RECORD_FIELDS, _PART_MAP, attrs]: entry.update({ - key: module_params_get(module, key) + key: module.params_get(key) for key in key_set - if module_params_get(module, key) is not None + if module.params_get(key) is not None }) return entry @@ -1436,10 +1412,10 @@ def main(): """Execute DNS record playbook.""" ansible_module = configure_module() - global_zone_name = module_params_get(ansible_module, "zone_name") - names = module_params_get(ansible_module, "name") - records = module_params_get(ansible_module, "records") - state = module_params_get(ansible_module, "state") + global_zone_name = ansible_module.params_get("zone_name") + names = ansible_module.params_get("name") + records = ansible_module.params_get("records") + state = ansible_module.params_get("state") # Check parameters @@ -1459,11 +1435,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - ccache_dir, ccache_name = connect_to_api(ansible_module) + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] @@ -1501,8 +1475,8 @@ def main(): # Execute commands for name, command, args in commands: try: - result = api_command( - ansible_module, command, to_text(name), args) + result = ansible_module.ipa_command( + command, to_text(name), args) if "completed" in result: if result["completed"] > 0: changed = True @@ -1519,12 +1493,6 @@ def main(): ansible_module.fail_json( msg="%s: %s: %s" % (command, name, error_message)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, host=exit_args) From 31ee4f9b69f941a715fda6654958a18bc6d8b595 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:10:58 +0200 Subject: [PATCH 13/35] group: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipagroup.py | 83 +++++++++++++------------------------ 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/plugins/modules/ipagroup.py b/plugins/modules/ipagroup.py index fa0d8942..c19644c2 100644 --- a/plugins/modules/ipagroup.py +++ b/plugins/modules/ipagroup.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipagroup short description: Manage FreeIPA groups description: Manage FreeIPA groups +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The group name required: false @@ -182,10 +178,8 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - api_check_param, module_params_get, gen_add_del_lists, api_check_command, \ +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \ gen_add_list, gen_intersection_list @@ -195,7 +189,7 @@ def find_group(module, name): "cn": name, } - _result = api_command(module, "group_find", name, _args) + _result = module.ipa_command("group_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -278,12 +272,9 @@ def should_modify_group(module, res_find, args, nonposix, posix, external): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -319,31 +310,24 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get( - ansible_module, - "ipaadmin_principal", - ) - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - description = module_params_get(ansible_module, "description") - gid = module_params_get(ansible_module, "gid") - nonposix = module_params_get(ansible_module, "nonposix") - external = module_params_get(ansible_module, "external") - posix = module_params_get(ansible_module, "posix") - nomembers = module_params_get(ansible_module, "nomembers") - user = module_params_get(ansible_module, "user") - group = module_params_get(ansible_module, "group") - service = module_params_get(ansible_module, "service") - membermanager_user = module_params_get(ansible_module, - "membermanager_user") - membermanager_group = module_params_get(ansible_module, - "membermanager_group") - externalmember = module_params_get(ansible_module, "externalmember") - action = module_params_get(ansible_module, "action") + description = ansible_module.params_get("description") + gid = ansible_module.params_get("gid") + nonposix = ansible_module.params_get("nonposix") + external = ansible_module.params_get("external") + posix = ansible_module.params_get("posix") + nomembers = ansible_module.params_get("nomembers") + user = ansible_module.params_get("user") + group = ansible_module.params_get("group") + service = ansible_module.params_get("service") + membermanager_user = ansible_module.params_get("membermanager_user") + membermanager_group = ansible_module.params_get("membermanager_group") + externalmember = ansible_module.params_get("externalmember") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -378,21 +362,19 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() - has_add_member_service = api_check_param("group_add_member", "service") + # Connect to IPA API + with ansible_module.ipa_connect(): + + has_add_member_service = ansible_module.ipa_command_param_exists( + "group_add_member", "service") if service is not None and not has_add_member_service: ansible_module.fail_json( msg="Managing a service as part of a group is not supported " "by your IPA version") - has_add_membermanager = api_check_command("group_add_member_manager") + has_add_membermanager = ansible_module.ipa_command_exists( + "group_add_member_manager") if ((membermanager_user is not None or membermanager_group is not None) and not has_add_membermanager): ansible_module.fail_json( @@ -684,8 +666,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -706,12 +687,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From f87520d90a9995e46389ad161e706ef09e2a43ea Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:11:12 +0200 Subject: [PATCH 14/35] hbacrule: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipahbacrule.py | 75 ++++++++++++---------------------- 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/plugins/modules/ipahbacrule.py b/plugins/modules/ipahbacrule.py index d81112f6..ea8fd73a 100644 --- a/plugins/modules/ipahbacrule.py +++ b/plugins/modules/ipahbacrule.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipahbacrule short description: Manage FreeIPA HBAC rules description: Manage FreeIPA HBAC rules +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The hbacrule name required: true @@ -156,11 +152,9 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - module_params_get, gen_add_del_lists, gen_add_list, \ - gen_intersection_list, api_get_domain, ensure_fqdn +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \ + gen_intersection_list, ensure_fqdn def find_hbacrule(module, name): @@ -169,7 +163,7 @@ def find_hbacrule(module, name): "cn": name, } - _result = api_command(module, "hbacrule_find", name, _args) + _result = module.ipa_command("hbacrule_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -198,12 +192,9 @@ def gen_args(description, usercategory, hostcategory, servicecategory, def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -236,26 +227,23 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - description = module_params_get(ansible_module, "description") - usercategory = module_params_get(ansible_module, "usercategory") - hostcategory = module_params_get(ansible_module, "hostcategory") - servicecategory = module_params_get(ansible_module, "servicecategory") - nomembers = module_params_get(ansible_module, "nomembers") - host = module_params_get(ansible_module, "host") - hostgroup = module_params_get(ansible_module, "hostgroup") - hbacsvc = module_params_get(ansible_module, "hbacsvc") - hbacsvcgroup = module_params_get(ansible_module, "hbacsvcgroup") - user = module_params_get(ansible_module, "user") - group = module_params_get(ansible_module, "group") - action = module_params_get(ansible_module, "action") + description = ansible_module.params_get("description") + usercategory = ansible_module.params_get("usercategory") + hostcategory = ansible_module.params_get("hostcategory") + servicecategory = ansible_module.params_get("servicecategory") + nomembers = ansible_module.params_get("nomembers") + host = ansible_module.params_get("host") + hostgroup = ansible_module.params_get("hostgroup") + hbacsvc = ansible_module.params_get("hbacsvc") + hbacsvcgroup = ansible_module.params_get("hbacsvcgroup") + user = ansible_module.params_get("user") + group = ansible_module.params_get("group") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -318,16 +306,12 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): # Get default domain - default_domain = api_get_domain() + default_domain = ansible_module.ipa_get_domain() # Ensure fqdn host names, use default domain for simple names if host is not None: @@ -620,8 +604,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -642,12 +625,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 7d2bdd7138389a46d3b96c21fe1d5e41205deea1 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 14:59:39 +0200 Subject: [PATCH 15/35] hbacsvc: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipahbacsvc.py | 51 +++++++++++------------------------ 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/plugins/modules/ipahbacsvc.py b/plugins/modules/ipahbacsvc.py index 26e6a957..c6e31d47 100644 --- a/plugins/modules/ipahbacsvc.py +++ b/plugins/modules/ipahbacsvc.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipahbacsvc short description: Manage FreeIPA HBAC Services description: Manage FreeIPA HBAC Services +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The group name required: false @@ -70,19 +66,17 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa def find_hbacsvc(module, name): _args = { "all": True, - "cn": to_text(name), + "cn": name, } - _result = api_command(module, "hbacsvc_find", to_text(name), _args) + _result = module.ipa_command("hbacsvc_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -96,18 +90,15 @@ def find_hbacsvc(module, name): def gen_args(description): _args = {} if description is not None: - _args["description"] = to_text(description) + _args["description"] = description return _args def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn", "service"], default=None, required=True), # present @@ -126,15 +117,13 @@ def main(): # Get parameters # general - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - names = ansible_module.params.get("name") + names = ansible_module.params_get("name") # present - description = ansible_module.params.get("description") + description = ansible_module.params_get("description") # state - state = ansible_module.params.get("state") + state = ansible_module.params_get("state") # Check parameters @@ -158,13 +147,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] @@ -203,18 +188,12 @@ def main(): for name, command, args in commands: try: - api_command(ansible_module, command, to_text(name), args) + ansible_module.ipa_command(command, name, args) changed = True except Exception as e: ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 9d4572be635ddbc0737e0510d822ea73725947c3 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 15:00:04 +0200 Subject: [PATCH 16/35] hbacsvcgroup: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipahbacsvcgroup.py | 75 +++++++++--------------------- 1 file changed, 23 insertions(+), 52 deletions(-) diff --git a/plugins/modules/ipahbacsvcgroup.py b/plugins/modules/ipahbacsvcgroup.py index 141fa539..5e301d6a 100644 --- a/plugins/modules/ipahbacsvcgroup.py +++ b/plugins/modules/ipahbacsvcgroup.py @@ -32,13 +32,9 @@ DOCUMENTATION = """ module: ipahbacsvcgroup short description: Manage FreeIPA hbacsvcgroups description: Manage FreeIPA hbacsvcgroups +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The hbacsvcgroup name required: false @@ -101,20 +97,17 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - gen_add_del_lists +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists def find_hbacsvcgroup(module, name): _args = { "all": True, - "cn": to_text(name), + "cn": name, } - _result = api_command(module, "hbacsvcgroup_find", to_text(name), _args) + _result = module.ipa_command("hbacsvcgroup_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -128,7 +121,7 @@ def find_hbacsvcgroup(module, name): def gen_args(description, nomembers): _args = {} if description is not None: - _args["description"] = to_text(description) + _args["description"] = description if nomembers is not None: _args["nomembers"] = nomembers @@ -138,18 +131,15 @@ def gen_args(description, nomembers): def gen_member_args(hbacsvc): _args = {} if hbacsvc is not None: - _args["member_hbacsvc"] = [to_text(svc) for svc in hbacsvc] + _args["member_hbacsvc"] = hbacsvc return _args def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -170,17 +160,15 @@ def main(): # Get parameters # general - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - names = ansible_module.params.get("name") + names = ansible_module.params_get("name") # present - description = ansible_module.params.get("description") - nomembers = ansible_module.params.get("nomembers") - hbacsvc = ansible_module.params.get("hbacsvc") - action = ansible_module.params.get("action") + description = ansible_module.params_get("description") + nomembers = ansible_module.params_get("nomembers") + hbacsvc = ansible_module.params_get("hbacsvc") + action = ansible_module.params_get("action") # state - state = ansible_module.params.get("state") + state = ansible_module.params_get("state") # Check parameters @@ -213,13 +201,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] @@ -257,18 +241,14 @@ def main(): if len(hbacsvc_add) > 0: commands.append([name, "hbacsvcgroup_add_member", { - "hbacsvc": - [to_text(svc) - for svc in hbacsvc_add], + "hbacsvc": hbacsvc_add }]) # Remove members if len(hbacsvc_del) > 0: commands.append([name, "hbacsvcgroup_remove_member", { - "hbacsvc": - [to_text(svc) - for svc in hbacsvc_del], + "hbacsvc": hbacsvc_del }]) elif action == "member": if res_find is None: @@ -278,8 +258,7 @@ def main(): # Ensure members are present commands.append([name, "hbacsvcgroup_add_member", { - "hbacsvc": [to_text(svc) - for svc in hbacsvc], + "hbacsvc": hbacsvc }]) elif state == "absent": if action == "hbacsvcgroup": @@ -294,8 +273,7 @@ def main(): # Ensure members are absent commands.append([name, "hbacsvcgroup_remove_member", { - "hbacsvc": [to_text(svc) - for svc in hbacsvc], + "hbacsvc": hbacsvc }]) else: ansible_module.fail_json(msg="Unkown state '%s'" % state) @@ -308,8 +286,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, to_text(name), - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -332,12 +309,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 91d8f5a6eefb1601eaed27b079ab648a58289975 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 15:00:28 +0200 Subject: [PATCH 17/35] hostgroup: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipahostgroup.py | 72 +++++++++++---------------------- 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/plugins/modules/ipahostgroup.py b/plugins/modules/ipahostgroup.py index b64f53e5..b0f7857b 100644 --- a/plugins/modules/ipahostgroup.py +++ b/plugins/modules/ipahostgroup.py @@ -32,13 +32,9 @@ DOCUMENTATION = """ module: ipahostgroup short description: Manage FreeIPA hostgroups description: Manage FreeIPA hostgroups +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The hostgroup name required: false @@ -138,11 +134,9 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - module_params_get, gen_add_del_lists, api_check_command, api_check_param, \ - gen_add_list, gen_intersection_list +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \ + gen_intersection_list def find_hostgroup(module, name): @@ -151,7 +145,7 @@ def find_hostgroup(module, name): "cn": name, } - _result = api_command(module, "hostgroup_find", name, _args) + _result = module.ipa_command("hostgroup_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -185,12 +179,9 @@ def gen_member_args(host, hostgroup): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -217,25 +208,19 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, - "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - description = module_params_get(ansible_module, "description") - nomembers = module_params_get(ansible_module, "nomembers") - host = module_params_get(ansible_module, "host") - hostgroup = module_params_get(ansible_module, "hostgroup") - membermanager_user = module_params_get(ansible_module, - "membermanager_user") - membermanager_group = module_params_get(ansible_module, - "membermanager_group") - rename = module_params_get(ansible_module, "rename") - action = module_params_get(ansible_module, "action") + description = ansible_module.params_get("description") + nomembers = ansible_module.params_get("nomembers") + host = ansible_module.params_get("host") + hostgroup = ansible_module.params_get("hostgroup") + membermanager_user = ansible_module.params_get("membermanager_user") + membermanager_group = ansible_module.params_get("membermanager_group") + rename = ansible_module.params_get("rename") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -287,15 +272,11 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() - has_add_membermanager = api_check_command( + # Connect to IPA API + with ansible_module.ipa_connect(): + + has_add_membermanager = ansible_module.ipa_command_exists( "hostgroup_add_member_manager") if ((membermanager_user is not None or membermanager_group is not None) and not has_add_membermanager): @@ -303,7 +284,8 @@ def main(): msg="Managing a membermanager user or group is not supported " "by your IPA version" ) - has_mod_rename = api_check_param("hostgroup_mod", "rename") + has_mod_rename = ansible_module.ipa_command_param_exists( + "hostgroup_mod", "rename") if not has_mod_rename and rename is not None: ansible_module.fail_json( msg="Renaming hostgroups is not supported by your IPA version") @@ -515,7 +497,7 @@ def main(): # Execute commands for name, command, args in commands: try: - result = api_command(ansible_module, command, name, args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -537,12 +519,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From ce00f32d1f1ce436bddff53fc0ae07f79c882f08 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:44:16 +0200 Subject: [PATCH 18/35] permission: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipapermission.py | 82 +++++++++++--------------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/plugins/modules/ipapermission.py b/plugins/modules/ipapermission.py index 3b2635c8..5921675f 100644 --- a/plugins/modules/ipapermission.py +++ b/plugins/modules/ipapermission.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipapermission short description: Manage FreeIPA permission description: Manage FreeIPA permission and permission members +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The permission name string. required: true @@ -133,20 +129,14 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get, api_check_ipa_version -import six - -if six.PY3: - unicode = str + IPAAnsibleModule, compare_args_ipa def find_permission(module, name): """Find if a permission with the given name already exist.""" try: - _result = api_command(module, "permission_show", name, {"all": True}) + _result = module.ipa_command("permission_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if permission name is not found. return None @@ -191,12 +181,9 @@ def gen_args(right, attrs, bindtype, subtree, def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -243,31 +230,27 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - right = module_params_get(ansible_module, "right") - attrs = module_params_get(ansible_module, "attrs") - bindtype = module_params_get(ansible_module, "bindtype") - subtree = module_params_get(ansible_module, "subtree") - extra_target_filter = module_params_get(ansible_module, - "extra_target_filter") - rawfilter = module_params_get(ansible_module, "rawfilter") - target = module_params_get(ansible_module, "target") - targetto = module_params_get(ansible_module, "targetto") - targetfrom = module_params_get(ansible_module, "targetfrom") - memberof = module_params_get(ansible_module, "memberof") - targetgroup = module_params_get(ansible_module, "targetgroup") - object_type = module_params_get(ansible_module, "object_type") - no_members = module_params_get(ansible_module, "no_members") - rename = module_params_get(ansible_module, "rename") - action = module_params_get(ansible_module, "action") + right = ansible_module.params_get("right") + attrs = ansible_module.params_get("attrs") + bindtype = ansible_module.params_get("bindtype") + subtree = ansible_module.params_get("subtree") + extra_target_filter = ansible_module.params_get("extra_target_filter") + rawfilter = ansible_module.params_get("rawfilter") + target = ansible_module.params_get("target") + targetto = ansible_module.params_get("targetto") + targetfrom = ansible_module.params_get("targetfrom") + memberof = ansible_module.params_get("memberof") + targetgroup = ansible_module.params_get("targetgroup") + object_type = ansible_module.params_get("object_type") + no_members = ansible_module.params_get("no_members") + rename = ansible_module.params_get("rename") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -311,7 +294,7 @@ def main(): msg="Argument '%s' can not be used with action " "'%s' and state '%s'" % (x, action, state)) - if bindtype == "self" and api_check_ipa_version("<", "4.8.7"): + if bindtype == "self" and ansible_module.ipa_check_version("<", "4.8.7"): ansible_module.fail_json( msg="Bindtype 'self' is not supported by your IPA version.") @@ -324,13 +307,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -454,8 +433,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -480,12 +458,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From ba72bd02a9d3f841fc8debc6e21c27a2bb438b19 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:44:40 +0200 Subject: [PATCH 19/35] privilege: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipaprivilege.py | 53 ++++++++++----------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/plugins/modules/ipaprivilege.py b/plugins/modules/ipaprivilege.py index 66af01e5..3256eba9 100644 --- a/plugins/modules/ipaprivilege.py +++ b/plugins/modules/ipaprivilege.py @@ -34,13 +34,9 @@ DOCUMENTATION = """ module: ipaprivilege short description: Manage FreeIPA privilege description: Manage FreeIPA privilege and privilege members +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The list of privilege name strings. required: true @@ -111,10 +107,8 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get, gen_add_del_lists + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists import six if six.PY3: @@ -124,7 +118,7 @@ if six.PY3: def find_privilege(module, name): """Find if a privilege with the given name already exist.""" try: - _result = api_command(module, "privilege_show", name, {"all": True}) + _result = module.ipa_command("privilege_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if privilege name is not found. return None @@ -133,12 +127,9 @@ def find_privilege(module, name): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -160,19 +151,16 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - description = module_params_get(ansible_module, "description") - permission = module_params_get(ansible_module, "permission") - rename = module_params_get(ansible_module, "rename") - action = module_params_get(ansible_module, "action") + description = ansible_module.params_get("description") + permission = ansible_module.params_get("permission") + rename = ansible_module.params_get("rename") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters invalid = [] @@ -211,13 +199,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -328,8 +312,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -354,12 +337,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 954ae98200a5836e2a012188a30d1ee4caeb4f7a Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:45:04 +0200 Subject: [PATCH 20/35] role: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/iparole.py | 82 ++++++++++++-------------------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/plugins/modules/iparole.py b/plugins/modules/iparole.py index 22adb867..3fafd37a 100644 --- a/plugins/modules/iparole.py +++ b/plugins/modules/iparole.py @@ -33,13 +33,9 @@ DOCUMENTATION = """ module: iparole short description: Manage FreeIPA role description: Manage FreeIPA role +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false role: description: The list of role name strings. required: true @@ -101,11 +97,9 @@ EXAMPLES = """ # pylint: disable=wrong-import-position # pylint: disable=import-error # pylint: disable=no-name-in-module -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - gen_add_del_lists, compare_args_ipa, module_params_get, api_get_realm + IPAAnsibleModule, gen_add_del_lists, compare_args_ipa import six @@ -116,7 +110,7 @@ if six.PY3: def find_role(module, name): """Find if a role with the given name already exist.""" try: - _result = api_command(module, "role_show", name, {"all": True}) + _result = module.ipa_command("role_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if role name is not found. return None @@ -133,7 +127,7 @@ def gen_args(module): args = {} for param, arg in arg_map.items(): - value = module_params_get(module, param) + value = module.params_get(param) if value is not None: args[arg] = value @@ -142,8 +136,8 @@ def gen_args(module): def check_parameters(module): """Check if parameters passed for module processing are valid.""" - action = module_params_get(module, "action") - state = module_params_get(module, "state") + action = module.params_get("action") + state = module.params_get("state") invalid = [] @@ -157,30 +151,15 @@ def check_parameters(module): invalid.extend(['privilege']) for arg in invalid: - if module_params_get(module, arg) is not None: + if module.params_get(arg) is not None: module.fail_json( msg="Argument '%s' can not be used with action '%s'" % (arg, state)) -def verify_credentials(module): - """Ensure there are valid Kerberos credentials.""" - ccache_dir = None - ccache_name = None - - ipaadmin_principal = module_params_get(module, "ipaadmin_principal") - ipaadmin_password = module_params_get(module, "ipaadmin_password") - - if not valid_creds(module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - - return (ccache_dir, ccache_name) - - def member_intersect(module, attr, memberof, res_find): """Filter member arguments from role found by intersection.""" - params = module_params_get(module, attr) + params = module.params_get(attr) if not res_find: return params filtered = [] @@ -192,7 +171,7 @@ def member_intersect(module, attr, memberof, res_find): def member_difference(module, attr, memberof, res_find): """Filter member arguments from role found by difference.""" - params = module_params_get(module, attr) + params = module.params_get(attr) if not res_find: return params filtered = [] @@ -247,7 +226,7 @@ def filter_service(module, res_find, predicate): modified service to be compared to. """ _services = [] - service = module_params_get(module, 'service') + service = module.params_get('service') if service: existing = [to_text(x) for x in res_find.get('member_service', [])] for svc in service: @@ -261,7 +240,7 @@ def ensure_role_with_members_is_present(module, name, res_find, action): """Define commands to ensure member are present for action `role`.""" commands = [] privilege_add, privilege_del = gen_add_del_lists( - module_params_get(module, "privilege"), + module.params_get("privilege"), res_find.get('memberof_privilege', [])) if privilege_add: @@ -276,7 +255,7 @@ def ensure_role_with_members_is_present(module, name, res_find, action): for key in ["user", "group", "host", "hostgroup"]: add_list, del_list = gen_add_del_lists( - module_params_get(module, key), + module.params_get(key), res_find.get('member_%s' % key, []) ) if add_list: @@ -285,8 +264,10 @@ def ensure_role_with_members_is_present(module, name, res_find, action): del_members[key] = [to_text(item) for item in del_list] service = [ - to_text(svc) if '@' in svc else ('%s@%s' % (svc, api_get_realm())) - for svc in (module_params_get(module, 'service') or []) + to_text(svc) + if '@' in svc + else ('%s@%s' % (svc, module.ipa_get_realm())) + for svc in (module.params_get('service') or []) ] existing = [str(svc) for svc in res_find.get('member_service', [])] add_list, del_list = gen_add_del_lists(service, existing) @@ -363,7 +344,7 @@ def process_commands(module, commands): for name, command, args in commands: try: - result = api_command(module, command, name, args) + result = module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -385,7 +366,7 @@ def role_commands_for_name(module, state, action, name): """Define commands for the Role module.""" commands = [] - rename = module_params_get(module, "rename") + rename = module.params_get("rename") res_find = find_role(module, name) @@ -420,12 +401,9 @@ def role_commands_for_name(module, state, action, name): def create_module(): """Create module description.""" - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # generalgroups - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -462,15 +440,13 @@ def main(): check_parameters(ansible_module) # Init - ccache_dir = None - ccache_name = None - try: - ccache_dir, ccache_name = verify_credentials(ansible_module) - api_connect() - state = module_params_get(ansible_module, "state") - action = module_params_get(ansible_module, "action") - names = module_params_get(ansible_module, "name") + # Connect to IPA API + with ansible_module.ipa_connect(): + + state = ansible_module.params_get("state") + action = ansible_module.params_get("action") + names = ansible_module.params_get("name") commands = [] for name in names: @@ -479,12 +455,6 @@ def main(): changed, exit_args = process_commands(ansible_module, commands) - except Exception as exception: # pylint: disable=broad-except - ansible_module.fail_json(msg=str(exception)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 325ffb7bd8ded80a34a3d1dbb133fae60f34b401 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:45:18 +0200 Subject: [PATCH 21/35] selfservice: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipaselfservice.py | 56 ++++++++----------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/plugins/modules/ipaselfservice.py b/plugins/modules/ipaselfservice.py index 801a8f54..81b4461c 100644 --- a/plugins/modules/ipaselfservice.py +++ b/plugins/modules/ipaselfservice.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipaselfservice short description: Manage FreeIPA selfservices description: Manage FreeIPA selfservices and selfservice attributes +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The list of selfservice name strings. required: true @@ -103,21 +99,14 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - compare_args_ipa, module_params_get -import six - - -if six.PY3: - unicode = str + IPAAnsibleModule, compare_args_ipa def find_selfservice(module, name): """Find if a selfservice with the given name already exist.""" try: - _result = api_command(module, "selfservice_show", name, {"all": True}) + _result = module.ipa_command("selfservice_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if selfservice name is not found. return None @@ -135,12 +124,9 @@ def gen_args(permission, attribute): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["aciname"], default=None, required=True), # present @@ -162,17 +148,14 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - permission = module_params_get(ansible_module, "permission") - attribute = module_params_get(ansible_module, "attribute") - action = module_params_get(ansible_module, "action") + permission = ansible_module.params_get("permission") + attribute = ansible_module.params_get("attribute") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -219,13 +202,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -301,8 +280,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -312,12 +290,6 @@ def main(): ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From ab6097d1d8d629db8634a6329f1407ac3c6e3136 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:46:03 +0200 Subject: [PATCH 22/35] server: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipaserver.py | 80 ++++++++++++------------------------ 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/plugins/modules/ipaserver.py b/plugins/modules/ipaserver.py index 167394e3..c1bfe119 100644 --- a/plugins/modules/ipaserver.py +++ b/plugins/modules/ipaserver.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipaserver short description: Manage FreeIPA server description: Manage FreeIPA server +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal. - default: admin - ipaadmin_password: - description: The admin password. - required: false name: description: The list of server name strings. required: true @@ -184,20 +180,14 @@ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.ansible_freeipa_module import \ - temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \ - api_command_no_name, compare_args_ipa, module_params_get, DNSName -import six - -if six.PY3: - unicode = str + IPAAnsibleModule, compare_args_ipa, DNSName def find_server(module, name): """Find if a server with the given name already exist.""" try: - _result = api_command(module, "server_show", name, {"all": True}) + _result = module.ipa_command("server_show", name, {"all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if server name is not found. return None @@ -208,12 +198,12 @@ def find_server(module, name): def server_role_status(module, name): """Get server role of a hidden server with the given name.""" try: - _result = api_command_no_name(module, "server_role_find", - {"server_server": name, - "role_servrole": 'IPA master', - "include_master": True, - "raw": True, - "all": True}) + _result = module.ipa_command_no_name("server_role_find", + {"server_server": name, + "role_servrole": 'IPA master', + "include_master": True, + "raw": True, + "all": True}) except Exception: # pylint: disable=broad-except # An exception is raised if server name is not found. return None @@ -246,12 +236,9 @@ def gen_args(location, service_weight, no_members, delete_continue, def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -282,14 +269,11 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present - location = module_params_get(ansible_module, "location") - service_weight = module_params_get(ansible_module, "service_weight") + location = ansible_module.params_get("location") + service_weight = ansible_module.params_get("service_weight") # Service weight smaller than 0 leads to resetting service weight if service_weight is not None and \ (service_weight < -1 or service_weight > 65535): @@ -298,19 +282,18 @@ def main(): service_weight) if service_weight == -1: service_weight = "" - hidden = module_params_get(ansible_module, "hidden") - no_members = module_params_get(ansible_module, "no_members") + hidden = ansible_module.params_get("hidden") + no_members = ansible_module.params_get("no_members") # absent - delete_continue = module_params_get(ansible_module, "delete_continue") - ignore_topology_disconnect = module_params_get( - ansible_module, "ignore_topology_disconnect") - ignore_last_of_role = module_params_get(ansible_module, - "ignore_last_of_role") - force = module_params_get(ansible_module, "force") + delete_continue = ansible_module.params_get("delete_continue") + ignore_topology_disconnect = ansible_module.params_get( + "ignore_topology_disconnect") + ignore_last_of_role = ansible_module.params_get("ignore_last_of_role") + force = ansible_module.params_get("force") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -338,13 +321,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] for name in names: @@ -414,8 +393,7 @@ def main(): for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -425,12 +403,6 @@ def main(): ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 748f05d3c2818b161b3a5e90031187e4c04a31a2 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:46:18 +0200 Subject: [PATCH 23/35] service: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipaservice.py | 112 ++++++++++++++-------------------- 1 file changed, 45 insertions(+), 67 deletions(-) diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py index 50081be5..b72e0915 100644 --- a/plugins/modules/ipaservice.py +++ b/plugins/modules/ipaservice.py @@ -32,13 +32,9 @@ DOCUMENTATION = """ module: ipaservice short description: Manage FreeIPA service description: Manage FreeIPA service +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The service to manage required: true @@ -226,11 +222,9 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - encode_certificate, gen_add_del_lists, module_params_get, to_text, \ - api_check_param, ipalib_errors +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, encode_certificate, \ + gen_add_del_lists, ipalib_errors def find_service(module, name): @@ -239,7 +233,7 @@ def find_service(module, name): } try: - _result = api_command(module, "service_show", to_text(name), _args) + _result = module.ipa_command("service_show", name, _args) except ipalib_errors.NotFound: return None @@ -349,12 +343,9 @@ def check_parameters(module, state, action, names, parameters): def init_ansible_module(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["service"], default=None, required=True), # service attributesstr @@ -424,51 +415,48 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # service attributes - principal = module_params_get(ansible_module, "principal") - certificate = module_params_get(ansible_module, "certificate") - pac_type = module_params_get(ansible_module, "pac_type") - auth_ind = module_params_get(ansible_module, "auth_ind") - skip_host_check = module_params_get(ansible_module, "skip_host_check") - force = module_params_get(ansible_module, "force") - requires_pre_auth = module_params_get(ansible_module, "requires_pre_auth") - ok_as_delegate = module_params_get(ansible_module, "ok_as_delegate") - ok_to_auth_as_delegate = module_params_get(ansible_module, - "ok_to_auth_as_delegate") + principal = ansible_module.params_get("principal") + certificate = ansible_module.params_get("certificate") + pac_type = ansible_module.params_get("pac_type") + auth_ind = ansible_module.params_get("auth_ind") + skip_host_check = ansible_module.params_get("skip_host_check") + force = ansible_module.params_get("force") + requires_pre_auth = ansible_module.params_get("requires_pre_auth") + ok_as_delegate = ansible_module.params_get("ok_as_delegate") + ok_to_auth_as_delegate = ansible_module.params_get( + "ok_to_auth_as_delegate") - smb = module_params_get(ansible_module, "smb") - netbiosname = module_params_get(ansible_module, "netbiosname") + smb = ansible_module.params_get("smb") + netbiosname = ansible_module.params_get("netbiosname") - host = module_params_get(ansible_module, "host") + host = ansible_module.params_get("host") - allow_create_keytab_user = module_params_get( - ansible_module, "allow_create_keytab_user") - allow_create_keytab_group = module_params_get( - ansible_module, "allow_create_keytab_group") - allow_create_keytab_host = module_params_get( - ansible_module, "allow_create_keytab_host") - allow_create_keytab_hostgroup = module_params_get( - ansible_module, "allow_create_keytab_hostgroup") + allow_create_keytab_user = ansible_module.params_get( + "allow_create_keytab_user") + allow_create_keytab_group = ansible_module.params_get( + "allow_create_keytab_group") + allow_create_keytab_host = ansible_module.params_get( + "allow_create_keytab_host") + allow_create_keytab_hostgroup = ansible_module.params_get( + "allow_create_keytab_hostgroup") - allow_retrieve_keytab_user = module_params_get( - ansible_module, "allow_retrieve_keytab_user") - allow_retrieve_keytab_group = module_params_get( - ansible_module, "allow_retrieve_keytab_group") - allow_retrieve_keytab_host = module_params_get( - ansible_module, "allow_retrieve_keytab_host") - allow_retrieve_keytab_hostgroup = module_params_get( - ansible_module, "allow_retrieve_keytab_hostgroup") - delete_continue = module_params_get(ansible_module, "delete_continue") + allow_retrieve_keytab_user = ansible_module.params_get( + "allow_retrieve_keytab_user") + allow_retrieve_keytab_group = ansible_module.params_get( + "allow_retrieve_keytab_group") + allow_retrieve_keytab_host = ansible_module.params_get( + "allow_retrieve_keytab_host") + allow_retrieve_keytab_hostgroup = ansible_module.params_get( + "allow_retrieve_keytab_hostgroup") + delete_continue = ansible_module.params_get("delete_continue") # action - action = module_params_get(ansible_module, "action") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # check parameters check_parameters(ansible_module, state, action, names, vars()) @@ -477,15 +465,11 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() - has_skip_host_check = api_check_param( + # Connect to IPA API + with ansible_module.ipa_connect(): + + has_skip_host_check = ansible_module.ipa_command_param_exists( "service_add", "skip_host_check") if skip_host_check and not has_skip_host_check: ansible_module.fail_json( @@ -850,7 +834,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, name, args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: @@ -876,12 +860,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as ex: - ansible_module.fail_json(msg=str(ex)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 09264767de39278f419cb12688366c3bd0dcc968 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:46:35 +0200 Subject: [PATCH 24/35] sudocmdgroup: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipasudocmdgroup.py | 69 +++++++++--------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/plugins/modules/ipasudocmdgroup.py b/plugins/modules/ipasudocmdgroup.py index 3d9e738b..8a77596e 100644 --- a/plugins/modules/ipasudocmdgroup.py +++ b/plugins/modules/ipasudocmdgroup.py @@ -32,13 +32,9 @@ DOCUMENTATION = """ module: ipasudocmdgroup short description: Manage FreeIPA sudocmd groups description: Manage FreeIPA sudocmd groups +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The sudocmodgroup name required: false @@ -103,18 +99,15 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - gen_add_del_lists, ipalib_errors +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors def find_sudocmdgroup(module, name): args = {"all": True} try: - _result = api_command(module, "sudocmdgroup_show", to_text(name), args) + _result = module.ipa_command("sudocmdgroup_show", name, args) except ipalib_errors.NotFound: return None else: @@ -124,7 +117,7 @@ def find_sudocmdgroup(module, name): def gen_args(description, nomembers): _args = {} if description is not None: - _args["description"] = to_text(description) + _args["description"] = description if nomembers is not None: _args["nomembers"] = nomembers @@ -140,12 +133,9 @@ def gen_member_args(sudocmd): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -166,17 +156,15 @@ def main(): # Get parameters # general - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - names = ansible_module.params.get("name") + names = ansible_module.params_get("name") # present - description = ansible_module.params.get("description") - nomembers = ansible_module.params.get("nomembers") - sudocmd = ansible_module.params.get("sudocmd") - action = ansible_module.params.get("action") + description = ansible_module.params_get("description") + nomembers = ansible_module.params_get("nomembers") + sudocmd = ansible_module.params_get("sudocmd") + action = ansible_module.params_get("action") # state - state = ansible_module.params.get("state") + state = ansible_module.params_get("state") # Check parameters @@ -209,13 +197,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] @@ -255,9 +239,7 @@ def main(): if len(sudocmd_add) > 0: commands.append([name, "sudocmdgroup_add_member", { - "sudocmd": [to_text(c) - for c in - sudocmd_add] + "sudocmd": sudocmd_add } ]) # Remove members @@ -265,9 +247,7 @@ def main(): commands.append([name, "sudocmdgroup_remove_member", { - "sudocmd": [to_text(c) - for c in - sudocmd_del] + "sudocmd": sudocmd_del } ]) elif action == "member": @@ -277,7 +257,7 @@ def main(): # Ensure members are present commands.append([name, "sudocmdgroup_add_member", - {"sudocmd": [to_text(c) for c in sudocmd]} + {"sudocmd": sudocmd} ]) elif state == "absent": if action == "sudocmdgroup": @@ -291,7 +271,7 @@ def main(): # Ensure members are absent commands.append([name, "sudocmdgroup_remove_member", - {"sudocmd": [to_text(c) for c in sudocmd]} + {"sudocmd": sudocmd} ]) else: ansible_module.fail_json(msg="Unkown state '%s'" % state) @@ -303,8 +283,7 @@ def main(): # Execute commands for name, command, args in commands: try: - result = api_command(ansible_module, command, to_text(name), - args) + result = ansible_module.ipa_command(command, name, args) if action == "member": if "completed" in result and result["completed"] > 0: changed = True @@ -331,12 +310,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From 7864adffa6cc7a82615f2a9b437d7ac5d7fae5a2 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:46:54 +0200 Subject: [PATCH 25/35] sudocmd: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipasudocmd.py | 52 ++++++++++------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/plugins/modules/ipasudocmd.py b/plugins/modules/ipasudocmd.py index 32a25ab0..1785e78e 100644 --- a/plugins/modules/ipasudocmd.py +++ b/plugins/modules/ipasudocmd.py @@ -32,13 +32,9 @@ DOCUMENTATION = """ module: ipasudocmd short description: Manage FreeIPA sudo command description: Manage FreeIPA sudo command +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The sudo command required: true @@ -71,19 +67,17 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa def find_sudocmd(module, name): _args = { "all": True, - "sudocmd": to_text(name), + "sudocmd": name, } - _result = api_command(module, "sudocmd_find", to_text(name), _args) + _result = module.ipa_command("sudocmd_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -97,18 +91,15 @@ def find_sudocmd(module, name): def gen_args(description): _args = {} if description is not None: - _args["description"] = to_text(description) + _args["description"] = description return _args def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["sudocmd"], default=None, required=True), # present @@ -125,14 +116,12 @@ def main(): # Get parameters # general - ipaadmin_principal = ansible_module.params.get("ipaadmin_principal") - ipaadmin_password = ansible_module.params.get("ipaadmin_password") - names = ansible_module.params.get("name") + names = ansible_module.params_get("name") # present - description = ansible_module.params.get("description") + description = ansible_module.params_get("description") # state - state = ansible_module.params.get("state") + state = ansible_module.params_get("state") # Check parameters if state == "absent": @@ -147,13 +136,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] @@ -189,8 +174,7 @@ def main(): # Execute commands for name, command, args in commands: try: - result = api_command(ansible_module, command, to_text(name), - args) + result = ansible_module.ipa_command(command, name, args) # Check if any changes were made by any command if command == 'sudocmd_del': changed |= "Deleted" in result['summary'] @@ -200,12 +184,6 @@ def main(): ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(e))) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From cb48d4b8bd42c479ff6caa520b65925ca5718737 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:47:10 +0200 Subject: [PATCH 26/35] sudorule: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipasudorule.py | 92 +++++++++++++--------------------- 1 file changed, 34 insertions(+), 58 deletions(-) diff --git a/plugins/modules/ipasudorule.py b/plugins/modules/ipasudorule.py index 89f0f519..ca60d090 100644 --- a/plugins/modules/ipasudorule.py +++ b/plugins/modules/ipasudorule.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipasudorule short description: Manage FreeIPA sudo rules description: Manage FreeIPA sudo rules +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The sudorule name required: true @@ -187,10 +183,9 @@ EXAMPLES = """ RETURN = """ """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - module_params_get, gen_add_del_lists, gen_add_list, gen_intersection_list +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \ + gen_intersection_list def find_sudorule(module, name): @@ -199,7 +194,7 @@ def find_sudorule(module, name): "cn": name, } - _result = api_command(module, "sudorule_find", name, _args) + _result = module.ipa_command("sudorule_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -235,12 +230,9 @@ def gen_args(description, usercat, hostcat, cmdcat, runasusercat, def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["cn"], default=None, required=True), # present @@ -286,42 +278,37 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") + names = ansible_module.params_get("name") # present # The 'noqa' variables are not used here, but required for vars(). # The use of 'noqa' ensures flake8 does not complain about them. - description = module_params_get(ansible_module, "description") # noqa - cmdcategory = module_params_get(ansible_module, 'cmdcategory') # noqa - usercategory = module_params_get(ansible_module, "usercategory") # noqa - hostcategory = module_params_get(ansible_module, "hostcategory") # noqa - runasusercategory = module_params_get(ansible_module, # noqa + description = ansible_module.params_get("description") # noqa + cmdcategory = ansible_module.params_get('cmdcategory') # noqa + usercategory = ansible_module.params_get("usercategory") # noqa + hostcategory = ansible_module.params_get("hostcategory") # noqa + runasusercategory = ansible_module.params_get( # noqa "runasusercategory") - runasgroupcategory = module_params_get(ansible_module, # noqa + runasgroupcategory = ansible_module.params_get( # noqa "runasgroupcategory") - hostcategory = module_params_get(ansible_module, "hostcategory") # noqa - nomembers = module_params_get(ansible_module, "nomembers") # noqa - host = module_params_get(ansible_module, "host") - hostgroup = module_params_get(ansible_module, "hostgroup") - user = module_params_get(ansible_module, "user") - group = module_params_get(ansible_module, "group") - allow_sudocmd = module_params_get(ansible_module, 'allow_sudocmd') - allow_sudocmdgroup = module_params_get(ansible_module, - 'allow_sudocmdgroup') - deny_sudocmd = module_params_get(ansible_module, 'deny_sudocmd') - deny_sudocmdgroup = module_params_get(ansible_module, - 'deny_sudocmdgroup') - sudooption = module_params_get(ansible_module, "sudooption") - order = module_params_get(ansible_module, "order") - runasuser = module_params_get(ansible_module, "runasuser") - runasgroup = module_params_get(ansible_module, "runasgroup") - action = module_params_get(ansible_module, "action") + hostcategory = ansible_module.params_get("hostcategory") # noqa + nomembers = ansible_module.params_get("nomembers") # noqa + host = ansible_module.params_get("host") + hostgroup = ansible_module.params_get("hostgroup") + user = ansible_module.params_get("user") + group = ansible_module.params_get("group") + allow_sudocmd = ansible_module.params_get('allow_sudocmd') + allow_sudocmdgroup = ansible_module.params_get('allow_sudocmdgroup') + deny_sudocmd = ansible_module.params_get('deny_sudocmd') + deny_sudocmdgroup = ansible_module.params_get('deny_sudocmdgroup') + sudooption = ansible_module.params_get("sudooption") + order = ansible_module.params_get("order") + runasuser = ansible_module.params_get("runasuser") + runasgroup = ansible_module.params_get("runasgroup") + action = ansible_module.params_get("action") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # Check parameters @@ -393,13 +380,9 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): commands = [] @@ -850,8 +833,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: @@ -873,12 +855,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as ex: - ansible_module.fail_json(msg=str(ex)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From c4fbd0d5fdd53f9e6e88b2f500c640f37e9e1fdd Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:47:25 +0200 Subject: [PATCH 27/35] trust: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipatrust.py | 65 ++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/plugins/modules/ipatrust.py b/plugins/modules/ipatrust.py index 3c14077b..0d9036eb 100644 --- a/plugins/modules/ipatrust.py +++ b/plugins/modules/ipatrust.py @@ -20,9 +20,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, module_params_get -from ansible.module_utils.basic import AnsibleModule ANSIBLE_METADATA = {'metadata_version': '1.1', 'supported_by': 'community', 'status': ['preview'], @@ -33,6 +30,8 @@ DOCUMENTATION = """ module: ipatrust short_description: Manage FreeIPA Domain Trusts. description: Manage FreeIPA Domain Trusts. +extends_documentation_fragment: + - ipamodule_base_docs options: realm: description: @@ -97,6 +96,7 @@ author: EXAMPLES = """ # add ad-trust - ipatrust: + ipaadmin_password: SomeADMINpassword realm: ad.example.test trust_type: ad admin: Administrator @@ -105,6 +105,7 @@ EXAMPLES = """ # delete ad-trust - ipatrust: + ipaadmin_password: SomeADMINpassword realm: ad.example.test state: absent """ @@ -113,13 +114,17 @@ RETURN = """ """ +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule + + def find_trust(module, realm): _args = { "all": True, "cn": realm, } - _result = api_command(module, "trust_find", realm, _args) + _result = module.ipa_command("trust_find", realm, _args) if len(_result["result"]) > 1: module.fail_json(msg="There is more than one realm '%s'" % (realm)) @@ -132,7 +137,7 @@ def find_trust(module, realm): def del_trust(module, realm): _args = {} - _result = api_command(module, "trust_del", realm, _args) + _result = module.ipa_command("trust_del", realm, _args) if len(_result["result"]["failed"]) > 0: module.fail_json( msg="Trust deletion has failed for '%s'" % (realm)) @@ -141,7 +146,7 @@ def del_trust(module, realm): def add_trust(module, realm, args): _args = args - _result = api_command(module, "trust_add", realm, _args) + _result = module.ipa_command("trust_add", realm, _args) if "cn" not in _result["result"]: module.fail_json( @@ -174,11 +179,9 @@ def gen_args(trust_type, admin, password, server, trust_secret, base_id, def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), realm=dict(type="str", default=None, required=True), # state state=dict(type="str", default="present", @@ -207,35 +210,29 @@ def main(): ansible_module._ansible_debug = True # general - ipaadmin_principal = module_params_get( - ansible_module, "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - realm = module_params_get(ansible_module, "realm") + realm = ansible_module.params_get("realm") # state - state = module_params_get(ansible_module, "state") + state = ansible_module.params_get("state") # trust - trust_type = module_params_get(ansible_module, "trust_type") - admin = module_params_get(ansible_module, "admin") - password = module_params_get(ansible_module, "password") - server = module_params_get(ansible_module, "server") - trust_secret = module_params_get(ansible_module, "trust_secret") - base_id = module_params_get(ansible_module, "base_id") - range_size = module_params_get(ansible_module, "range_size") - range_type = module_params_get(ansible_module, "range_type") - two_way = module_params_get(ansible_module, "two_way") - external = module_params_get(ansible_module, "external") + trust_type = ansible_module.params_get("trust_type") + admin = ansible_module.params_get("admin") + password = ansible_module.params_get("password") + server = ansible_module.params_get("server") + trust_secret = ansible_module.params_get("trust_secret") + base_id = ansible_module.params_get("base_id") + range_size = ansible_module.params_get("range_size") + range_type = ansible_module.params_get("range_type") + two_way = ansible_module.params_get("two_way") + external = ansible_module.params_get("external") changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit( - ipaadmin_principal, ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): + res_find = find_trust(ansible_module, realm) if state == "absent": @@ -257,12 +254,6 @@ def main(): add_trust(ansible_module, realm, args) changed = True - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, **exit_args) From c2df7be22aa13451be1497ef12ded2ab372530b4 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:47:43 +0200 Subject: [PATCH 28/35] user: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipauser.py | 152 +++++++++++++++---------------------- 1 file changed, 63 insertions(+), 89 deletions(-) diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py index 32aa9454..7e44a3f3 100644 --- a/plugins/modules/ipauser.py +++ b/plugins/modules/ipauser.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipauser short description: Manage FreeIPA users description: Manage FreeIPA users +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The list of users (internally uid). required: false @@ -472,16 +468,11 @@ user: returned: always """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, date_format, \ - compare_args_ipa, module_params_get, api_check_param, api_get_realm, \ - api_command_no_name, gen_add_del_lists, encode_certificate, \ - load_cert_from_str, DN_x500_text, api_check_command + +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, date_format, \ + encode_certificate, load_cert_from_str, DN_x500_text, to_text import six - - if six.PY3: unicode = str @@ -494,7 +485,7 @@ def find_user(module, name, preserved=False): if preserved: _args["preserved"] = preserved - _result = api_command(module, "user_find", name, _args) + _result = module.ipa_command("user_find", name, _args) if len(_result["result"]) > 1: module.fail_json( @@ -792,12 +783,9 @@ def main(): nomembers=dict(type='bool', default=None), ) - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - name=dict(type="list", aliases=["login"], default=None, required=False), users=dict(type="list", aliases=["login"], default=None, @@ -836,69 +824,65 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password") - names = module_params_get(ansible_module, "name") - users = module_params_get(ansible_module, "users") + names = ansible_module.params_get("name") + users = ansible_module.params_get("users") # present - first = module_params_get(ansible_module, "first") - last = module_params_get(ansible_module, "last") - fullname = module_params_get(ansible_module, "fullname") - displayname = module_params_get(ansible_module, "displayname") - initials = module_params_get(ansible_module, "initials") - homedir = module_params_get(ansible_module, "homedir") - shell = module_params_get(ansible_module, "shell") - email = module_params_get(ansible_module, "email") - principal = module_params_get(ansible_module, "principal") - principalexpiration = module_params_get(ansible_module, - "principalexpiration") + first = ansible_module.params_get("first") + last = ansible_module.params_get("last") + fullname = ansible_module.params_get("fullname") + displayname = ansible_module.params_get("displayname") + initials = ansible_module.params_get("initials") + homedir = ansible_module.params_get("homedir") + shell = ansible_module.params_get("shell") + email = ansible_module.params_get("email") + principal = ansible_module.params_get("principal") + principalexpiration = ansible_module.params_get( + "principalexpiration") if principalexpiration is not None: if principalexpiration[:-1] != "Z": principalexpiration = principalexpiration + "Z" principalexpiration = date_format(principalexpiration) - passwordexpiration = module_params_get(ansible_module, - "passwordexpiration") + passwordexpiration = ansible_module.params_get("passwordexpiration") if passwordexpiration is not None: if passwordexpiration[:-1] != "Z": passwordexpiration = passwordexpiration + "Z" passwordexpiration = date_format(passwordexpiration) - password = module_params_get(ansible_module, "password") - random = module_params_get(ansible_module, "random") - uid = module_params_get(ansible_module, "uid") - gid = module_params_get(ansible_module, "gid") - city = module_params_get(ansible_module, "city") - userstate = module_params_get(ansible_module, "userstate") - postalcode = module_params_get(ansible_module, "postalcode") - phone = module_params_get(ansible_module, "phone") - mobile = module_params_get(ansible_module, "mobile") - pager = module_params_get(ansible_module, "pager") - fax = module_params_get(ansible_module, "fax") - orgunit = module_params_get(ansible_module, "orgunit") - title = module_params_get(ansible_module, "title") - manager = module_params_get(ansible_module, "manager") - carlicense = module_params_get(ansible_module, "carlicense") - sshpubkey = module_params_get(ansible_module, "sshpubkey") - userauthtype = module_params_get(ansible_module, "userauthtype") - userclass = module_params_get(ansible_module, "userclass") - radius = module_params_get(ansible_module, "radius") - radiususer = module_params_get(ansible_module, "radiususer") - departmentnumber = module_params_get(ansible_module, "departmentnumber") - employeenumber = module_params_get(ansible_module, "employeenumber") - employeetype = module_params_get(ansible_module, "employeetype") - preferredlanguage = module_params_get(ansible_module, "preferredlanguage") - certificate = module_params_get(ansible_module, "certificate") - certmapdata = module_params_get(ansible_module, "certmapdata") - noprivate = module_params_get(ansible_module, "noprivate") - nomembers = module_params_get(ansible_module, "nomembers") + password = ansible_module.params_get("password") + random = ansible_module.params_get("random") + uid = ansible_module.params_get("uid") + gid = ansible_module.params_get("gid") + city = ansible_module.params_get("city") + userstate = ansible_module.params_get("userstate") + postalcode = ansible_module.params_get("postalcode") + phone = ansible_module.params_get("phone") + mobile = ansible_module.params_get("mobile") + pager = ansible_module.params_get("pager") + fax = ansible_module.params_get("fax") + orgunit = ansible_module.params_get("orgunit") + title = ansible_module.params_get("title") + manager = ansible_module.params_get("manager") + carlicense = ansible_module.params_get("carlicense") + sshpubkey = ansible_module.params_get("sshpubkey") + userauthtype = ansible_module.params_get("userauthtype") + userclass = ansible_module.params_get("userclass") + radius = ansible_module.params_get("radius") + radiususer = ansible_module.params_get("radiususer") + departmentnumber = ansible_module.params_get("departmentnumber") + employeenumber = ansible_module.params_get("employeenumber") + employeetype = ansible_module.params_get("employeetype") + preferredlanguage = ansible_module.params_get("preferredlanguage") + certificate = ansible_module.params_get("certificate") + certmapdata = ansible_module.params_get("certmapdata") + noprivate = ansible_module.params_get("noprivate") + nomembers = ansible_module.params_get("nomembers") # deleted - preserve = module_params_get(ansible_module, "preserve") + preserve = ansible_module.params_get("preserve") # mod - update_password = module_params_get(ansible_module, "update_password") + update_password = ansible_module.params_get("update_password") # general - action = module_params_get(ansible_module, "action") - state = module_params_get(ansible_module, "state") + action = ansible_module.params_get("action") + state = ansible_module.params_get("state") # Check parameters @@ -930,21 +914,17 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): # Check version specific settings - server_realm = api_get_realm() + server_realm = ansible_module.ipa_get_realm() # Default email domain - result = api_command_no_name(ansible_module, "config_show", {}) + result = ansible_module.ipa_command_no_name("config_show", {}) default_email_domain = result["result"]["ipadefaultemaildomain"][0] # Extend email addresses @@ -1048,7 +1028,8 @@ def main(): # be part of check_parameters as this is used also before the # connection to the API has been established. if passwordexpiration is not None and \ - not api_check_param("user_add", "krbpasswordexpiration"): + not ansible_module.ipa_command_param_exists( + "user_add", "krbpasswordexpiration"): ansible_module.fail_json( msg="The use of passwordexpiration is not supported by " "your IPA version") @@ -1058,7 +1039,7 @@ def main(): # be part of check_parameters as this is used also before the # connection to the API has been established. if certmapdata is not None and \ - not api_check_command("user_add_certmapdata"): + not ansible_module.ipa_command_exists("user_add_certmapdata"): ansible_module.fail_json( msg="The use of certmapdata is not supported by " "your IPA version") @@ -1387,8 +1368,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, name, - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -1432,12 +1412,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, user=exit_args) From d356fa42bb0001daa61a9f1cd8d86cc451474dd6 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Fri, 27 Aug 2021 18:54:35 +0200 Subject: [PATCH 29/35] host: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipahost.py | 148 +++++++++++++++---------------------- 1 file changed, 60 insertions(+), 88 deletions(-) diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py index 0bd5b1b4..86453a77 100644 --- a/plugins/modules/ipahost.py +++ b/plugins/modules/ipahost.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipahost short description: Manage FreeIPA hosts description: Manage FreeIPA hosts +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The full qualified domain name. aliases: ["fqdn"] @@ -380,7 +376,7 @@ EXAMPLES = """ # Ensure host is absent - ipahost: - ipaadmin_password: password1 + ipaadmin_password: SomeADMINpassword name: host01.example.com state: absent """ @@ -404,15 +400,10 @@ host: returned: always """ -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_text -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \ - module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \ - is_ipv4_addr, is_ipv6_addr, ipalib_errors +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \ + encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors import six - - if six.PY3: unicode = str @@ -423,7 +414,7 @@ def find_host(module, name): } try: - _result = api_command(module, "host_show", to_text(name), _args) + _result = module.ipa_command("host_show", name, _args) except ipalib_errors.NotFound as e: msg = str(e) if "host not found" in msg: @@ -450,17 +441,16 @@ def find_dnsrecord(module, name): _args = { "all": True, - "idnsname": to_text(host_name) + "idnsname": host_name } - _result = api_command(module, "dnsrecord_show", to_text(domain_name), - _args) + _result = module.ipa_command("dnsrecord_show", domain_name, _args) return _result["result"] def show_host(module, name): - _result = api_command(module, "host_show", to_text(name), {}) + _result = module.ipa_command("host_show", name, {}) return _result["result"] @@ -663,12 +653,9 @@ def main(): # krbprincipalname ) - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", no_log=True), - name=dict(type="list", aliases=["fqdn"], default=None, required=False), @@ -705,56 +692,52 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, - "ipaadmin_password") - names = module_params_get(ansible_module, "name") - hosts = module_params_get(ansible_module, "hosts") + names = ansible_module.params_get("name") + hosts = ansible_module.params_get("hosts") # present - description = module_params_get(ansible_module, "description") - locality = module_params_get(ansible_module, "locality") - location = module_params_get(ansible_module, "location") - platform = module_params_get(ansible_module, "platform") - os = module_params_get(ansible_module, "os") - password = module_params_get(ansible_module, "password") - random = module_params_get(ansible_module, "random") - certificate = module_params_get(ansible_module, "certificate") - managedby_host = module_params_get(ansible_module, "managedby_host") - principal = module_params_get(ansible_module, "principal") - allow_create_keytab_user = module_params_get( - ansible_module, "allow_create_keytab_user") - allow_create_keytab_group = module_params_get( - ansible_module, "allow_create_keytab_group") - allow_create_keytab_host = module_params_get( - ansible_module, "allow_create_keytab_host") - allow_create_keytab_hostgroup = module_params_get( - ansible_module, "allow_create_keytab_hostgroup") - allow_retrieve_keytab_user = module_params_get( - ansible_module, "allow_retrieve_keytab_user") - allow_retrieve_keytab_group = module_params_get( - ansible_module, "allow_retrieve_keytab_group") - allow_retrieve_keytab_host = module_params_get( - ansible_module, "allow_retrieve_keytab_host") - allow_retrieve_keytab_hostgroup = module_params_get( - ansible_module, "allow_retrieve_keytab_hostgroup") - mac_address = module_params_get(ansible_module, "mac_address") - sshpubkey = module_params_get(ansible_module, "sshpubkey") - userclass = module_params_get(ansible_module, "userclass") - auth_ind = module_params_get(ansible_module, "auth_ind") - requires_pre_auth = module_params_get(ansible_module, "requires_pre_auth") - ok_as_delegate = module_params_get(ansible_module, "ok_as_delegate") - ok_to_auth_as_delegate = module_params_get(ansible_module, - "ok_to_auth_as_delegate") - force = module_params_get(ansible_module, "force") - reverse = module_params_get(ansible_module, "reverse") - ip_address = module_params_get(ansible_module, "ip_address") - update_dns = module_params_get(ansible_module, "update_dns") - update_password = module_params_get(ansible_module, "update_password") + description = ansible_module.params_get("description") + locality = ansible_module.params_get("locality") + location = ansible_module.params_get("location") + platform = ansible_module.params_get("platform") + os = ansible_module.params_get("os") + password = ansible_module.params_get("password") + random = ansible_module.params_get("random") + certificate = ansible_module.params_get("certificate") + managedby_host = ansible_module.params_get("managedby_host") + principal = ansible_module.params_get("principal") + allow_create_keytab_user = ansible_module.params_get( + "allow_create_keytab_user") + allow_create_keytab_group = ansible_module.params_get( + "allow_create_keytab_group") + allow_create_keytab_host = ansible_module.params_get( + "allow_create_keytab_host") + allow_create_keytab_hostgroup = ansible_module.params_get( + "allow_create_keytab_hostgroup") + allow_retrieve_keytab_user = ansible_module.params_get( + "allow_retrieve_keytab_user") + allow_retrieve_keytab_group = ansible_module.params_get( + "allow_retrieve_keytab_group") + allow_retrieve_keytab_host = ansible_module.params_get( + "allow_retrieve_keytab_host") + allow_retrieve_keytab_hostgroup = ansible_module.params_get( + "allow_retrieve_keytab_hostgroup") + mac_address = ansible_module.params_get("mac_address") + sshpubkey = ansible_module.params_get("sshpubkey") + userclass = ansible_module.params_get("userclass") + auth_ind = ansible_module.params_get("auth_ind") + requires_pre_auth = ansible_module.params_get("requires_pre_auth") + ok_as_delegate = ansible_module.params_get("ok_as_delegate") + ok_to_auth_as_delegate = ansible_module.params_get( + "ok_to_auth_as_delegate") + force = ansible_module.params_get("force") + reverse = ansible_module.params_get("reverse") + ip_address = ansible_module.params_get("ip_address") + update_dns = ansible_module.params_get("update_dns") + update_password = ansible_module.params_get("update_password") # general - action = module_params_get(ansible_module, "action") - state = module_params_get(ansible_module, "state") + action = ansible_module.params_get("action") + state = ansible_module.params_get("state") # Check parameters @@ -786,17 +769,13 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): # Check version specific settings - server_realm = api_get_realm() + server_realm = ansible_module.ipa_get_realm() commands = [] host_set = set() @@ -973,7 +952,7 @@ def main(): # Principals are not returned as utf8 for IPA using # python2 using host_show, therefore we need to # convert the principals that we should remove. - principal_del = [to_text(x) for x in principal_del] + principal_del = [unicode(x) for x in principal_del] (allow_create_keytab_user_add, allow_create_keytab_user_del) = \ @@ -1373,8 +1352,7 @@ def main(): errors = [] for name, command, args in commands: try: - result = api_command(ansible_module, command, to_text(name), - args) + result = ansible_module.ipa_command(command, name, args) if "completed" in result: if result["completed"] > 0: changed = True @@ -1428,12 +1406,6 @@ def main(): if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) - except Exception as e: - ansible_module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # Done ansible_module.exit_json(changed=changed, host=exit_args) From 0c430d0aa97a9a31c21f91a0979815c9bed20e37 Mon Sep 17 00:00:00 2001 From: Rafael Guterres Jeffman Date: Tue, 31 Aug 2021 18:56:16 -0300 Subject: [PATCH 30/35] Make IPAAnsibleModule base class of FreeIPABaseModule. By making IPAAnsibleModule the base class of FreeIPABaseModule, instead of AnsibleModule, some methods on FreeIPABaseModule can be removed and suport for commom parameters in modules using the older class can use the same commom parameters (ipaadmin_principal and ipaadmin_password) as the other parameters. This will also allow easier deprecation of FreeIPABaseModule, which is hard to maintain. To be able to use IPAAnsibleModule as the base class, it was moved within the file, to position before FreeIPABaseModule declaration. This patch also modifies IPAAnsibleModule by: * removing usage of `self` in methods not requiring it, turning the methods into @statimethod; * adding comments to all the methods in IPAAnsibleModule, which makes it easier to understand what the individual methods do, and what their parameters represent. --- .../module_utils/ansible_freeipa_module.py | 368 +++++++++--------- 1 file changed, 189 insertions(+), 179 deletions(-) diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index 7c60eb28..0b50720c 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -44,7 +44,6 @@ else: import netaddr import gssapi from datetime import datetime - from pprint import pformat from contextlib import contextmanager # ansible-freeipa requires locale to be C, IPA requires utf-8. @@ -569,7 +568,192 @@ else: def __getattr__(self, name): return self.get(name) - class FreeIPABaseModule(AnsibleModule): + class IPAAnsibleModule(AnsibleModule): + """ + IPA Ansible Module. + + This class is an extended version of the Ansible Module that provides + IPA specific methods to simplify module generation. + + Simple example: + + from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule + + def main(): + ansible_module = IPAAnsibleModule( + argument_spec=dict( + name=dict(type="str", aliases=["cn"], default=None), + state=dict(type="str", default="present", + choices=["present", "absent"]), + ), + ) + + # Get parameters + name = ansible_module.params_get("name") + state = ansible_module.params_get("state") + + # Connect to IPA API + with ansible_module.ipa_connect(): + + # Execute command + if state == "present": + ansible_module.ipa_command(["command_add", name, {}]) + else: + ansible_module.ipa_command(["command_del", name, {}]) + + # Done + + ansible_module.exit_json(changed=True) + + if __name__ == "__main__": + main() + + """ + + def __init__(self, *args, **kwargs): + # Extend argument_spec with ipamodule_base_spec + if "argument_spec" in kwargs: + _spec = kwargs["argument_spec"] + _spec.update(ipamodule_base_spec) + kwargs["argument_spec"] = _spec + + # pylint: disable=super-with-arguments + super(IPAAnsibleModule, self).__init__(*args, **kwargs) + + # ipaadmin vars + self.ipaadmin_principal = self.params_get("ipaadmin_principal") + self.ipaadmin_password = self.params_get("ipaadmin_password") + + # Attributes to store kerberos credentials (if needed) + self.ccache_dir = None + self.ccache_name = None + + @contextmanager + def ipa_connect(self, context=None): + """ + Create a context with a connection to IPA API. + + Parameters + ---------- + context: string + An optional parameter defining which context API + commands will be executed. + + """ + ccache_dir = None + ccache_name = None + try: + if not valid_creds(self, self.ipaadmin_principal): + ccache_dir, ccache_name = temp_kinit( + self.ipaadmin_principal, self.ipaadmin_password) + api_connect(context) + except Exception as e: + self.fail_json(msg=str(e)) + else: + try: + yield ccache_name + except Exception as e: + self.fail_json(msg=str(e)) + finally: + temp_kdestroy(ccache_dir, ccache_name) + + def params_get(self, name): + """ + Retrieve value set for module parameter. + + Parameters + ---------- + name: string + The name of the parameter to retrieve. + + """ + return module_params_get(self, name) + + def ipa_command(self, command, name, args): + """ + Execute an IPA API command with a required `name` argument. + + Parameters + ---------- + command: string + The IPA API command to execute. + name: string + The name parameter to pass to the command. + args: dict + The parameters to pass to the command. + + """ + return api_command(self, command, name, args) + + def ipa_command_no_name(self, command, args): + """ + Execute an IPA API command requiring no `name` argument. + + Parameters + ---------- + command: string + The IPA API command to execute. + args: dict + The parameters to pass to the command. + + """ + return api_command_no_name(self, command, args) + + @staticmethod + def ipa_get_domain(): + """Retrieve IPA API domain.""" + return api_get_domain() + + @staticmethod + def ipa_get_realm(): + """Retrieve IPA API realm.""" + return api_get_realm() + + @staticmethod + def ipa_command_exists(command): + """ + Check if IPA command is supported. + + Parameters + ---------- + command: string + The IPA API command to verify. + + """ + return api_check_command(command) + + @staticmethod + def ipa_command_param_exists(command, name): + """ + Check if IPA command support a specific parameter. + + Parameters + ---------- + command: string + The IPA API command to test. + name: string + The parameter name to verify. + + """ + return api_check_param(command, name) + + @staticmethod + def ipa_check_version(oper, requested_version): + """ + Compare available IPA version. + + Parameters + ---------- + oper: string + The relational operator to use. + requested_version: string + The version to compare to. + + """ + return api_check_ipa_version(oper, requested_version) + + class FreeIPABaseModule(IPAAnsibleModule): """ Base class for FreeIPA Ansible modules. @@ -642,10 +826,6 @@ else: # pylint: disable=super-with-arguments super(FreeIPABaseModule, self).__init__(*args, **kwargs) - # Attributes to store kerberos credentials (if needed) - self.ccache_dir = None - self.ccache_name = None - # Status of an execution. Will be changed to True # if something is actually peformed. self.changed = False @@ -724,64 +904,6 @@ else: """Define commands that will be run in IPA server.""" raise NotImplementedError - def api_command(self, command, name=None, args=None): - """Execute a single command in IPA server.""" - if args is None: - args = {} - - if name is None: - return api_command_no_name(self, command, args) - - return api_command(self, command, name, args) - - def __enter__(self): - """ - Connect to IPA server. - - Check the there are working Kerberos credentials to connect to - IPA server. If there are not we perform a temporary kinit - that will be terminated when exiting the context. - - If the connection fails ``ipa_connected`` attribute will be set - to False. - """ - principal = self.ipa_params.ipaadmin_principal - password = self.ipa_params.ipaadmin_password - - try: - if not valid_creds(self, principal): - self.ccache_dir, self.ccache_name = temp_kinit( - principal, password, - ) - - api_connect() - - except Exception as excpt: - self.fail_json(msg=str(excpt)) - else: - self.ipa_connected = True - - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """ - Terminate a connection with the IPA server. - - Deal with exceptions, destroy temporary kinit credentials and - exit the module with proper arguments. - - """ - # TODO: shouldn't we also disconnect from api backend? - temp_kdestroy(self.ccache_dir, self.ccache_name) - - if exc_type == SystemExit: - raise - - if exc_val: - self.fail_json(msg=str(exc_val)) - - self.exit_json(changed=self.changed, **self.exit_args) - def get_command_errors(self, command, result): """Look for erros into command results.""" # Get all errors @@ -815,7 +937,7 @@ else: for name, command, args in self.ipa_commands: try: - result = self.api_command(command, name, args) + result = self.ipa_command(command, name, args) except Exception as excpt: self.fail_json(msg="%s: %s: %s" % (command, name, str(excpt))) @@ -847,122 +969,10 @@ else: equal = compare_args_ipa(self, command_args, ipa_attrs) return not equal - def pdebug(self, value): - """Debug with pretty formatting.""" - self.debug(pformat(value)) - def ipa_run(self): """Execute module actions.""" - with self: - if not self.ipa_connected: - return - + with self.ipa_connect(): self.check_ipa_params() self.define_ipa_commands() self._run_ipa_commands() - - class IPAAnsibleModule(AnsibleModule): - """ - IPA Ansible Module. - - This class is an extended version of the Ansible Module that provides - IPA specific methods to simplify module generation. - - Simple example: - - from ansible.module_utils.ansible_freeipa_module import \ - IPAAnsibleModule - - def main(): - ansible_module = IPAAnsibleModule( - argument_spec=dict( - name=dict(type="str", aliases=["cn"], default=None), - state=dict(type="str", default="present", - choices=["present", "absent"]), - ), - ) - - # Get parameters - name = ansible_module.params_get("name") - state = ansible_module.params_get("state") - - # Connect to IPA API - with ansible_module.ipa_connect(): - - # Execute command - if state == "present": - ansible_module.ipa_command(["command_add", name, {}]) - else: - ansible_module.ipa_command(["command_del", name, {}]) - - # Done - - ansible_module.exit_json(changed=True) - - if __name__ == "__main__": - main() - - """ - - def __init__(self, *args, **kwargs): - # Extend argument_spec with ipamodule_base_spec - if "argument_spec" in kwargs: - _spec = kwargs["argument_spec"] - _spec.update(ipamodule_base_spec) - kwargs["argument_spec"] = _spec - - # pylint: disable=super-with-arguments - super(IPAAnsibleModule, self).__init__(*args, **kwargs) - - # ipaadmin vars - self.ipaadmin_principal = self.params_get("ipaadmin_principal") - self.ipaadmin_password = self.params_get("ipaadmin_password") - - # Attributes to store kerberos credentials (if needed) - self.ccache_dir = None - self.ccache_name = None - - @contextmanager - def ipa_connect(self, context=None): - ccache_dir = None - ccache_name = None - try: - if not valid_creds(self, self.ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit( - self.ipaadmin_principal, self.ipaadmin_password) - api_connect(context) - except Exception as e: - self.fail_json(msg=str(e)) - else: - try: - yield ccache_name - except Exception as e: - self.fail_json(msg=str(e)) - finally: - temp_kdestroy(ccache_dir, ccache_name) - - def params_get(self, name): - return module_params_get(self, name) - - def ipa_command(self, command, name, args): - return api_command(self, command, name, args) - - def ipa_command_no_name(self, command, args): - return api_command_no_name(self, command, args) - - def ipa_get_domain(self): # pylint: disable=R0201 - return api_get_domain() - - def ipa_get_realm(self): # pylint: disable=R0201 - return api_get_realm() - - def ipa_command_exists(self, command): # pylint: disable=R0201 - return api_check_command(command) - - # pylint: disable=R0201 - def ipa_command_param_exists(self, command, name): - return api_check_param(command, name) - - # pylint: disable=R0201 - def ipa_check_version(self, oper, requested_version): - return api_check_ipa_version(oper, requested_version) + self.exit_json(changed=self.changed, **self.exit_args) From 21a48d59685e90981c6df3fe528a042186e135ce Mon Sep 17 00:00:00 2001 From: Rafael Guterres Jeffman Date: Tue, 31 Aug 2021 19:04:19 -0300 Subject: [PATCH 31/35] dnszone: Use IPAAnsibleModule class. Use IPAAnsibleModule methods and ipamodule_base_docs ducument fragment. --- plugins/modules/ipadnszone.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py index ae8ed93a..266a0723 100644 --- a/plugins/modules/ipadnszone.py +++ b/plugins/modules/ipadnszone.py @@ -31,13 +31,9 @@ DOCUMENTATION = """ module: ipadnszone short description: Manage FreeIPA dnszone description: Manage FreeIPA dnszone +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The zone name string. required: true @@ -408,7 +404,9 @@ class DNSZoneModule(FreeIPABaseModule): get_zone_args = {"idnsname": zone_name, "all": True} try: - response = self.api_command("dnszone_show", args=get_zone_args) + response = self.ipa_command_no_name( + "dnszone_show", args=get_zone_args + ) except ipalib_errors.NotFound: zone = None is_zone_active = False From 706e1f5be36412531f12f99bb0455beb44e370fb Mon Sep 17 00:00:00 2001 From: Rafael Guterres Jeffman Date: Tue, 31 Aug 2021 19:07:42 -0300 Subject: [PATCH 32/35] automountlocation: Use IPAAnsibleModule class Use IPAAnsibleModule methods and ipamodule_base_docs ducument fragment. --- plugins/modules/ipaautomountlocation.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/plugins/modules/ipaautomountlocation.py b/plugins/modules/ipaautomountlocation.py index 44748c0e..f1925ddc 100644 --- a/plugins/modules/ipaautomountlocation.py +++ b/plugins/modules/ipaautomountlocation.py @@ -33,13 +33,9 @@ author: chris procter short_description: Manage FreeIPA autommount locations description: - Add and delete an IPA automount location +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false name: description: The automount location to be managed required: true @@ -79,9 +75,9 @@ class AutomountLocation(FreeIPABaseModule): def get_location(self, location): try: - response = self.api_command("automountlocation_show", - location, - {}) + response = self.ipa_command( + "automountlocation_show", location, {} + ) except ipalib_errors.NotFound: return None else: From 55341a0310d5a6a633f4dee1f046ab93106c4af5 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Wed, 1 Sep 2021 21:25:02 +0200 Subject: [PATCH 33/35] ansible_freeipa_module: New execute_ipa_commands in IPAAnsibleModule execute_ipa_commands executes IPA API commands from the given command list. With the handle_result and handle_result_user_args it is possible to have a handling of the result return by the ipa commands for example to return passwords. Parameters commands: list of string tuple 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 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 Example (ipauser module) def handle_result(result, command, name, args, exit_args): if "random" in args and command in ["user_add", "user_mod"] \ and "randompassword" in result["result"]: exit_args.setdefault(name, {})["randompassword"] = \ result["result"]["randompassword"] exit_args = {} changed = module.execute_ipa_commands(commands, handle_result, exit_args=exit_args) 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) --- .../module_utils/ansible_freeipa_module.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index 0b50720c..79d6c019 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -753,6 +753,72 @@ else: """ return api_check_ipa_version(oper, requested_version) + def execute_ipa_commands(self, commands, handle_result=None, + **handle_result_user_args): + """ + Execute IPA API commands from command list. + + Parameters + ---------- + commands: list of string tuple + 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 + 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 + + Example (ipauser module): + + def handle_result(module, result, command, name, args, exit_args): + if "random" in args and command in ["user_add", "user_mod"] \ + and "randompassword" in result["result"]: + exit_args.setdefault(name, {})["randompassword"] = \ + result["result"]["randompassword"] + + exit_args = {} + changed = module.execute_ipa_commands(commands, handle_result, + exit_args=exit_args) + + 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) + + """ + # No commands, report no changes + if commands is None: + return False + + # In check_mode return if there are commands to do + if self.check_mode: + return len(commands) > 0 + + changed = False + for name, command, args in commands: + try: + if name is None: + result = self.ipa_command_no_name(command, args) + else: + result = self.ipa_command(command, name, args) + + if "completed" in result: + if result["completed"] > 0: + changed = True + else: + changed = True + + if handle_result is not None: + handle_result(self, result, command, name, args, + **handle_result_user_args) + + except Exception as e: + self.fail_json(msg="%s: %s: %s" % (command, name, str(e))) + + return changed + class FreeIPABaseModule(IPAAnsibleModule): """ Base class for FreeIPA Ansible modules. From 3e98ed544238ca93b3b6e8f6f459a1ec2c3220e2 Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 2 Sep 2021 10:07:43 +0200 Subject: [PATCH 34/35] ansible_freeipa_module: Cleanup of unused functions and attributes Moved ipamodule_base_spec into IPAAnsibleModule. This simplifies to add addtitional base vars to derived classes without the need to dusplicae the code to extend argument_spec. Removed execute_api_command. This is not used anymore. Removed get_ipamodule_base_vars. This is not used anymore. Removed self.ipaadmin_* and self.ccache_* from IPAAnsibleModule.__init__ These are not used and therefore can be removed. --- .../module_utils/ansible_freeipa_module.py | 65 ++++--------------- 1 file changed, 14 insertions(+), 51 deletions(-) diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index 79d6c019..b5716277 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -109,22 +109,6 @@ else: if six.PY3: unicode = str - # AnsibleModule argument specs for all modules - ipamodule_base_spec = dict( - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), - ) - - # Get ipamodule common vars as nonlocal - def get_ipamodule_base_vars(module): - ipaadmin_principal = module_params_get(module, "ipaadmin_principal") - ipaadmin_password = module_params_get(module, "ipaadmin_password") - - return dict( - ipaadmin_principal=ipaadmin_principal, - ipaadmin_password=ipaadmin_password, - ) - def valid_creds(module, principal): # noqa """Get valid credentials matching the princial, try GSSAPI first.""" if "KRB5CCNAME" in os.environ: @@ -254,29 +238,6 @@ else: return operation(version.parse(VERSION), version.parse(requested_version)) - def execute_api_command(module, principal, password, command, name, args): - """ - Execute an API command. - - Get KRB ticket if not already there, initialize api, connect, - execute command and destroy ticket again if it has been created also. - """ - ccache_dir = None - ccache_name = None - try: - if not valid_creds(module, principal): - ccache_dir, ccache_name = temp_kinit(principal, password) - api_connect() - - return api_command(module, command, name, args) - except Exception as e: - module.fail_json(msg=str(e)) - - finally: - temp_kdestroy(ccache_dir, ccache_name) - # fix pylint inconsistent return - return None - def date_format(value): accepted_date_formats = [ LDAP_GENERALIZED_TIME_FORMAT, # generalized time @@ -611,24 +572,22 @@ else: """ + # IPAAnsibleModule argument specs used for all modules + ipa_module_base_spec = dict( + ipaadmin_principal=dict(type="str", default="admin"), + ipaadmin_password=dict(type="str", required=False, no_log=True), + ) + def __init__(self, *args, **kwargs): - # Extend argument_spec with ipamodule_base_spec + # Extend argument_spec with ipa_module_base_spec if "argument_spec" in kwargs: _spec = kwargs["argument_spec"] - _spec.update(ipamodule_base_spec) + _spec.update(self.ipa_module_base_spec) kwargs["argument_spec"] = _spec # pylint: disable=super-with-arguments super(IPAAnsibleModule, self).__init__(*args, **kwargs) - # ipaadmin vars - self.ipaadmin_principal = self.params_get("ipaadmin_principal") - self.ipaadmin_password = self.params_get("ipaadmin_password") - - # Attributes to store kerberos credentials (if needed) - self.ccache_dir = None - self.ccache_name = None - @contextmanager def ipa_connect(self, context=None): """ @@ -641,12 +600,16 @@ else: commands will be executed. """ + # ipaadmin vars + ipaadmin_principal = self.params_get("ipaadmin_principal") + ipaadmin_password = self.params_get("ipaadmin_password") + ccache_dir = None ccache_name = None try: - if not valid_creds(self, self.ipaadmin_principal): + if not valid_creds(self, ipaadmin_principal): ccache_dir, ccache_name = temp_kinit( - self.ipaadmin_principal, self.ipaadmin_password) + ipaadmin_principal, ipaadmin_password) api_connect(context) except Exception as e: self.fail_json(msg=str(e)) From ed3dd3ea71b9db7abd0aed76375105f312a7631f Mon Sep 17 00:00:00 2001 From: Thomas Woerner Date: Thu, 2 Sep 2021 10:36:25 +0200 Subject: [PATCH 35/35] config: Use IPAAnsibleModule class ipaadmin_variables are handled by IPAAnsibleModule, ansible_module.params_get is used to get the parameters and ansible_module.ipa_connect is used to simplify the module. --- plugins/modules/ipaconfig.py | 58 ++++++++++++------------------------ 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/plugins/modules/ipaconfig.py b/plugins/modules/ipaconfig.py index dad9bd9d..dc27b341 100644 --- a/plugins/modules/ipaconfig.py +++ b/plugins/modules/ipaconfig.py @@ -34,13 +34,9 @@ author: chris procter short_description: Modify IPA global config options description: - Modify IPA global config options +extends_documentation_fragment: + - ipamodule_base_docs options: - ipaadmin_principal: - description: The admin principal - default: admin - ipaadmin_password: - description: The admin password - required: false maxusername: description: Set the maximum username length between 1-255 required: false @@ -159,7 +155,7 @@ EXAMPLES = ''' tasks: - name: return current values of the global configuration options ipaconfig: - ipaadmin_password: password + ipaadmin_password: SomeADMINpassword register: result - name: display default login shell debug: @@ -167,7 +163,7 @@ EXAMPLES = ''' - name: set defaultshell and maxusername ipaconfig: - ipaadmin_password: password + ipaadmin_password: SomeADMINpassword defaultshell: /bin/bash maxusername: 64 ''' @@ -251,14 +247,12 @@ config: ''' -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.ansible_freeipa_module import temp_kinit, \ - temp_kdestroy, valid_creds, api_connect, api_command_no_name, \ - compare_args_ipa, module_params_get, ipalib_errors +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, ipalib_errors def config_show(module): - _result = api_command_no_name(module, "config_show", {}) + _result = module.ipa_command_no_name("config_show", {}) return _result["result"] @@ -270,11 +264,9 @@ def gen_args(params): def main(): - ansible_module = AnsibleModule( + ansible_module = IPAAnsibleModule( argument_spec=dict( # general - ipaadmin_principal=dict(type="str", default="admin"), - ipaadmin_password=dict(type="str", required=False, no_log=True), maxusername=dict(type="int", required=False, aliases=['ipamaxusernamelength']), maxhostname=dict(type="int", required=False, @@ -334,11 +326,6 @@ def main(): # Get parameters # general - ipaadmin_principal = module_params_get(ansible_module, - "ipaadmin_principal") - ipaadmin_password = module_params_get(ansible_module, - "ipaadmin_password") - field_map = { "maxusername": "ipamaxusernamelength", "maxhostname": "ipamaxhostnamelength", @@ -366,7 +353,7 @@ def main(): params = {} for x in field_map: - val = module_params_get(ansible_module, x) + val = ansible_module.params_get(x) if val is not None: params[field_map.get(x, x)] = val @@ -407,14 +394,11 @@ def main(): changed = False exit_args = {} - ccache_dir = None - ccache_name = None res_show = None - try: - if not valid_creds(ansible_module, ipaadmin_principal): - ccache_dir, ccache_name = temp_kinit(ipaadmin_principal, - ipaadmin_password) - api_connect() + + # Connect to IPA API + with ansible_module.ipa_connect(): + if params: res_show = config_show(ansible_module) params = { @@ -425,10 +409,13 @@ def main(): and not compare_args_ipa(ansible_module, params, res_show): changed = True if not ansible_module.check_mode: - api_command_no_name(ansible_module, "config_mod", params) - + try: + ansible_module.ipa_command_no_name("config_mod", + params) + except ipalib_errors.EmptyModlist: + changed = False else: - rawresult = api_command_no_name(ansible_module, "config_show", {}) + rawresult = ansible_module.ipa_command_no_name("config_show", {}) result = rawresult['result'] del result['dn'] for key, value in result.items(): @@ -460,13 +447,6 @@ def main(): exit_args[k] = (value[0] == "TRUE") else: exit_args[k] = value - except ipalib_errors.EmptyModlist: - changed = False - except Exception as e: - ansible_module.fail_json(msg="%s %s" % (params, str(e))) - - finally: - temp_kdestroy(ccache_dir, ccache_name) # Done ansible_module.exit_json(changed=changed, config=exit_args)