1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-02-04 16:01:55 +00:00
community.general/plugins/modules/ipa_host.py
patchback[bot] b769b0bc01
[PR #11400/236b9c0e backport][stable-12] Sort imports with ruff check --fix (#11409)
Sort imports with ruff check --fix (#11400)

Sort imports with ruff check --fix.

(cherry picked from commit 236b9c0e04)

Co-authored-by: Felix Fontein <felix@fontein.de>
2026-01-09 19:36:52 +01:00

358 lines
11 KiB
Python

#!/usr/bin/python
# Copyright (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
module: ipa_host
author: Thomas Krahn (@Nosmoht)
short_description: Manage FreeIPA host
description:
- Add, modify and delete an IPA host using IPA API.
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
fqdn:
description:
- Full qualified domain name.
- Can not be changed as it is the unique identifier.
required: true
aliases: ["name"]
type: str
description:
description:
- A description of this host.
type: str
userclass:
description:
- Host category (semantics placed on this attribute are for local interpretation).
type: str
version_added: 12.0.0
force:
description:
- Force host name even if not in DNS.
type: bool
ip_address:
description:
- Add the host to DNS with this IP address.
type: str
mac_address:
description:
- List of Hardware MAC address(es) off this host.
- If option is omitted MAC addresses are not checked nor changed.
- If an empty list is passed all assigned MAC addresses are removed.
- MAC addresses that are already assigned but not passed are removed.
aliases: ["macaddress"]
type: list
elements: str
l:
description:
- Host locality (for example V(Baltimore, MD)).
aliases: ["locality"]
type: str
version_added: 12.0.0
ns_host_location:
description:
- Host location (for example V(Lab 2)).
aliases: ["nshostlocation"]
type: str
ns_hardware_platform:
description:
- Host hardware platform (for example V(Lenovo T61")).
aliases: ["nshardwareplatform"]
type: str
ns_os_version:
description:
- Host operating system and version (for example V(Fedora 9)).
aliases: ["nsosversion"]
type: str
user_certificate:
description:
- List of Base-64 encoded server certificates.
- If option is omitted certificates are not checked nor changed.
- If an empty list is passed all assigned certificates are removed.
- Certificates already assigned but not passed are removed.
aliases: ["usercertificate"]
type: list
elements: str
state:
description:
- State to ensure.
default: present
choices: ["absent", "disabled", "enabled", "present"]
type: str
force_creation:
description:
- Create host if O(state=disabled) or O(state=enabled) but not present.
default: true
type: bool
version_added: 9.5.0
update_dns:
description:
- If set V(true) with O(state=absent), then removes DNS records of the host managed by FreeIPA DNS.
- This option has no effect for states other than V(absent).
type: bool
random_password:
description: Generate a random password to be used in bulk enrollment.
type: bool
extends_documentation_fragment:
- community.general.ipa.documentation
- community.general.ipa.connection_notes
- community.general.attributes
"""
EXAMPLES = r"""
- name: Ensure host is present
community.general.ipa_host:
name: host01.example.com
description: Example host
userclass: Server
ip_address: 192.168.0.123
locality: Baltimore, MD
ns_host_location: Lab
ns_os_version: CentOS 7
ns_hardware_platform: Lenovo T61
mac_address:
- "08:00:27:E3:B1:2D"
- "52:54:00:BD:97:1E"
state: present
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
- name: Generate a random password for bulk enrolment
community.general.ipa_host:
name: host01.example.com
description: Example host
ip_address: 192.168.0.123
state: present
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
random_password: true
- name: Ensure host is disabled
community.general.ipa_host:
name: host01.example.com
state: disabled
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
- name: Ensure that all user certificates are removed
community.general.ipa_host:
name: host01.example.com
user_certificate: []
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
- name: Ensure host is absent
community.general.ipa_host:
name: host01.example.com
state: absent
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
- name: Ensure host and its DNS record is absent
community.general.ipa_host:
name: host01.example.com
state: absent
ipa_host: ipa.example.com
ipa_user: admin
ipa_pass: topsecret
update_dns: true
"""
RETURN = r"""
host:
description: Host as returned by IPA API.
returned: always
type: dict
host_diff:
description: List of options that differ and would be changed.
returned: if check mode and a difference is found
type: list
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.ipa import IPAClient, ipa_argument_spec
class HostIPAClient(IPAClient):
def __init__(self, module, host, port, protocol):
super().__init__(module, host, port, protocol)
def host_show(self, name):
return self._post_json(method="host_show", name=name)
def host_find(self, name):
return self._post_json(method="host_find", name=None, item={"all": True, "fqdn": name})
def host_add(self, name, host):
return self._post_json(method="host_add", name=name, item=host)
def host_mod(self, name, host):
return self._post_json(method="host_mod", name=name, item=host)
def host_del(self, name, update_dns):
return self._post_json(method="host_del", name=name, item={"updatedns": update_dns})
def host_disable(self, name):
return self._post_json(method="host_disable", name=name)
def get_host_dict(
description=None,
userclass=None,
force=None,
ip_address=None,
l=None,
ns_host_location=None,
ns_hardware_platform=None,
ns_os_version=None,
user_certificate=None,
mac_address=None,
random_password=None,
):
data = {}
if description is not None:
data["description"] = description
if userclass is not None:
data["userclass"] = userclass
if force is not None:
data["force"] = force
if ip_address is not None:
data["ip_address"] = ip_address
if l is not None:
data["l"] = l
if ns_host_location is not None:
data["nshostlocation"] = ns_host_location
if ns_hardware_platform is not None:
data["nshardwareplatform"] = ns_hardware_platform
if ns_os_version is not None:
data["nsosversion"] = ns_os_version
if user_certificate is not None:
data["usercertificate"] = [{"__base64__": item} for item in user_certificate]
if mac_address is not None:
data["macaddress"] = mac_address
if random_password is not None:
data["random"] = random_password
return data
def get_host_diff(client, ipa_host, module_host):
non_updateable_keys = ["force", "ip_address"]
if not module_host.get("random"):
non_updateable_keys.append("random")
for key in non_updateable_keys:
if key in module_host:
del module_host[key]
return client.get_diff(ipa_data=ipa_host, module_data=module_host)
def ensure(module, client):
name = module.params["fqdn"]
state = module.params["state"]
force_creation = module.params["force_creation"]
ipa_host = client.host_find(name=name)
module_host = get_host_dict(
description=module.params["description"],
userclass=module.params["userclass"],
force=module.params["force"],
ip_address=module.params["ip_address"],
l=module.params["l"],
ns_host_location=module.params["ns_host_location"],
ns_hardware_platform=module.params["ns_hardware_platform"],
ns_os_version=module.params["ns_os_version"],
user_certificate=module.params["user_certificate"],
mac_address=module.params["mac_address"],
random_password=module.params["random_password"],
)
changed = False
if state in ["present", "enabled", "disabled"]:
if not ipa_host and (force_creation or state == "present"):
changed = True
if not module.check_mode:
# OTP password generated by FreeIPA is visible only for host_add command
# so, return directly from here.
return changed, client.host_add(name=name, host=module_host)
else:
if state in ["disabled", "enabled"]:
module.fail_json(msg=f"No host with name {ipa_host} found")
diff = get_host_diff(client, ipa_host, module_host)
if len(diff) > 0:
changed = True
if not module.check_mode:
data = {}
for key in diff:
data[key] = module_host.get(key)
if "usercertificate" not in data:
data["usercertificate"] = [cert["__base64__"] for cert in ipa_host.get("usercertificate", [])]
ipa_host_show = client.host_show(name=name)
if ipa_host_show.get("has_keytab", True) and (
state == "disabled" or module.params.get("random_password")
):
client.host_disable(name=name)
return changed, client.host_mod(name=name, host=data)
elif state == "absent":
if ipa_host:
changed = True
update_dns = module.params.get("update_dns", False)
if not module.check_mode:
client.host_del(name=name, update_dns=update_dns)
return changed, client.host_find(name=name)
def main():
argument_spec = ipa_argument_spec()
argument_spec.update(
description=dict(type="str"),
fqdn=dict(type="str", required=True, aliases=["name"]),
force=dict(type="bool"),
ip_address=dict(type="str"),
l=dict(type="str", aliases=["locality"]),
ns_host_location=dict(type="str", aliases=["nshostlocation"]),
ns_hardware_platform=dict(type="str", aliases=["nshardwareplatform"]),
ns_os_version=dict(type="str", aliases=["nsosversion"]),
userclass=dict(type="str"),
user_certificate=dict(type="list", aliases=["usercertificate"], elements="str"),
mac_address=dict(type="list", aliases=["macaddress"], elements="str"),
update_dns=dict(type="bool"),
state=dict(type="str", default="present", choices=["present", "absent", "enabled", "disabled"]),
random_password=dict(type="bool", no_log=False),
force_creation=dict(type="bool", default=True),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
client = HostIPAClient(
module=module,
host=module.params["ipa_host"],
port=module.params["ipa_port"],
protocol=module.params["ipa_prot"],
)
try:
client.login(username=module.params["ipa_user"], password=module.params["ipa_pass"])
changed, host = ensure(module, client)
module.exit_json(changed=changed, host=host)
except Exception as e:
module.fail_json(msg=f"{e}", exception=traceback.format_exc())
if __name__ == "__main__":
main()