diff --git a/README-hostgroup.md b/README-hostgroup.md index e021d89a444af71119b4abcf468f5a50b491eb59..cedca7e2729b0fcb0b00b65450bd107e47133570 100644 --- a/README-hostgroup.md +++ b/README-hostgroup.md @@ -19,6 +19,8 @@ Supported FreeIPA Versions FreeIPA versions 4.4.0 and up are supported by the ipahostgroup module. +Some variables are only supported on newer versions of FreeIPA. Check `Variables` section for details. + Requirements ------------ @@ -105,6 +107,23 @@ Example playbook to make sure hosts and hostgroups are absent in databases hostg state: absent ``` +Example playbook to rename an existing playbook: + +```yaml +--- +- name: Playbook to handle hostgroups + hosts: ipaserver + become: true + + tasks: + # Ensure host-group databases is absent + - ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + rename: datalake + state: renamed +``` + Example playbook to make sure host-group databases is absent: ```yaml @@ -121,7 +140,6 @@ Example playbook to make sure host-group databases is absent: state: absent ``` - Variables ========= @@ -139,8 +157,9 @@ Variable | Description | Required `hostgroup` | List of hostgroup name strings assigned to this hostgroup. | no `membermanager_user` | List of member manager users assigned to this hostgroup. Only usable with IPA versions 4.8.4 and up. | no `membermanager_group` | List of member manager groups assigned to this hostgroup. Only usable with IPA versions 4.8.4 and up. | no +`rename` \| `new_name` | Rename hostgroup to the provided name. Only usable with IPA versions 4.8.7 and up. | no `action` | Work on hostgroup or member level. It can be on of `member` or `hostgroup` and defaults to `hostgroup`. | no -`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | no +`state` | The state to ensure. It can be one of `present`, `absent` or `renamed`, default: `present`. | no Authors diff --git a/playbooks/hostgroup/rename-hostgroup.yml b/playbooks/hostgroup/rename-hostgroup.yml new file mode 100644 index 0000000000000000000000000000000000000000..53937aa150c2f83a56691e7955c89f6ce991e51b --- /dev/null +++ b/playbooks/hostgroup/rename-hostgroup.yml @@ -0,0 +1,12 @@ +--- +- name: Playbook to handle hostgroups + hosts: ipaserver + become: yes + + tasks: + - name : Rename host-group from `databases` to `datalake` + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + rename: datalake + state: renamed diff --git a/plugins/modules/ipahostgroup.py b/plugins/modules/ipahostgroup.py index 4c18e940e61d481a2ac990f739178a19eaddc0c1..8616fbaac0ce1e85d95cca15844e15252e9098a0 100644 --- a/plugins/modules/ipahostgroup.py +++ b/plugins/modules/ipahostgroup.py @@ -70,6 +70,12 @@ options: - Only usable with IPA versions 4.8.4 and up. required: false type: list + rename: + description: + - Rename hostgroup to the given name. + - Only usable with IPA versions 4.8.7 and up. + required: false + aliases: ["new_name"] action: description: Work on hostgroup or member level default: hostgroup @@ -77,7 +83,7 @@ options: state: description: State to ensure default: present - choices: ["present", "absent"] + choices: ["present", "absent", "renamed"] author: - Thomas Woerner """ @@ -116,6 +122,12 @@ EXAMPLES = """ action: member state: absent +# Rename hostgroup +- ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + rename: datalake + # Ensure host-group databases is absent - ipahostgroup: ipaadmin_password: SomeADMINpassword @@ -129,7 +141,7 @@ 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 + module_params_get, gen_add_del_lists, api_check_command, api_check_param def find_hostgroup(module, name): @@ -149,12 +161,14 @@ def find_hostgroup(module, name): return None -def gen_args(description, nomembers): +def gen_args(description, nomembers, rename): _args = {} if description is not None: _args["description"] = description if nomembers is not None: _args["nomembers"] = nomembers + if rename is not None: + _args["rename"] = rename return _args @@ -186,11 +200,13 @@ def main(): membermanager_user=dict(required=False, type='list', default=None), membermanager_group=dict(required=False, type='list', default=None), + rename=dict(required=False, type='str', default=None, + aliases=["new_name"]), action=dict(type="str", default="hostgroup", choices=["member", "hostgroup"]), # state state=dict(type="str", default="present", - choices=["present", "absent"]), + choices=["present", "absent", "renamed"]), ), supports_check_mode=True, ) @@ -215,6 +231,7 @@ def main(): "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") # state state = module_params_get(ansible_module, "state") @@ -222,22 +239,41 @@ def main(): # Check parameters if state == "present": + if len(names) != 1: + ansible_module.fail_json( + msg="Only one hostgroup can be added at a time.") + invalid = ["rename"] + if action == "member": + invalid.extend(["description", "nomembers"]) + for x in invalid: + if vars()[x] is not None: + ansible_module.fail_json( + msg="Argument '%s' can not be used with action " + "'%s'" % (x, action)) + + if state == "renamed": if len(names) != 1: ansible_module.fail_json( msg="Only one hostgroup can be added at a time.") if action == "member": - invalid = ["description", "nomembers"] - for x in invalid: - if vars()[x] is not None: - ansible_module.fail_json( - msg="Argument '%s' can not be used with action " - "'%s'" % (x, action)) + ansible_module.fail_json( + msg="Action '%s' can not be used with state '%s'" % + (action, state)) + invalid = [ + "description", "nomembers", "host", "hostgroup", + "membermanager_user", "membermanager_group" + ] + for x in invalid: + if vars()[x] is not None: + ansible_module.fail_json( + msg="Argument '%s' can not be used with state '%s'" % + (x, state)) if state == "absent": if len(names) < 1: ansible_module.fail_json( msg="No name given.") - invalid = ["description", "nomembers"] + invalid = ["description", "nomembers", "rename"] if action == "hostgroup": invalid.extend(["host", "hostgroup"]) for x in invalid: @@ -266,6 +302,10 @@ 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") + if not has_mod_rename and rename is not None: + ansible_module.fail_json( + msg="Renaming hostgroups is not supported by your IPA version") commands = [] @@ -276,7 +316,7 @@ def main(): # Create command if state == "present": # Generate args - args = gen_args(description, nomembers) + args = gen_args(description, nomembers, rename) if action == "hostgroup": # Found the hostgroup @@ -375,6 +415,22 @@ def main(): }] ) + elif state == "renamed": + if res_find is not None: + if rename != name: + commands.append( + [name, "hostgroup_mod", {"rename": rename}] + ) + else: + # If a hostgroup with the desired name exists, do nothing. + new_find = find_hostgroup(ansible_module, rename) + if new_find is None: + # Fail only if the either hostsgroups do not exist. + ansible_module.fail_json( + msg="Attribute `rename` can not be used, unless " + "hostgroup exists." + ) + elif state == "absent": if action == "hostgroup": if res_find is not None: diff --git a/tests/hostgroup/test_hostgroup_rename.yml b/tests/hostgroup/test_hostgroup_rename.yml new file mode 100644 index 0000000000000000000000000000000000000000..8d13338fa5a907f6738518d1af741e3f0c9b70cd --- /dev/null +++ b/tests/hostgroup/test_hostgroup_rename.yml @@ -0,0 +1,105 @@ +--- +- name: Test hostgroup + hosts: ipaserver + become: true + gather_facts: false + + tasks: + - name: Ensure testing host-group are absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: + - databases + - datalake + - inexistenthostgroup + state: absent + + - name: Ensure host-group databases is present + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + state: present + register: result + failed_when: not result.changed + + - name: Rename host-group from `databases` to `datalake` + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + rename: datalake + state: renamed + register: result + failed_when: not result.changed + + - name: Ensure host-group database was already absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: database + state: absent + register: result + failed_when: result.changed + + - name: Rename host-group from `databases` to `datalake`, again + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + rename: datalake + state: renamed + register: result + failed_when: result.changed or result.failed + + - name: Rename host-group with same name. + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: datalake + rename: datalake + state: renamed + register: result + failed_when: result.changed + + - name: Ensure testing hostgroups do not exist. + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: inexistenthostgroup,alsoinexistent + state: absent + + - name: Rename inexistent host-group to an existing one. + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: inexistenthostgroup + rename: datalake + state: renamed + register: result + failed_when: result.changed or result.failed + + - name: Rename inexistent host-group to a non-existing one. + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: inexistenthostgroup + rename: alsoinexistent + state: renamed + register: result + failed_when: not result.failed or "Attribute `rename` can not be used, unless hostgroup exists." not in result.msg + + - name: Ensure host-group databases is present + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + state: present + + - name: Rename host-group to an existing one. + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: databases + rename: datalake + state: renamed + register: result + failed_when: not result.failed or "This entry already exists" not in result.msg + + - name: Ensure host-group databases and datalake are absent + ipahostgroup: + ipaadmin_password: SomeADMINpassword + name: + - databases + - datalake + state: absent