diff --git a/plugins/modules/ipahostgroup.py b/plugins/modules/ipahostgroup.py index 9413c57ec5..3184bf866e 100644 --- a/plugins/modules/ipahostgroup.py +++ b/plugins/modules/ipahostgroup.py @@ -224,16 +224,20 @@ def main(): # Get parameters # general - names = ansible_module.params_get("name") + names = ansible_module.params_get_lowercase("name") # present 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") + hostgroup = ansible_module.params_get_lowercase("hostgroup") + membermanager_user = ansible_module.params_get_lowercase( + "membermanager_user" + ) + membermanager_group = ansible_module.params_get_lowercase( + "membermanager_group" + ) + rename = ansible_module.params_get_lowercase("rename") action = ansible_module.params_get("action") # state state = ansible_module.params_get("state") @@ -306,6 +310,12 @@ def main(): commands = [] for name in names: + # clean add/del lists + host_add, host_del = [], [] + hostgroup_add, hostgroup_del = [], [] + membermanager_user_add, membermanager_user_del = [], [] + membermanager_group_add, membermanager_group_del = [], [] + # Make sure hostgroup exists res_find = find_hostgroup(ansible_module, name) @@ -328,63 +338,26 @@ def main(): # Set res_find to empty dict for next step res_find = {} - member_args = gen_member_args(host, hostgroup) - if not compare_args_ipa(ansible_module, member_args, - res_find): - # Generate addition and removal lists - host_add, host_del = gen_add_del_lists( - host, res_find.get("member_host")) - - hostgroup_add, hostgroup_del = gen_add_del_lists( - hostgroup, res_find.get("member_hostgroup")) - - # Add members - if len(host_add) > 0 or len(hostgroup_add) > 0: - commands.append([name, "hostgroup_add_member", - { - "host": host_add, - "hostgroup": hostgroup_add, - }]) - # Remove members - if len(host_del) > 0 or len(hostgroup_del) > 0: - commands.append([name, "hostgroup_remove_member", - { - "host": host_del, - "hostgroup": hostgroup_del, - }]) - - membermanager_user_add, membermanager_user_del = \ - gen_add_del_lists( - membermanager_user, - res_find.get("membermanager_user") - ) + # Generate addition and removal lists + host_add, host_del = gen_add_del_lists( + host, res_find.get("member_host") + ) - membermanager_group_add, membermanager_group_del = \ - gen_add_del_lists( - membermanager_group, - res_find.get("membermanager_group") - ) + hostgroup_add, hostgroup_del = gen_add_del_lists( + hostgroup, res_find.get("member_hostgroup") + ) if has_add_membermanager: - # Add membermanager users and groups - if len(membermanager_user_add) > 0 or \ - len(membermanager_group_add) > 0: - commands.append( - [name, "hostgroup_add_member_manager", - { - "user": membermanager_user_add, - "group": membermanager_group_add, - }] + membermanager_user_add, membermanager_user_del = \ + gen_add_del_lists( + membermanager_user, + res_find.get("membermanager_user") ) - # Remove member manager - if len(membermanager_user_del) > 0 or \ - len(membermanager_group_del) > 0: - commands.append( - [name, "hostgroup_remove_member_manager", - { - "user": membermanager_user_del, - "group": membermanager_group_del, - }] + + membermanager_group_add, membermanager_group_del = \ + gen_add_del_lists( + membermanager_group, + res_find.get("membermanager_group") ) elif action == "member": @@ -394,45 +367,25 @@ def main(): # Reduce add lists for member_host and member_hostgroup, # to new entries only that are not in res_find. - if host is not None and "member_host" in res_find: - host = gen_add_list(host, res_find["member_host"]) - if hostgroup is not None \ - and "member_hostgroup" in res_find: - hostgroup = gen_add_list( - hostgroup, res_find["member_hostgroup"]) - - # Ensure members are present - commands.append([name, "hostgroup_add_member", - { - "host": host, - "hostgroup": hostgroup, - }]) + host_add = gen_add_list( + host, res_find.get("member_host") + ) + hostgroup_add = gen_add_list( + hostgroup, res_find.get("member_hostgroup") + ) if has_add_membermanager: # Reduce add list for membermanager_user and # membermanager_group to new entries only that are # not in res_find. - if membermanager_user is not None \ - and "membermanager_user" in res_find: - membermanager_user = gen_add_list( - membermanager_user, - res_find["membermanager_user"]) - if membermanager_group is not None \ - and "membermanager_group" in res_find: - membermanager_group = gen_add_list( - membermanager_group, - res_find["membermanager_group"]) - - # Add membermanager users and groups - if membermanager_user is not None or \ - membermanager_group is not None: - commands.append( - [name, "hostgroup_add_member_manager", - { - "user": membermanager_user, - "group": membermanager_group, - }] - ) + membermanager_user_add = gen_add_list( + membermanager_user, + res_find.get("membermanager_user") + ) + membermanager_group_add = gen_add_list( + membermanager_group, + res_find.get("membermanager_group") + ) elif state == "renamed": if res_find is not None: @@ -463,46 +416,72 @@ def main(): # Reduce del lists of member_host and member_hostgroup, # to the entries only that are in res_find. if host is not None: - host = gen_intersection_list( - host, res_find.get("member_host")) + host_del = gen_intersection_list( + host, res_find.get("member_host") + ) if hostgroup is not None: - hostgroup = gen_intersection_list( - hostgroup, res_find.get("member_hostgroup")) - - # Ensure members are absent - commands.append([name, "hostgroup_remove_member", - { - "host": host, - "hostgroup": hostgroup, - }]) + hostgroup_del = gen_intersection_list( + hostgroup, res_find.get("member_hostgroup") + ) if has_add_membermanager: - # Reduce del lists of membermanager_user and - # membermanager_group to the entries only that are - # in res_find. - if membermanager_user is not None: - membermanager_user = gen_intersection_list( - membermanager_user, - res_find.get("membermanager_user")) - if membermanager_group is not None: - membermanager_group = gen_intersection_list( - membermanager_group, - res_find.get("membermanager_group")) - - # Remove membermanager users and groups - if membermanager_user is not None or \ - membermanager_group is not None: - commands.append( - [name, "hostgroup_remove_member_manager", - { - "user": membermanager_user, - "group": membermanager_group, - }] - ) + # Get lists of membermanager users that exist + # in IPA and should be removed. + membermanager_user_del = gen_intersection_list( + membermanager_user, + res_find.get("membermanager_user") + ) + membermanager_group_del = gen_intersection_list( + membermanager_group, + res_find.get("membermanager_group") + ) else: ansible_module.fail_json(msg="Unkown state '%s'" % state) + # Manage members + + # Add members + if host_add or hostgroup_add: + commands.append([ + name, "hostgroup_add_member", + { + "host": host_add, + "hostgroup": hostgroup_add, + } + ]) + + # Remove members + if host_del or hostgroup_del: + commands.append([ + name, "hostgroup_remove_member", + { + "host": host_del, + "hostgroup": hostgroup_del, + } + ]) + + # Manage membermanager users and groups + if has_add_membermanager: + # Add membermanager users and groups + if membermanager_user_add or membermanager_group_add: + commands.append([ + name, "hostgroup_add_member_manager", + { + "user": membermanager_user_add, + "group": membermanager_group_add, + } + ]) + # Remove membermanager users and groups + if membermanager_user_del or membermanager_group_del: + commands.append([ + name, "hostgroup_remove_member_manager", + { + "user": membermanager_user_del, + "group": membermanager_group_del, + } + ]) + # Execute commands changed = ansible_module.execute_ipa_commands( diff --git a/tests/hostgroup/test_hostgroup_case_insensitive.yml b/tests/hostgroup/test_hostgroup_case_insensitive.yml new file mode 100644 index 0000000000..ca0b7bad2b --- /dev/null +++ b/tests/hostgroup/test_hostgroup_case_insensitive.yml @@ -0,0 +1,144 @@ +--- +- name: Test hostgroup members case insensitive + hosts: ipaserver + become: true + gather_facts: false + module_defaults: + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + ipahostgroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + + vars: + # Hostnames are supposed to have first letter + # capitalized for this test. + test_hosts: + - Host1 + - Host2 + test_hostgroups: + - testhostgroup1 + # TestHostgrop2 is meant to use CamelCase here. + - TestHostGroup2 + + tasks: + - name: Test in all supported versions of IPA + block: + # setup environment + - name: Ensure domain name is set + ansible.builtin.set_fact: + ipa_domain: "test.local" + when: ipa_domain is not defined + + - name: Ensure hostgroup testhostgroup1 and testhostgroup2 are absent + ipahostgroup: + name: "{{ test_hostgroups }}" + state: absent + + - name: Ensure test hosts are present + ipahost: + name: "{{ item }}.{{ ipa_domain }}" + force: true + loop: "{{ test_hosts }}" + + - name: Ensure hostgroup testhostgroup2 is present + ipahostgroup: + name: testhostgroup2 + + # tests + - name: Hostgroup should not be renamed only due to case + ipahostgroup: + name: testhostgroup2 + rename: testhostgroup2 + state: renamed + register: result + failed_when: result.changed or result.failed + + - name: Test hostgroup presence with single host and action hostgroup + vars: + test_cases: + - { id: 1, value: "{{ test_hosts[0] | lower }}", expected: true } + - { id: 2, value: "{{ test_hosts[0] | upper }}", expected: false } + - { id: 3, value: "{{ test_hosts[0] }}", expected: false } + block: + - name: "Ensure hostgroup testhostgroup with host 'host1'" + ipahostgroup: + name: testhostgroup1 + host: "{{ item.value }}" + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "Test id: {{ item.id }}" + + - name: Test hostgroup presence with multiple hosts and action hostgroup + vars: + test_cases: + - { id: 1, value: "{{ test_hosts | lower }}", expected: true } + - { id: 2, value: "{{ test_hosts | upper }}", expected: false } + - { id: 3, value: "{{ test_hosts }}", expected: false } + - { id: 4, value: "{{ test_hosts[1] }}", expected: true } + - { id: 5, value: "{{ test_hosts[1] | lower }}", expected: false } + - { id: 6, value: "{{ test_hosts[1] | upper }}", expected: false } + - { id: 7, value: "{{ test_hosts[0] }}", expected: true } + - { id: 8, value: "{{ test_hosts[0] | lower }}", expected: false } + - { id: 9, value: "{{ test_hosts[0] | upper }}", expected: false } + block: + - name: "Ensure hostgroup testhostgroup with host 'host1'" + ipahostgroup: + name: testhostgroup1 + host: "{{ item.value }}" + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "Test id: {{ item.id }}" + + - name: Test hostgroup with multiple hosts and action member + vars: + test_cases: + - { id: 1, value: "{{ test_hosts | lower }}", state: "absent", expected: true } + - { id: 2, value: "{{ test_hosts | upper }}", state: "absent", expected: false } + - { id: 3, value: "{{ test_hosts }}", state: "present", expected: true } + - { id: 4, value: "{{ test_hosts[1] }}", state: "absent", expected: true } + - { id: 5, value: "{{ test_hosts[1] | lower }}", state: "absent", expected: false } + - { id: 6, value: "{{ test_hosts[1] | upper }}", state: "absent", expected: false } + - { id: 7, value: "{{ test_hosts[0] | lower }}", state: "present", expected: false } + - { id: 8, value: "{{ test_hosts[0] }}", state: "present", expected: false } + - { id: 9, value: "{{ test_hosts[0] | upper }}", state: "present", expected: false } + - { id: 10, value: "{{ test_hosts | upper }}", state: "present", expected: true } + - { id: 11, value: "{{ test_hosts[1] }}", state: "present", expected: false } + - { id: 12, value: "{{ test_hosts[0] | lower }}", state: "present", expected: false } + - { id: 13, value: "{{ test_hosts[0] }}", state: "absent", expected: true } + - { id: 14, value: "{{ test_hosts[0] | lower }}", state: "absent", expected: false } + - { id: 15, value: "{{ test_hosts[0] | upper }}", state: "absent", expected: false } + block: + - name: "Ensure hostgroup testhostgroup with host 'host1'" + ipahostgroup: + name: testhostgroup1 + host: "{{ item.value }}" + action: member + state: "{{ item.state }}" + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "Test id: {{ item.id }}" + always: + # cleanup + - name: Ensure hostgroup testhostgroup is absent + ipahostgroup: + name: "{{ test_hostgroups }}" + state: absent + + - name: Ensure test hosts are absent + ipahost: + name: "{{ test_hosts | product([ipa_domain]) | map('join') | list }}" + state: absent diff --git a/tests/hostgroup/test_hostgroup_membermanager_case_insensitive.yml b/tests/hostgroup/test_hostgroup_membermanager_case_insensitive.yml new file mode 100644 index 0000000000..ff3a2a0add --- /dev/null +++ b/tests/hostgroup/test_hostgroup_membermanager_case_insensitive.yml @@ -0,0 +1,179 @@ +--- +- name: Test hostgroup membermanagers + hosts: ipaserver + become: true + gather_facts: false + module_defaults: + ipagroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + ipahostgroup: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + + tasks: + - name: Include tasks ../env_freeipa_facts.yml + ansible.builtin.include_tasks: ../env_freeipa_facts.yml + + - name: Tests requiring IPA version 4.8.4+ + when: ipa_version is version('4.8.4', '>=') + block: + # setup environment + - name: Ensure host-group testhostgroup is absent + ipahostgroup: + name: testhostgroup + state: absent + + - name: Ensure user manageruser1 and manageruser2 are present + ipauser: + users: + - name: manageruser1 + first: manageruser1 + last: Last1 + - name: manageruser2 + first: manageruser2 + last: Last2 + + - name: Ensure managergroup1 and managergroup2 are present + ipagroup: + groups: + - name: managergroup1 + - name: managergroup2 + + # tests + - name: Ensure host-group testhostgroup is present + ipahostgroup: + name: testhostgroup + + - name: Test membermanager_user parameter presence + vars: + test_cases: + - { id: 1, value: "{{ 'ManagerUser1' | lower }}", expected: true } + - { id: 2, value: "{{ 'ManagerUser1' | upper }}", expected: false } + - { id: 3, value: 'ManagerUser1', expected: false } + block: + - name: "Ensure membermanager_user 'manageruser1' is present for testhostgroup" + ipahostgroup: + name: testhostgroup + membermanager_user: "{{ item.value }}" + action: member + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "{{ item.value }}" + + - name: Test membermanager_group parameter presence + vars: + test_cases: + - { id: 1, value: "{{ 'ManagerGroup1' | upper }}", expected: true } + - { id: 2, value: "{{ 'ManagerGroup1' | lower }}", expected: false } + - { id: 3, value: 'ManagerGroup1', expected: false } + block: + - name: "Ensure membermanager_group 'managergroup1' is present for testhostgroup" + ipahostgroup: + name: testhostgroup + membermanager_group: "{{ item.value }}" + action: member + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "{{ item.value }}" + + - name: Test membermanager_group and membermanager_user parameters presence + vars: + test_cases: + - { id: 1, user: 'ManagerUser2', group: 'ManagerGroup2', expected: true } + - { id: 2, user: "{{ 'ManagerUser2' | upper }}", group: "{{ 'ManagerGroup2' | upper }}", expected: false } + - { id: 3, user: "{{ 'ManagerUser2' | lower }}", group: "{{ 'ManagerGroup2' | lower }}", expected: false } + block: + - name: "Ensure membermanager_group 'managergroup2' and membermanager_user 'manageruser2' are present for testhostgroup" + ipahostgroup: + name: testhostgroup + membermanager_group: "{{ item.group }}" + membermanager_user: "{{ item.user }}" + action: member + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "Test id: {{ item.id }}" + + - name: Test membermanager_group parameter absence + vars: + test_cases: + - { id: 1, value: 'ManagerGroup1', expected: true } + - { id: 2, value: "{{ 'ManagerGroup1' | lower }}", expected: false } + - { id: 3, value: "{{ 'ManagerGroup1' | upper }}", expected: false } + block: + - name: "Ensure membermanager_group 'managergroup1' is absent for testhostgroup" + ipahostgroup: + name: testhostgroup + membermanager_group: "{{ item.value }}" + action: member + state: absent + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "{{ item.value }}" + + - name: Test membermanager_user parameter absence + vars: + test_cases: + - { id: 1, value: 'ManagerUser1', expected: true } + - { id: 2, value: "{{ 'ManagerUser1' | lower }}", expected: false } + - { id: 3, value: "{{ 'ManagerUser1' | upper }}", expected: false } + block: + - name: "Ensure membermanager_user 'manageruser1' is absent for testhostgroup" + ipahostgroup: + name: testhostgroup + membermanager_user: "{{ item.value }}" + action: member + state: absent + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "{{ item.value }}" + + - name: Test membermanager_group and membermanager_user parameters absence + vars: + test_cases: + - { id: 1, user: "{{ 'ManagerUser2' | lower }}", group: "{{ 'ManagerGroup2' | lower }}", expected: true } + - { id: 2, user: 'ManagerUser2', group: 'ManagerGroup2', expected: false } + - { id: 3, user: "{{ 'ManagerUser2' | upper }}", group: "{{ 'ManagerGroup2' | upper }}", expected: false } + block: + - name: "Ensure membermanager_user 'manageruser2' and membermanager_group 'managergroup2' are absent for testhostgroup" + ipahostgroup: + name: testhostgroup + membermanager_group: "{{ item.group }}" + membermanager_user: "{{ item.user }}" + action: member + state: absent + register: output + failed_when: output.changed != item.expected or output.failed + loop: "{{ test_cases }}" + loop_control: + label: "Test id: {{ item.id }}" + + always: + # cleanup + - name: Ensure host-group testhostgroup is absent + ipahostgroup: + name: testhostgroup + state: absent + + - name: Ensure user manangeruser1 and manageruser2 is absent + ipauser: + name: manageruser1,manageruser2 + state: absent + + - name: Ensure group managergroup1 and managergroup2 are absent + ipagroup: + name: managergroup1,managergroup2 + state: absent