mirror of
https://github.com/containers/ansible-podman-collections.git
synced 2026-02-03 23:01:48 +00:00
1075 lines
38 KiB
Python
1075 lines
38 KiB
Python
from __future__ import absolute_import, division, print_function
|
|
import json # noqa: F402
|
|
|
|
from ansible.module_utils._text import to_bytes, to_native
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
|
LooseVersion,
|
|
)
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
|
lower_keys,
|
|
)
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
|
generate_systemd,
|
|
)
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
|
delete_systemd,
|
|
)
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
|
diff_generic,
|
|
)
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.common import (
|
|
createcommand,
|
|
)
|
|
from ansible_collections.containers.podman.plugins.module_utils.podman.quadlet import (
|
|
create_quadlet_state,
|
|
PodQuadlet,
|
|
)
|
|
|
|
|
|
__metaclass__ = type
|
|
|
|
ARGUMENTS_SPEC_POD = dict(
|
|
state=dict(
|
|
type="str",
|
|
default="created",
|
|
choices=[
|
|
"created",
|
|
"killed",
|
|
"restarted",
|
|
"absent",
|
|
"started",
|
|
"stopped",
|
|
"paused",
|
|
"unpaused",
|
|
"quadlet",
|
|
],
|
|
),
|
|
recreate=dict(type="bool", default=False),
|
|
add_host=dict(type="list", required=False, elements="str"),
|
|
blkio_weight=dict(type="str", required=False),
|
|
blkio_weight_device=dict(type="list", elements="str", required=False),
|
|
cgroup_parent=dict(type="str", required=False),
|
|
cpus=dict(type="str", required=False),
|
|
cpuset_cpus=dict(type="str", required=False),
|
|
cpuset_mems=dict(type="str", required=False),
|
|
cpu_shares=dict(type="str", required=False),
|
|
device=dict(type="list", elements="str", required=False),
|
|
device_read_bps=dict(type="list", elements="str", required=False),
|
|
device_write_bps=dict(type="list", elements="str", required=False),
|
|
dns=dict(type="list", elements="str", required=False),
|
|
dns_opt=dict(type="list", elements="str", aliases=["dns_option"], required=False),
|
|
dns_search=dict(type="list", elements="str", required=False),
|
|
exit_policy=dict(type="str", required=False, choices=["continue", "stop"]),
|
|
generate_systemd=dict(type="dict", default={}),
|
|
gidmap=dict(type="list", elements="str", required=False),
|
|
gpus=dict(type="str", required=False),
|
|
hostname=dict(type="str", required=False),
|
|
infra=dict(type="bool", required=False),
|
|
infra_conmon_pidfile=dict(type="str", required=False),
|
|
infra_command=dict(type="str", required=False),
|
|
infra_image=dict(type="str", required=False),
|
|
infra_name=dict(type="str", required=False),
|
|
ip=dict(type="str", required=False),
|
|
ip6=dict(type="str", required=False),
|
|
label=dict(type="dict", required=False),
|
|
label_file=dict(type="str", required=False),
|
|
mac_address=dict(type="str", required=False),
|
|
memory=dict(type="str", required=False),
|
|
memory_swap=dict(type="str", required=False),
|
|
name=dict(type="str", required=True),
|
|
network=dict(type="list", elements="str", required=False),
|
|
network_alias=dict(
|
|
type="list", elements="str", required=False, aliases=["network_aliases"]
|
|
),
|
|
no_hosts=dict(type="bool", required=False),
|
|
pid=dict(type="str", required=False),
|
|
pod_id_file=dict(type="str", required=False),
|
|
publish=dict(type="list", required=False, elements="str", aliases=["ports"]),
|
|
quadlet_dir=dict(type="path"),
|
|
quadlet_filename=dict(type="str"),
|
|
quadlet_file_mode=dict(type="raw", required=False),
|
|
quadlet_options=dict(type="list", elements="str"),
|
|
restart_policy=dict(type="str", required=False),
|
|
security_opt=dict(type="list", elements="str", required=False),
|
|
share=dict(type="str", required=False),
|
|
share_parent=dict(type="bool", required=False),
|
|
shm_size=dict(type="str", required=False),
|
|
shm_size_systemd=dict(type="str", required=False),
|
|
subgidname=dict(type="str", required=False),
|
|
subuidname=dict(type="str", required=False),
|
|
sysctl=dict(type="dict", required=False),
|
|
uidmap=dict(type="list", elements="str", required=False),
|
|
userns=dict(type="str", required=False),
|
|
uts=dict(type="str", required=False),
|
|
volume=dict(type="list", elements="str", aliases=["volumes"], required=False),
|
|
volumes_from=dict(type="list", elements="str", required=False),
|
|
executable=dict(type="str", required=False, default="podman"),
|
|
debug=dict(type="bool", default=False),
|
|
)
|
|
|
|
|
|
class PodmanPodModuleParams:
|
|
"""Creates list of arguments for podman CLI command.
|
|
|
|
Arguments:
|
|
action {str} -- action type from 'run', 'stop', 'create', 'delete',
|
|
'start'
|
|
params {dict} -- dictionary of module parameters
|
|
|
|
"""
|
|
|
|
def __init__(self, action, params, podman_version, module):
|
|
self.params = params
|
|
self.action = action
|
|
self.podman_version = podman_version
|
|
self.module = module
|
|
|
|
def construct_command_from_params(self):
|
|
"""Create a podman command from given module parameters.
|
|
|
|
Returns:
|
|
list -- list of byte strings for Popen command
|
|
"""
|
|
if self.action in [
|
|
"start",
|
|
"restart",
|
|
"stop",
|
|
"delete",
|
|
"pause",
|
|
"unpause",
|
|
"kill",
|
|
]:
|
|
return self._simple_action()
|
|
if self.action in ["create"]:
|
|
return self._create_action()
|
|
self.module.fail_json(msg="Unknown action %s" % self.action)
|
|
|
|
def _simple_action(self):
|
|
if self.action in ["start", "restart", "stop", "pause", "unpause", "kill"]:
|
|
cmd = [self.action, self.params["name"]]
|
|
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
|
|
|
if self.action == "delete":
|
|
cmd = ["rm", "-f", self.params["name"]]
|
|
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
|
self.module.fail_json(msg="Unknown action %s" % self.action)
|
|
|
|
def _create_action(self):
|
|
cmd = [self.action]
|
|
all_param_methods = [
|
|
func
|
|
for func in dir(self)
|
|
if callable(getattr(self, func)) and func.startswith("addparam")
|
|
]
|
|
params_set = (i for i in self.params if self.params[i] is not None)
|
|
for param in params_set:
|
|
func_name = "_".join(["addparam", param])
|
|
if func_name in all_param_methods:
|
|
cmd = getattr(self, func_name)(cmd)
|
|
return [to_bytes(i, errors="surrogate_or_strict") for i in cmd]
|
|
|
|
def check_version(self, param, minv=None, maxv=None):
|
|
if minv and LooseVersion(minv) > LooseVersion(self.podman_version):
|
|
self.module.fail_json(
|
|
msg="Parameter %s is supported from podman "
|
|
"version %s only! Current version is %s"
|
|
% (param, minv, self.podman_version)
|
|
)
|
|
if maxv and LooseVersion(maxv) < LooseVersion(self.podman_version):
|
|
self.module.fail_json(
|
|
msg="Parameter %s is supported till podman "
|
|
"version %s only! Current version is %s"
|
|
% (param, minv, self.podman_version)
|
|
)
|
|
|
|
def addparam_add_host(self, c):
|
|
for g in self.params["add_host"]:
|
|
c += ["--add-host", g]
|
|
return c
|
|
|
|
def addparam_blkio_weight(self, c):
|
|
self.check_version("--blkio-weight", minv="4.3.0")
|
|
return c + ["--blkio-weight", self.params["blkio_weight"]]
|
|
|
|
def addparam_blkio_weight_device(self, c):
|
|
self.check_version("--blkio-weight-device", minv="4.3.0")
|
|
for dev in self.params["blkio_weight_device"]:
|
|
c += ["--blkio-weight-device", dev]
|
|
return c
|
|
|
|
def addparam_cgroup_parent(self, c):
|
|
return c + ["--cgroup-parent", self.params["cgroup_parent"]]
|
|
|
|
def addparam_cpus(self, c):
|
|
self.check_version("--cpus", minv="4.2.0")
|
|
return c + ["--cpus", self.params["cpus"]]
|
|
|
|
def addparam_cpuset_cpus(self, c):
|
|
self.check_version("--cpus", minv="4.2.0")
|
|
return c + ["--cpuset-cpus", self.params["cpuset_cpus"]]
|
|
|
|
def addparam_cpuset_mems(self, c):
|
|
self.check_version("--cpuset-mems", minv="4.3.0")
|
|
return c + ["--cpuset-mems", self.params["cpuset_mems"]]
|
|
|
|
def addparam_cpu_shares(self, c):
|
|
self.check_version("--cpu-shares", minv="4.3.0")
|
|
return c + ["--cpu-shares", self.params["cpu_shares"]]
|
|
|
|
def addparam_device(self, c):
|
|
for dev in self.params["device"]:
|
|
c += ["--device", dev]
|
|
return c
|
|
|
|
def addparam_device_read_bps(self, c):
|
|
self.check_version("--device-read-bps", minv="4.3.0")
|
|
for dev in self.params["device_read_bps"]:
|
|
c += ["--device-read-bps", dev]
|
|
return c
|
|
|
|
def addparam_device_write_bps(self, c):
|
|
self.check_version("--device-write-bps", minv="4.3.0")
|
|
for dev in self.params["device_write_bps"]:
|
|
c += ["--device-write-bps", dev]
|
|
return c
|
|
|
|
def addparam_dns(self, c):
|
|
for g in self.params["dns"]:
|
|
c += ["--dns", g]
|
|
return c
|
|
|
|
def addparam_dns_opt(self, c):
|
|
for g in self.params["dns_opt"]:
|
|
c += ["--dns-option", g]
|
|
return c
|
|
|
|
def addparam_dns_search(self, c):
|
|
for g in self.params["dns_search"]:
|
|
c += ["--dns-search", g]
|
|
return c
|
|
|
|
def addparam_exit_policy(self, c):
|
|
return c + ["--exit-policy=%s" % self.params["exit_policy"]]
|
|
|
|
def addparam_gidmap(self, c):
|
|
for gidmap in self.params["gidmap"]:
|
|
c += ["--gidmap", gidmap]
|
|
return c
|
|
|
|
def addparam_gpus(self, c):
|
|
return c + ["--gpus", self.params["gpus"]]
|
|
|
|
def addparam_hostname(self, c):
|
|
return c + ["--hostname", self.params["hostname"]]
|
|
|
|
def addparam_infra(self, c):
|
|
return c + [
|
|
b"=".join(
|
|
[
|
|
b"--infra",
|
|
to_bytes(self.params["infra"], errors="surrogate_or_strict"),
|
|
]
|
|
)
|
|
]
|
|
|
|
def addparam_infra_conmon_pidfile(self, c):
|
|
return c + ["--infra-conmon-pidfile", self.params["infra_conmon_pidfile"]]
|
|
|
|
def addparam_infra_command(self, c):
|
|
return c + ["--infra-command", self.params["infra_command"]]
|
|
|
|
def addparam_infra_image(self, c):
|
|
return c + ["--infra-image", self.params["infra_image"]]
|
|
|
|
def addparam_infra_name(self, c):
|
|
return c + ["--infra-name", self.params["infra_name"]]
|
|
|
|
def addparam_ip(self, c):
|
|
return c + ["--ip", self.params["ip"]]
|
|
|
|
def addparam_ip6(self, c):
|
|
return c + ["--ip6", self.params["ip6"]]
|
|
|
|
def addparam_label(self, c):
|
|
for label in self.params["label"].items():
|
|
c += [
|
|
"--label",
|
|
b"=".join([to_bytes(i, errors="surrogate_or_strict") for i in label]),
|
|
]
|
|
return c
|
|
|
|
def addparam_label_file(self, c):
|
|
return c + ["--label-file", self.params["label_file"]]
|
|
|
|
def addparam_mac_address(self, c):
|
|
return c + ["--mac-address", self.params["mac_address"]]
|
|
|
|
def addparam_memory(self, c):
|
|
self.check_version("--memory", minv="4.2.0")
|
|
return c + ["--memory", self.params["memory"]]
|
|
|
|
def addparam_memory_swap(self, c):
|
|
self.check_version("--memory-swap", minv="4.3.0")
|
|
return c + ["--memory-swap", self.params["memory_swap"]]
|
|
|
|
def addparam_name(self, c):
|
|
return c + ["--name", self.params["name"]]
|
|
|
|
def addparam_network(self, c):
|
|
if LooseVersion(self.podman_version) >= LooseVersion("4.0.0"):
|
|
for net in self.params["network"]:
|
|
c += ["--network", net]
|
|
return c
|
|
return c + ["--network", ",".join(self.params["network"])]
|
|
|
|
def addparam_network_aliases(self, c):
|
|
for alias in self.params["network_aliases"]:
|
|
c += ["--network-alias", alias]
|
|
return c
|
|
|
|
def addparam_no_hosts(self, c):
|
|
return c + ["=".join(["--no-hosts", self.params["no_hosts"]])]
|
|
|
|
def addparam_pid(self, c):
|
|
return c + ["--pid", self.params["pid"]]
|
|
|
|
def addparam_pod_id_file(self, c):
|
|
return c + ["--pod-id-file", self.params["pod_id_file"]]
|
|
|
|
def addparam_publish(self, c):
|
|
for g in self.params["publish"]:
|
|
c += ["--publish", g]
|
|
return c
|
|
|
|
def addparam_restart_policy(self, c):
|
|
return c + ["--restart=%s" % self.params["restart_policy"]]
|
|
|
|
def addparam_security_opt(self, c):
|
|
for g in self.params["security_opt"]:
|
|
c += ["--security-opt", g]
|
|
return c
|
|
|
|
def addparam_share(self, c):
|
|
return c + ["--share", self.params["share"]]
|
|
|
|
def addparam_share_parent(self, c):
|
|
if self.params["share_parent"] is not None:
|
|
return c + ["--share-parent=%s" % self.params["share_parent"]]
|
|
return c
|
|
|
|
def addparam_shm_size(self, c):
|
|
return c + ["--shm-size=%s" % self.params["shm_size"]]
|
|
|
|
def addparam_shm_size_systemd(self, c):
|
|
return c + ["--shm-size-systemd=%s" % self.params["shm_size_systemd"]]
|
|
|
|
def addparam_subgidname(self, c):
|
|
return c + ["--subgidname", self.params["subgidname"]]
|
|
|
|
def addparam_subuidname(self, c):
|
|
return c + ["--subuidname", self.params["subuidname"]]
|
|
|
|
def addparam_sysctl(self, c):
|
|
for k, v in self.params["sysctl"].items():
|
|
c += ["--sysctl", "%s=%s" % (k, v)]
|
|
return c
|
|
|
|
def addparam_uidmap(self, c):
|
|
for uidmap in self.params["uidmap"]:
|
|
c += ["--uidmap", uidmap]
|
|
return c
|
|
|
|
def addparam_userns(self, c):
|
|
return c + ["--userns", self.params["userns"]]
|
|
|
|
def addparam_uts(self, c):
|
|
return c + ["--uts", self.params["uts"]]
|
|
|
|
def addparam_volume(self, c):
|
|
for vol in self.params["volume"]:
|
|
if vol:
|
|
c += ["--volume", vol]
|
|
return c
|
|
|
|
def addparam_volumes_from(self, c):
|
|
for vol in self.params["volumes_from"]:
|
|
c += ["--volumes-from", vol]
|
|
return c
|
|
|
|
|
|
class PodmanPodDefaults:
|
|
def __init__(self, module, podman_version):
|
|
self.module = module
|
|
self.version = podman_version
|
|
self.defaults = {
|
|
"infra": True,
|
|
"label": {},
|
|
}
|
|
|
|
def default_dict(self):
|
|
# make here any changes to self.defaults related to podman version
|
|
# https://github.com/containers/libpod/pull/5669
|
|
# if (LooseVersion(self.version) >= LooseVersion('1.8.0')
|
|
# and LooseVersion(self.version) < LooseVersion('1.9.0')):
|
|
# self.defaults['cpu_shares'] = 1024
|
|
return self.defaults
|
|
|
|
|
|
class PodmanPodDiff:
|
|
def __init__(self, module, module_params, info, infra_info, podman_version):
|
|
self.module = module
|
|
self.module_params = module_params
|
|
self.version = podman_version
|
|
self.default_dict = None
|
|
self.info = lower_keys(info)
|
|
self.infra_info = lower_keys(infra_info)
|
|
self.params = self.defaultize()
|
|
self.diff = {"before": {}, "after": {}}
|
|
self.non_idempotent = {}
|
|
|
|
def defaultize(self):
|
|
params_with_defaults = {}
|
|
self.default_dict = PodmanPodDefaults(self.module, self.version).default_dict()
|
|
for p in self.module_params:
|
|
if self.module_params[p] is None and p in self.default_dict:
|
|
params_with_defaults[p] = self.default_dict[p]
|
|
else:
|
|
params_with_defaults[p] = self.module_params[p]
|
|
return params_with_defaults
|
|
|
|
def _diff_update_and_compare(self, param_name, before, after):
|
|
if before != after:
|
|
self.diff["before"].update({param_name: before})
|
|
self.diff["after"].update({param_name: after})
|
|
return True
|
|
return False
|
|
|
|
def _diff_generic(self, module_arg, cmd_arg, boolean_type=False):
|
|
"""
|
|
Generic diff function for module arguments from CreateCommand
|
|
in Podman inspection output.
|
|
|
|
Args:
|
|
module_arg (str): module argument name
|
|
cmd_arg (str): command line argument name
|
|
boolean_type (bool): if True, then argument is boolean type
|
|
|
|
Returns:
|
|
bool: True if there is a difference, False otherwise
|
|
|
|
"""
|
|
info_config = self.info
|
|
before, after = diff_generic(
|
|
self.params, info_config, module_arg, cmd_arg, boolean_type
|
|
)
|
|
return self._diff_update_and_compare(module_arg, before, after)
|
|
|
|
def diffparam_add_host(self):
|
|
return self._diff_generic("add_host", "--add-host")
|
|
|
|
def diffparam_blkio_weight(self):
|
|
return self._diff_generic("blkio_weight", "--blkio-weight")
|
|
|
|
def diffparam_blkio_weight_device(self):
|
|
return self._diff_generic("blkio_weight_device", "--blkio-weight-device")
|
|
|
|
def diffparam_cgroup_parent(self):
|
|
return self._diff_generic("cgroup_parent", "--cgroup-parent")
|
|
|
|
def diffparam_cpu_shares(self):
|
|
return self._diff_generic("cpu_shares", "--cpu-shares")
|
|
|
|
def diffparam_cpus(self):
|
|
return self._diff_generic("cpus", "--cpus")
|
|
|
|
def diffparam_cpuset_cpus(self):
|
|
return self._diff_generic("cpuset_cpus", "--cpuset-cpus")
|
|
|
|
def diffparam_cpuset_mems(self):
|
|
return self._diff_generic("cpuset_mems", "--cpuset-mems")
|
|
|
|
def diffparam_device(self):
|
|
return self._diff_generic("device", "--device")
|
|
|
|
def diffparam_device_read_bps(self):
|
|
return self._diff_generic("device_read_bps", "--device-read-bps")
|
|
|
|
def diffparam_device_write_bps(self):
|
|
return self._diff_generic("device_write_bps", "--device-write-bps")
|
|
|
|
def diffparam_dns(self):
|
|
return self._diff_generic("dns", "--dns")
|
|
|
|
def diffparam_dns_opt(self):
|
|
return self._diff_generic("dns_opt", "--dns-option")
|
|
|
|
def diffparam_dns_search(self):
|
|
return self._diff_generic("dns_search", "--dns-search")
|
|
|
|
# Disabling idemotency check for exit policy as it's added by systemd generator
|
|
# https://github.com/containers/ansible-podman-collections/issues/774
|
|
# def diffparam_exit_policy(self):
|
|
# return self._diff_generic('exit_policy', '--exit-policy')
|
|
|
|
def diffparam_gidmap(self):
|
|
return self._diff_generic("gidmap", "--gidmap")
|
|
|
|
def diffparam_gpus(self):
|
|
return self._diff_generic("gpus", "--gpus")
|
|
|
|
def diffparam_hostname(self):
|
|
return self._diff_generic("hostname", "--hostname")
|
|
|
|
# TODO(sshnaidm): https://github.com/containers/podman/issues/6968
|
|
def diffparam_infra(self):
|
|
if "state" in self.info and "infracontainerid" in self.info["state"]:
|
|
before = self.info["state"]["infracontainerid"] != ""
|
|
else:
|
|
# TODO(sshnaidm): https://github.com/containers/podman/issues/6968
|
|
before = "infracontainerid" in self.info
|
|
after = self.params["infra"]
|
|
return self._diff_update_and_compare("infra", before, after)
|
|
|
|
def diffparam_infra_command(self):
|
|
return self._diff_generic("infra_command", "--infra-command")
|
|
|
|
# Disabling idemotency check for infra_conmon_pidfile as it's added by systemd generator
|
|
# https://github.com/containers/ansible-podman-collections/issues/774
|
|
# def diffparam_infra_conmon_pidfile(self):
|
|
# return self._diff_generic('infra_conmon_pidfile', '--infra-conmon-pidfile')
|
|
|
|
def diffparam_infra_image(self):
|
|
return self._diff_generic("infra_image", "--infra-image")
|
|
|
|
def diffparam_infra_name(self):
|
|
return self._diff_generic("infra_name", "--infra-name")
|
|
|
|
def diffparam_ip(self):
|
|
return self._diff_generic("ip", "--ip")
|
|
|
|
def diffparam_ip6(self):
|
|
return self._diff_generic("ip6", "--ip6")
|
|
|
|
def diffparam_label(self):
|
|
if "config" in self.info and "labels" in self.info["config"]:
|
|
before = self.info["config"].get("labels") or {}
|
|
else:
|
|
before = self.info["labels"] if "labels" in self.info else {}
|
|
after = self.params["label"]
|
|
# Strip out labels that are coming from systemd files
|
|
# https://github.com/containers/ansible-podman-collections/issues/276
|
|
if "podman_systemd_unit" in before:
|
|
after.pop("podman_systemd_unit", None)
|
|
before.pop("podman_systemd_unit", None)
|
|
return self._diff_update_and_compare("label", before, after)
|
|
|
|
def diffparam_label_file(self):
|
|
return self._diff_generic("label_file", "--label-file")
|
|
|
|
def diffparam_mac_address(self):
|
|
return self._diff_generic("mac_address", "--mac-address")
|
|
|
|
def diffparam_memory(self):
|
|
return self._diff_generic("memory", "--memory")
|
|
|
|
def diffparam_memory_swap(self):
|
|
return self._diff_generic("memory_swap", "--memory-swap")
|
|
|
|
def diffparam_network(self):
|
|
return self._diff_generic("network", "--network")
|
|
|
|
def diffparam_network_alias(self):
|
|
return self._diff_generic("network_alias", "--network-alias")
|
|
|
|
def diffparam_no_hosts(self):
|
|
return self._diff_generic("no_hosts", "--no-hosts", boolean_type=True)
|
|
|
|
def diffparam_pid(self):
|
|
return self._diff_generic("pid", "--pid")
|
|
|
|
# Disabling idemotency check for pod id file as it's added by systemd generator
|
|
# https://github.com/containers/ansible-podman-collections/issues/774
|
|
# def diffparam_pod_id_file(self):
|
|
# return self._diff_generic('pod_id_file', '--pod-id-file')
|
|
|
|
def diffparam_publish(self):
|
|
return self._diff_generic("publish", "--publish")
|
|
|
|
def diffparam_restart_policy(self):
|
|
return self._diff_generic("restart_policy", "--restart")
|
|
|
|
def diffparam_security_opt(self):
|
|
return self._diff_generic("security_opt", "--security-opt")
|
|
|
|
def diffparam_share(self):
|
|
return self._diff_generic("share", "--share")
|
|
|
|
def diffparam_share_parent(self):
|
|
return self._diff_generic("share_parent", "--share-parent")
|
|
|
|
def diffparam_shm_size(self):
|
|
return self._diff_generic("shm_size", "--shm-size")
|
|
|
|
def diffparam_shm_size_systemd(self):
|
|
return self._diff_generic("shm_size_systemd", "--shm-size-systemd")
|
|
|
|
def diffparam_subgidname(self):
|
|
return self._diff_generic("subgidname", "--subgidname")
|
|
|
|
def diffparam_subuidname(self):
|
|
return self._diff_generic("subuidname", "--subuidname")
|
|
|
|
def diffparam_sysctl(self):
|
|
return self._diff_generic("sysctl", "--sysctl")
|
|
|
|
def diffparam_uidmap(self):
|
|
return self._diff_generic("uidmap", "--uidmap")
|
|
|
|
def diffparam_userns(self):
|
|
return self._diff_generic("userns", "--userns")
|
|
|
|
def diffparam_uts(self):
|
|
return self._diff_generic("uts", "--uts")
|
|
|
|
def diffparam_volume(self):
|
|
def clean_volume(x):
|
|
"""Remove trailing and double slashes from volumes."""
|
|
if not x.rstrip("/"):
|
|
return "/"
|
|
return x.replace("//", "/").rstrip("/")
|
|
|
|
before = createcommand("--volume", self.info)
|
|
if before == []:
|
|
before = None
|
|
after = self.params["volume"]
|
|
if after is not None:
|
|
after = [
|
|
":".join([clean_volume(i) for i in v.split(":")[:2]])
|
|
for v in self.params["volume"]
|
|
]
|
|
if before is not None:
|
|
before = [
|
|
":".join([clean_volume(i) for i in v.split(":")[:2]]) for v in before
|
|
]
|
|
self.module.log("PODMAN Before: %s and After: %s" % (before, after))
|
|
if before is None and after is None:
|
|
return self._diff_update_and_compare("volume", before, after)
|
|
if after is not None:
|
|
after = ",".join(sorted([str(i).lower() for i in after]))
|
|
if before:
|
|
before = ",".join(sorted([str(i).lower() for i in before]))
|
|
return self._diff_update_and_compare("volume", before, after)
|
|
|
|
def diffparam_volumes_from(self):
|
|
return self._diff_generic("volumes_from", "--volumes-from")
|
|
|
|
def is_different(self):
|
|
diff_func_list = [
|
|
func
|
|
for func in dir(self)
|
|
if callable(getattr(self, func)) and func.startswith("diffparam")
|
|
]
|
|
fail_fast = not bool(self.module._diff)
|
|
different = False
|
|
for func_name in diff_func_list:
|
|
dff_func = getattr(self, func_name)
|
|
if dff_func():
|
|
if fail_fast:
|
|
return True
|
|
different = True
|
|
# Check non idempotent parameters
|
|
for p in self.non_idempotent:
|
|
if self.module_params[p] is not None and self.module_params[p] not in [
|
|
{},
|
|
[],
|
|
"",
|
|
]:
|
|
different = True
|
|
return different
|
|
|
|
|
|
class PodmanPod:
|
|
"""Perform pod tasks.
|
|
|
|
Manages podman pod, inspects it and checks its current state
|
|
"""
|
|
|
|
def __init__(self, module, name, module_params):
|
|
"""Initialize PodmanPod class.
|
|
|
|
Arguments:
|
|
module {obj} -- ansible module object
|
|
name {str} -- name of pod
|
|
"""
|
|
|
|
self.module = module
|
|
self.module_params = module_params
|
|
self.name = name
|
|
self.stdout, self.stderr = "", ""
|
|
self.info = self.get_info()
|
|
self.infra_info = self.get_infra_info()
|
|
self.version = self._get_podman_version()
|
|
self.diff = {}
|
|
self.actions = []
|
|
|
|
@property
|
|
def exists(self):
|
|
"""Check if pod exists."""
|
|
return bool(self.info != {})
|
|
|
|
@property
|
|
def different(self):
|
|
"""Check if pod is different."""
|
|
diffcheck = PodmanPodDiff(
|
|
self.module, self.module_params, self.info, self.infra_info, self.version
|
|
)
|
|
is_different = diffcheck.is_different()
|
|
diffs = diffcheck.diff
|
|
if self.module._diff and is_different and diffs["before"] and diffs["after"]:
|
|
self.diff["before"] = (
|
|
"\n".join(
|
|
["%s - %s" % (k, v) for k, v in sorted(diffs["before"].items())]
|
|
)
|
|
+ "\n"
|
|
)
|
|
self.diff["after"] = (
|
|
"\n".join(
|
|
["%s - %s" % (k, v) for k, v in sorted(diffs["after"].items())]
|
|
)
|
|
+ "\n"
|
|
)
|
|
return is_different
|
|
|
|
@property
|
|
def running(self):
|
|
"""Return True if pod is running now."""
|
|
if "status" in self.info["State"]:
|
|
return self.info["State"]["status"] == "Running"
|
|
# older podman versions (1.6.x) don't have status in 'podman pod inspect'
|
|
# if other methods fail, use 'podman pod ps'
|
|
ps_info = self.get_ps()
|
|
if "status" in ps_info:
|
|
return ps_info["status"] == "Running"
|
|
return self.info["State"] == "Running"
|
|
|
|
@property
|
|
def paused(self):
|
|
"""Return True if pod is paused now."""
|
|
if "status" in self.info["State"]:
|
|
return self.info["State"]["status"] == "Paused"
|
|
return self.info["State"] == "Paused"
|
|
|
|
@property
|
|
def stopped(self):
|
|
"""Return True if pod exists and is not running now."""
|
|
if not self.exists:
|
|
return False
|
|
if "status" in self.info["State"]:
|
|
return not (self.info["State"]["status"] == "Running")
|
|
return not (self.info["State"] == "Running")
|
|
|
|
def get_info(self):
|
|
"""Inspect pod and gather info about it."""
|
|
# pylint: disable=unused-variable
|
|
rc, out, err = self.module.run_command(
|
|
[self.module_params["executable"], b"pod", b"inspect", self.name]
|
|
)
|
|
if rc == 0:
|
|
info = json.loads(out)
|
|
# from podman 5 onwards, this is a list of dicts,
|
|
# before it was just a single dict when querying
|
|
# a single pod
|
|
if isinstance(info, list):
|
|
return info[0]
|
|
else:
|
|
return info
|
|
return {}
|
|
|
|
def get_ps(self):
|
|
"""Inspect pod process and gather info about it."""
|
|
# pylint: disable=unused-variable
|
|
rc, out, err = self.module.run_command(
|
|
[
|
|
self.module_params["executable"],
|
|
b"pod",
|
|
b"ps",
|
|
b"--format",
|
|
b"json",
|
|
b"--filter",
|
|
"name=" + self.name,
|
|
]
|
|
)
|
|
return json.loads(out)[0] if rc == 0 else {}
|
|
|
|
def get_infra_info(self):
|
|
"""Inspect pod and gather info about it."""
|
|
if not self.info:
|
|
return {}
|
|
if "InfraContainerID" in self.info:
|
|
infra_container_id = self.info["InfraContainerID"]
|
|
elif "State" in self.info and "infraContainerID" in self.info["State"]:
|
|
infra_container_id = self.info["State"]["infraContainerID"]
|
|
else:
|
|
return {}
|
|
# pylint: disable=unused-variable
|
|
rc, out, err = self.module.run_command(
|
|
[self.module_params["executable"], b"inspect", infra_container_id]
|
|
)
|
|
return json.loads(out)[0] if rc == 0 else {}
|
|
|
|
def _get_podman_version(self):
|
|
# pylint: disable=unused-variable
|
|
rc, out, err = self.module.run_command(
|
|
[self.module_params["executable"], b"--version"]
|
|
)
|
|
if rc != 0 or not out or "version" not in out:
|
|
self.module.fail_json(
|
|
msg="%s run failed!" % self.module_params["executable"]
|
|
)
|
|
return out.split("version")[1].strip()
|
|
|
|
def _perform_action(self, action):
|
|
"""Perform action with pod.
|
|
|
|
Arguments:
|
|
action {str} -- action to perform - start, create, stop, pause
|
|
unpause, delete, restart, kill
|
|
"""
|
|
b_command = PodmanPodModuleParams(
|
|
action,
|
|
self.module_params,
|
|
self.version,
|
|
self.module,
|
|
).construct_command_from_params()
|
|
full_cmd = " ".join(
|
|
[self.module_params["executable"], "pod"]
|
|
+ [to_native(i) for i in b_command]
|
|
)
|
|
self.module.log("PODMAN-POD-DEBUG: %s" % full_cmd)
|
|
self.actions.append(full_cmd)
|
|
if not self.module.check_mode:
|
|
rc, out, err = self.module.run_command(
|
|
[self.module_params["executable"], b"pod"] + b_command,
|
|
expand_user_and_vars=False,
|
|
)
|
|
self.stdout = out
|
|
self.stderr = err
|
|
if rc != 0:
|
|
self.module.fail_json(
|
|
msg="Can't %s pod %s" % (action, self.name), stdout=out, stderr=err
|
|
)
|
|
|
|
def delete(self):
|
|
"""Delete the pod."""
|
|
self._perform_action("delete")
|
|
|
|
def stop(self):
|
|
"""Stop the pod."""
|
|
self._perform_action("stop")
|
|
|
|
def start(self):
|
|
"""Start the pod."""
|
|
self._perform_action("start")
|
|
|
|
def create(self):
|
|
"""Create the pod."""
|
|
self._perform_action("create")
|
|
|
|
def recreate(self):
|
|
"""Recreate the pod."""
|
|
self.delete()
|
|
self.create()
|
|
|
|
def restart(self):
|
|
"""Restart the pod."""
|
|
self._perform_action("restart")
|
|
|
|
def kill(self):
|
|
"""Kill the pod."""
|
|
self._perform_action("kill")
|
|
|
|
def pause(self):
|
|
"""Pause the pod."""
|
|
self._perform_action("pause")
|
|
|
|
def unpause(self):
|
|
"""Unpause the pod."""
|
|
self._perform_action("unpause")
|
|
|
|
|
|
class PodmanPodManager:
|
|
"""Module manager class.
|
|
|
|
Defines according to parameters what actions should be applied to pod
|
|
"""
|
|
|
|
def __init__(self, module, params):
|
|
"""Initialize PodmanManager class.
|
|
|
|
Arguments:
|
|
module {obj} -- ansible module object
|
|
"""
|
|
|
|
self.module = module
|
|
self.module_params = params
|
|
self.results = {
|
|
"changed": False,
|
|
"actions": [],
|
|
"pod": {},
|
|
}
|
|
self.name = self.module_params["name"]
|
|
self.executable = self.module.get_bin_path(
|
|
self.module_params["executable"], required=True
|
|
)
|
|
self.state = self.module_params["state"]
|
|
self.recreate = self.module_params["recreate"]
|
|
self.pod = PodmanPod(self.module, self.name, self.module_params)
|
|
|
|
def update_pod_result(self, changed=True):
|
|
"""Inspect the current pod, update results with last info, exit.
|
|
|
|
Keyword Arguments:
|
|
changed {bool} -- whether any action was performed
|
|
(default: {True})
|
|
"""
|
|
facts = self.pod.get_info() if changed else self.pod.info
|
|
if isinstance(facts, list):
|
|
facts = facts[0]
|
|
out, err = self.pod.stdout, self.pod.stderr
|
|
self.results.update(
|
|
{"changed": changed, "pod": facts, "podman_actions": self.pod.actions},
|
|
stdout=out,
|
|
stderr=err,
|
|
)
|
|
if self.pod.diff:
|
|
self.results.update({"diff": self.pod.diff})
|
|
if self.module.params["debug"] or self.module_params["debug"]:
|
|
self.results.update({"podman_version": self.pod.version})
|
|
sysd = generate_systemd(
|
|
self.module, self.module_params, self.name, self.pod.version
|
|
)
|
|
self.results["changed"] = changed or sysd["changed"]
|
|
self.results.update({"podman_systemd": sysd["systemd"]})
|
|
if sysd["diff"]:
|
|
if "diff" not in self.results:
|
|
self.results.update({"diff": sysd["diff"]})
|
|
else:
|
|
self.results["diff"]["before"] += sysd["diff"]["before"]
|
|
self.results["diff"]["after"] += sysd["diff"]["after"]
|
|
quadlet = PodQuadlet(self.module_params)
|
|
quadlet_content = quadlet.create_quadlet_content()
|
|
self.results.update({"podman_quadlet": quadlet_content})
|
|
|
|
def execute(self):
|
|
"""Execute the desired action according to map of actions & states."""
|
|
states_map = {
|
|
"created": self.make_created,
|
|
"started": self.make_started,
|
|
"stopped": self.make_stopped,
|
|
"restarted": self.make_restarted,
|
|
"absent": self.make_absent,
|
|
"killed": self.make_killed,
|
|
"paused": self.make_paused,
|
|
"unpaused": self.make_unpaused,
|
|
"quadlet": self.make_quadlet,
|
|
}
|
|
process_action = states_map[self.state]
|
|
process_action()
|
|
return self.results
|
|
|
|
def _create_or_recreate_pod(self):
|
|
"""Ensure pod exists and is exactly as it should be by input params."""
|
|
changed = False
|
|
if self.pod.exists:
|
|
if self.pod.different or self.recreate:
|
|
self.pod.recreate()
|
|
self.results["actions"].append("recreated %s" % self.pod.name)
|
|
changed = True
|
|
elif not self.pod.exists:
|
|
self.pod.create()
|
|
self.results["actions"].append("created %s" % self.pod.name)
|
|
changed = True
|
|
return changed
|
|
|
|
def make_created(self):
|
|
"""Run actions if desired state is 'created'."""
|
|
if self.pod.exists and not self.pod.different:
|
|
self.update_pod_result(changed=False)
|
|
return
|
|
self._create_or_recreate_pod()
|
|
self.update_pod_result()
|
|
|
|
def make_killed(self):
|
|
"""Run actions if desired state is 'killed'."""
|
|
self._create_or_recreate_pod()
|
|
self.pod.kill()
|
|
self.results["actions"].append("killed %s" % self.pod.name)
|
|
self.update_pod_result()
|
|
|
|
def make_paused(self):
|
|
"""Run actions if desired state is 'paused'."""
|
|
changed = self._create_or_recreate_pod()
|
|
if self.pod.paused:
|
|
self.update_pod_result(changed=changed)
|
|
return
|
|
self.pod.pause()
|
|
self.results["actions"].append("paused %s" % self.pod.name)
|
|
self.update_pod_result()
|
|
|
|
def make_unpaused(self):
|
|
"""Run actions if desired state is 'unpaused'."""
|
|
changed = self._create_or_recreate_pod()
|
|
if not self.pod.paused:
|
|
self.update_pod_result(changed=changed)
|
|
return
|
|
self.pod.unpause()
|
|
self.results["actions"].append("unpaused %s" % self.pod.name)
|
|
self.update_pod_result()
|
|
|
|
def make_started(self):
|
|
"""Run actions if desired state is 'started'."""
|
|
changed = self._create_or_recreate_pod()
|
|
if not changed and self.pod.running:
|
|
self.update_pod_result(changed=changed)
|
|
return
|
|
|
|
# self.pod.unpause() TODO(sshnaidm): to unpause if state == started?
|
|
self.pod.start()
|
|
self.results["actions"].append("started %s" % self.pod.name)
|
|
self.update_pod_result()
|
|
|
|
def make_stopped(self):
|
|
"""Run actions if desired state is 'stopped'."""
|
|
if not self.pod.exists:
|
|
self.module.fail_json("Pod %s doesn't exist!" % self.pod.name)
|
|
if self.pod.running:
|
|
self.pod.stop()
|
|
self.results["actions"].append("stopped %s" % self.pod.name)
|
|
self.update_pod_result()
|
|
elif self.pod.stopped:
|
|
self.update_pod_result(changed=False)
|
|
|
|
def make_restarted(self):
|
|
"""Run actions if desired state is 'restarted'."""
|
|
if self.pod.exists:
|
|
self.pod.restart()
|
|
self.results["actions"].append("restarted %s" % self.pod.name)
|
|
self.results.update({"changed": True})
|
|
self.update_pod_result()
|
|
else:
|
|
self.module.fail_json("Pod %s doesn't exist!" % self.pod.name)
|
|
|
|
def make_absent(self):
|
|
"""Run actions if desired state is 'absent'."""
|
|
if not self.pod.exists:
|
|
self.results.update({"changed": False})
|
|
elif self.pod.exists:
|
|
delete_systemd(self.module, self.module_params, self.name, self.pod.version)
|
|
self.pod.delete()
|
|
self.results["actions"].append("deleted %s" % self.pod.name)
|
|
self.results.update({"changed": True})
|
|
self.results.update({"pod": {}, "podman_actions": self.pod.actions})
|
|
|
|
def make_quadlet(self):
|
|
results_update = create_quadlet_state(self.module, "pod")
|
|
self.results.update(results_update)
|