diff --git a/README-automember.md b/README-automember.md index eb44a4f0bcac6b8cc46d74d33e6e958b7065b83a..1e1f1d3a46c2444fbf9784baa6c5b2cfed06f7d6 100644 --- a/README-automember.md +++ b/README-automember.md @@ -230,6 +230,34 @@ Example playbook to ensure default hostgroup for all unmatched group entries is state: absent ``` +Example playbook to ensure all orphan automember group rules are removed: + +```yaml +- name: Playbook to ensure all orphan automember group rules are removed + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + state: orphans_removed +``` + +Example playbook to ensure all orphan automember hostgroup rules are removed: + +```yaml +- name: Playbook to ensure all orphan automember hostgroup rules are removed + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + state: orphans_removed +``` + Variables --------- @@ -253,7 +281,7 @@ Variable | Description | Required `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 +`state` | The state to ensure. It can be one of `present`, `absent`, 'rebuilt'. 'orphans_removed' default: `present`. | no Authors diff --git a/playbooks/automember/automember-group-orphans-removed.yml b/playbooks/automember/automember-group-orphans-removed.yml new file mode 100644 index 0000000000000000000000000000000000000000..46121101f8053390f19a5748fce2378c48f5afa6 --- /dev/null +++ b/playbooks/automember/automember-group-orphans-removed.yml @@ -0,0 +1,10 @@ +--- +- name: Automember orphan group rules are removed example + hosts: ipaserver + become: true + tasks: + - name: Ensure orphan group rules are removed + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + state: orphans_removed diff --git a/playbooks/automember/automember-hostgroup-orphans-removed.yml b/playbooks/automember/automember-hostgroup-orphans-removed.yml new file mode 100644 index 0000000000000000000000000000000000000000..62d7e93defbcb800db4f28c948a86d5992082a3b --- /dev/null +++ b/playbooks/automember/automember-hostgroup-orphans-removed.yml @@ -0,0 +1,10 @@ +--- +- name: Automember orphan hostgroup rules are removed example + hosts: ipaserver + become: true + tasks: + - name: Ensure orphan hostgroup rules are removed + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + state: orphans_removed diff --git a/plugins/modules/ipaautomember.py b/plugins/modules/ipaautomember.py index 0fab2f328a45e42522c54efae8398e73175ebd04..833a8a848803bd6045d38aa5ab95920a63381338 100644 --- a/plugins/modules/ipaautomember.py +++ b/plugins/modules/ipaautomember.py @@ -100,7 +100,7 @@ options: state: description: State to ensure default: present - choices: ["present", "absent", "rebuilt"] + choices: ["present", "absent", "rebuilt", "orphans_removed"] author: - Mark Hahl - Jake Reynolds @@ -191,6 +191,18 @@ EXAMPLES = """ automember_type: hostgroup default_group: "" state: absent + +# Example playbook to ensure all orphan automember group rules are removed: +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + state: orphans_removed + +# Example playbook to ensure all orphan automember hostgroup rules are removed: +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + state: orphans_removed """ RETURN = """ @@ -215,6 +227,19 @@ def find_automember(module, name, automember_type): return _result["result"] +def find_automember_orphans(module, automember_type): + _args = { + "all": True, + "type": automember_type + } + + try: + _result = module.ipa_command_no_name("automember_find_orphans", _args) + except ipalib_errors.NotFound: + return None + return _result + + def find_automember_default_group(module, automember_type): _args = { "all": True, @@ -310,7 +335,8 @@ def main(): action=dict(type="str", default="automember", choices=["member", "automember"]), state=dict(type="str", default="present", - choices=["present", "absent", "rebuilt"]), + choices=["present", "absent", "rebuilt", + "orphans_removed"]), users=dict(type="list", default=None), hosts=dict(type="list", default=None), ), @@ -353,7 +379,7 @@ def main(): # Check parameters invalid = [] - if state in ["rebuilt"]: + if state in ["rebuilt", "orphans_removed"]: invalid = ["name", "description", "exclusive", "inclusive", "default_group"] @@ -371,6 +397,13 @@ def main(): msg="state %s: users can not be set when type is '%s'" % (state, automember_type)) + elif state == "orphans_removed": + invalid.extend(["users", "hosts"]) + + if not automember_type: + ansible_module.fail_json( + msg="'automember_type' is required unless state: rebuilt") + else: if default_group is not None: for param in ["name", "exclusive", "inclusive", "users", "hosts" @@ -533,6 +566,14 @@ def main(): rebuild_hosts, no_wait) commands.append([None, 'automember_rebuild', args]) + elif state == "orphans_removed": + res_find = find_automember_orphans(ansible_module, + automember_type) + if res_find["count"] > 0: + commands.append([None, 'automember_find_orphans', + {'type': automember_type, + 'remove': True}]) + elif default_group is not None and state == "present": res_find = find_automember_default_group(ansible_module, automember_type) diff --git a/tests/automember/test_automember_orphans_removed.yml b/tests/automember/test_automember_orphans_removed.yml new file mode 100644 index 0000000000000000000000000000000000000000..0b9bcd3a32dd375364268ee22c9b2dc5dc790406 --- /dev/null +++ b/tests/automember/test_automember_orphans_removed.yml @@ -0,0 +1,250 @@ +--- +- name: Test automember orphans_removed + hosts: "{{ ipa_test_host | default('ipaserver') }}" + become: true + + tasks: + + # SET FACTS + + - name: Get Domain from server name + set_fact: + ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] | + join ('.') }}" + when: ipaserver_domain is not defined + + # CLEANUP TEST ITEMS + + - name: Ensure user testuser is absent + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testuser + state: absent + + - name: Ensure group testgroup is absent + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + state: absent + + - name: Ensure host testhost is absent + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "{{ 'testhost.' + ipaserver_domain }}" + 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 group testgroup is absent + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + automember_type: group + state: absent + + - name: Ensure automember hostgroup testhostgroup is absent + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + automember_type: hostgroup + state: absent + + # CREATE TEST ITEMS + + - name: Ensure user testuser is present + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testuser + first: Test + last: User + register: result + failed_when: not result.changed or result.failed + + - name: Ensure host testhost is present + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "{{ 'testhost.' + ipaserver_domain }}" + force: yes + reverse: no + register: result + failed_when: not result.changed or result.failed + + - 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 group testgroup exists + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + automember_type: group + register: result + failed_when: not result.changed or result.failed + + - name: Ensure automember group condition exits for users + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + automember_type: group + action: member + inclusive: + - key: uid + expression: uid + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group testgroup is absent + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group orphans have been removed + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: group + state: orphans_removed + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group orphans have been removed again + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: group + state: orphans_removed + register: result + failed_when: result.changed or result.failed + + # HOSTGROUP TEST + + - name: Ensure automember hostgroup testhostgroup exists + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + automember_type: hostgroup + register: result + failed_when: not result.changed or result.failed + + - name: Ensure automember hostgroup condition exits for hosts + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + automember_type: hostgroup + action: member + inclusive: + - key: fqdn + expression: "{{ '.*.' + ipaserver_domain }}" + register: result + failed_when: not result.changed or result.failed + + - name: Ensure hostgroup testhostgroup is absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure hostgroup orphans have been removed + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: hostgroup + state: orphans_removed + register: result + failed_when: not result.changed or result.failed + + - name: Ensure hostgroup orphans have been removed again + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: hostgroup + state: orphans_removed + register: result + failed_when: result.changed or result.failed + + # CLEANUP TEST ITEMS + + - name: Ensure user testuser is absent + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testuser + state: absent + + - name: Ensure group testgroup is absent + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + state: absent + + - name: Ensure host testhost is absent + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "{{ 'testhost.' + ipaserver_domain }}" + 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 group testgroup is absent + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testgroup + automember_type: group + state: absent + + - name: Ensure automember hostgroup testhostgroup is absent + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testhostgroup + automember_type: hostgroup + state: absent