diff --git a/README-server.md b/README-server.md new file mode 100644 index 0000000000000000000000000000000000000000..b899cdaca02141e60b55018deb6717600c987f7b --- /dev/null +++ b/README-server.md @@ -0,0 +1,248 @@ +Server module +============ + +Description +----------- + +The server module allows to ensure presence and absence of servers. The module requires an existing server, the deployment of a new server can not be done with the module. + +Features +-------- + +* Server management + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.4.0 and up are supported by the ipaserver module. + + +Requirements +------------ + +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FreeIPA version (see above) + + +Usage +===== + +Example inventory file + +```ini +[ipaserver] +ipaserver.test.local +``` + + +Example playbook to make sure server "server.example.com" is present: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com +``` + + +Example playbook to make sure server "server.example.com" is present with location mylocation: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + location: mylocation +``` + + +Example playbook to make sure server "server.example.com" is present without a location: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + location: "" +``` + + +Example playbook to make sure server "server.example.com" is present with service weight 1: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + service_weight: 1 +``` + + +Example playbook to make sure server "server.example.com" is present without service weight: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + service_weight: -1 +``` + + +Example playbook to make sure server "server.example.com" is present and hidden: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + hidden: yes +``` + + +Example playbook to make sure server "server.example.com" is present and not hidden: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + hidden: no +``` + + +Example playbook to make sure server "server.example.com" is absent: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + state: absent +``` + + +Example playbook to make sure server "server.example.com" is absent in continuous mode in error case: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + continue: yes + state: absent +``` + + +Example playbook to make sure server "server.example.com" is absent with last of role check skip: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + ignore_last_of_role: yes + state: absent +``` + + +Example playbook to make sure server "server.example.com" is absent iwith topology disconnect check skip: + +```yaml +--- +- name: Playbook to manage IPA server. + hosts: ipaserver + become: yes + + tasks: + - ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + ignore_topology_disconnect: yes + state: absent +``` + + +MORE EXAMPLE PLAYBOOKS HERE + + +Variables +--------- + +ipaserver +------- + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no +`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no +`name` \| `cn` | The list of server name strings. | yes +`location` \| `ipalocation_location` | The server location string. Only in state: present. "" for location reset. | no +`service_weight` \| `ipaserviceweight` | Weight for server services. Type Values 0 to 65535, -1 for weight reset. Only in state: present. (int) | no +`hidden` | Set hidden state of a server. Only in state: present. (bool) | no +`no_members` | Suppress processing of membership attributes. Only in state: present. (bool) | no +`delete_continue` \| `continue` | Continuous mode: Don't stop on errors. Only in state: absent. (bool) | no +`ignore_last_of_role` | Skip a check whether the last CA master or DNS server is removed. Only in state: absent. (bool) | no +`ignore_topology_disconnect` | Ignore topology connectivity problems after removal. Only in state: absent. (bool) | no +`force` | Force server removal even if it does not exist. Will always result in changed. Only in state: absent. (bool) | no +`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. `present` is only working with existing servers. | no + + +Authors +======= + +Thomas Woerner diff --git a/README.md b/README.md index f0b6d6088e6be2d32a6a15c8ac55ca970967d6dc..6839c31e651887b5b3772904bdac56d93926b03a 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Features * Modules for pwpolicy management * Modules for role management * Modules for self service management +* Modules for server management * Modules for service management * Modules for sudocmd management * Modules for sudocmdgroup management @@ -439,6 +440,7 @@ Modules in plugin/modules * [ipapwpolicy](README-pwpolicy.md) * [iparole](README-role.md) * [ipaselfservice](README-ipaselfservice.md) +* [ipaserver](README-server.md) * [ipaservice](README-service.md) * [ipasudocmd](README-sudocmd.md) * [ipasudocmdgroup](README-sudocmdgroup.md) diff --git a/playbooks/server/server-absent-continue.yml b/playbooks/server/server-absent-continue.yml new file mode 100644 index 0000000000000000000000000000000000000000..40bba4a5e50e0175330dce5e1e17d5845eec5f87 --- /dev/null +++ b/playbooks/server/server-absent-continue.yml @@ -0,0 +1,12 @@ +--- +- name: Server absent continuous mode example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "absent.example.com" is absent continuous mode + ipaserver: + ipaadmin_password: SomeADMINpassword + name: absent.example.com + continue: yes + state: absent diff --git a/playbooks/server/server-absent-force.yml b/playbooks/server/server-absent-force.yml new file mode 100644 index 0000000000000000000000000000000000000000..702c405358bf37d2c9e86c11339473d2b0f479b8 --- /dev/null +++ b/playbooks/server/server-absent-force.yml @@ -0,0 +1,12 @@ +--- +- name: Server absent with force example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "absent.example.com" is absent with force + ipaserver: + ipaadmin_password: SomeADMINpassword + name: absent.example.com + force: yes + state: absent diff --git a/playbooks/server/server-absent-ignore_last_of_role.yml b/playbooks/server/server-absent-ignore_last_of_role.yml new file mode 100644 index 0000000000000000000000000000000000000000..364896044dd6bac0ec3c6e25f42a2e66a26821df --- /dev/null +++ b/playbooks/server/server-absent-ignore_last_of_role.yml @@ -0,0 +1,12 @@ +--- +- name: Server absent with last of role skip example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "absent.example.com" is absent with last of role skip + ipaserver: + ipaadmin_password: SomeADMINpassword + name: absent.example.com + ignore_last_of_role: yes + state: absent diff --git a/playbooks/server/server-absent-ignore_topology_disconnect.yml b/playbooks/server/server-absent-ignore_topology_disconnect.yml new file mode 100644 index 0000000000000000000000000000000000000000..cc76441c8e835df64acd752e2aecabb599d3dfc3 --- /dev/null +++ b/playbooks/server/server-absent-ignore_topology_disconnect.yml @@ -0,0 +1,12 @@ +--- +- name: Server absent with ignoring topology disconnects example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "absent.example.com" is absent with ignoring topology disconnects + ipaserver: + ipaadmin_password: SomeADMINpassword + name: absent.example.com + ignore_topology_disconnect: yes + state: absent diff --git a/playbooks/server/server-absent.yml b/playbooks/server/server-absent.yml new file mode 100644 index 0000000000000000000000000000000000000000..428d83705ad3aac6ef568e8f51c53bdcd9afe64d --- /dev/null +++ b/playbooks/server/server-absent.yml @@ -0,0 +1,11 @@ +--- +- name: Server absent example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "absent.example.com" is absent + ipaserver: + ipaadmin_password: SomeADMINpassword + name: absent.example.com + state: absent diff --git a/playbooks/server/server-hidden.yml b/playbooks/server/server-hidden.yml new file mode 100644 index 0000000000000000000000000000000000000000..0939ec38aece8ff185e6134d3698ff13e4634230 --- /dev/null +++ b/playbooks/server/server-hidden.yml @@ -0,0 +1,11 @@ +--- +- name: Server hidden example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "ipareplica1.example.com" is hidden + ipaserver: + ipaadmin_password: SomeADMINpassword + name: ipareplica1.example.com + hidden: True diff --git a/playbooks/server/server-location.yml b/playbooks/server/server-location.yml new file mode 100644 index 0000000000000000000000000000000000000000..99ce16fc025575cf1ee8af825a712c9ca10d09f2 --- /dev/null +++ b/playbooks/server/server-location.yml @@ -0,0 +1,11 @@ +--- +- name: Server enabled example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "{{ 'ipareplica1.' + ipaserver_domain }}" with location "mylocation" + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipareplica1.' + ipaserver_domain }}" + location: "mylocation" diff --git a/playbooks/server/server-no-location.yml b/playbooks/server/server-no-location.yml new file mode 100644 index 0000000000000000000000000000000000000000..87c0bdfdaa7dc175440c93b2ec4cd9cdde687f29 --- /dev/null +++ b/playbooks/server/server-no-location.yml @@ -0,0 +1,11 @@ +--- +- name: Server no location example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "ipareplica1.example.com" with no location + ipaserver: + ipaadmin_password: SomeADMINpassword + name: ipareplica1.example.com + location: "" diff --git a/playbooks/server/server-no-service-weight.yml b/playbooks/server/server-no-service-weight.yml new file mode 100644 index 0000000000000000000000000000000000000000..716214c5ec4d74468919389bbc0ed79e499d7d25 --- /dev/null +++ b/playbooks/server/server-no-service-weight.yml @@ -0,0 +1,11 @@ +--- +- name: Server service weight example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "ipareplica1.example.com" with no service weight + ipaserver: + ipaadmin_password: SomeADMINpassword + name: ipareplica1.example.com + service_weight: -1 diff --git a/playbooks/server/server-not-hidden.yml b/playbooks/server/server-not-hidden.yml new file mode 100644 index 0000000000000000000000000000000000000000..fd8a01c89c2dfb7d1590d695d3a3fd458234a61b --- /dev/null +++ b/playbooks/server/server-not-hidden.yml @@ -0,0 +1,11 @@ +--- +- name: Server not hidden example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "ipareplica1.example.com" is not hidden + ipaserver: + ipaadmin_password: SomeADMINpassword + name: ipareplica1.example.com + hidden: no diff --git a/playbooks/server/server-present.yml b/playbooks/server/server-present.yml new file mode 100644 index 0000000000000000000000000000000000000000..7937c169b8ab56c9fac85f7217c72ba04552ba07 --- /dev/null +++ b/playbooks/server/server-present.yml @@ -0,0 +1,10 @@ +--- +- name: Server present example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "ipareplica1.exmple.com" is present + ipaserver: + ipaadmin_password: SomeADMINpassword + name: ipareplica1.example.com diff --git a/playbooks/server/server-service-weight.yml b/playbooks/server/server-service-weight.yml new file mode 100644 index 0000000000000000000000000000000000000000..7a47edc03ff436cf9452416ace4f67c844e1ac2e --- /dev/null +++ b/playbooks/server/server-service-weight.yml @@ -0,0 +1,11 @@ +--- +- name: Server service weight example + hosts: ipaserver + become: true + + tasks: + - name: Ensure server "ipareplica1.example.com" with service weight 1 + ipaserver: + ipaadmin_password: SomeADMINpassword + name: ipareplica1.example.com + service_weight: 1 diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py index 32098b351a05c16fb0456481730d19a2cba93f3d..cf62b026a5580c7771e4982a13788019854e41e3 100644 --- a/plugins/module_utils/ansible_freeipa_module.py +++ b/plugins/module_utils/ansible_freeipa_module.py @@ -26,7 +26,7 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env", "DEFAULT_CONFIG", "LDAP_GENERALIZED_TIME_FORMAT", "kinit_password", "kinit_keytab", "run", "DN", "VERSION", "paths", "get_credentials_if_valid", "Encoding", - "load_pem_x509_certificate"] + "load_pem_x509_certificate", "DNSName"] import sys @@ -81,6 +81,7 @@ else: from ipapython.version import VERSION from ipaplatform.paths import paths from ipalib.krb_utils import get_credentials_if_valid + from ipapython.dnsutil import DNSName from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text from ansible.module_utils.common.text.converters import jsonify diff --git a/plugins/modules/ipaserver.py b/plugins/modules/ipaserver.py new file mode 100644 index 0000000000000000000000000000000000000000..167394e372bd1f6de15bc82009e8cf9fc43f418a --- /dev/null +++ b/plugins/modules/ipaserver.py @@ -0,0 +1,440 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2021 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +ANSIBLE_METADATA = { + "metadata_version": "1.0", + "supported_by": "community", + "status": ["preview"], +} + +DOCUMENTATION = """ +--- +module: ipaserver +short description: Manage FreeIPA server +description: Manage FreeIPA server +options: + ipaadmin_principal: + description: The admin principal. + default: admin + ipaadmin_password: + description: The admin password. + required: false + name: + description: The list of server name strings. + required: true + aliases: ["cn"] + location: + description: | + The server location string. + "" for location reset. + Only in state: present. + required: false + aliases: ["ipalocation_location"] + service_weight: + description: | + Weight for server services + Values 0 to 65535, -1 for weight reset. + Only in state: present. + required: false + type: int + aliases: ["ipaserviceweight"] + hidden: + description: | + Set hidden state of a server. + Only in state: present. + required: false + type: bool + no_members: + description: | + Suppress processing of membership attributes + Only in state: present. + required: false + type: bool + delete_continue: + description: | + Continuous mode: Don't stop on errors. + Only in state: absent. + required: false + type: bool + aliases: ["continue"] + ignore_last_of_role: + description: | + Skip a check whether the last CA master or DNS server is removed. + Only in state: absent. + required: false + type: bool + ignore_topology_disconnect: + description: | + Ignore topology connectivity problems after removal. + Only in state: absent. + required: false + type: bool + force: + description: | + Force server removal even if it does not exist. + Will always result in changed. + Only in state: absent. + required: false + type: bool + state: + description: The state to ensure. + choices: ["present", "absent"] + default: present + required: true +""" + +EXAMPLES = """ +# Ensure server server.example.com is present +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + +# Ensure server server.example.com is absent +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + state: absent + +# Ensure server server.example.com is present with location mylocation +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + location: mylocation + +# Ensure server server.example.com is present without a location +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + location: "" + +# Ensure server server.example.com is present with service weight 1 +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + service_weight: 1 + +# Ensure server server.example.com is present without service weight +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + service_weight: -1 + +# Ensure server server.example.com is present and hidden +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + hidden: yes + +# Ensure server server.example.com is present and not hidden +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + hidden: no + +# Ensure server server.example.com is absent in continuous mode in error case +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + continue: yes + state: absent + +# Ensure server server.example.com is absent with last of role check skip +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + ignore_last_of_role: yes + state: absent + +# Ensure server server.example.com is absent with topology disconnect check +# skip +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + ignore_topology_disconnect: yes + state: absent + +# Ensure server server.example.com is absent in force mode +- ipaserver: + ipaadmin_password: SomeADMINpassword + name: server.example.com + force: yes + state: absent +""" + +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, \ + api_command_no_name, compare_args_ipa, module_params_get, DNSName +import six + +if six.PY3: + unicode = str + + +def find_server(module, name): + """Find if a server with the given name already exist.""" + try: + _result = api_command(module, "server_show", name, {"all": True}) + except Exception: # pylint: disable=broad-except + # An exception is raised if server name is not found. + return None + else: + return _result["result"] + + +def server_role_status(module, name): + """Get server role of a hidden server with the given name.""" + try: + _result = api_command_no_name(module, "server_role_find", + {"server_server": name, + "role_servrole": 'IPA master', + "include_master": True, + "raw": True, + "all": True}) + except Exception: # pylint: disable=broad-except + # An exception is raised if server name is not found. + return None + else: + return _result["result"][0] + + +def gen_args(location, service_weight, no_members, delete_continue, + ignore_topology_disconnect, ignore_last_of_role, force): + _args = {} + if location is not None: + if location != "": + _args["ipalocation_location"] = DNSName(location) + else: + _args["ipalocation_location"] = None + if service_weight is not None: + _args["ipaserviceweight"] = service_weight + if no_members is not None: + _args["no_members"] = no_members + if delete_continue is not None: + _args["continue"] = delete_continue + if ignore_topology_disconnect is not None: + _args["ignore_topology_disconnect"] = ignore_topology_disconnect + if ignore_last_of_role is not None: + _args["ignore_last_of_role"] = ignore_last_of_role + if force is not None: + _args["force"] = force + + return _args + + +def main(): + ansible_module = AnsibleModule( + 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=["cn"], + default=None, required=True), + # present + location=dict(required=False, type='str', + aliases=["ipalocation_location"], default=None), + service_weight=dict(required=False, type='int', + aliases=["ipaserviceweight"], default=None), + hidden=dict(required=False, type='bool', default=None), + no_members=dict(required=False, type='bool', default=None), + # absent + delete_continue=dict(required=False, type='bool', + aliases=["continue"], default=None), + ignore_topology_disconnect=dict(required=False, type='bool', + default=None), + ignore_last_of_role=dict(required=False, type='bool', + default=None), + force=dict(required=False, type='bool', + default=None), + # state + state=dict(type="str", default="present", + choices=["present", "absent"]), + ), + supports_check_mode=True, + ) + + ansible_module._ansible_debug = True + + # 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") + + # present + location = module_params_get(ansible_module, "location") + service_weight = module_params_get(ansible_module, "service_weight") + # Service weight smaller than 0 leads to resetting service weight + if service_weight is not None and \ + (service_weight < -1 or service_weight > 65535): + ansible_module.fail_json( + msg="service_weight %d is out of range [-1 .. 65535]" % + service_weight) + if service_weight == -1: + service_weight = "" + hidden = module_params_get(ansible_module, "hidden") + no_members = module_params_get(ansible_module, "no_members") + + # absent + delete_continue = module_params_get(ansible_module, "delete_continue") + ignore_topology_disconnect = module_params_get( + ansible_module, "ignore_topology_disconnect") + ignore_last_of_role = module_params_get(ansible_module, + "ignore_last_of_role") + force = module_params_get(ansible_module, "force") + + # state + state = module_params_get(ansible_module, "state") + + # Check parameters + + invalid = [] + + if state == "present": + if len(names) != 1: + ansible_module.fail_json( + msg="Only one server can be ensured at a time.") + invalid = ["delete_continue", "ignore_topology_disconnect", + "ignore_last_of_role", "force"] + + if state == "absent": + if len(names) < 1: + ansible_module.fail_json(msg="No name given.") + invalid = ["location", "service_weight", "hidden", "no_members"] + + 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)) + + # Init + + 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() + + commands = [] + for name in names: + # Make sure server exists + res_find = find_server(ansible_module, name) + + # Generate args + args = gen_args(location, service_weight, no_members, + delete_continue, ignore_topology_disconnect, + ignore_last_of_role, force) + + # Create command + if state == "present": + # Server not found + if res_find is None: + ansible_module.fail_json( + msg="Server '%s' not found" % name) + + # Remove location from args if "" (transformed to None) + # and "ipalocation_location" not in res_find for idempotency + if "ipalocation_location" in args and \ + args["ipalocation_location"] is None and \ + "ipalocation_location" not in res_find: + del args["ipalocation_location"] + + # Remove service weight from args if "" + # and "ipaserviceweight" not in res_find for idempotency + if "ipaserviceweight" in args and \ + args["ipaserviceweight"] == "" and \ + "ipaserviceweight" not in res_find: + del args["ipaserviceweight"] + + # For all settings is args, check if there are + # different settings in the find result. + # If yes: modify + if not compare_args_ipa(ansible_module, args, res_find): + commands.append([name, "server_mod", args]) + + # hidden handling + if hidden is not None: + res_role_status = server_role_status(ansible_module, + name) + + if "status" in res_role_status: + # Fail if status is configured, it should be done + # only in the installer + if res_role_status["status"] == "configured": + ansible_module.fail_json( + msg="'%s' in configured state, " + "unable to change state" % state) + + if hidden and res_role_status["status"] == "enabled": + commands.append([name, "server_state", + {"state": "hidden"}]) + if not hidden and \ + res_role_status["status"] == "hidden": + commands.append([name, "server_state", + {"state": "enabled"}]) + + elif state == "absent": + if res_find is not None or force: + commands.append([name, "server_del", args]) + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + + # Execute commands + + for name, command, args in commands: + try: + result = api_command(ansible_module, command, name, + args) + if "completed" in result: + if result["completed"] > 0: + changed = True + else: + changed = True + except Exception as e: + 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) + + +if __name__ == "__main__": + main() diff --git a/tests/server/test_server.yml b/tests/server/test_server.yml new file mode 100644 index 0000000000000000000000000000000000000000..d42a55657ee5a24cd7e53e2c28a11d58d78eb4fa --- /dev/null +++ b/tests/server/test_server.yml @@ -0,0 +1,134 @@ +--- +- name: Test server + hosts: ipaserver + become: true + + tasks: + + # CLEANUP TEST ITEMS + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" without location + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + location: "" + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" without service weight + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + service_weight: -1 + + - name: Ensure location "mylocation" is absent + ipalocation: + ipaadmin_password: SomeADMINpassword + name: mylocation + state: absent + +# CREATE TEST ITEMS + + - name: Ensure location "mylocation" is present + ipalocation: + ipaadmin_password: SomeADMINpassword + name: mylocation + register: result + failed_when: not result.changed or result.failed + + # TESTS + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" is present + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + register: result + failed_when: result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" with location "mylocation" + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + location: "mylocation" + register: result + failed_when: not result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" with location "mylocation" again + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + location: "mylocation" + register: result + failed_when: result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" without location + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + location: "" + register: result + failed_when: not result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" without location again + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + location: "" + register: result + failed_when: result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" with service weight 1 + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + service_weight: 1 + register: result + failed_when: not result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" with service weight 1 again + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + service_weight: 1 + register: result + failed_when: result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" without service weight + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + service_weight: -1 + register: result + failed_when: not result.changed or result.failed + + - name: Ensure server "{{ 'ipaserver.' + ipaserver_domain }}" without service weight again + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'ipaserver.' + ipaserver_domain }}" + service_weight: -1 + register: result + failed_when: result.changed or result.failed + + # hidden requires an additional server, not tested + + # absent requires an additional server, only sanity test with absent server + + - name: Ensure server "{{ 'absent.' + ipaserver_domain }}" is absent + ipaserver: + ipaadmin_password: SomeADMINpassword + name: "{{ 'absent.' + ipaserver_domain }}" + state: absent + register: result + failed_when: result.changed or result.failed + + # ignore_last_of_role requires an additional server, not tested + + # ignore_topology_disconnect requires an additional server, not tested + + # CLEANUP TEST ITEMS + + - name: Ensure location "mylocation" is absent + ipalocation: + ipaadmin_password: SomeADMINpassword + name: mylocation + state: absent + register: result + failed_when: not result.changed or result.failed