1from __future__ import (absolute_import, division, print_function)
2import json
3from distutils.version import LooseVersion
4
5from ansible.module_utils._text import to_bytes, to_native
6
7from ansible_collections.containers.podman.plugins.module_utils.podman.common import lower_keys
8from ansible_collections.containers.podman.plugins.module_utils.podman.common import generate_systemd
9
10__metaclass__ = type
11
12ARGUMENTS_SPEC_POD = dict(
13    state=dict(
14        type='str',
15        default="created",
16        choices=[
17            'created',
18            'killed',
19            'restarted',
20            'absent',
21            'started',
22            'stopped',
23            'paused',
24            'unpaused',
25        ]),
26    recreate=dict(type='bool', default=False),
27    add_host=dict(type='list', required=False, elements='str'),
28    cgroup_parent=dict(type='str', required=False),
29    dns=dict(type='list', elements='str', required=False),
30    dns_opt=dict(type='list', elements='str', required=False),
31    dns_search=dict(type='list', elements='str', required=False),
32    generate_systemd=dict(type='dict', default={}),
33    hostname=dict(type='str', required=False),
34    infra=dict(type='bool', required=False),
35    infra_conmon_pidfile=dict(type='str', required=False),
36    infra_command=dict(type='str', required=False),
37    infra_image=dict(type='str', required=False),
38    infra_name=dict(type='str', required=False),
39    ip=dict(type='str', required=False),
40    label=dict(type='dict', required=False),
41    label_file=dict(type='str', required=False),
42    mac_address=dict(type='str', required=False),
43    name=dict(type='str', required=True),
44    network=dict(type='str', required=False),
45    no_hosts=dict(type='bool', required=False),
46    pod_id_file=dict(type='str', required=False),
47    publish=dict(type='list', required=False,
48                 elements='str', aliases=['ports']),
49    share=dict(type='str', required=False),
50    executable=dict(type='str', required=False, default='podman'),
51    debug=dict(type='bool', default=False),
52)
53
54
55class PodmanPodModuleParams:
56    """Creates list of arguments for podman CLI command.
57
58       Arguments:
59           action {str} -- action type from 'run', 'stop', 'create', 'delete',
60                           'start'
61           params {dict} -- dictionary of module parameters
62
63       """
64
65    def __init__(self, action, params, podman_version, module):
66        self.params = params
67        self.action = action
68        self.podman_version = podman_version
69        self.module = module
70
71    def construct_command_from_params(self):
72        """Create a podman command from given module parameters.
73
74        Returns:
75           list -- list of byte strings for Popen command
76        """
77        if self.action in ['start', 'restart', 'stop', 'delete', 'pause',
78                           'unpause', 'kill']:
79            return self._simple_action()
80        if self.action in ['create']:
81            return self._create_action()
82        self.module.fail_json(msg="Unknown action %s" % self.action)
83
84    def _simple_action(self):
85        if self.action in ['start', 'restart', 'stop', 'pause', 'unpause', 'kill']:
86            cmd = [self.action, self.params['name']]
87            return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
88
89        if self.action == 'delete':
90            cmd = ['rm', '-f', self.params['name']]
91            return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
92        self.module.fail_json(msg="Unknown action %s" % self.action)
93
94    def _create_action(self):
95        cmd = [self.action]
96        all_param_methods = [func for func in dir(self)
97                             if callable(getattr(self, func))
98                             and func.startswith("addparam")]
99        params_set = (i for i in self.params if self.params[i] is not None)
100        for param in params_set:
101            func_name = "_".join(["addparam", param])
102            if func_name in all_param_methods:
103                cmd = getattr(self, func_name)(cmd)
104        return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
105
106    def check_version(self, param, minv=None, maxv=None):
107        if minv and LooseVersion(minv) > LooseVersion(
108                self.podman_version):
109            self.module.fail_json(msg="Parameter %s is supported from podman "
110                                  "version %s only! Current version is %s" % (
111                                      param, minv, self.podman_version))
112        if maxv and LooseVersion(maxv) < LooseVersion(
113                self.podman_version):
114            self.module.fail_json(msg="Parameter %s is supported till podman "
115                                  "version %s only! Current version is %s" % (
116                                      param, minv, self.podman_version))
117
118    def addparam_add_host(self, c):
119        for g in self.params['add_host']:
120            c += ['--add-host', g]
121        return c
122
123    def addparam_cgroup_parent(self, c):
124        return c + ['--cgroup-parent', self.params['cgroup_parent']]
125
126    def addparam_dns(self, c):
127        for g in self.params['dns']:
128            c += ['--dns', g]
129        return c
130
131    def addparam_dns_opt(self, c):
132        for g in self.params['dns_opt']:
133            c += ['--dns-opt', g]
134        return c
135
136    def addparam_dns_search(self, c):
137        for g in self.params['dns_search']:
138            c += ['--dns-search', g]
139        return c
140
141    def addparam_hostname(self, c):
142        return c + ['--hostname', self.params['hostname']]
143
144    def addparam_infra(self, c):
145        return c + [b'='.join([b'--infra',
146                              to_bytes(self.params['infra'],
147                                       errors='surrogate_or_strict')])]
148
149    def addparam_infra_conmon_pidfile(self, c):
150        return c + ['--infra-conmon-pidfile', self.params['infra_conmon_pidfile']]
151
152    def addparam_infra_command(self, c):
153        return c + ['--infra-command', self.params['infra_command']]
154
155    def addparam_infra_image(self, c):
156        return c + ['--infra-image', self.params['infra_image']]
157
158    def addparam_infra_name(self, c):
159        return c + ['--infra-name', self.params['infra_name']]
160
161    def addparam_ip(self, c):
162        return c + ['--ip', self.params['ip']]
163
164    def addparam_label(self, c):
165        for label in self.params['label'].items():
166            c += ['--label', b'='.join(
167                [to_bytes(i, errors='surrogate_or_strict') for i in label])]
168        return c
169
170    def addparam_label_file(self, c):
171        return c + ['--label-file', self.params['label_file']]
172
173    def addparam_mac_address(self, c):
174        return c + ['--mac-address', self.params['mac_address']]
175
176    def addparam_name(self, c):
177        return c + ['--name', self.params['name']]
178
179    def addparam_network(self, c):
180        return c + ['--network', self.params['network']]
181
182    def addparam_no_hosts(self, c):
183        return c + ["=".join('--no-hosts', self.params['no_hosts'])]
184
185    def addparam_pod_id_file(self, c):
186        return c + ['--pod-id-file', self.params['pod_id_file']]
187
188    def addparam_publish(self, c):
189        for g in self.params['publish']:
190            c += ['--publish', g]
191        return c
192
193    def addparam_share(self, c):
194        return c + ['--share', self.params['share']]
195
196
197class PodmanPodDefaults:
198    def __init__(self, module, podman_version):
199        self.module = module
200        self.version = podman_version
201        self.defaults = {
202            'add_host': [],
203            'dns': [],
204            'dns_opt': [],
205            'dns_search': [],
206            'infra': True,
207            'label': {},
208        }
209
210    def default_dict(self):
211        # make here any changes to self.defaults related to podman version
212        # https://github.com/containers/libpod/pull/5669
213        # if (LooseVersion(self.version) >= LooseVersion('1.8.0')
214        #         and LooseVersion(self.version) < LooseVersion('1.9.0')):
215        #     self.defaults['cpu_shares'] = 1024
216        return self.defaults
217
218
219class PodmanPodDiff:
220    def __init__(self, module, module_params, info, infra_info, podman_version):
221        self.module = module
222        self.module_params = module_params
223        self.version = podman_version
224        self.default_dict = None
225        self.info = lower_keys(info)
226        self.infra_info = lower_keys(infra_info)
227        self.params = self.defaultize()
228        self.diff = {'before': {}, 'after': {}}
229        self.non_idempotent = {}
230
231    def defaultize(self):
232        params_with_defaults = {}
233        self.default_dict = PodmanPodDefaults(
234            self.module, self.version).default_dict()
235        for p in self.module_params:
236            if self.module_params[p] is None and p in self.default_dict:
237                params_with_defaults[p] = self.default_dict[p]
238            else:
239                params_with_defaults[p] = self.module_params[p]
240        return params_with_defaults
241
242    def _diff_update_and_compare(self, param_name, before, after):
243        if before != after:
244            self.diff['before'].update({param_name: before})
245            self.diff['after'].update({param_name: after})
246            return True
247        return False
248
249    def diffparam_add_host(self):
250        if not self.infra_info:
251            return self._diff_update_and_compare('add_host', '', '')
252        before = self.infra_info['hostconfig']['extrahosts'] or []
253        after = self.params['add_host']
254        before, after = sorted(list(set(before))), sorted(list(set(after)))
255        return self._diff_update_and_compare('add_host', before, after)
256
257    def diffparam_cgroup_parent(self):
258        if 'cgroupparent' in self.info:
259            before = self.info['cgroupparent']
260        elif 'config' in self.info and self.info['config'].get('cgroupparent'):
261            before = self.info['config']['cgroupparent']
262        after = self.params['cgroup_parent'] or before
263        return self._diff_update_and_compare('cgroup_parent', before, after)
264
265    def diffparam_dns(self):
266        if not self.infra_info:
267            return self._diff_update_and_compare('dns', '', '')
268        before = self.infra_info['hostconfig']['dns'] or []
269        after = self.params['dns']
270        before, after = sorted(list(set(before))), sorted(list(set(after)))
271        return self._diff_update_and_compare('dns', before, after)
272
273    def diffparam_dns_opt(self):
274        if not self.infra_info:
275            return self._diff_update_and_compare('dns_opt', '', '')
276        before = self.infra_info['hostconfig']['dnsoptions'] or []
277        after = self.params['dns_opt']
278        before, after = sorted(list(set(before))), sorted(list(set(after)))
279        return self._diff_update_and_compare('dns_opt', before, after)
280
281    def diffparam_dns_search(self):
282        if not self.infra_info:
283            return self._diff_update_and_compare('dns_search', '', '')
284        before = self.infra_info['hostconfig']['dnssearch'] or []
285        after = self.params['dns_search']
286        before, after = sorted(list(set(before))), sorted(list(set(after)))
287        return self._diff_update_and_compare('dns_search', before, after)
288
289    def diffparam_hostname(self):
290        if not self.infra_info:
291            return self._diff_update_and_compare('hostname', '', '')
292        before = self.infra_info['config']['hostname']
293        after = self.params['hostname'] or before
294        return self._diff_update_and_compare('hostname', before, after)
295
296    # TODO(sshnaidm): https://github.com/containers/podman/issues/6968
297    def diffparam_infra(self):
298        if 'state' in self.info and 'infracontainerid' in self.info['state']:
299            before = self.info['state']['infracontainerid'] != ""
300        else:
301            # TODO(sshnaidm): https://github.com/containers/podman/issues/6968
302            before = 'infracontainerid' in self.info
303        after = self.params['infra']
304        return self._diff_update_and_compare('infra', before, after)
305
306    # TODO(sshnaidm): https://github.com/containers/podman/issues/6969
307    # def diffparam_infra_command(self):
308    #     before = str(self.info['hostconfig']['infra_command'])
309    #     after = self.params['infra_command']
310    #     return self._diff_update_and_compare('infra_command', before, after)
311
312    def diffparam_infra_image(self):
313        if not self.infra_info:
314            return self._diff_update_and_compare('infra_image', '', '')
315        before = str(self.infra_info['imagename'])
316        after = before
317        if self.module_params['infra_image']:
318            after = self.params['infra_image']
319        before = before.replace(":latest", "")
320        after = after.replace(":latest", "")
321        before = before.split("/")[-1]  # pylint: disable=W,C,R
322        after = after.split("/")[-1]  # pylint: disable=W,C,R
323        return self._diff_update_and_compare('infra_image', before, after)
324
325    # TODO(sshnaidm): https://github.com/containers/podman/pull/6956
326    # def diffparam_ip(self):
327    #     before = str(self.info['hostconfig']['ip'])
328    #     after = self.params['ip']
329    #     return self._diff_update_and_compare('ip', before, after)
330
331    def diffparam_label(self):
332        if 'config' in self.info and 'labels' in self.info['config']:
333            before = self.info['config'].get('labels') or {}
334        else:
335            before = self.info['labels'] if 'labels' in self.info else {}
336        after = self.params['label']
337        return self._diff_update_and_compare('label', before, after)
338
339    # TODO(sshnaidm): https://github.com/containers/podman/pull/6956
340    # def diffparam_mac_address(self):
341    #     before = str(self.info['hostconfig']['mac_address'])
342    #     after = self.params['mac_address']
343    #     return self._diff_update_and_compare('mac_address', before, after)
344
345    def diffparam_network(self):
346        if not self.infra_info:
347            return self._diff_update_and_compare('network', [], [])
348        net_mode_before = self.infra_info['hostconfig']['networkmode']
349        net_mode_after = ''
350        before = list(self.infra_info['networksettings'].get('networks', {}))
351        # Remove default 'podman' network in v3 for comparison
352        if before == ['podman']:
353            before = []
354        after = self.params['network']
355        # Currently supported only 'host' and 'none' network modes idempotency
356        if after in ['bridge', 'host', 'slirp4netns']:
357            net_mode_after = after
358        elif after:
359            after = after.split(",")
360        else:
361            after = []
362        if net_mode_after and not before:
363            # Remove differences between v1 and v2
364            net_mode_after = net_mode_after.replace('bridge', 'default')
365            net_mode_after = net_mode_after.replace('slirp4netns', 'default')
366            net_mode_before = net_mode_before.replace('bridge', 'default')
367            net_mode_before = net_mode_before.replace('slirp4netns', 'default')
368            return self._diff_update_and_compare('network', net_mode_before, net_mode_after)
369        before, after = sorted(list(set(before))), sorted(list(set(after)))
370        return self._diff_update_and_compare('network', before, after)
371
372    # TODO(sshnaidm)
373    # def diffparam_no_hosts(self):
374    #     before = str(self.info['hostconfig']['no_hosts'])
375    #     after = self.params['no_hosts']
376    #     return self._diff_update_and_compare('no_hosts', before, after)
377
378    # TODO(sshnaidm) Need to add port ranges support
379    def diffparam_publish(self):
380        if not self.infra_info:
381            return self._diff_update_and_compare('publish', '', '')
382        ports = self.infra_info['hostconfig']['portbindings']
383        before = [":".join([
384            j[0]['hostip'],
385            str(j[0]["hostport"]),
386            i.replace('/tcp', '')
387        ]).strip(':') for i, j in ports.items()]
388        after = self.params['publish'] or []
389        after = [
390            i.replace("/tcp", "").replace("[", "").replace("]", "").strip(":")
391            for i in after]
392        # No support for port ranges yet
393        for ports in after:
394            if "-" in ports:
395                return self._diff_update_and_compare('publish', '', '')
396        before, after = sorted(list(set(before))), sorted(list(set(after)))
397        return self._diff_update_and_compare('publish', before, after)
398
399    def diffparam_share(self):
400        if not self.infra_info:
401            return self._diff_update_and_compare('share', '', '')
402        if 'sharednamespaces' in self.info:
403            before = self.info['sharednamespaces']
404        elif 'config' in self.info:
405            before = [
406                i.split('shares')[1].lower()
407                for i in self.info['config'] if 'shares' in i]
408            # TODO(sshnaidm): to discover why in podman v1 'cgroup' appears
409            before.remove('cgroup')
410        else:
411            before = []
412        if self.params['share'] is not None:
413            after = self.params['share'].split(",")
414        else:
415            after = ['uts', 'ipc', 'net']
416        before, after = sorted(list(set(before))), sorted(list(set(after)))
417        return self._diff_update_and_compare('share', before, after)
418
419    def is_different(self):
420        diff_func_list = [func for func in dir(self)
421                          if callable(getattr(self, func)) and func.startswith(
422                              "diffparam")]
423        fail_fast = not bool(self.module._diff)
424        different = False
425        for func_name in diff_func_list:
426            dff_func = getattr(self, func_name)
427            if dff_func():
428                if fail_fast:
429                    return True
430                different = True
431        # Check non idempotent parameters
432        for p in self.non_idempotent:
433            if self.module_params[p] is not None and self.module_params[p] not in [{}, [], '']:
434                different = True
435        return different
436
437
438class PodmanPod:
439    """Perform pod tasks.
440
441    Manages podman pod, inspects it and checks its current state
442    """
443
444    def __init__(self, module, name, module_params):
445        """Initialize PodmanPod class.
446
447        Arguments:
448            module {obj} -- ansible module object
449            name {str} -- name of pod
450        """
451
452        self.module = module
453        self.module_params = module_params
454        self.name = name
455        self.stdout, self.stderr = '', ''
456        self.info = self.get_info()
457        self.infra_info = self.get_infra_info()
458        self.version = self._get_podman_version()
459        self.diff = {}
460        self.actions = []
461
462    @property
463    def exists(self):
464        """Check if pod exists."""
465        return bool(self.info != {})
466
467    @property
468    def different(self):
469        """Check if pod is different."""
470        diffcheck = PodmanPodDiff(
471            self.module,
472            self.module_params,
473            self.info,
474            self.infra_info,
475            self.version)
476        is_different = diffcheck.is_different()
477        diffs = diffcheck.diff
478        if self.module._diff and is_different and diffs['before'] and diffs['after']:
479            self.diff['before'] = "\n".join(
480                ["%s - %s" % (k, v) for k, v in sorted(
481                    diffs['before'].items())]) + "\n"
482            self.diff['after'] = "\n".join(
483                ["%s - %s" % (k, v) for k, v in sorted(
484                    diffs['after'].items())]) + "\n"
485        return is_different
486
487    @property
488    def running(self):
489        """Return True if pod is running now."""
490        if 'status' in self.info['State']:
491            return self.info['State']['status'] == 'Running'
492        return self.info['State'] == 'Running'
493
494    @property
495    def paused(self):
496        """Return True if pod is paused now."""
497        if 'status' in self.info['State']:
498            return self.info['State']['status'] == 'Paused'
499        return self.info['State'] == 'Paused'
500
501    @property
502    def stopped(self):
503        """Return True if pod exists and is not running now."""
504        if not self.exists:
505            return False
506        if 'status' in self.info['State']:
507            return not (self.info['State']['status'] == 'Running')
508        return not (self.info['State'] == 'Running')
509
510    def get_info(self):
511        """Inspect pod and gather info about it."""
512        # pylint: disable=unused-variable
513        rc, out, err = self.module.run_command(
514            [self.module_params['executable'], b'pod', b'inspect', self.name])
515        return json.loads(out) if rc == 0 else {}
516
517    def get_infra_info(self):
518        """Inspect pod and gather info about it."""
519        if not self.info:
520            return {}
521        if 'InfraContainerID' in self.info:
522            infra_container_id = self.info['InfraContainerID']
523        elif 'State' in self.info and 'infraContainerID' in self.info['State']:
524            infra_container_id = self.info['State']['infraContainerID']
525        else:
526            return {}
527        # pylint: disable=unused-variable
528        rc, out, err = self.module.run_command(
529            [self.module_params['executable'], b'inspect', infra_container_id])
530        return json.loads(out)[0] if rc == 0 else {}
531
532    def _get_podman_version(self):
533        # pylint: disable=unused-variable
534        rc, out, err = self.module.run_command(
535            [self.module_params['executable'], b'--version'])
536        if rc != 0 or not out or "version" not in out:
537            self.module.fail_json(msg="%s run failed!" % self.module_params['executable'])
538        return out.split("version")[1].strip()
539
540    def _perform_action(self, action):
541        """Perform action with pod.
542
543        Arguments:
544            action {str} -- action to perform - start, create, stop, pause
545                            unpause, delete, restart, kill
546        """
547        b_command = PodmanPodModuleParams(action,
548                                          self.module_params,
549                                          self.version,
550                                          self.module,
551                                          ).construct_command_from_params()
552        full_cmd = " ".join([self.module_params['executable'], 'pod']
553                            + [to_native(i) for i in b_command])
554        self.module.log("PODMAN-POD-DEBUG: %s" % full_cmd)
555        self.actions.append(full_cmd)
556        if not self.module.check_mode:
557            rc, out, err = self.module.run_command(
558                [self.module_params['executable'], b'pod'] + b_command,
559                expand_user_and_vars=False)
560            self.stdout = out
561            self.stderr = err
562            if rc != 0:
563                self.module.fail_json(
564                    msg="Can't %s pod %s" % (action, self.name),
565                    stdout=out, stderr=err)
566
567    def delete(self):
568        """Delete the pod."""
569        self._perform_action('delete')
570
571    def stop(self):
572        """Stop the pod."""
573        self._perform_action('stop')
574
575    def start(self):
576        """Start the pod."""
577        self._perform_action('start')
578
579    def create(self):
580        """Create the pod."""
581        self._perform_action('create')
582
583    def recreate(self):
584        """Recreate the pod."""
585        self.delete()
586        self.create()
587
588    def restart(self):
589        """Restart the pod."""
590        self._perform_action('restart')
591
592    def kill(self):
593        """Kill the pod."""
594        self._perform_action('kill')
595
596    def pause(self):
597        """Pause the pod."""
598        self._perform_action('pause')
599
600    def unpause(self):
601        """Unpause the pod."""
602        self._perform_action('unpause')
603
604
605class PodmanPodManager:
606    """Module manager class.
607
608    Defines according to parameters what actions should be applied to pod
609    """
610
611    def __init__(self, module, params):
612        """Initialize PodmanManager class.
613
614        Arguments:
615            module {obj} -- ansible module object
616        """
617
618        self.module = module
619        self.module_params = params
620        self.results = {
621            'changed': False,
622            'actions': [],
623            'pod': {},
624        }
625        self.name = self.module_params['name']
626        self.executable = \
627            self.module.get_bin_path(self.module_params['executable'],
628                                     required=True)
629        self.state = self.module_params['state']
630        self.recreate = self.module_params['recreate']
631        self.pod = PodmanPod(self.module, self.name, self.module_params)
632
633    def update_pod_result(self, changed=True):
634        """Inspect the current pod, update results with last info, exit.
635
636        Keyword Arguments:
637            changed {bool} -- whether any action was performed
638                              (default: {True})
639        """
640        facts = self.pod.get_info() if changed else self.pod.info
641        out, err = self.pod.stdout, self.pod.stderr
642        self.results.update({'changed': changed, 'pod': facts,
643                             'podman_actions': self.pod.actions},
644                            stdout=out, stderr=err)
645        if self.pod.diff:
646            self.results.update({'diff': self.pod.diff})
647        if self.module.params['debug'] or self.module_params['debug']:
648            self.results.update({'podman_version': self.pod.version})
649        self.results.update(
650            {'podman_systemd': generate_systemd(self.module, self.module_params, self.name)})
651
652    def execute(self):
653        """Execute the desired action according to map of actions & states."""
654        states_map = {
655            'created': self.make_created,
656            'started': self.make_started,
657            'stopped': self.make_stopped,
658            'restarted': self.make_restarted,
659            'absent': self.make_absent,
660            'killed': self.make_killed,
661            'paused': self.make_paused,
662            'unpaused': self.make_unpaused,
663
664        }
665        process_action = states_map[self.state]
666        process_action()
667        return self.results
668
669    def _create_or_recreate_pod(self):
670        """Ensure pod exists and is exactly as it should be by input params."""
671        changed = False
672        if self.pod.exists:
673            if self.pod.different or self.recreate:
674                self.pod.recreate()
675                self.results['actions'].append('recreated %s' % self.pod.name)
676                changed = True
677        elif not self.pod.exists:
678            self.pod.create()
679            self.results['actions'].append('created %s' % self.pod.name)
680            changed = True
681        return changed
682
683    def make_created(self):
684        """Run actions if desired state is 'created'."""
685        if self.pod.exists and not self.pod.different:
686            self.update_pod_result(changed=False)
687            return
688        self._create_or_recreate_pod()
689        self.update_pod_result()
690
691    def make_killed(self):
692        """Run actions if desired state is 'killed'."""
693        self._create_or_recreate_pod()
694        self.pod.kill()
695        self.results['actions'].append('killed %s' % self.pod.name)
696        self.update_pod_result()
697
698    def make_paused(self):
699        """Run actions if desired state is 'paused'."""
700        changed = self._create_or_recreate_pod()
701        if self.pod.paused:
702            self.update_pod_result(changed=changed)
703            return
704        self.pod.pause()
705        self.results['actions'].append('paused %s' % self.pod.name)
706        self.update_pod_result()
707
708    def make_unpaused(self):
709        """Run actions if desired state is 'unpaused'."""
710        changed = self._create_or_recreate_pod()
711        if not self.pod.paused:
712            self.update_pod_result(changed=changed)
713            return
714        self.pod.unpause()
715        self.results['actions'].append('unpaused %s' % self.pod.name)
716        self.update_pod_result()
717
718    def make_started(self):
719        """Run actions if desired state is 'started'."""
720        changed = self._create_or_recreate_pod()
721        if not changed and self.pod.running:
722            self.update_pod_result(changed=changed)
723            return
724
725        # self.pod.unpause()  TODO(sshnaidm): to unpause if state == started?
726        self.pod.start()
727        self.results['actions'].append('started %s' % self.pod.name)
728        self.update_pod_result()
729
730    def make_stopped(self):
731        """Run actions if desired state is 'stopped'."""
732        if not self.pod.exists:
733            self.module.fail_json("Pod %s doesn't exist!" % self.pod.name)
734        if self.pod.running:
735            self.pod.stop()
736            self.results['actions'].append('stopped %s' % self.pod.name)
737            self.update_pod_result()
738        elif self.pod.stopped:
739            self.update_pod_result(changed=False)
740
741    def make_restarted(self):
742        """Run actions if desired state is 'restarted'."""
743        if self.pod.exists:
744            self.pod.restart()
745            self.results['actions'].append('restarted %s' % self.pod.name)
746            self.results.update({'changed': True})
747            self.update_pod_result()
748        else:
749            self.module.fail_json("Pod %s doesn't exist!" % self.pod.name)
750
751    def make_absent(self):
752        """Run actions if desired state is 'absent'."""
753        if not self.pod.exists:
754            self.results.update({'changed': False})
755        elif self.pod.exists:
756            self.pod.delete()
757            self.results['actions'].append('deleted %s' % self.pod.name)
758            self.results.update({'changed': True})
759        self.results.update({'pod': {},
760                             'podman_actions': self.pod.actions})
761