Skip to content
Snippets Groups Projects
Select Git revision
  • 5c5b05a74f1ed073f848b7ffbad8de5dd603ea10
  • master default protected
  • v1.14.7
  • v1.14.6
  • v1.14.5
  • v1.14.4
  • v1.14.3
  • v1.14.2
  • v1.14.1
  • v1.14.0
  • v1.13.2
  • v1.13.1
  • v1.13.0
  • v1.12.1
  • v1.12.0
  • v1.11.1
  • v1.11.0
  • v1.10.0
  • v1.9.2
  • v1.9.1
  • v1.9.0
  • v1.8.4
22 results

ipaserver.py

Blame
  • ipaserver.py 12.95 KiB
    # -*- 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/>.
    
    from __future__ import (absolute_import, division, print_function)
    
    __metaclass__ = type
    
    ANSIBLE_METADATA = {
        "metadata_version": "1.0",
        "supported_by": "community",
        "status": ["preview"],
    }
    
    DOCUMENTATION = """
    ---
    module: ipaserver
    short description: Manage FreeIPA server
    description: Manage FreeIPA server
    extends_documentation_fragment:
      - ipamodule_base_docs
    options:
      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 already present in the topology
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
    
    # Ensure server server.example.com is absent from the topology
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        state: absent
    
    # Ensure server server.example.com has location mylocation
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        location: mylocation
    
    # Ensure server server.example.com does not have a location
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        location: ""
    
    # Ensure server server.example.com has service weight 1
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        service_weight: 1
    
    # Ensure server server.example.com does not have a service weight
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        service_weight: -1
    
    # Ensure server server.example.com is hidden
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        hidden: yes
    
    # Ensure server server.example.com is not hidden
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        hidden: no
    
    # Ensure server server.example.com is absent from the topology in continuous
    # mode to ignore errors
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        continue: yes
        state: absent
    
    # Ensure server "server.example.com" is absent from the topology with skipping
    # the last of role check
    - ipaserver:
        ipaadmin_password: SomeADMINpassword
        name: server.example.com
        ignore_last_of_role: yes
        state: absent
    
    # Ensure server server "server.example.com" is absent from the topology with
    # skipping the topology disconnect check
    - 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.ansible_freeipa_module import \
        IPAAnsibleModule, compare_args_ipa, DNSName
    
    
    def find_server(module, name):
        """Find if a server with the given name already exist."""
        try:
            _result = module.ipa_command("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 = module.ipa_command_no_name("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 = IPAAnsibleModule(
            argument_spec=dict(
                # general
                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
        names = ansible_module.params_get("name")
    
        # present
        location = ansible_module.params_get("location")
        service_weight = ansible_module.params_get("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 = ansible_module.params_get("hidden")
        no_members = ansible_module.params_get("no_members")
    
        # absent
        delete_continue = ansible_module.params_get("delete_continue")
        ignore_topology_disconnect = ansible_module.params_get(
            "ignore_topology_disconnect")
        ignore_last_of_role = ansible_module.params_get("ignore_last_of_role")
        force = ansible_module.params_get("force")
    
        # state
        state = ansible_module.params_get("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"]
    
        ansible_module.params_fail_used_invalid(invalid, state)
    
        # Init
    
        changed = False
        exit_args = {}
    
        # Connect to IPA API
        with ansible_module.ipa_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
    
            changed = ansible_module.execute_ipa_commands(commands)
    
        # Done
    
        ansible_module.exit_json(changed=changed, **exit_args)
    
    
    if __name__ == "__main__":
        main()