1
0
Fork 0
mirror of https://github.com/containers/ansible-podman-collections.git synced 2026-02-04 07:11:49 +00:00
ansible-podman-collections/plugins/module_utils/podman/podman_pod_lib.py
Sergey 9c40a88a71
Fix idempotency for pods in case of systemd generation (#777)
Fix #774

Signed-off-by: Sagi Shnaidman <sshnaidm@redhat.com>
2024-06-07 00:15:57 +03:00

1013 lines
37 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_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)