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)