mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-04-07 19:23:05 +00:00
execute_ipa_commands replces the check mode exit, the loop over the generated commands and also in the member failure handling for modules with member support.
403 lines
13 KiB
Python
403 lines
13 KiB
Python
#!/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
|
|
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 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.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"]
|
|
|
|
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 = {}
|
|
|
|
# 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()
|