From d2648b142a44511e6841e08bd3a14ff877c34c74 Mon Sep 17 00:00:00 2001 From: Thomas Woerner <twoerner@redhat.com> Date: Wed, 22 Dec 2021 13:12:14 +0100 Subject: [PATCH] automember: Add automember default group handling The fallback group and hostgroup for unmached entries can be set and unset using default_group. If default_group is empty, then the default group will be unset. DN and ipa_get_based provided by ansible_freeipa_module are used in the code. New parameters: - default_group: Default (fallback) group for all unmatched entries. New parameters and examples have been added to README-automember.md New playbooks: - playbooks/automember/automember-default-group-not-set.yml - playbooks/automember/automember-default-group-set.yml - playbooks/automember/automember-default-hostgroup-not-set.yml - playbooks/automember/automember-default-hostgroup-set.yml New tests: - tests/automember/test_automember_default_group.yml --- README-automember.md | 59 +++++++ .../automember-default-group-not-set.yml | 10 ++ .../automember-default-group-set.yml | 10 ++ .../automember-default-hostgroup-not-set.yml | 10 ++ .../automember-default-hostgroup-set.yml | 10 ++ plugins/modules/ipaautomember.py | 92 +++++++++- .../test_automember_default_group.yml | 166 ++++++++++++++++++ 7 files changed, 354 insertions(+), 3 deletions(-) create mode 100644 playbooks/automember/automember-default-group-not-set.yml create mode 100644 playbooks/automember/automember-default-group-set.yml create mode 100644 playbooks/automember/automember-default-hostgroup-not-set.yml create mode 100644 playbooks/automember/automember-default-hostgroup-set.yml create mode 100644 tests/automember/test_automember_default_group.yml diff --git a/README-automember.md b/README-automember.md index f5e075ff..eb44a4f0 100644 --- a/README-automember.md +++ b/README-automember.md @@ -172,6 +172,64 @@ Example playbook to ensure hostgroup membership for given hosts has been rebuilt state: rebuilt ``` +Example playbook to ensure default group fallback_group for all unmatched group entries is set + +```yaml +- name: Playbook to ensure default group fallback_group for all unmatched group entries is set + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + default_group: fallback_group +``` + +Example playbook to ensure default group for all unmatched group entries is not set + +```yaml +- name: Playbook to ensure default group for all unmatched group entries is not set + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + default_group: "" + automember_type: group + state: absent +``` + +Example playbook to ensure default hostgroup fallback_hostgroup for all unmatched group entries + +```yaml +- name: Playbook to ensure default hostgroup fallback_hostgroup for all unmatched group entries + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + default_group: fallback_hostgroup +``` + +Example playbook to ensure default hostgroup for all unmatched group entries is not set + +```yaml +- name: Playbook to ensure default hostgroup for all unmatched group entries is not set + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + default_group: "" + state: absent +``` + Variables --------- @@ -193,6 +251,7 @@ Variable | Description | Required `users` | Users to rebuild membership for. | no `hosts` | Hosts to rebuild membership for. | no `no_wait` | Don't wait for rebuilding membership. | no +`default_group` | Default (fallback) group for all unmatched entries. Use the empty string "" for ensuring the default group is not set. | no `action` | Work on automember or member level. It can be one of `member` or `automember` and defaults to `automember`. | no `state` | The state to ensure. It can be one of `present`, `absent`, 'rebuilt'. default: `present`. | no diff --git a/playbooks/automember/automember-default-group-not-set.yml b/playbooks/automember/automember-default-group-not-set.yml new file mode 100644 index 00000000..7c85bfb4 --- /dev/null +++ b/playbooks/automember/automember-default-group-not-set.yml @@ -0,0 +1,10 @@ +--- +- name: Automember default group not set + hosts: ipaserver + become: true + tasks: + - name: Ensure automember default group is not set + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + default_group: "" diff --git a/playbooks/automember/automember-default-group-set.yml b/playbooks/automember/automember-default-group-set.yml new file mode 100644 index 00000000..41ce1185 --- /dev/null +++ b/playbooks/automember/automember-default-group-set.yml @@ -0,0 +1,10 @@ +--- +- name: Automember default group set + hosts: ipaserver + become: true + tasks: + - name: Ensure automember default group is set + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + default_group: fallback_group diff --git a/playbooks/automember/automember-default-hostgroup-not-set.yml b/playbooks/automember/automember-default-hostgroup-not-set.yml new file mode 100644 index 00000000..c37a8757 --- /dev/null +++ b/playbooks/automember/automember-default-hostgroup-not-set.yml @@ -0,0 +1,10 @@ +--- +- name: Automember default hostgroup not set + hosts: ipaserver + become: true + tasks: + - name: Ensure automember default hostgroup is not set + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + default_group: "" diff --git a/playbooks/automember/automember-default-hostgroup-set.yml b/playbooks/automember/automember-default-hostgroup-set.yml new file mode 100644 index 00000000..ac3b8d6d --- /dev/null +++ b/playbooks/automember/automember-default-hostgroup-set.yml @@ -0,0 +1,10 @@ +--- +- name: Automember default hostgroup set + hosts: ipaserver + become: true + tasks: + - name: Ensure automember default hostgroup is set + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + default_group: fallback_hostgroup diff --git a/plugins/modules/ipaautomember.py b/plugins/modules/ipaautomember.py index cd2aac68..0fab2f32 100644 --- a/plugins/modules/ipaautomember.py +++ b/plugins/modules/ipaautomember.py @@ -90,6 +90,9 @@ options: no_wait: description: Don't wait for rebuilding membership. type: bool + default_group: + description: Default (fallback) group for all unmatched entries. + type: str action: description: Work on automember or member level default: automember @@ -161,6 +164,33 @@ EXAMPLES = """ - host1.mydomain.com - host2.mydomain.com state: rebuilt + +# Ensure default group fallback_group for all unmatched group entries is set +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + default_group: fallback_group + +# Ensure default group for all unmatched group entries is not set +- ipaautomember: + ipaadmin_password: SomeADMINpassword + default_group: "" + automember_type: group + state: absent + +# Ensure default hostgroup fallback_hostgroup for all unmatched group entries +# is set +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + default_group: fallback_hostgroup + +# Ensure default hostgroup for all unmatched group entries is not set +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + default_group: "" + state: absent """ RETURN = """ @@ -168,7 +198,7 @@ RETURN = """ from ansible.module_utils.ansible_freeipa_module import ( - IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors + IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors, DN ) @@ -185,6 +215,20 @@ def find_automember(module, name, automember_type): return _result["result"] +def find_automember_default_group(module, automember_type): + _args = { + "all": True, + "type": automember_type + } + + try: + _result = module.ipa_command_no_name("automember_default_group_show", + _args) + except ipalib_errors.NotFound: + return None + return _result["result"] + + def gen_condition_args(automember_type, key, inclusiveregex=None, @@ -262,6 +306,7 @@ def main(): automember_type=dict(type='str', required=False, choices=['group', 'hostgroup']), no_wait=dict(type="bool", default=None), + default_group=dict(type="str", default=None), action=dict(type="str", default="automember", choices=["member", "automember"]), state=dict(type="str", default="present", @@ -291,6 +336,9 @@ def main(): # no_wait for rebuilt no_wait = ansible_module.params_get("no_wait") + # default_group + default_group = ansible_module.params_get("default_group") + # action action = ansible_module.params_get("action") # state @@ -306,7 +354,8 @@ def main(): invalid = [] if state in ["rebuilt"]: - invalid = ["name", "description", "exclusive", "inclusive"] + invalid = ["name", "description", "exclusive", "inclusive", + "default_group"] if action == "member": ansible_module.fail_json( @@ -323,7 +372,21 @@ def main(): (state, automember_type)) else: - invalid = ["users", "hosts", "no_wait"] + if default_group is not None: + for param in ["name", "exclusive", "inclusive", "users", "hosts" + "no_wait"]: + if ansible_module.params.get(param) is not None: + msg = "Cannot use {0} together with default_group" + ansible_module.fail_json(msg=msg.format(param)) + if action == "member": + ansible_module.fail_json( + msg="Cannot use default_group with action:member") + if state == "absent": + ansible_module.fail_json( + msg="Cannot use default_group with state:absent") + + else: + invalid = ["users", "hosts", "no_wait"] if not automember_type: ansible_module.fail_json( @@ -470,6 +533,29 @@ def main(): rebuild_hosts, no_wait) commands.append([None, 'automember_rebuild', args]) + elif default_group is not None and state == "present": + res_find = find_automember_default_group(ansible_module, + automember_type) + + if default_group == "": + if isinstance(res_find["automemberdefaultgroup"], list): + commands.append([None, + 'automember_default_group_remove', + {'type': automember_type}]) + ansible_module.warn("commands: %s" % repr(commands)) + + else: + dn_default_group = [DN(('cn', default_group), + ('cn', '%ss' % automember_type), + ('cn', 'accounts'), + ansible_module.ipa_get_basedn())] + if repr(res_find["automemberdefaultgroup"]) != \ + repr(dn_default_group): + commands.append( + [None, 'automember_default_group_set', + {'type': automember_type, + 'automemberdefaultgroup': default_group}]) + else: ansible_module.fail_json(msg="Invalid operation") diff --git a/tests/automember/test_automember_default_group.yml b/tests/automember/test_automember_default_group.yml new file mode 100644 index 00000000..9b082127 --- /dev/null +++ b/tests/automember/test_automember_default_group.yml @@ -0,0 +1,166 @@ +--- +- name: Test automember default groups + hosts: "{{ ipa_test_host | default('ipaserver') }}" + become: true + + tasks: + + # SET FACTS + + # CLEANUP TEST ITEMS + + - name: Ensure group testgroup is absent + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + state: absent + + - name: Ensure hostgroup testhostgroup is absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + state: absent + + - name: Ensure automember default group is unset + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: group + + - name: Ensure automember default hostgroup is unset + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: hostgroup + + # CREATE TEST ITEMS + + - name: Ensure group testgroup is present + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + state: present + register: result + failed_when: not result.changed or result.failed + + - name: Ensure hostgroup testhostgroup is present + ipahostgroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + state: present + register: result + failed_when: not result.changed or result.failed + + # TESTS + + # GROUP TEST + + - name: Ensure automember default group is set to testgroup + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: testgroup + automember_type: group + register: result + failed_when: not result.changed or result.failed + + - name: Ensure automember default group is set to testgroup, again + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: testgroup + automember_type: group + register: result + failed_when: result.changed or result.failed + + - name: Ensure automember default group is unset + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: group + register: result + failed_when: not result.changed or result.failed + + - name: Ensure automember default group is unset, again + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: group + register: result + failed_when: result.changed or result.failed + + # HOSTGROUP TEST + + - name: Ensure automember default hostgroup is set to testhostgroup + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: testhostgroup + automember_type: hostgroup + register: result + failed_when: not result.changed or result.failed + + - name: Ensure automember default hostgroup is set to testhostgroup, again + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: testhostgroup + automember_type: hostgroup + register: result + failed_when: result.changed or result.failed + + - name: Ensure automember default hostgroup is unset + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: hostgroup + register: result + failed_when: not result.changed or result.failed + + - name: Ensure automember default hostgroup is unset, again + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: hostgroup + register: result + failed_when: result.changed or result.failed + + # CLEANUP TEST ITEMS + + - name: Ensure group testgroup is absent + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + state: absent + + - name: Ensure hostgroup testhostgroup is absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + state: absent + + - name: Ensure automember default group is unset + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: group + + - name: Ensure automember default hostgroup is unset + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + default_group: "" + automember_type: hostgroup -- GitLab