mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-02-04 16:01:55 +00:00
use FQCN for extending docs with files and url (#11277)
* use FQCN for extending docs with files and url
* remove typo
(cherry picked from commit 1b15e595e0)
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
280 lines
8.1 KiB
Python
280 lines
8.1 KiB
Python
#!/usr/bin/python
|
|
|
|
# Copyright (c) 2023, Salvatore Mesoraca <s.mesoraca16@gmail.com>
|
|
# GNU General Public License v3.0+ (see COPYING 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: kdeconfig
|
|
short_description: Manage KDE configuration files
|
|
version_added: "6.5.0"
|
|
description:
|
|
- Add or change individual settings in KDE configuration files.
|
|
- It uses B(kwriteconfig) under the hood.
|
|
options:
|
|
path:
|
|
description:
|
|
- Path to the config file. If the file does not exist it is created.
|
|
type: path
|
|
required: true
|
|
kwriteconfig_path:
|
|
description:
|
|
- Path to the kwriteconfig executable. If not specified, Ansible tries to discover it.
|
|
type: path
|
|
values:
|
|
description:
|
|
- List of values to set.
|
|
type: list
|
|
elements: dict
|
|
suboptions:
|
|
group:
|
|
description:
|
|
- The option's group. One between this and O(values[].groups) is required.
|
|
type: str
|
|
groups:
|
|
description:
|
|
- List of the option's groups. One between this and O(values[].group) is required.
|
|
type: list
|
|
elements: str
|
|
key:
|
|
description:
|
|
- The option's name.
|
|
type: str
|
|
required: true
|
|
value:
|
|
description:
|
|
- The option's value. One between this and O(values[].bool_value) is required.
|
|
type: str
|
|
bool_value:
|
|
description:
|
|
- Boolean value.
|
|
- One between this and O(values[].value) is required.
|
|
type: bool
|
|
required: true
|
|
backup:
|
|
description:
|
|
- Create a backup file.
|
|
type: bool
|
|
default: false
|
|
extends_documentation_fragment:
|
|
- ansible.builtin.files
|
|
- community.general.attributes
|
|
attributes:
|
|
check_mode:
|
|
support: full
|
|
diff_mode:
|
|
support: full
|
|
requirements:
|
|
- kwriteconfig
|
|
author:
|
|
- Salvatore Mesoraca (@smeso)
|
|
"""
|
|
|
|
EXAMPLES = r"""
|
|
- name: Ensure "Homepage=https://www.ansible.com/" in group "Branding"
|
|
community.general.kdeconfig:
|
|
path: /etc/xdg/kickoffrc
|
|
values:
|
|
- group: Branding
|
|
key: Homepage
|
|
value: https://www.ansible.com/
|
|
mode: '0644'
|
|
|
|
- name: Ensure "KEY=true" in groups "Group" and "Subgroup", and "KEY=VALUE" in Group2
|
|
community.general.kdeconfig:
|
|
path: /etc/xdg/someconfigrc
|
|
values:
|
|
- groups: [Group, Subgroup]
|
|
key: KEY
|
|
bool_value: true
|
|
- group: Group2
|
|
key: KEY
|
|
value: VALUE
|
|
backup: true
|
|
"""
|
|
|
|
RETURN = r""" # """
|
|
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
import traceback
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
|
|
|
|
|
class TemporaryDirectory:
|
|
"""Basic backport of tempfile.TemporaryDirectory"""
|
|
|
|
def __init__(self, suffix="", prefix="tmp", dir=None):
|
|
self.name = None
|
|
self.name = tempfile.mkdtemp(suffix, prefix, dir)
|
|
|
|
def __enter__(self):
|
|
return self.name
|
|
|
|
def rm(self):
|
|
if self.name:
|
|
shutil.rmtree(self.name, ignore_errors=True)
|
|
self.name = None
|
|
|
|
def __exit__(self, exc, value, tb):
|
|
self.rm()
|
|
|
|
def __del__(self):
|
|
self.rm()
|
|
|
|
|
|
def run_kwriteconfig(module, cmd, path, groups, key, value):
|
|
"""Invoke kwriteconfig with arguments"""
|
|
args = [cmd, "--file", path, "--key", key]
|
|
for group in groups:
|
|
args.extend(["--group", group])
|
|
if isinstance(value, bool):
|
|
args.extend(["--type", "bool"])
|
|
if value:
|
|
args.append("true")
|
|
else:
|
|
args.append("false")
|
|
else:
|
|
args.extend(["--", value])
|
|
module.run_command(args, check_rc=True)
|
|
|
|
|
|
def run_module(module, tmpdir, kwriteconfig):
|
|
result = dict(changed=False, msg="OK", path=module.params["path"])
|
|
b_path = to_bytes(module.params["path"])
|
|
tmpfile = os.path.join(tmpdir, "file")
|
|
b_tmpfile = to_bytes(tmpfile)
|
|
diff = dict(
|
|
before="",
|
|
after="",
|
|
before_header=result["path"],
|
|
after_header=result["path"],
|
|
)
|
|
try:
|
|
with open(b_tmpfile, "wb") as dst:
|
|
try:
|
|
with open(b_path, "rb") as src:
|
|
b_data = src.read()
|
|
except IOError:
|
|
result["changed"] = True
|
|
else:
|
|
dst.write(b_data)
|
|
try:
|
|
diff["before"] = to_text(b_data)
|
|
except UnicodeError:
|
|
diff["before"] = repr(b_data)
|
|
except IOError:
|
|
module.fail_json(msg="Unable to create temporary file", traceback=traceback.format_exc())
|
|
|
|
for row in module.params["values"]:
|
|
groups = row["groups"]
|
|
if groups is None:
|
|
groups = [row["group"]]
|
|
key = row["key"]
|
|
value = row["bool_value"]
|
|
if value is None:
|
|
value = row["value"]
|
|
run_kwriteconfig(module, kwriteconfig, tmpfile, groups, key, value)
|
|
|
|
with open(b_tmpfile, "rb") as tmpf:
|
|
b_data = tmpf.read()
|
|
try:
|
|
diff["after"] = to_text(b_data)
|
|
except UnicodeError:
|
|
diff["after"] = repr(b_data)
|
|
|
|
result["changed"] = result["changed"] or diff["after"] != diff["before"]
|
|
|
|
file_args = module.load_file_common_arguments(module.params)
|
|
|
|
if module.check_mode:
|
|
if not result["changed"]:
|
|
shutil.copystat(b_path, b_tmpfile)
|
|
uid, gid = module.user_and_group(b_path)
|
|
os.chown(b_tmpfile, uid, gid)
|
|
if module._diff:
|
|
diff = {}
|
|
else:
|
|
diff = None
|
|
result["changed"] = module.set_fs_attributes_if_different(file_args, result["changed"], diff=diff)
|
|
if module._diff:
|
|
result["diff"] = diff
|
|
module.exit_json(**result)
|
|
|
|
if result["changed"]:
|
|
if module.params["backup"] and os.path.exists(b_path):
|
|
result["backup_file"] = module.backup_local(result["path"])
|
|
try:
|
|
module.atomic_move(b_tmpfile, os.path.abspath(b_path))
|
|
except IOError:
|
|
module.ansible.fail_json(
|
|
msg=f"Unable to move temporary file {tmpfile} to {result['path']}, IOError",
|
|
traceback=traceback.format_exc(),
|
|
)
|
|
|
|
if result["changed"]:
|
|
module.set_fs_attributes_if_different(file_args, result["changed"])
|
|
else:
|
|
if module._diff:
|
|
diff = {}
|
|
else:
|
|
diff = None
|
|
result["changed"] = module.set_fs_attributes_if_different(file_args, result["changed"], diff=diff)
|
|
if module._diff:
|
|
result["diff"] = diff
|
|
module.exit_json(**result)
|
|
|
|
|
|
def main():
|
|
single_value_arg = dict(
|
|
group=dict(type="str"),
|
|
groups=dict(type="list", elements="str"),
|
|
key=dict(type="str", required=True, no_log=False),
|
|
value=dict(type="str"),
|
|
bool_value=dict(type="bool"),
|
|
)
|
|
required_alternatives = [("group", "groups"), ("value", "bool_value")]
|
|
module_args = dict(
|
|
values=dict(
|
|
type="list",
|
|
elements="dict",
|
|
options=single_value_arg,
|
|
mutually_exclusive=required_alternatives,
|
|
required_one_of=required_alternatives,
|
|
required=True,
|
|
),
|
|
path=dict(type="path", required=True),
|
|
kwriteconfig_path=dict(type="path"),
|
|
backup=dict(type="bool", default=False),
|
|
)
|
|
|
|
module = AnsibleModule(
|
|
argument_spec=module_args,
|
|
add_file_common_args=True,
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
kwriteconfig = None
|
|
if module.params["kwriteconfig_path"] is not None:
|
|
kwriteconfig = module.get_bin_path(module.params["kwriteconfig_path"], required=True)
|
|
else:
|
|
for progname in ("kwriteconfig6", "kwriteconfig5", "kwriteconfig", "kwriteconfig4"):
|
|
kwriteconfig = module.get_bin_path(progname)
|
|
if kwriteconfig is not None:
|
|
break
|
|
if kwriteconfig is None:
|
|
module.fail_json(msg="kwriteconfig is not installed")
|
|
for v in module.params["values"]:
|
|
if not v["key"]:
|
|
module.fail_json(msg="'key' cannot be empty")
|
|
with TemporaryDirectory(dir=module.tmpdir) as tmpdir:
|
|
run_module(module, tmpdir, kwriteconfig)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|