From b4d39dfd8b12f4c941a14f5c08c56fef7fd4c609 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 26 Aug 2021 19:18:11 +0200
Subject: [PATCH] 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)
-- 
GitLab