mirror of
https://github.com/containers/ansible-podman-collections.git
synced 2026-02-03 23:01:48 +00:00
This commit introduces two new modules for managing Podman Quadlets: - podman_quadlet: Install and remove Podman Quadlet files * Supports installing single files, directories, and additional config files * Implements idempotent state management (present/absent) * Validates parameters and provides meaningful error messages * Default force=true for removal operations * Removed deprecated 'ignore' parameter in favor of built-in idempotency - podman_quadlet_info: Gather information about installed Quadlets * Lists all installed quadlets or prints specific quadlet content * Supports filtering by quadlet kinds (container, pod, network, etc.) * Provides detailed quadlet metadata including status and paths Key features: - Shared utilities in module_utils/podman/quadlet.py for code reuse - Comprehensive integration tests for both modules - Full idempotency support for all operations - Proper handling of edge cases (missing files, malformed quadlets, etc.) - Check mode support for safe dry-run operations - Extensive documentation and examples The modules use relative imports for module_utils to support local development and testing with the containers.podman collection. Signed-off-by: Sagi Shnaidman <sshnaidm@redhat.com>
255 lines
7 KiB
Python
255 lines
7 KiB
Python
#!/usr/bin/python
|
|
# Copyright (c) 2025 Red Hat
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
# flake8: noqa: E501
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
DOCUMENTATION = r"""
|
|
module: podman_quadlet_info
|
|
author:
|
|
- "Sagi Shnaidman (@sshnaidm)"
|
|
short_description: Gather information about Podman Quadlets
|
|
description:
|
|
- List installed Podman Quadlets or print one quadlet content using C(podman quadlet list/print).
|
|
- Gather information about Podman Quadlets available on the system.
|
|
options:
|
|
name:
|
|
description:
|
|
- Name of the quadlet to print content for.
|
|
- When specified, runs C(podman quadlet print) instead of list.
|
|
type: str
|
|
required: false
|
|
kinds:
|
|
description:
|
|
- List of quadlet kinds to filter by (based on file suffix).
|
|
- For example, C(container) matches quadlets ending with C(.container).
|
|
type: list
|
|
elements: str
|
|
choices:
|
|
- container
|
|
- pod
|
|
- network
|
|
- volume
|
|
- kube
|
|
- image
|
|
required: false
|
|
quadlet_dir:
|
|
description:
|
|
- Filter results to quadlets whose path is under this directory.
|
|
- By default no filtering is applied.
|
|
type: path
|
|
required: false
|
|
executable:
|
|
description:
|
|
- Path to the podman executable.
|
|
type: str
|
|
default: podman
|
|
cmd_args:
|
|
description:
|
|
- Extra global arguments to pass to the C(podman) command (e.g., C(--log-level=debug)).
|
|
- These are placed after the executable and before the subcommand.
|
|
type: list
|
|
elements: str
|
|
required: false
|
|
debug:
|
|
description:
|
|
- Return additional debug information.
|
|
type: bool
|
|
default: false
|
|
"""
|
|
|
|
EXAMPLES = r"""
|
|
- name: List all quadlets
|
|
containers.podman.podman_quadlet_info:
|
|
|
|
- name: Get information about a specific quadlet
|
|
containers.podman.podman_quadlet_info:
|
|
name: myapp.container
|
|
|
|
- name: List only container quadlets
|
|
containers.podman.podman_quadlet_info:
|
|
kinds:
|
|
- container
|
|
|
|
- name: List quadlets in a custom directory
|
|
containers.podman.podman_quadlet_info:
|
|
quadlet_dir: /etc/containers/systemd
|
|
"""
|
|
|
|
|
|
RETURN = r"""
|
|
changed:
|
|
description: Always false
|
|
returned: always
|
|
type: bool
|
|
quadlets:
|
|
description: List of installed quadlets when listing
|
|
returned: when name is not provided
|
|
type: list
|
|
content:
|
|
description: Content of the quadlet when name is provided
|
|
returned: when name is provided
|
|
type: str
|
|
stdout:
|
|
description: podman stdout
|
|
returned: when debug=true
|
|
type: str
|
|
stderr:
|
|
description: podman stderr
|
|
returned: when debug=true
|
|
type: str
|
|
"""
|
|
|
|
|
|
import json
|
|
import os
|
|
|
|
from ansible.module_utils.basic import AnsibleModule # noqa: F402
|
|
|
|
try:
|
|
from ansible.module_utils.common.text.converters import to_native # noqa: F402
|
|
except ImportError:
|
|
from ansible.module_utils.common.text import to_native # noqa: F402
|
|
|
|
|
|
# Mapping from kind name to file suffix
|
|
KIND_SUFFIXES = {
|
|
"container": ".container",
|
|
"pod": ".pod",
|
|
"network": ".network",
|
|
"volume": ".volume",
|
|
"kube": ".kube",
|
|
"image": ".image",
|
|
}
|
|
|
|
|
|
def _get_quadlet_kind(name):
|
|
"""Extract kind from quadlet name based on suffix."""
|
|
if not name:
|
|
return None
|
|
for kind, suffix in KIND_SUFFIXES.items():
|
|
if name.endswith(suffix):
|
|
return kind
|
|
return None
|
|
|
|
|
|
def _build_base_cmd(module, executable):
|
|
"""Build base command with executable and global args."""
|
|
cmd = [executable]
|
|
if module.params.get("cmd_args"):
|
|
cmd.extend(module.params["cmd_args"])
|
|
return cmd
|
|
|
|
|
|
def _list_quadlets(module, executable):
|
|
"""List installed quadlets with optional filtering."""
|
|
cmd = _build_base_cmd(module, executable)
|
|
cmd.extend(["quadlet", "list", "--format", "json"])
|
|
|
|
module.log("PODMAN-QUADLET-INFO-DEBUG: %s" % " ".join([to_native(i) for i in cmd]))
|
|
rc, out, err = module.run_command(cmd)
|
|
|
|
if rc != 0:
|
|
module.fail_json(msg="Failed to list quadlets: %s" % err, stdout=out, stderr=err)
|
|
|
|
# Strict JSON parsing - fail on errors instead of returning empty
|
|
try:
|
|
data = json.loads(out) if out.strip() else []
|
|
except json.JSONDecodeError as e:
|
|
module.fail_json(
|
|
msg="Failed to parse quadlet list output: %s" % str(e),
|
|
stdout=out,
|
|
stderr=err,
|
|
)
|
|
|
|
# Filter by kinds (based on file suffix in Name)
|
|
kinds = module.params.get("kinds")
|
|
if kinds:
|
|
kinds_set = set(kinds)
|
|
filtered = []
|
|
for q in data:
|
|
name = q.get("Name", "")
|
|
kind = _get_quadlet_kind(name)
|
|
if kind and kind in kinds_set:
|
|
filtered.append(q)
|
|
data = filtered
|
|
|
|
# Filter by quadlet_dir (based on Path)
|
|
quadlet_dir = module.params.get("quadlet_dir")
|
|
if quadlet_dir:
|
|
# Normalize the directory path
|
|
quadlet_dir = os.path.normpath(quadlet_dir)
|
|
filtered = []
|
|
for q in data:
|
|
path = q.get("Path", "")
|
|
if path:
|
|
# Check if the quadlet's path is under the specified directory
|
|
normalized_path = os.path.normpath(path)
|
|
if normalized_path.startswith(quadlet_dir + os.sep) or os.path.dirname(normalized_path) == quadlet_dir:
|
|
filtered.append(q)
|
|
data = filtered
|
|
|
|
result = {
|
|
"changed": False,
|
|
"quadlets": data,
|
|
}
|
|
if module.params["debug"]:
|
|
result.update({"stdout": out, "stderr": err})
|
|
return result
|
|
|
|
|
|
def _print_quadlet(module, executable):
|
|
"""Print content of a specific quadlet."""
|
|
name = module.params["name"]
|
|
cmd = _build_base_cmd(module, executable)
|
|
cmd.extend(["quadlet", "print", name])
|
|
|
|
module.log("PODMAN-QUADLET-INFO-DEBUG: %s" % " ".join([to_native(i) for i in cmd]))
|
|
rc, out, err = module.run_command(cmd)
|
|
|
|
if rc != 0:
|
|
module.fail_json(msg="Failed to print quadlet %s: %s" % (name, err), stdout=out, stderr=err)
|
|
|
|
result = {
|
|
"changed": False,
|
|
"content": out,
|
|
}
|
|
if module.params["debug"]:
|
|
result.update({"stdout": out, "stderr": err})
|
|
return result
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
name=dict(type="str", required=False),
|
|
quadlet_dir=dict(type="path", required=False),
|
|
kinds=dict(
|
|
type="list",
|
|
elements="str",
|
|
required=False,
|
|
choices=["container", "pod", "network", "volume", "kube", "image"],
|
|
),
|
|
executable=dict(type="str", default="podman"),
|
|
cmd_args=dict(type="list", elements="str", required=False),
|
|
debug=dict(type="bool", default=False),
|
|
),
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
executable = module.get_bin_path(module.params["executable"], required=True)
|
|
|
|
if module.params.get("name"):
|
|
result = _print_quadlet(module, executable)
|
|
else:
|
|
result = _list_quadlets(module, executable)
|
|
|
|
module.exit_json(**result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|