summaryrefslogtreecommitdiffstats
path: root/ansible_collections/containers/podman/plugins/modules
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/containers/podman/plugins/modules
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/containers/podman/plugins/modules')
-rw-r--r--ansible_collections/containers/podman/plugins/modules/__init__.py0
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_container.py1063
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_container_info.py416
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_containers.py132
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_export.py106
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_generate_systemd.py604
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_image.py862
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_image_info.py236
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_import.py157
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_load.py199
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_login.py184
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_login_info.py116
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_logout.py153
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_network.py673
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_network_info.py138
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_play.py311
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_pod.py415
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_pod_info.py145
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_prune.py252
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_save.py145
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_secret.py178
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_tag.py91
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_volume.py484
-rw-r--r--ansible_collections/containers/podman/plugins/modules/podman_volume_info.py100
24 files changed, 7160 insertions, 0 deletions
diff --git a/ansible_collections/containers/podman/plugins/modules/__init__.py b/ansible_collections/containers/podman/plugins/modules/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/__init__.py
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_container.py b/ansible_collections/containers/podman/plugins/modules/podman_container.py
new file mode 100644
index 000000000..7878352da
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_container.py
@@ -0,0 +1,1063 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# flake8: noqa: E501
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = r"""
+module: podman_container
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.0.0'
+short_description: Manage podman containers
+notes: []
+description:
+ - Start, stop, restart and manage Podman containers
+requirements:
+ - podman
+options:
+ name:
+ description:
+ - Name of the container
+ required: True
+ type: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+ state:
+ description:
+ - I(absent) - A container matching the specified name will be stopped and
+ removed.
+ - I(present) - Asserts the existence of a container matching the name and
+ any provided configuration parameters. If no container matches the
+ name, a container will be created. If a container matches the name but
+ the provided configuration does not match, the container will be
+ updated, if it can be. If it cannot be updated, it will be removed and
+ re-created with the requested config. Image version will be taken into
+ account when comparing configuration. Use the recreate option to force
+ the re-creation of the matching container.
+ - I(started) - Asserts there is a running container matching the name and
+ any provided configuration. If no container matches the name, a
+ container will be created and started. Use recreate to always re-create
+ a matching container, even if it is running. Use force_restart to force
+ a matching container to be stopped and restarted.
+ - I(stopped) - Asserts that the container is first I(present), and then
+ if the container is running moves it to a stopped state.
+ - I(created) - Asserts that the container exists with given configuration.
+ If container doesn't exist, the module creates it and leaves it in
+ 'created' state. If configuration doesn't match or 'recreate' option is
+ set, the container will be recreated
+ type: str
+ default: started
+ choices:
+ - absent
+ - present
+ - stopped
+ - started
+ - created
+ image:
+ description:
+ - Repository path (or image name) and tag used to create the container.
+ If an image is not found, the image will be pulled from the registry.
+ If no tag is included, C(latest) will be used.
+ - Can also be an image ID. If this is the case, the image is assumed to
+ be available locally.
+ type: str
+ annotation:
+ description:
+ - Add an annotation to the container. The format is key value, multiple
+ times.
+ type: dict
+ authfile:
+ description:
+ - Path of the authentication file. Default is
+ ``${XDG_RUNTIME_DIR}/containers/auth.json``
+ (Not available for remote commands) You can also override the default
+ path of the authentication file by setting the ``REGISTRY_AUTH_FILE``
+ environment variable. ``export REGISTRY_AUTH_FILE=path``
+ type: path
+ blkio_weight:
+ description:
+ - Block IO weight (relative weight) accepts a weight value between 10 and
+ 1000
+ type: int
+ blkio_weight_device:
+ description:
+ - Block IO weight (relative device weight, format DEVICE_NAME[:]WEIGHT).
+ type: dict
+ cap_add:
+ description:
+ - List of capabilities to add to the container.
+ type: list
+ elements: str
+ aliases:
+ - capabilities
+ cap_drop:
+ description:
+ - List of capabilities to drop from the container.
+ type: list
+ elements: str
+ cgroup_parent:
+ description:
+ - Path to cgroups under which the cgroup for the container will be
+ created.
+ If the path is not absolute, the path is considered to be relative to
+ the cgroups path of the init process. Cgroups will be created if they
+ do not already exist.
+ type: path
+ cgroupns:
+ description:
+ - Path to cgroups under which the cgroup for the container will be
+ created.
+ type: str
+ cgroups:
+ description:
+ - Determines whether the container will create CGroups.
+ Valid values are enabled and disabled, which the default being enabled.
+ The disabled option will force the container to not create CGroups,
+ and thus conflicts with CGroup options cgroupns and cgroup-parent.
+ type: str
+ cidfile:
+ description:
+ - Write the container ID to the file
+ type: path
+ cmd_args:
+ description:
+ - Any additional command options you want to pass to podman command itself,
+ for example C(--log-level=debug) or C(--syslog). This is NOT command to
+ run in container, but rather options for podman itself.
+ For container command please use I(command) option.
+ type: list
+ elements: str
+ conmon_pidfile:
+ description:
+ - Write the pid of the conmon process to a file.
+ conmon runs in a separate process than Podman,
+ so this is necessary when using systemd to restart Podman containers.
+ type: path
+ command:
+ description:
+ - Override command of container. Can be a string or a list.
+ type: raw
+ cpu_period:
+ description:
+ - Limit the CPU real-time period in microseconds
+ type: int
+ cpu_rt_period:
+ description:
+ - Limit the CPU real-time period in microseconds.
+ Limit the container's Real Time CPU usage. This flag tell the kernel to
+ restrict the container's Real Time CPU usage to the period you specify.
+ type: int
+ cpu_rt_runtime:
+ description:
+ - Limit the CPU real-time runtime in microseconds.
+ This flag tells the kernel to limit the amount of time in a given CPU
+ period Real Time tasks may consume.
+ type: int
+ cpu_shares:
+ description:
+ - CPU shares (relative weight)
+ type: int
+ cpus:
+ description:
+ - Number of CPUs. The default is 0.0 which means no limit.
+ type: str
+ cpuset_cpus:
+ description:
+ - CPUs in which to allow execution (0-3, 0,1)
+ type: str
+ cpuset_mems:
+ description:
+ - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only
+ effective on NUMA systems.
+ type: str
+ detach:
+ description:
+ - Run container in detach mode
+ type: bool
+ default: True
+ debug:
+ description:
+ - Return additional information which can be helpful for investigations.
+ type: bool
+ default: False
+ detach_keys:
+ description:
+ - Override the key sequence for detaching a container. Format is a single
+ character or ctrl-value
+ type: str
+ device:
+ description:
+ - Add a host device to the container.
+ The format is <device-on-host>[:<device-on-container>][:<permissions>]
+ (e.g. device /dev/sdc:/dev/xvdc:rwm)
+ type: list
+ elements: str
+ device_read_bps:
+ description:
+ - Limit read rate (bytes per second) from a device
+ (e.g. device-read-bps /dev/sda:1mb)
+ type: list
+ elements: str
+ device_read_iops:
+ description:
+ - Limit read rate (IO per second) from a device
+ (e.g. device-read-iops /dev/sda:1000)
+ type: list
+ elements: str
+ device_write_bps:
+ description:
+ - Limit write rate (bytes per second) to a device
+ (e.g. device-write-bps /dev/sda:1mb)
+ type: list
+ elements: str
+ device_write_iops:
+ description:
+ - Limit write rate (IO per second) to a device
+ (e.g. device-write-iops /dev/sda:1000)
+ type: list
+ elements: str
+ dns:
+ description:
+ - Set custom DNS servers
+ type: list
+ elements: str
+ aliases:
+ - dns_servers
+ dns_option:
+ description:
+ - Set custom DNS options
+ type: str
+ aliases:
+ - dns_opts
+ dns_search:
+ description:
+ - Set custom DNS search domains (Use dns_search with '' if you don't wish
+ to set the search domain)
+ type: str
+ aliases:
+ - dns_search_domains
+ entrypoint:
+ description:
+ - Overwrite the default ENTRYPOINT of the image
+ type: str
+ env:
+ description:
+ - Set environment variables.
+ This option allows you to specify arbitrary environment variables that
+ are available for the process that will be launched inside of the
+ container.
+ type: dict
+ env_file:
+ description:
+ - Read in a line delimited file of environment variables. Doesn't support
+ idempotency. If users changes the file with environment variables it's
+ on them to recreate the container.
+ type: path
+ env_host:
+ description:
+ - Use all current host environment variables in container.
+ Defaults to false.
+ type: bool
+ etc_hosts:
+ description:
+ - Dict of host-to-IP mappings, where each host name is a key in the
+ dictionary. Each host name will be added to the container's
+ ``/etc/hosts`` file.
+ type: dict
+ aliases:
+ - add_hosts
+ expose:
+ description:
+ - Expose a port, or a range of ports (e.g. expose "3300-3310") to set up
+ port redirection on the host system.
+ type: list
+ elements: str
+ aliases:
+ - exposed
+ - exposed_ports
+ force_restart:
+ description:
+ - Force restart of container.
+ type: bool
+ default: False
+ aliases:
+ - restart
+ generate_systemd:
+ description:
+ - Generate systemd unit file for container.
+ type: dict
+ default: {}
+ suboptions:
+ path:
+ description:
+ - Specify a path to the directory where unit files will be generated.
+ Required for this option. If it doesn't exist, the directory will be created.
+ type: str
+ required: false
+ restart_policy:
+ description:
+ - Specify a restart policy for the service. The restart-policy must be one of
+ "no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always".
+ The default policy is "on-failure".
+ type: str
+ required: false
+ choices:
+ - 'no'
+ - 'on-success'
+ - 'on-failure'
+ - 'on-abnormal'
+ - 'on-watchdog'
+ - 'on-abort'
+ - 'always'
+ time:
+ description:
+ - Override the default stop timeout for the container with the given value.
+ type: int
+ required: false
+ no_header:
+ description:
+ - Do not generate the header including meta data such as the Podman version and the timestamp.
+ From podman version 3.1.0.
+ type: bool
+ default: false
+ names:
+ description:
+ - Use names of the containers for the start, stop, and description in the unit file.
+ Default is true.
+ type: bool
+ default: true
+ container_prefix:
+ description:
+ - Set the systemd unit name prefix for containers. The default is "container".
+ type: str
+ required: false
+ pod_prefix:
+ description:
+ - Set the systemd unit name prefix for pods. The default is "pod".
+ type: str
+ required: false
+ separator:
+ description:
+ - Set the systemd unit name separator between the name/id of a
+ container/pod and the prefix. The default is "-" (dash).
+ type: str
+ required: false
+ new:
+ description:
+ - Create containers and pods when the unit is started instead of
+ expecting them to exist. The default is "false".
+ Refer to podman-generate-systemd(1) for more information.
+ type: bool
+ default: false
+ after:
+ type: list
+ elements: str
+ required: false
+ description:
+ - Add the systemd unit after (After=) option, that ordering dependencies between the list of dependencies and this service.
+ wants:
+ type: list
+ elements: str
+ required: false
+ description:
+ - Add the systemd unit wants (Wants=) option, that this service is (weak) dependent on.
+ requires:
+ type: list
+ elements: str
+ required: false
+ description:
+ - Set the systemd unit requires (Requires=) option. Similar to wants, but declares a stronger requirement dependency.
+ gidmap:
+ description:
+ - Run the container in a new user namespace using the supplied mapping.
+ type: list
+ elements: str
+ group_add:
+ description:
+ - Add additional groups to run as
+ type: list
+ elements: str
+ aliases:
+ - groups
+ healthcheck:
+ description:
+ - Set or alter a healthcheck command for a container.
+ type: str
+ healthcheck_interval:
+ description:
+ - Set an interval for the healthchecks
+ (a value of disable results in no automatic timer setup)
+ (default "30s")
+ type: str
+ healthcheck_retries:
+ description:
+ - The number of retries allowed before a healthcheck is considered to be
+ unhealthy. The default value is 3.
+ type: int
+ healthcheck_start_period:
+ description:
+ - The initialization time needed for a container to bootstrap.
+ The value can be expressed in time format like 2m3s. The default value
+ is 0s
+ type: str
+ healthcheck_timeout:
+ description:
+ - The maximum time allowed to complete the healthcheck before an interval
+ is considered failed. Like start-period, the value can be expressed in
+ a time format such as 1m22s. The default value is 30s
+ type: str
+ hooks_dir:
+ description:
+ - Each .json file in the path configures a hook for Podman containers.
+ For more details on the syntax of the JSON files and the semantics of
+ hook injection, see oci-hooks(5). Can be set multiple times.
+ type: list
+ elements: str
+ hostname:
+ description:
+ - Container host name. Sets the container host name that is available
+ inside the container.
+ type: str
+ http_proxy:
+ description:
+ - By default proxy environment variables are passed into the container if
+ set for the podman process. This can be disabled by setting the
+ http_proxy option to false. The environment variables passed in
+ include http_proxy, https_proxy, ftp_proxy, no_proxy, and also the
+ upper case versions of those.
+ Defaults to true
+ type: bool
+ image_volume:
+ description:
+ - Tells podman how to handle the builtin image volumes.
+ The options are bind, tmpfs, or ignore (default bind)
+ type: str
+ choices:
+ - 'bind'
+ - 'tmpfs'
+ - 'ignore'
+ image_strict:
+ description:
+ - Whether to compare images in idempotency by taking into account a full
+ name with registry and namespaces.
+ type: bool
+ default: False
+ init:
+ description:
+ - Run an init inside the container that forwards signals and reaps
+ processes. The default is false.
+ type: bool
+ init_path:
+ description:
+ - Path to the container-init binary.
+ type: str
+ interactive:
+ description:
+ - Keep STDIN open even if not attached. The default is false.
+ When set to true, keep stdin open even if not attached.
+ The default is false.
+ type: bool
+ ip:
+ description:
+ - Specify a static IP address for the container, for example
+ '10.88.64.128'.
+ Can only be used if no additional CNI networks to join were specified
+ via 'network:', and if the container is not joining another container's
+ network namespace via 'network container:<name|id>'.
+ The address must be within the default CNI network's pool
+ (default 10.88.0.0/16).
+ type: str
+ ipc:
+ description:
+ - Default is to create a private IPC namespace (POSIX SysV IPC) for the
+ container
+ type: str
+ aliases:
+ - ipc_mode
+ kernel_memory:
+ description:
+ - Kernel memory limit
+ (format <number>[<unit>], where unit = b, k, m or g)
+ Note - idempotency is supported for integers only.
+ type: str
+ label:
+ description:
+ - Add metadata to a container, pass dictionary of label names and values
+ aliases:
+ - labels
+ type: dict
+ label_file:
+ description:
+ - Read in a line delimited file of labels
+ type: str
+ log_driver:
+ description:
+ - Logging driver. Used to set the log driver for the container.
+ For example log_driver "k8s-file".
+ type: str
+ choices:
+ - k8s-file
+ - journald
+ - json-file
+ log_level:
+ description:
+ - Logging level for Podman. Log messages above specified level
+ ("debug"|"info"|"warn"|"error"|"fatal"|"panic") (default "error")
+ type: str
+ choices:
+ - debug
+ - info
+ - warn
+ - error
+ - fatal
+ - panic
+ log_opt:
+ description:
+ - Logging driver specific options. Used to set the path to the container
+ log file.
+ type: dict
+ aliases:
+ - log_options
+ suboptions:
+ path:
+ description:
+ - Specify a path to the log file (e.g. /var/log/container/mycontainer.json).
+ type: str
+ required: false
+ max_size:
+ description:
+ - Specify a max size of the log file (e.g 10mb).
+ type: str
+ required: false
+ tag:
+ description:
+ - Specify a custom log tag for the container.
+ type: str
+ required: false
+
+ mac_address:
+ description:
+ - Specify a MAC address for the container, for example
+ '92:d0:c6:0a:29:33'.
+ Don't forget that it must be unique within one Ethernet network.
+ type: str
+ memory:
+ description:
+ - Memory limit (format 10k, where unit = b, k, m or g)
+ Note - idempotency is supported for integers only.
+ type: str
+ memory_reservation:
+ description:
+ - Memory soft limit (format 100m, where unit = b, k, m or g)
+ Note - idempotency is supported for integers only.
+ type: str
+ memory_swap:
+ description:
+ - A limit value equal to memory plus swap. Must be used with the -m
+ (--memory) flag.
+ The swap LIMIT should always be larger than -m (--memory) value.
+ By default, the swap LIMIT will be set to double the value of --memory
+ Note - idempotency is supported for integers only.
+ type: str
+ memory_swappiness:
+ description:
+ - Tune a container's memory swappiness behavior. Accepts an integer
+ between 0 and 100.
+ type: int
+ mount:
+ description:
+ - Attach a filesystem mount to the container. bind or tmpfs
+ For example mount
+ "type=bind,source=/path/on/host,destination=/path/in/container"
+ type: list
+ elements: str
+ aliases:
+ - mounts
+ network:
+ description:
+ - Set the Network mode for the container
+ * bridge create a network stack on the default bridge
+ * none no networking
+ * container:<name|id> reuse another container's network stack
+ * host use the podman host network stack.
+ * <network-name>|<network-id> connect to a user-defined network
+ * ns:<path> path to a network namespace to join
+ * slirp4netns use slirp4netns to create a user network stack.
+ This is the default for rootless containers
+ type: list
+ elements: str
+ aliases:
+ - net
+ - network_mode
+ network_aliases:
+ description:
+ - Add network-scoped alias for the container.
+ A container will only have access to aliases on the first network that it joins.
+ This is a limitation that will be removed in a later release.
+ type: list
+ elements: str
+ no_hosts:
+ description:
+ - Do not create /etc/hosts for the container
+ Default is false.
+ type: bool
+ oom_kill_disable:
+ description:
+ - Whether to disable OOM Killer for the container or not.
+ Default is false.
+ type: bool
+ oom_score_adj:
+ description:
+ - Tune the host's OOM preferences for containers (accepts -1000 to 1000)
+ type: int
+ pid:
+ description:
+ - Set the PID mode for the container
+ type: str
+ aliases:
+ - pid_mode
+ pids_limit:
+ description:
+ - Tune the container's PIDs limit. Set -1 to have unlimited PIDs for the
+ container.
+ type: str
+ pod:
+ description:
+ - Run container in an existing pod.
+ If you want podman to make the pod for you, prefix the pod name
+ with "new:"
+ type: str
+ privileged:
+ description:
+ - Give extended privileges to this container. The default is false.
+ type: bool
+ publish:
+ description:
+ - Publish a container's port, or range of ports, to the host.
+ Format - ip:hostPort:containerPort | ip::containerPort |
+ hostPort:containerPort | containerPort
+ In case of only containerPort is set, the hostPort will chosen
+ randomly by Podman.
+ type: list
+ elements: str
+ aliases:
+ - ports
+ - published
+ - published_ports
+ publish_all:
+ description:
+ - Publish all exposed ports to random ports on the host interfaces. The
+ default is false.
+ type: bool
+ read_only:
+ description:
+ - Mount the container's root filesystem as read only. Default is false
+ type: bool
+ read_only_tmpfs:
+ description:
+ - If container is running in --read-only mode, then mount a read-write
+ tmpfs on /run, /tmp, and /var/tmp. The default is true
+ type: bool
+ recreate:
+ description:
+ - Use with present and started states to force the re-creation of an
+ existing container.
+ type: bool
+ default: False
+ requires:
+ description:
+ - Specify one or more requirements. A requirement is a dependency
+ container that will be started before this container.
+ Containers can be specified by name or ID.
+ type: list
+ elements: str
+ restart_policy:
+ description:
+ - Restart policy to follow when containers exit.
+ Restart policy will not take effect if a container is stopped via the
+ podman kill or podman stop commands. Valid values are
+ * no - Do not restart containers on exit
+ * on-failure[:max_retries] - Restart containers when they exit with a
+ non-0 exit code, retrying indefinitely
+ or until the optional max_retries count is hit
+ * always - Restart containers when they exit, regardless of status,
+ retrying indefinitely
+ type: str
+ rm:
+ description:
+ - Automatically remove the container when it exits. The default is false.
+ type: bool
+ aliases:
+ - remove
+ - auto_remove
+ rootfs:
+ description:
+ - If true, the first argument refers to an exploded container on the file
+ system. The default is false.
+ type: bool
+ sdnotify:
+ description:
+ - Determines how to use the NOTIFY_SOCKET, as passed with systemd and Type=notify.
+ Can be container, conmon, ignore.
+ type: str
+ secrets:
+ description:
+ - Add the named secrets into the container.
+ The format is C(secret[,opt=opt...]), see
+ L(documentation,https://docs.podman.io/en/latest/markdown/podman-run.1.html#secret-secret-opt-opt) for more details.
+ type: list
+ elements: str
+ security_opt:
+ description:
+ - Security Options. For example security_opt "seccomp=unconfined"
+ type: list
+ elements: str
+ shm_size:
+ description:
+ - Size of /dev/shm. The format is <number><unit>. number must be greater
+ than 0.
+ Unit is optional and can be b (bytes), k (kilobytes), m(megabytes), or
+ g (gigabytes).
+ If you omit the unit, the system uses bytes. If you omit the size
+ entirely, the system uses 64m
+ type: str
+ sig_proxy:
+ description:
+ - Proxy signals sent to the podman run command to the container process.
+ SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is true.
+ type: bool
+ stop_signal:
+ description:
+ - Signal to stop a container. Default is SIGTERM.
+ type: int
+ stop_timeout:
+ description:
+ - Timeout (in seconds) to stop a container. Default is 10.
+ type: int
+ subgidname:
+ description:
+ - Run the container in a new user namespace using the map with 'name' in
+ the /etc/subgid file.
+ type: str
+ subuidname:
+ description:
+ - Run the container in a new user namespace using the map with 'name' in
+ the /etc/subuid file.
+ type: str
+ sysctl:
+ description:
+ - Configure namespaced kernel parameters at runtime
+ type: dict
+ systemd:
+ description:
+ - Run container in systemd mode. The default is true.
+ type: str
+ timezone:
+ description:
+ - Set timezone in container. This flag takes area-based timezones,
+ GMT time, as well as local, which sets the timezone in the container to
+ match the host machine.
+ See /usr/share/zoneinfo/ for valid timezones.
+ Remote connections use local containers.conf for defaults.
+ type: str
+ tmpfs:
+ description:
+ - Create a tmpfs mount. For example tmpfs
+ "/tmp" "rw,size=787448k,mode=1777"
+ type: dict
+ tty:
+ description:
+ - Allocate a pseudo-TTY. The default is false.
+ type: bool
+ uidmap:
+ description:
+ - Run the container in a new user namespace using the supplied mapping.
+ type: list
+ elements: str
+ ulimit:
+ description:
+ - Ulimit options
+ type: list
+ elements: str
+ aliases:
+ - ulimits
+ user:
+ description:
+ - Sets the username or UID used and optionally the groupname or GID for
+ the specified command.
+ type: str
+ userns:
+ description:
+ - Set the user namespace mode for the container.
+ It defaults to the PODMAN_USERNS environment variable.
+ An empty value means user namespaces are disabled.
+ type: str
+ aliases:
+ - userns_mode
+ uts:
+ description:
+ - Set the UTS mode for the container
+ type: str
+ volume:
+ description:
+ - Create a bind mount. If you specify, volume /HOST-DIR:/CONTAINER-DIR,
+ podman bind mounts /HOST-DIR in the host to /CONTAINER-DIR in the
+ podman container.
+ type: list
+ elements: str
+ aliases:
+ - volumes
+ volumes_from:
+ description:
+ - Mount volumes from the specified container(s).
+ type: list
+ elements: str
+ workdir:
+ description:
+ - Working directory inside the container.
+ The default working directory for running binaries within a container
+ is the root directory (/).
+ type: str
+ aliases:
+ - working_dir
+"""
+
+EXAMPLES = r"""
+- name: Run container
+ containers.podman.podman_container:
+ name: container
+ image: quay.io/bitnami/wildfly
+ state: started
+
+- name: Create a data container
+ containers.podman.podman_container:
+ name: mydata
+ image: busybox
+ volume:
+ - /tmp/data
+
+- name: Re-create a redis container with systemd service file generated in /tmp/
+ containers.podman.podman_container:
+ name: myredis
+ image: redis
+ command: redis-server --appendonly yes
+ state: present
+ recreate: true
+ expose:
+ - 6379
+ volumes_from:
+ - mydata
+ generate_systemd:
+ path: /tmp/
+ restart_policy: always
+ time: 120
+ names: true
+ container_prefix: ainer
+
+- name: Restart a container
+ containers.podman.podman_container:
+ name: myapplication
+ image: redis
+ state: started
+ restart: true
+ etc_hosts:
+ other: "127.0.0.1"
+ restart_policy: "no"
+ device: "/dev/sda:/dev/xvda:rwm"
+ ports:
+ - "8080:9000"
+ - "127.0.0.1:8081:9001/udp"
+ env:
+ SECRET_KEY: "ssssh"
+ BOOLEAN_KEY: "yes"
+
+- name: Container present
+ containers.podman.podman_container:
+ name: mycontainer
+ state: present
+ image: ubuntu:14.04
+ command: "sleep 1d"
+
+- name: Stop a container
+ containers.podman.podman_container:
+ name: mycontainer
+ state: stopped
+
+- name: Start 4 load-balanced containers
+ containers.podman.podman_container:
+ name: "container{{ item }}"
+ recreate: true
+ image: someuser/anotherappimage
+ command: sleep 1d
+ with_sequence: count=4
+
+- name: remove container
+ containers.podman.podman_container:
+ name: ohno
+ state: absent
+
+- name: Writing output
+ containers.podman.podman_container:
+ name: myservice
+ image: busybox
+ log_options: path=/var/log/container/mycontainer.json
+ log_driver: k8s-file
+"""
+
+RETURN = r"""
+container:
+ description:
+ - Facts representing the current state of the container. Matches the
+ podman inspection output.
+ - Note that facts are part of the registered vars since Ansible 2.8. For
+ compatibility reasons, the facts
+ are also accessible directly as C(podman_container). Note that the
+ returned fact will be removed in Ansible 2.12.
+ - Empty if C(state) is I(absent).
+ returned: always
+ type: dict
+ sample: '{
+ "AppArmorProfile": "",
+ "Args": [
+ "sh"
+ ],
+ "BoundingCaps": [
+ "CAP_CHOWN",
+ ...
+ ],
+ "Config": {
+ "Annotations": {
+ "io.kubernetes.cri-o.ContainerType": "sandbox",
+ "io.kubernetes.cri-o.TTY": "false"
+ },
+ "AttachStderr": false,
+ "AttachStdin": false,
+ "AttachStdout": false,
+ "Cmd": [
+ "sh"
+ ],
+ "Domainname": "",
+ "Entrypoint": "",
+ "Env": [
+ "PATH=/usr/sbin:/usr/bin:/sbin:/bin",
+ "TERM=xterm",
+ "HOSTNAME=",
+ "container=podman"
+ ],
+ "Hostname": "",
+ "Image": "docker.io/library/busybox:latest",
+ "Labels": null,
+ "OpenStdin": false,
+ "StdinOnce": false,
+ "StopSignal": 15,
+ "Tty": false,
+ "User": {
+ "gid": 0,
+ "uid": 0
+ },
+ "Volumes": null,
+ "WorkingDir": "/"
+ },
+ "ConmonPidFile": "...",
+ "Created": "2019-06-17T19:13:09.873858307+03:00",
+ "Dependencies": [],
+ "Driver": "overlay",
+ "EffectiveCaps": [
+ "CAP_CHOWN",
+ ...
+ ],
+ "ExecIDs": [],
+ "ExitCommand": [
+ "/usr/bin/podman",
+ "--root",
+ ...
+ ],
+ "GraphDriver": {
+ ...
+ },
+ "HostConfig": {
+ ...
+ },
+ "HostnamePath": "...",
+ "HostsPath": "...",
+ "ID": "...",
+ "Image": "...",
+ "ImageName": "docker.io/library/busybox:latest",
+ "IsInfra": false,
+ "LogPath": "/tmp/container/mycontainer.json",
+ "MountLabel": "system_u:object_r:container_file_t:s0:c282,c782",
+ "Mounts": [
+ ...
+ ],
+ "Name": "myservice",
+ "Namespace": "",
+ "NetworkSettings": {
+ "Bridge": "",
+ ...
+ },
+ "Path": "sh",
+ "ProcessLabel": "system_u:system_r:container_t:s0:c282,c782",
+ "ResolvConfPath": "...",
+ "RestartCount": 0,
+ "Rootfs": "",
+ "State": {
+ "Dead": false,
+ "Error": "",
+ "ExitCode": 0,
+ "FinishedAt": "2019-06-17T19:13:10.157518963+03:00",
+ "Healthcheck": {
+ "FailingStreak": 0,
+ "Log": null,
+ "Status": ""
+ },
+ "OOMKilled": false,
+ "OciVersion": "1.0.1-dev",
+ "Paused": false,
+ "Pid": 4083,
+ "Restarting": false,
+ "Running": false,
+ "StartedAt": "2019-06-17T19:13:10.152479729+03:00",
+ "Status": "exited"
+ },
+ "StaticDir": "..."
+ ...
+ }'
+"""
+
+from ansible.module_utils.basic import AnsibleModule # noqa: F402
+from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402
+from ..module_utils.podman.podman_container_lib import ARGUMENTS_SPEC_CONTAINER # noqa: F402
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=ARGUMENTS_SPEC_CONTAINER,
+ mutually_exclusive=(
+ ['no_hosts', 'etc_hosts'],
+ ),
+ supports_check_mode=True,
+ )
+
+ # work on input vars
+ if (module.params['state'] in ['present', 'created']
+ and not module.params['force_restart']
+ and not module.params['image']):
+ module.fail_json(msg="State '%s' required image to be configured!" %
+ module.params['state'])
+
+ results = PodmanManager(module, module.params).execute()
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_container_info.py b/ansible_collections/containers/podman/plugins/modules/podman_container_info.py
new file mode 100644
index 000000000..bbdd29fb9
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_container_info.py
@@ -0,0 +1,416 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+module: podman_container_info
+author:
+ - Sagi Shnaidman (@sshnaidm)
+ - Emilien Macchi (@EmilienM)
+short_description: Gather facts about containers using podman
+notes:
+ - Podman may require elevated privileges in order to run properly.
+description:
+ - Gather facts about containers using C(podman)
+requirements:
+ - "Podman installed on host"
+options:
+ name:
+ description:
+ - List of container names to gather facts about. If no name is given
+ return facts about all containers.
+ type: list
+ elements: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+'''
+
+EXAMPLES = r"""
+- name: Gather facts for all containers
+ containers.podman.podman_container_info:
+
+- name: Gather facts on a specific container
+ containers.podman.podman_container_info:
+ name: web1
+
+- name: Gather facts on several containers
+ containers.podman.podman_container_info:
+ name:
+ - redis
+ - web1
+"""
+
+RETURN = r"""
+containers:
+ description: Facts from all or specificed containers
+ returned: always
+ type: list
+ elements: dict
+ sample: [
+ {
+ "Id": "c5c39f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f",
+ "Created": "2019-10-01T12:51:00.233106443Z",
+ "Path": "dumb-init",
+ "Args": [
+ "--single-child",
+ "--",
+ "kolla_start"
+ ],
+ "State": {
+ "OciVersion": "1.0.1-dev",
+ "Status": "configured",
+ "Running": false,
+ "Paused": false,
+ "Restarting": false,
+ "OOMKilled": false,
+ "Dead": false,
+ "Pid": 0,
+ "ExitCode": 0,
+ "Error": "",
+ "StartedAt": "0001-01-01T00:00:00Z",
+ "FinishedAt": "0001-01-01T00:00:00Z",
+ "Healthcheck": {
+ "Status": "",
+ "FailingStreak": 0,
+ "Log": null
+ }
+ },
+ "Image": "0e267acda67d0ebd643e900d820a91b961d859743039e620191ca1",
+ "ImageName": "docker.io/tripleomaster/centos-haproxy:latest",
+ "Rootfs": "",
+ "Pod": "",
+ "ResolvConfPath": "",
+ "HostnamePath": "",
+ "HostsPath": "",
+ "OCIRuntime": "runc",
+ "Name": "haproxy",
+ "RestartCount": 0,
+ "Driver": "overlay",
+ "MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c78,c866",
+ "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c785,c866",
+ "AppArmorProfile": "",
+ "EffectiveCaps": [
+ "CAP_CHOWN",
+ "CAP_DAC_OVERRIDE",
+ "CAP_FSETID",
+ "CAP_FOWNER",
+ "CAP_MKNOD",
+ "CAP_NET_RAW",
+ "CAP_SETGID",
+ "CAP_SETUID",
+ "CAP_SETFCAP",
+ "CAP_SETPCAP",
+ "CAP_NET_BIND_SERVICE",
+ "CAP_SYS_CHROOT",
+ "CAP_KILL",
+ "CAP_AUDIT_WRITE"
+ ],
+ "BoundingCaps": [
+ "CAP_CHOWN",
+ "CAP_DAC_OVERRIDE",
+ "CAP_FSETID",
+ "CAP_FOWNER",
+ "CAP_MKNOD",
+ "CAP_NET_RAW",
+ "CAP_SETGID",
+ "CAP_SETUID",
+ "CAP_SETFCAP",
+ "CAP_SETPCAP",
+ "CAP_NET_BIND_SERVICE",
+ "CAP_SYS_CHROOT",
+ "CAP_KILL",
+ "CAP_AUDIT_WRITE"
+ ],
+ "ExecIDs": [],
+ "GraphDriver": {
+ "Name": "overlay"
+ },
+ "Mounts": [],
+ "Dependencies": [],
+ "NetworkSettings": {
+ "Bridge": "",
+ "SandboxID": "",
+ "HairpinMode": false,
+ "LinkLocalIPv6Address": "",
+ "LinkLocalIPv6PrefixLen": 0,
+ "Ports": [],
+ "SandboxKey": "",
+ "SecondaryIPAddresses": null,
+ "SecondaryIPv6Addresses": null,
+ "EndpointID": "",
+ "Gateway": "",
+ "GlobalIPv6Address": "",
+ "GlobalIPv6PrefixLen": 0,
+ "IPAddress": "",
+ "IPPrefixLen": 0,
+ "IPv6Gateway": "",
+ "MacAddress": ""
+ },
+ "ExitCommand": [
+ "/usr/bin/podman",
+ "--root",
+ "/var/lib/containers/storage",
+ "--runroot",
+ "/var/run/containers/storage",
+ "--log-level",
+ "error",
+ "--cgroup-manager",
+ "systemd",
+ "--tmpdir",
+ "/var/run/libpod",
+ "--runtime",
+ "runc",
+ "--storage-driver",
+ "overlay",
+ "--events-backend",
+ "journald",
+ "container",
+ "cleanup",
+ "c9e813703f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f"
+ ],
+ "Namespace": "",
+ "IsInfra": false,
+ "Config": {
+ "Hostname": "c5c39e813703",
+ "Domainname": "",
+ "User": "",
+ "AttachStdin": false,
+ "AttachStdout": false,
+ "AttachStderr": false,
+ "Tty": false,
+ "OpenStdin": false,
+ "StdinOnce": false,
+ "Env": [
+ "PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "TERM=xterm",
+ "HOSTNAME=",
+ "container=oci",
+ "KOLLA_INSTALL_METATYPE=rdo",
+ "KOLLA_BASE_DISTRO=centos",
+ "KOLLA_INSTALL_TYPE=binary",
+ "KOLLA_DISTRO_PYTHON_VERSION=2.7",
+ "KOLLA_BASE_ARCH=x86_64"
+ ],
+ "Cmd": [
+ "kolla_start"
+ ],
+ "Image": "docker.io/tripleomaster/centos-haproxy:latest",
+ "Volumes": null,
+ "WorkingDir": "/",
+ "Entrypoint": "dumb-init --single-child --",
+ "OnBuild": null,
+ "Labels": {
+ "build-date": "20190919",
+ "kolla_version": "8.1.0",
+ "name": "haproxy",
+ "org.label-schema.build-date": "20190801",
+ "org.label-schema.license": "GPLv2",
+ "org.label-schema.name": "CentOS Base Image",
+ "org.label-schema.schema-version": "1.0",
+ "org.label-schema.vendor": "CentOS"
+ },
+ "Annotations": {
+ "io.kubernetes.cri-o.ContainerType": "sandbox",
+ "io.kubernetes.cri-o.TTY": "false",
+ "io.podman.annotations.autoremove": "FALSE",
+ "io.podman.annotations.init": "FALSE",
+ "io.podman.annotations.privileged": "FALSE",
+ "io.podman.annotations.publish-all": "FALSE"
+ },
+ "StopSignal": 15
+ },
+ "HostConfig": {
+ "Binds": [],
+ "ContainerIDFile": "",
+ "LogConfig": {
+ "Type": "k8s-file",
+ "Config": null
+ },
+ "NetworkMode": "default",
+ "PortBindings": {},
+ "RestartPolicy": {
+ "Name": "",
+ "MaximumRetryCount": 0
+ },
+ "AutoRemove": false,
+ "VolumeDriver": "",
+ "VolumesFrom": null,
+ "CapAdd": [],
+ "CapDrop": [],
+ "Dns": [],
+ "DnsOptions": [],
+ "DnsSearch": [],
+ "ExtraHosts": [],
+ "GroupAdd": [],
+ "IpcMode": "",
+ "Cgroup": "",
+ "Links": null,
+ "OomScoreAdj": 0,
+ "PidMode": "",
+ "Privileged": false,
+ "PublishAllPorts": false,
+ "ReadonlyRootfs": false,
+ "SecurityOpt": [],
+ "Tmpfs": {},
+ "UTSMode": "",
+ "UsernsMode": "",
+ "ShmSize": 65536000,
+ "Runtime": "oci",
+ "ConsoleSize": [
+ 0,
+ 0
+ ],
+ "Isolation": "",
+ "CpuShares": 0,
+ "Memory": 0,
+ "NanoCpus": 0,
+ "CgroupParent": "",
+ "BlkioWeight": 0,
+ "BlkioWeightDevice": null,
+ "BlkioDeviceReadBps": null,
+ "BlkioDeviceWriteBps": null,
+ "BlkioDeviceReadIOps": null,
+ "BlkioDeviceWriteIOps": null,
+ "CpuPeriod": 0,
+ "CpuQuota": 0,
+ "CpuRealtimePeriod": 0,
+ "CpuRealtimeRuntime": 0,
+ "CpusetCpus": "",
+ "CpusetMems": "",
+ "Devices": [],
+ "DiskQuota": 0,
+ "KernelMemory": 0,
+ "MemoryReservation": 0,
+ "MemorySwap": 0,
+ "MemorySwappiness": -1,
+ "OomKillDisable": false,
+ "PidsLimit": 0,
+ "Ulimits": [
+ {
+ "Name": "RLIMIT_NOFILE",
+ "Soft": 1048576,
+ "Hard": 1048576
+ },
+ {
+ "Name": "RLIMIT_NPROC",
+ "Soft": 1048576,
+ "Hard": 1048576
+ }
+ ],
+ "CpuCount": 0,
+ "CpuPercent": 0,
+ "IOMaximumIOps": 0,
+ "IOMaximumBandwidth": 0
+ }
+ }
+ ]
+"""
+
+import json
+import time
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_containers_facts(module, executable, name):
+ """Collect containers facts for all containers or for specified in 'name'.
+
+ Arguments:
+ module {AnsibleModule} -- instance of AnsibleModule
+ executable {string} -- binary to execute when inspecting containers
+ name {list} -- list of names or None in case of all containers
+
+ Returns:
+ list of containers info, stdout, stderr
+ """
+ retry = 0
+ retry_limit = 4
+ if not name:
+ all_names = [executable, 'container', 'ls', '-q', '-a']
+ rc, out, err = module.run_command(all_names)
+ # This should not fail in regular circumstances, so retry again
+ # https://github.com/containers/podman/issues/10225
+ while rc != 0 and retry <= retry_limit:
+ module.log(msg="Unable to get list of containers: %s" % err)
+ time.sleep(1)
+ retry += 1
+ rc, out, err = module.run_command(all_names)
+ if rc != 0:
+ module.fail_json(msg="Unable to get list of containers during"
+ " %s retries" % retry_limit)
+ name = out.split()
+ if not name:
+ return [], out, err
+ command = [executable, 'container', 'inspect']
+ command.extend(name)
+ rc, out, err = module.run_command(command)
+ if rc == 0:
+ json_out = json.loads(out) if out else None
+ if json_out is None:
+ return [], out, err
+ return json_out, out, err
+ if rc != 0 and 'no such ' in err:
+ if len(name) < 2:
+ return [], out, err
+ return cycle_over(module, executable, name)
+ module.fail_json(msg="Unable to gather info for %s: %s" % (",".join(name), err))
+
+
+def cycle_over(module, executable, name):
+ """Inspect each container in a cycle in case some of them don't exist.
+
+ Arguments:
+ module {AnsibleModule} -- instance of AnsibleModule
+ executable {string} -- binary to execute when inspecting containers
+ name {list} -- list of containers names to inspect
+
+ Returns:
+ list of containers info, stdout as empty, stderr
+ """
+ inspection = []
+ stderrs = []
+ for container in name:
+ command = [executable, 'container', 'inspect', container]
+ rc, out, err = module.run_command(command)
+ if rc != 0 and 'no such ' not in err:
+ module.fail_json(msg="Unable to gather info for %s: %s" % (container, err))
+ if rc == 0 and out:
+ json_out = json.loads(out)
+ if json_out:
+ inspection += json_out
+ stderrs.append(err)
+ return inspection, "", "\n".join(stderrs)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec={
+ 'executable': {'type': 'str', 'default': 'podman'},
+ 'name': {'type': 'list', 'elements': 'str'},
+ },
+ supports_check_mode=True,
+ )
+
+ name = module.params['name']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+ # pylint: disable=unused-variable
+ inspect_results, out, err = get_containers_facts(module, executable, name)
+
+ results = {
+ "changed": False,
+ "containers": inspect_results,
+ "stderr": err
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_containers.py b/ansible_collections/containers/podman/plugins/modules/podman_containers.py
new file mode 100644
index 000000000..c67aee344
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_containers.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: podman_containers
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.4.0'
+short_description: Manage podman containers in a batch
+description:
+ - Manage groups of podman containers
+requirements:
+ - "podman"
+options:
+ containers:
+ description:
+ - List of dictionaries with data for running containers for podman_container module.
+ required: True
+ type: list
+ elements: dict
+ debug:
+ description:
+ - Return additional information which can be helpful for investigations.
+ type: bool
+ default: False
+'''
+
+EXAMPLES = '''
+- name: Run three containers at once
+ podman_containers:
+ containers:
+ - name: alpine
+ image: alpine
+ command: sleep 1d
+ - name: web
+ image: nginx
+ - name: test
+ image: python:3-alpine
+ command: python -V
+'''
+
+from ansible.module_utils.basic import AnsibleModule # noqa: F402
+from ..module_utils.podman.podman_container_lib import PodmanManager # noqa: F402
+from ..module_utils.podman.podman_container_lib import set_container_opts # noqa: F402
+
+
+def combine(results):
+ changed = any(i.get('changed', False) for i in results)
+ failed = any(i.get('failed', False) for i in results)
+ actions = []
+ podman_actions = []
+ containers = []
+ podman_version = ''
+ diffs = {}
+ stderr = ''
+ stdout = ''
+ for i in results:
+ if 'actions' in i and i['actions']:
+ actions += i['actions']
+ if 'podman_actions' in i and i['podman_actions']:
+ podman_actions += i['podman_actions']
+ if 'container' in i and i['container']:
+ containers.append(i['container'])
+ if 'podman_version' in i:
+ podman_version = i['podman_version']
+ if 'diff' in i:
+ diffs[i['container']['Name']] = i['diff']
+ if 'stderr' in i:
+ stderr += i['stderr']
+ if 'stdout' in i:
+ stdout += i['stdout']
+
+ total = {
+ 'changed': changed,
+ 'failed': failed,
+ 'actions': actions,
+ 'podman_actions': podman_actions,
+ 'containers': containers,
+ 'stdout': stdout,
+ 'stderr': stderr,
+ }
+ if podman_version:
+ total['podman_version'] = podman_version
+ if diffs:
+ before = after = ''
+ for k, v in diffs.items():
+ before += "".join([str(k), ": ", str(v['before']), "\n"])
+ after += "".join([str(k), ": ", str(v['after']), "\n"])
+ total['diff'] = {
+ 'before': before,
+ 'after': after
+ }
+ return total
+
+
+def check_input_strict(container):
+ if container['state'] in ['started', 'present'] and not container['image']:
+ return "State '%s' required image to be configured!" % container['state']
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ containers=dict(type='list', elements='dict', required=True),
+ debug=dict(type='bool', default=False),
+ ),
+ supports_check_mode=True,
+ )
+ # work on input vars
+
+ results = []
+ for container in module.params['containers']:
+ options_dict = set_container_opts(container)
+ options_dict['debug'] = module.params['debug'] or options_dict['debug']
+ test_input = check_input_strict(options_dict)
+ if test_input:
+ module.fail_json(
+ msg="Failed to run container %s because: %s" % (options_dict['name'], test_input))
+ res = PodmanManager(module, options_dict).execute()
+ results.append(res)
+ total_results = combine(results)
+ module.exit_json(**total_results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_export.py b/ansible_collections/containers/podman/plugins/modules/podman_export.py
new file mode 100644
index 000000000..e2bb19614
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_export.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# Copyright (c) 2021, Sagi Shnaidman <sshnaidm@redhat.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_export
+short_description: Export a podman container
+author: Sagi Shnaidman (@sshnaidm)
+description:
+ - podman export exports the filesystem of a container and saves it as a
+ tarball on the local machine
+options:
+ dest:
+ description:
+ - Path to export container to.
+ type: str
+ required: true
+ container:
+ description:
+ - Container to export.
+ type: str
+ required: true
+ force:
+ description:
+ - Force saving to file even if it exists.
+ type: bool
+ default: True
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+requirements:
+ - "Podman installed on host"
+'''
+
+RETURN = '''
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- containers.podman.podman_export:
+ dest: /path/to/tar/file
+ container: container-name
+'''
+
+import os # noqa: E402
+from ansible.module_utils.basic import AnsibleModule # noqa: E402
+from ..module_utils.podman.common import remove_file_or_dir # noqa: E402
+
+
+def export(module, executable):
+ changed = False
+ command = [executable, 'export']
+ command += ['-o=%s' % module.params['dest'], module.params['container']]
+ if module.params['force']:
+ dest = module.params['dest']
+ if os.path.exists(dest):
+ changed = True
+ if module.check_mode:
+ return changed, '', ''
+ try:
+ remove_file_or_dir(dest)
+ except Exception as e:
+ module.fail_json(msg="Error deleting %s path: %s" % (dest, e))
+ else:
+ changed = not os.path.exists(module.params['dest'])
+ if module.check_mode:
+ return changed, '', ''
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ module.fail_json(msg="Error exporting container %s: %s" % (
+ module.params['container'], err))
+ return changed, out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ dest=dict(type='str', required=True),
+ container=dict(type='str', required=True),
+ force=dict(type='bool', default=True),
+ executable=dict(type='str', default='podman')
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.get_bin_path(module.params['executable'], required=True)
+ changed, out, err = export(module, executable)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ }
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_generate_systemd.py b/ansible_collections/containers/podman/plugins/modules/podman_generate_systemd.py
new file mode 100644
index 000000000..9c9bc7b27
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_generate_systemd.py
@@ -0,0 +1,604 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# 2022, Sébastien Gendre <seb@k-7.ch>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+module: podman_generate_systemd
+author:
+ - Sébastien Gendre (@CyberFox001)
+short_description: Generate systemd unit from a pod or a container
+description:
+ - Generate systemd .service unit file(s) from a pod or a container
+ - Support Ansible check mode
+options:
+ name:
+ description:
+ - Name of the pod or container to export
+ type: str
+ required: true
+ dest:
+ description:
+ - Destination of the generated systemd unit file(s).
+ - Use C(/etc/systemd/system) for the system-wide systemd instance.
+ - Use C(/etc/systemd/user) or C(~/.config/systemd/user) for use with per-user instances of systemd.
+ type: path
+ new:
+ description:
+ - Generate unit files that create containers and pods, not only start them.
+ - Refer to podman-generate-systemd(1) man page for more information.
+ type: bool
+ default: false
+ restart_policy:
+ description:
+ - Restart policy of the service
+ type: str
+ choices:
+ - no-restart
+ - on-success
+ - on-failure
+ - on-abnormal
+ - on-watchdog
+ - on-abort
+ - always
+ restart_sec:
+ description:
+ - Configures the time to sleep before restarting a service (as configured with restart-policy).
+ - Takes a value in seconds.
+ - Only with Podman 4.0.0 and above
+ type: int
+ start_timeout:
+ description:
+ - Override the default start timeout for the container with the given value in seconds.
+ - Only with Podman 4.0.0 and above
+ type: int
+ stop_timeout:
+ description:
+ - Override the default stop timeout for the container with the given value in seconds.
+ type: int
+ env:
+ description:
+ - Set environment variables to the systemd unit files.
+ - Keys are the environment variable names, and values are the environment variable values
+ - Only with Podman 4.3.0 and above
+ type: dict
+ use_names:
+ description:
+ - Use name of the containers for the start, stop, and description in the unit file.
+ type: bool
+ default: true
+ container_prefix:
+ description:
+ - Set the systemd unit name prefix for containers.
+ - If not set, use the default defined by podman, C(container).
+ - Refer to podman-generate-systemd(1) man page for more information.
+ type: str
+ pod_prefix:
+ description:
+ - Set the systemd unit name prefix for pods.
+ - If not set, use the default defined by podman, C(pod).
+ - Refer to podman-generate-systemd(1) man page for more information.
+ type: str
+ separator:
+ description:
+ - Systemd unit name separator between the name/id of a container/pod and the prefix.
+ - If not set, use the default defined by podman, C(-).
+ - Refer to podman-generate-systemd(1) man page for more information.
+ type: str
+ no_header:
+ description:
+ - Do not generate the header including meta data such as the Podman version and the timestamp.
+ type: bool
+ default: false
+ after:
+ description:
+ - Add the systemd unit after (C(After=)) option, that ordering dependencies between the list of dependencies and this service.
+ - This option may be specified more than once.
+ - User-defined dependencies will be appended to the generated unit file
+ - But any existing options such as needed or defined by default (e.g. C(online.target)) will not be removed or overridden.
+ - Only with Podman 4.0.0 and above
+ type: list
+ elements: str
+ wants:
+ description:
+ - Add the systemd unit wants (C(Wants=)) option, that this service is (weak) dependent on.
+ - This option may be specified more than once.
+ - This option does not influence the order in which services are started or stopped.
+ - User-defined dependencies will be appended to the generated unit file
+ - But any existing options such as needed or defined by default (e.g. C(online.target)) will not be removed or overridden.
+ - Only with Podman 4.0.0 and above
+ type: list
+ elements: str
+ requires:
+ description:
+ - Set the systemd unit requires (Requires=) option.
+ - Similar to wants, but declares a stronger requirement dependency.
+ - Only with Podman 4.0.0 and above
+ type: list
+ elements: str
+ executable:
+ description:
+ - C(Podman) executable name or full path
+ type: str
+ default: podman
+requirements:
+ - Podman installed on target host
+notes:
+ - If you indicate a pod, the systemd units for it and all its containers will be generated
+ - Create all your pods, containers and their dependencies before generating the systemd files
+ - If a container or pod is already started before you do a C(systemctl daemon-reload),
+ systemd will not see the container or pod as started
+ - Stop your container or pod before you do a C(systemctl daemon-reload),
+ then you can start them with C(systemctl start my_container.service)
+'''
+
+EXAMPLES = '''
+# Example of creating a container and systemd unit file.
+# When using podman_generate_systemd with new:true then
+# the container needs rm:true for idempotence.
+- name: Create postgres container
+ containers.podman.podman_container:
+ name: postgres
+ image: docker.io/library/postgres:latest
+ rm: true
+ state: created
+
+- name: Generate systemd unit file for postgres container
+ containers.podman.podman_generate_systemd:
+ name: postgres
+ new: true
+ no_header: true
+ dest: /etc/systemd/system
+
+- name: Ensure postgres container is started and enabled
+ ansible.builtin.systemd:
+ name: container-postgres
+ daemon_reload: true
+ state: started
+ enabled: true
+
+
+# Example of creating a container and integrate it into systemd
+- name: A postgres container must exist, stopped
+ containers.podman.podman_container:
+ name: postgres_local
+ image: docker.io/library/postgres:latest
+ state: stopped
+
+- name: Systemd unit files for postgres container must exist
+ containers.podman.podman_generate_systemd:
+ name: postgres_local
+ dest: ~/.config/systemd/user/
+
+- name: Postgres container must be started and enabled on systemd
+ ansible.builtin.systemd:
+ name: container-postgres_local
+ daemon_reload: true
+ state: started
+ enabled: true
+
+
+# Generate the unit files, but store them on an Ansible variable
+# instead of writing them on target host
+- name: Systemd unit files for postgres container must be generated
+ containers.podman.podman_generate_systemd:
+ name: postgres_local
+ register: postgres_local_systemd_unit
+
+# Generate the unit files with environment variables sets
+- name: Systemd unit files for postgres container must be generated
+ containers.podman.podman_generate_systemd:
+ name: postgres_local
+ env:
+ POSTGRES_USER: my_app
+ POSTGRES_PASSWORD: example
+ register: postgres_local_systemd_unit
+'''
+
+RETURN = '''
+systemd_units:
+ description: A copy of the generated systemd .service unit(s)
+ returned: always
+ type: dict
+ sample: {
+ "container-postgres_local": " #Content of the systemd .servec unit for postgres_local container",
+ "pod-my_webapp": " #Content of the systemd .servec unit for my_webapp pod",
+ }
+podman_command:
+ description: A copy of the podman command used to generate the systemd unit(s)
+ returned: always
+ type: str
+ sample: "podman generate systemd my_webapp"
+'''
+
+
+import os
+from ansible.module_utils.basic import AnsibleModule
+import json
+
+
+RESTART_POLICY_CHOICES = [
+ 'no-restart',
+ 'on-success',
+ 'on-failure',
+ 'on-abnormal',
+ 'on-watchdog',
+ 'on-abort',
+ 'always',
+]
+
+
+def generate_systemd(module):
+ '''Generate systemd .service unit file from a pod or container.
+
+ Parameter:
+ - module (AnsibleModule): An AnsibleModule object
+
+ Returns (tuple[bool, list[str], str]):
+ - A boolean which indicate whether the targeted systemd state is modified
+ - A copy of the generated systemd .service units content
+ - A copy of the command, as a string
+ '''
+ # Flag which indicate whether the targeted system state is modified
+ changed = False
+
+ # Build the podman command, based on the module parameters
+ command_options = []
+
+ # New option
+ if module.params['new']:
+ command_options.append('--new')
+
+ # Restart policy option
+ restart_policy = module.params['restart_policy']
+ if restart_policy:
+ # add the restart policy to options
+ if restart_policy == 'no-restart':
+ restart_policy = 'no'
+ command_options.append(
+ '--restart-policy={restart_policy}'.format(
+ restart_policy=restart_policy,
+ ),
+ )
+
+ # Restart-sec option (only for Podman 4.0.0 and above)
+ restart_sec = module.params['restart_sec']
+ if restart_sec:
+ command_options.append(
+ '--restart-sec={restart_sec}'.format(
+ restart_sec=restart_sec,
+ ),
+ )
+
+ # Start-timeout option (only for Podman 4.0.0 and above)
+ start_timeout = module.params['start_timeout']
+ if start_timeout:
+ command_options.append(
+ '--start-timeout={start_timeout}'.format(
+ start_timeout=start_timeout,
+ ),
+ )
+
+ # Stop-timeout option
+ stop_timeout = module.params['stop_timeout']
+ if stop_timeout:
+ command_options.append(
+ '--stop-timeout={stop_timeout}'.format(
+ stop_timeout=stop_timeout,
+ ),
+ )
+
+ # Use container name(s) option
+ if module.params['use_names']:
+ command_options.append('--name')
+
+ # Container-prefix option
+ container_prefix = module.params['container_prefix']
+ if container_prefix is not None:
+ command_options.append(
+ '--container-prefix={container_prefix}'.format(
+ container_prefix=container_prefix,
+ ),
+ )
+
+ # Pod-prefix option
+ pod_prefix = module.params['pod_prefix']
+ if pod_prefix is not None:
+ command_options.append(
+ '--pod-prefix={pod_prefix}'.format(
+ pod_prefix=pod_prefix,
+ ),
+ )
+
+ # Separator option
+ separator = module.params['separator']
+ if separator is not None:
+ command_options.append(
+ '--separator={separator}'.format(
+ separator=separator,
+ ),
+ )
+
+ # No-header option
+ if module.params['no_header']:
+ command_options.append('--no-header')
+
+ # After option (only for Podman 4.0.0 and above)
+ after = module.params['after']
+ if after:
+ for item in after:
+ command_options.append(
+ '--after={item}'.format(
+ item=item,
+ ),
+ )
+
+ # Wants option (only for Podman 4.0.0 and above)
+ wants = module.params['wants']
+ if wants:
+ for item in wants:
+ command_options.append(
+ '--wants={item}'.format(
+ item=item,
+ )
+ )
+
+ # Requires option (only for Podman 4.0.0 and above)
+ requires = module.params['requires']
+ if requires:
+ for item in requires:
+ command_options.append(
+ '--requires={item}'.format(
+ item=item,
+ ),
+ )
+
+ # Environment variables (only for Podman 4.3.0 and above)
+ environment_variables = module.params['env']
+ if environment_variables:
+ for env_var_name, env_var_value in environment_variables.items():
+ command_options.append(
+ "-e='{env_var_name}={env_var_value}'".format(
+ env_var_name=env_var_name,
+ env_var_value=env_var_value,
+ ),
+ )
+
+ # Set output format, of podman command, to json
+ command_options.extend(['--format', 'json'])
+
+ # Full command build, with option included
+ # Base of the command
+ command = [
+ module.params['executable'], 'generate', 'systemd',
+ ]
+ # Add the options to the commande
+ command.extend(command_options)
+ # Add pod or container name to the command
+ command.append(module.params['name'])
+ # Build the string version of the command, only for module return
+ command_str = ' '.join(command)
+
+ # Run the podman command to generated systemd .service unit(s) content
+ return_code, stdout, stderr = module.run_command(command)
+
+ # In case of error in running the command
+ if return_code != 0:
+ # Print informations about the error and return and empty dictionary
+ message = 'Error generating systemd .service unit(s).'
+ message += ' Command executed: {command_str}'
+ message += ' Command returned with code: {return_code}.'
+ message += ' Error message: {stderr}.'
+ module.fail_json(
+ msg=message.format(
+ command_str=command_str,
+ return_code=return_code,
+ stderr=stderr,
+ ),
+ changed=changed,
+ systemd_units={},
+ podman_command=command_str,
+ )
+
+ # In case of command execution success, its stdout is a json
+ # dictionary. This dictionary is all the generated systemd units.
+ # Each key value pair is one systemd unit. The key is the unit name
+ # and the value is the unit content.
+
+ # Load the returned json dictionary as a python dictionary
+ systemd_units = json.loads(stdout)
+
+ # Write the systemd .service unit(s) content to file(s), if
+ # requested
+ if module.params['dest']:
+ try:
+ systemd_units_dest = module.params['dest']
+ # If destination don't exist
+ if not os.path.exists(systemd_units_dest):
+ # If not in check mode, make it
+ if not module.check_mode:
+ os.makedirs(systemd_units_dest)
+ changed = True
+ # If destination exist but not a directory
+ if not os.path.isdir(systemd_units_dest):
+ # Stop and tell user that the destination is not a directry
+ message = "Destination {systemd_units_dest} is not a directory."
+ message += " Can't save systemd unit files in."
+ module.fail_json(
+ msg=message.format(
+ systemd_units_dest=systemd_units_dest,
+ ),
+ changed=changed,
+ systemd_units=systemd_units,
+ podman_command=command_str,
+ )
+
+ # Write each systemd unit, if needed
+ for unit_name, unit_content in systemd_units.items():
+ # Build full path to unit file
+ unit_file_name = unit_name + '.service'
+ unit_file_full_path = os.path.join(
+ systemd_units_dest,
+ unit_file_name,
+ )
+
+ # See if we need to write the unit file, default yes
+ need_to_write_file = True
+ # If the unit file already exist, compare it with the
+ # generated content
+ if os.path.exists(unit_file_full_path):
+ # Read the file
+ with open(unit_file_full_path, 'r') as unit_file:
+ current_unit_file_content = unit_file.read()
+ # If current unit file content is the same as the
+ # generated content
+ # Remove comments from files, before comparing
+ current_unit_file_content_nocmnt = "\n".join([
+ line for line in current_unit_file_content.splitlines()
+ if not line.startswith('#')])
+ unit_content_nocmnt = "\n".join([
+ line for line in unit_content.splitlines()
+ if not line.startswith('#')])
+ if current_unit_file_content_nocmnt == unit_content_nocmnt:
+ # We don't need to write it
+ need_to_write_file = False
+
+ # Write the file, if needed
+ if need_to_write_file:
+ with open(unit_file_full_path, 'w') as unit_file:
+ # If not in check mode, write the file
+ if not module.check_mode:
+ unit_file.write(unit_content)
+ changed = True
+
+ except Exception as exception:
+ # When exception occurs while trying to write units file
+ message = 'PODMAN-GENERATE-SYSTEMD-DEBUG: '
+ message += 'Error writing systemd units files: '
+ message += '{exception}'
+ module.log(
+ message.format(
+ exception=exception
+ ),
+ )
+ # Return the systemd .service unit(s) content
+ return changed, systemd_units, command_str
+
+
+def run_module():
+ '''Run the module on the target'''
+ # Build the list of parameters user can use
+ module_parameters = {
+ 'name': {
+ 'type': 'str',
+ 'required': True,
+ },
+ 'dest': {
+ 'type': 'path',
+ 'required': False,
+ },
+ 'new': {
+ 'type': 'bool',
+ 'required': False,
+ 'default': False,
+ },
+ 'restart_policy': {
+ 'type': 'str',
+ 'required': False,
+ 'choices': RESTART_POLICY_CHOICES,
+ },
+ 'restart_sec': {
+ 'type': 'int',
+ 'required': False,
+ },
+ 'start_timeout': {
+ 'type': 'int',
+ 'required': False,
+ },
+ 'stop_timeout': {
+ 'type': 'int',
+ 'required': False,
+ },
+ 'env': {
+ 'type': 'dict',
+ 'required': False,
+ },
+ 'use_names': {
+ 'type': 'bool',
+ 'required': False,
+ 'default': True,
+ },
+ 'container_prefix': {
+ 'type': 'str',
+ 'required': False,
+ },
+ 'pod_prefix': {
+ 'type': 'str',
+ 'required': False,
+ },
+ 'separator': {
+ 'type': 'str',
+ 'required': False,
+ },
+ 'no_header': {
+ 'type': 'bool',
+ 'required': False,
+ 'default': False,
+ },
+ 'after': {
+ 'type': 'list',
+ 'elements': 'str',
+ 'required': False,
+ },
+ 'wants': {
+ 'type': 'list',
+ 'elements': 'str',
+ 'required': False,
+ },
+ 'requires': {
+ 'type': 'list',
+ 'elements': 'str',
+ 'required': False,
+ },
+ 'executable': {
+ 'type': 'str',
+ 'required': False,
+ 'default': 'podman',
+ },
+ }
+
+ # Build result dictionary
+ result = {
+ 'changed': False,
+ 'systemd_units': {},
+ 'podman_command': '',
+ }
+
+ # Build the Ansible Module
+ module = AnsibleModule(
+ argument_spec=module_parameters,
+ supports_check_mode=True
+ )
+
+ # Generate the systemd units
+ state_changed, systemd_units, podman_command = generate_systemd(module)
+
+ result['changed'] = state_changed
+ result['systemd_units'] = systemd_units
+ result['podman_command'] = podman_command
+
+ # Return the result
+ module.exit_json(**result)
+
+
+def main():
+ '''Main function of this script.'''
+ run_module()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_image.py b/ansible_collections/containers/podman/plugins/modules/podman_image.py
new file mode 100644
index 000000000..d66ff5d49
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_image.py
@@ -0,0 +1,862 @@
+#!/usr/bin/python
+# Copyright (c) 2018 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+ module: podman_image
+ author:
+ - Sam Doran (@samdoran)
+ short_description: Pull images for use by podman
+ notes: []
+ description:
+ - Build, pull, or push images using Podman.
+ options:
+ arch:
+ description:
+ - CPU architecutre for the container image
+ type: str
+ name:
+ description:
+ - Name of the image to pull, push, or delete. It may contain a tag using the format C(image:tag).
+ required: True
+ type: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the machine running C(podman).
+ default: 'podman'
+ type: str
+ ca_cert_dir:
+ description:
+ - Path to directory containing TLS certificates and keys to use.
+ type: 'path'
+ tag:
+ description:
+ - Tag of the image to pull, push, or delete.
+ default: "latest"
+ type: str
+ pull:
+ description: Whether or not to pull the image.
+ default: True
+ type: bool
+ push:
+ description: Whether or not to push an image.
+ default: False
+ type: bool
+ path:
+ description: Path to the build context directory.
+ type: str
+ force:
+ description:
+ - Whether or not to force push or pull an image.
+ - When building, force the build even if the image already exists.
+ type: bool
+ default: False
+ state:
+ description:
+ - Whether an image should be present, absent, or built.
+ default: "present"
+ type: str
+ choices:
+ - present
+ - absent
+ - build
+ validate_certs:
+ description:
+ - Require HTTPS and validate certificates when pulling or pushing. Also used during build if a pull or push is necessary.
+ type: bool
+ aliases:
+ - tlsverify
+ - tls_verify
+ password:
+ description:
+ - Password to use when authenticating to remote registries.
+ type: str
+ username:
+ description:
+ - username to use when authenticating to remote registries.
+ type: str
+ auth_file:
+ description:
+ - Path to file containing authorization credentials to the remote registry.
+ aliases:
+ - authfile
+ type: path
+ build:
+ description: Arguments that control image build.
+ type: dict
+ default: {}
+ aliases:
+ - build_args
+ - buildargs
+ suboptions:
+ file:
+ description:
+ - Path to the Containerfile if it is not in the build context directory.
+ type: path
+ volume:
+ description:
+ - Specify multiple volume / mount options to mount one or more mounts to a container.
+ type: list
+ elements: str
+ annotation:
+ description:
+ - Dictionary of key=value pairs to add to the image. Only works with OCI images. Ignored for Docker containers.
+ type: dict
+ force_rm:
+ description:
+ - Always remove intermediate containers after a build, even if the build is unsuccessful.
+ type: bool
+ default: False
+ format:
+ description:
+ - Format of the built image.
+ type: str
+ choices:
+ - docker
+ - oci
+ default: "oci"
+ cache:
+ description:
+ - Whether or not to use cached layers when building an image
+ type: bool
+ default: True
+ rm:
+ description: Remove intermediate containers after a successful build
+ type: bool
+ default: True
+ extra_args:
+ description:
+ - Extra args to pass to build, if executed. Does not idempotently check for new build args.
+ type: str
+ push_args:
+ description: Arguments that control pushing images.
+ type: dict
+ default: {}
+ suboptions:
+ compress:
+ description:
+ - Compress tarball image layers when pushing to a directory using the 'dir' transport.
+ type: bool
+ format:
+ description:
+ - Manifest type to use when pushing an image using the 'dir' transport (default is manifest type of source).
+ type: str
+ choices:
+ - oci
+ - v2s1
+ - v2s2
+ remove_signatures:
+ description: Discard any pre-existing signatures in the image
+ type: bool
+ sign_by:
+ description:
+ - Path to a key file to use to sign the image.
+ type: str
+ dest:
+ description: Path or URL where image will be pushed.
+ type: str
+ aliases:
+ - destination
+ transport:
+ description:
+ - Transport to use when pushing in image. If no transport is set, will attempt to push to a remote registry.
+ type: str
+ choices:
+ - dir
+ - docker-archive
+ - docker-daemon
+ - oci-archive
+ - ostree
+'''
+
+EXAMPLES = r"""
+- name: Pull an image
+ containers.podman.podman_image:
+ name: quay.io/bitnami/wildfly
+
+- name: Remove an image
+ containers.podman.podman_image:
+ name: quay.io/bitnami/wildfly
+ state: absent
+
+- name: Remove an image with image id
+ containers.podman.podman_image:
+ name: 0e901e68141f
+ state: absent
+
+- name: Pull a specific version of an image
+ containers.podman.podman_image:
+ name: redis
+ tag: 4
+
+- name: Build a basic OCI image
+ containers.podman.podman_image:
+ name: nginx
+ path: /path/to/build/dir
+
+- name: Build a basic OCI image with advanced parameters
+ containers.podman.podman_image:
+ name: nginx
+ path: /path/to/build/dir
+ build:
+ cache: no
+ force_rm: true
+ format: oci
+ annotation:
+ app: nginx
+ function: proxy
+ info: Load balancer for my cool app
+ extra_args: "--build-arg KEY=value"
+
+- name: Build a Docker formatted image
+ containers.podman.podman_image:
+ name: nginx
+ path: /path/to/build/dir
+ build:
+ format: docker
+
+- name: Build and push an image using existing credentials
+ containers.podman.podman_image:
+ name: nginx
+ path: /path/to/build/dir
+ push: true
+ push_args:
+ dest: quay.io/acme
+
+- name: Build and push an image using an auth file
+ containers.podman.podman_image:
+ name: nginx
+ push: true
+ auth_file: /etc/containers/auth.json
+ push_args:
+ dest: quay.io/acme
+
+- name: Build and push an image using username and password
+ containers.podman.podman_image:
+ name: nginx
+ push: true
+ username: bugs
+ password: "{{ vault_registry_password }}"
+ push_args:
+ dest: quay.io/acme
+
+- name: Build and push an image to multiple registries
+ containers.podman.podman_image:
+ name: "{{ item }}"
+ path: /path/to/build/dir
+ push: true
+ auth_file: /etc/containers/auth.json
+ loop:
+ - quay.io/acme/nginx
+ - docker.io/acme/nginx
+
+- name: Build and push an image to multiple registries with separate parameters
+ containers.podman.podman_image:
+ name: "{{ item.name }}"
+ tag: "{{ item.tag }}"
+ path: /path/to/build/dir
+ push: true
+ auth_file: /etc/containers/auth.json
+ push_args:
+ dest: "{{ item.dest }}"
+ loop:
+ - name: nginx
+ tag: 4
+ dest: docker.io/acme
+
+ - name: nginx
+ tag: 3
+ dest: docker.io/acme
+
+- name: Pull an image for a specific CPU architecture
+ containers.podman.podman_image:
+ name: nginx
+ arch: amd64
+"""
+
+RETURN = r"""
+ image:
+ description:
+ - Image inspection results for the image that was pulled, pushed, or built.
+ returned: success
+ type: dict
+ sample: [
+ {
+ "Annotations": {},
+ "Architecture": "amd64",
+ "Author": "",
+ "Comment": "from Bitnami with love",
+ "ContainerConfig": {
+ "Cmd": [
+ "/run.sh"
+ ],
+ "Entrypoint": [
+ "/app-entrypoint.sh"
+ ],
+ "Env": [
+ "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "IMAGE_OS=debian-9",
+ "NAMI_VERSION=1.0.0-1",
+ "GPG_KEY_SERVERS_LIST=ha.pool.sks-keyservers.net",
+ "TINI_VERSION=v0.13.2",
+ "TINI_GPG_KEY=595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7",
+ "GOSU_VERSION=1.10",
+ "GOSU_GPG_KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4",
+ "BITNAMI_IMAGE_VERSION=16.0.0-debian-9-r27",
+ "BITNAMI_PKG_CHMOD=-R g+rwX",
+ "BITNAMI_PKG_EXTRA_DIRS=/home/wildfly",
+ "HOME=/",
+ "BITNAMI_APP_NAME=wildfly",
+ "NAMI_PREFIX=/.nami",
+ "WILDFLY_HOME=/home/wildfly",
+ "WILDFLY_JAVA_HOME=",
+ "WILDFLY_JAVA_OPTS=",
+ "WILDFLY_MANAGEMENT_HTTP_PORT_NUMBER=9990",
+ "WILDFLY_PASSWORD=bitnami",
+ "WILDFLY_PUBLIC_CONSOLE=true",
+ "WILDFLY_SERVER_AJP_PORT_NUMBER=8009",
+ "WILDFLY_SERVER_HTTP_PORT_NUMBER=8080",
+ "WILDFLY_SERVER_INTERFACE=0.0.0.0",
+ "WILDFLY_USERNAME=user",
+ "WILDFLY_WILDFLY_HOME=/home/wildfly",
+ "WILDFLY_WILDFLY_OPTS=-Dwildfly.as.deployment.ondemand=false"
+ ],
+ "ExposedPorts": {
+ "8080/tcp": {},
+ "9990/tcp": {}
+ },
+ "Labels": {
+ "maintainer": "Bitnami <containers@bitnami.com>"
+ },
+ "User": "1001"
+ },
+ "Created": "2019-04-10T05:48:03.553887623Z",
+ "Digest": "sha256:5a8ab28e314c2222de3feaf6dac94a0436a37fc08979d2722c99d2bef2619a9b",
+ "GraphDriver": {
+ "Data": {
+ "LowerDir": "/var/lib/containers/storage/overlay/142c1beadf1bb09fbd929465ec98c9dca3256638220450efb4214727d0d0680e/diff:/var/lib/containers/s",
+ "MergedDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/merged",
+ "UpperDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/diff",
+ "WorkDir": "/var/lib/containers/storage/overlay/9aa10191f5bddb59e28508e721fdeb43505e5b395845fa99723ed787878dbfea/work"
+ },
+ "Name": "overlay"
+ },
+ "History": [
+ {
+ "comment": "from Bitnami with love",
+ "created": "2019-04-09T22:27:40.659377677Z"
+ },
+ {
+ "created": "2019-04-09T22:38:53.86336555Z",
+ "created_by": "/bin/sh -c #(nop) LABEL maintainer=Bitnami <containers@bitnami.com>",
+ "empty_layer": true
+ },
+ {
+ "created": "2019-04-09T22:38:54.022778765Z",
+ "created_by": "/bin/sh -c #(nop) ENV IMAGE_OS=debian-9",
+ "empty_layer": true
+ },
+ ],
+ "Id": "ace34da54e4af2145e1ad277005adb235a214e4dfe1114c2db9ab460b840f785",
+ "Labels": {
+ "maintainer": "Bitnami <containers@bitnami.com>"
+ },
+ "ManifestType": "application/vnd.docker.distribution.manifest.v1+prettyjws",
+ "Os": "linux",
+ "Parent": "",
+ "RepoDigests": [
+ "quay.io/bitnami/wildfly@sha256:5a8ab28e314c2222de3feaf6dac94a0436a37fc08979d2722c99d2bef2619a9b"
+ ],
+ "RepoTags": [
+ "quay.io/bitnami/wildfly:latest"
+ ],
+ "RootFS": {
+ "Layers": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ],
+ "Type": "layers"
+ },
+ "Size": 466180019,
+ "User": "1001",
+ "Version": "18.09.3",
+ "VirtualSize": 466180019
+ }
+ ]
+"""
+
+import json
+import re
+import shlex
+
+from ansible.module_utils._text import to_native
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.containers.podman.plugins.module_utils.podman.common import run_podman_command
+
+
+class PodmanImageManager(object):
+
+ def __init__(self, module, results):
+
+ super(PodmanImageManager, self).__init__()
+
+ self.module = module
+ self.results = results
+ self.name = self.module.params.get('name')
+ self.executable = self.module.get_bin_path(module.params.get('executable'), required=True)
+ self.tag = self.module.params.get('tag')
+ self.pull = self.module.params.get('pull')
+ self.push = self.module.params.get('push')
+ self.path = self.module.params.get('path')
+ self.force = self.module.params.get('force')
+ self.state = self.module.params.get('state')
+ self.validate_certs = self.module.params.get('validate_certs')
+ self.auth_file = self.module.params.get('auth_file')
+ self.username = self.module.params.get('username')
+ self.password = self.module.params.get('password')
+ self.ca_cert_dir = self.module.params.get('ca_cert_dir')
+ self.build = self.module.params.get('build')
+ self.push_args = self.module.params.get('push_args')
+ self.arch = self.module.params.get('arch')
+
+ repo, repo_tag = parse_repository_tag(self.name)
+ if repo_tag:
+ self.name = repo
+ self.tag = repo_tag
+
+ delimiter = ':' if "sha256" not in self.tag else '@'
+ self.image_name = '{name}{d}{tag}'.format(name=self.name, d=delimiter, tag=self.tag)
+
+ if self.state in ['present', 'build']:
+ self.present()
+
+ if self.state in ['absent']:
+ self.absent()
+
+ def _run(self, args, expected_rc=0, ignore_errors=False):
+ cmd = " ".join([self.executable]
+ + [to_native(i) for i in args])
+ self.module.log("PODMAN-IMAGE-DEBUG: %s" % cmd)
+ self.results['podman_actions'].append(cmd)
+ return run_podman_command(
+ module=self.module,
+ executable=self.executable,
+ args=args,
+ expected_rc=expected_rc,
+ ignore_errors=ignore_errors)
+
+ def _get_id_from_output(self, lines, startswith=None, contains=None, split_on=' ', maxsplit=1):
+ layer_ids = []
+ for line in lines.splitlines():
+ if startswith and line.startswith(startswith) or contains and contains in line:
+ splitline = line.rsplit(split_on, maxsplit)
+ layer_ids.append(splitline[1])
+
+ # Podman 1.4 changed the output to only include the layer id when run in quiet mode
+ if not layer_ids:
+ layer_ids = lines.splitlines()
+
+ return (layer_ids[-1])
+
+ def present(self):
+ image = self.find_image()
+
+ if image:
+ digest_before = image[0].get('Digest', image[0].get('digest'))
+ else:
+ digest_before = None
+
+ if not image or self.force:
+ if self.path:
+ # Build the image
+ self.results['actions'].append('Built image {image_name} from {path}'.format(image_name=self.image_name, path=self.path))
+ if not self.module.check_mode:
+ self.results['image'], self.results['stdout'] = self.build_image()
+ image = self.results['image']
+ else:
+ # Pull the image
+ self.results['actions'].append('Pulled image {image_name}'.format(image_name=self.image_name))
+ if not self.module.check_mode:
+ image = self.results['image'] = self.pull_image()
+
+ if not image:
+ image = self.find_image()
+ if not self.module.check_mode:
+ digest_after = image[0].get('Digest', image[0].get('digest'))
+ self.results['changed'] = digest_before != digest_after
+ else:
+ self.results['changed'] = True
+
+ if self.push:
+ # Push the image
+ if '/' in self.image_name:
+ push_format_string = 'Pushed image {image_name}'
+ else:
+ push_format_string = 'Pushed image {image_name} to {dest}'
+ self.results['actions'].append(push_format_string.format(image_name=self.image_name, dest=self.push_args['dest']))
+ self.results['changed'] = True
+ if not self.module.check_mode:
+ self.results['image'], output = self.push_image()
+ self.results['stdout'] += "\n" + output
+
+ def absent(self):
+ image = self.find_image()
+ image_id = self.find_image_id()
+
+ if image:
+ self.results['actions'].append('Removed image {name}'.format(name=self.name))
+ self.results['changed'] = True
+ self.results['image']['state'] = 'Deleted'
+ if not self.module.check_mode:
+ self.remove_image()
+ elif image_id:
+ self.results['actions'].append(
+ 'Removed image with id {id}'.format(id=self.image_name))
+ self.results['changed'] = True
+ self.results['image']['state'] = 'Deleted'
+ if not self.module.check_mode:
+ self.remove_image_id()
+
+ def find_image(self, image_name=None):
+ if image_name is None:
+ image_name = self.image_name
+ args = ['image', 'ls', image_name, '--format', 'json']
+ rc, images, err = self._run(args, ignore_errors=True)
+ images = json.loads(images)
+ if len(images) > 0:
+ inspect_json = self.inspect_image(image_name)
+ if self._is_target_arch(inspect_json, self.arch) or not self.arch:
+ return images
+
+ return None
+
+ def _is_target_arch(self, inspect_json=None, arch=None):
+ return arch and inspect_json[0]['Architecture'] == arch
+
+ def find_image_id(self, image_id=None):
+ if image_id is None:
+ # If image id is set as image_name, remove tag
+ image_id = re.sub(':.*$', '', self.image_name)
+ args = ['image', 'ls', '--quiet', '--no-trunc']
+ rc, candidates, err = self._run(args, ignore_errors=True)
+ candidates = [re.sub('^sha256:', '', c)
+ for c in str.splitlines(candidates)]
+ for c in candidates:
+ if c.startswith(image_id):
+ return image_id
+ return None
+
+ def inspect_image(self, image_name=None):
+ if image_name is None:
+ image_name = self.image_name
+ args = ['inspect', image_name, '--format', 'json']
+ rc, image_data, err = self._run(args)
+ image_data = json.loads(image_data)
+ if len(image_data) > 0:
+ return image_data
+ else:
+ return None
+
+ def pull_image(self, image_name=None):
+ if image_name is None:
+ image_name = self.image_name
+
+ args = ['pull', image_name, '-q']
+
+ if self.arch:
+ args.extend(['--arch', self.arch])
+
+ if self.auth_file:
+ args.extend(['--authfile', self.auth_file])
+
+ if self.username and self.password:
+ cred_string = '{user}:{password}'.format(user=self.username, password=self.password)
+ args.extend(['--creds', cred_string])
+
+ if self.validate_certs is not None:
+ if self.validate_certs:
+ args.append('--tls-verify')
+ else:
+ args.append('--tls-verify=false')
+
+ if self.ca_cert_dir:
+ args.extend(['--cert-dir', self.ca_cert_dir])
+
+ rc, out, err = self._run(args, ignore_errors=True)
+ if rc != 0:
+ if not self.pull:
+ self.module.fail_json(msg='Failed to find image {image_name} locally, image pull set to {pull_bool}'.format(
+ pull_bool=self.pull, image_name=image_name))
+ else:
+ self.module.fail_json(msg='Failed to pull image {image_name}'.format(image_name=image_name))
+ return self.inspect_image(out.strip())
+
+ def build_image(self):
+ args = ['build']
+ args.extend(['-t', self.image_name])
+
+ if self.validate_certs is not None:
+ if self.validate_certs:
+ args.append('--tls-verify')
+ else:
+ args.append('--tls-verify=false')
+
+ annotation = self.build.get('annotation')
+ if annotation:
+ for k, v in annotation.items():
+ args.extend(['--annotation', '{k}={v}'.format(k=k, v=v)])
+
+ if self.ca_cert_dir:
+ args.extend(['--cert-dir', self.ca_cert_dir])
+
+ if self.build.get('force_rm'):
+ args.append('--force-rm')
+
+ image_format = self.build.get('format')
+ if image_format:
+ args.extend(['--format', image_format])
+
+ if not self.build.get('cache'):
+ args.append('--no-cache')
+
+ if self.build.get('rm'):
+ args.append('--rm')
+
+ containerfile = self.build.get('file')
+ if containerfile:
+ args.extend(['--file', containerfile])
+
+ volume = self.build.get('volume')
+ if volume:
+ for v in volume:
+ args.extend(['--volume', v])
+
+ if self.auth_file:
+ args.extend(['--authfile', self.auth_file])
+
+ if self.username and self.password:
+ cred_string = '{user}:{password}'.format(user=self.username, password=self.password)
+ args.extend(['--creds', cred_string])
+
+ extra_args = self.build.get('extra_args')
+ if extra_args:
+ args.extend(shlex.split(extra_args))
+
+ args.append(self.path)
+
+ rc, out, err = self._run(args, ignore_errors=True)
+ if rc != 0:
+ self.module.fail_json(msg="Failed to build image {image}: {out} {err}".format(image=self.image_name, out=out, err=err))
+
+ last_id = self._get_id_from_output(out, startswith='-->')
+ return self.inspect_image(last_id), out + err
+
+ def push_image(self):
+ args = ['push']
+
+ if self.validate_certs is not None:
+ if self.validate_certs:
+ args.append('--tls-verify')
+ else:
+ args.append('--tls-verify=false')
+
+ if self.ca_cert_dir:
+ args.extend(['--cert-dir', self.ca_cert_dir])
+
+ if self.username and self.password:
+ cred_string = '{user}:{password}'.format(user=self.username, password=self.password)
+ args.extend(['--creds', cred_string])
+
+ if self.auth_file:
+ args.extend(['--authfile', self.auth_file])
+
+ if self.push_args.get('compress'):
+ args.append('--compress')
+
+ push_format = self.push_args.get('format')
+ if push_format:
+ args.extend(['--format', push_format])
+
+ if self.push_args.get('remove_signatures'):
+ args.append('--remove-signatures')
+
+ sign_by_key = self.push_args.get('sign_by')
+ if sign_by_key:
+ args.extend(['--sign-by', sign_by_key])
+
+ args.append(self.image_name)
+
+ # Build the destination argument
+ dest = self.push_args.get('dest')
+ dest_format_string = '{dest}/{image_name}'
+ regexp = re.compile(r'/{name}(:{tag})?'.format(name=self.name, tag=self.tag))
+ if not dest:
+ if '/' not in self.name:
+ self.module.fail_json(msg="'push_args['dest']' is required when pushing images that do not have the remote registry in the image name")
+
+ # If the push destination contains the image name and/or the tag
+ # remove it and warn since it's not needed.
+ elif regexp.search(dest):
+ dest = regexp.sub('', dest)
+ self.module.warn("Image name and tag are automatically added to push_args['dest']. Destination changed to {dest}".format(dest=dest))
+
+ if dest and dest.endswith('/'):
+ dest = dest[:-1]
+
+ transport = self.push_args.get('transport')
+ if transport:
+ if not dest:
+ self.module.fail_json("'push_args['transport'] requires 'push_args['dest'] but it was not provided.")
+ if transport == 'docker':
+ dest_format_string = '{transport}://{dest}'
+ elif transport == 'ostree':
+ dest_format_string = '{transport}:{name}@{dest}'
+ else:
+ dest_format_string = '{transport}:{dest}'
+
+ dest_string = dest_format_string.format(transport=transport, name=self.name, dest=dest, image_name=self.image_name,)
+
+ # Only append the destination argument if the image name is not a URL
+ if '/' not in self.name:
+ args.append(dest_string)
+
+ rc, out, err = self._run(args, ignore_errors=True)
+ if rc != 0:
+ self.module.fail_json(msg="Failed to push image {image_name}: {err}".format(image_name=self.image_name, err=err))
+ last_id = self._get_id_from_output(
+ out + err, contains=':', split_on=':')
+
+ return self.inspect_image(last_id), out + err
+
+ def remove_image(self, image_name=None):
+ if image_name is None:
+ image_name = self.image_name
+
+ args = ['rmi', image_name]
+ if self.force:
+ args.append('--force')
+ rc, out, err = self._run(args, ignore_errors=True)
+ if rc != 0:
+ self.module.fail_json(msg='Failed to remove image {image_name}. {err}'.format(image_name=image_name, err=err))
+ return out
+
+ def remove_image_id(self, image_id=None):
+ if image_id is None:
+ image_id = re.sub(':.*$', '', self.image_name)
+
+ args = ['rmi', image_id]
+ if self.force:
+ args.append('--force')
+ rc, out, err = self._run(args, ignore_errors=True)
+ if rc != 0:
+ self.module.fail_json(msg='Failed to remove image with id {image_id}. {err}'.format(
+ image_id=image_id, err=err))
+ return out
+
+
+def parse_repository_tag(repo_name):
+ parts = repo_name.rsplit('@', 1)
+ if len(parts) == 2:
+ return tuple(parts)
+ parts = repo_name.rsplit(':', 1)
+ if len(parts) == 2 and '/' not in parts[1]:
+ return tuple(parts)
+ return repo_name, None
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ name=dict(type='str', required=True),
+ arch=dict(type='str'),
+ tag=dict(type='str', default='latest'),
+ pull=dict(type='bool', default=True),
+ push=dict(type='bool', default=False),
+ path=dict(type='str'),
+ force=dict(type='bool', default=False),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'build']),
+ validate_certs=dict(type='bool', aliases=['tlsverify', 'tls_verify']),
+ executable=dict(type='str', default='podman'),
+ auth_file=dict(type='path', aliases=['authfile']),
+ username=dict(type='str'),
+ password=dict(type='str', no_log=True),
+ ca_cert_dir=dict(type='path'),
+ build=dict(
+ type='dict',
+ aliases=['build_args', 'buildargs'],
+ default={},
+ options=dict(
+ annotation=dict(type='dict'),
+ force_rm=dict(type='bool', default=False),
+ file=dict(type='path'),
+ format=dict(
+ type='str',
+ choices=['oci', 'docker'],
+ default='oci'
+ ),
+ cache=dict(type='bool', default=True),
+ rm=dict(type='bool', default=True),
+ volume=dict(type='list', elements='str'),
+ extra_args=dict(type='str'),
+ ),
+ ),
+ push_args=dict(
+ type='dict',
+ default={},
+ options=dict(
+ compress=dict(type='bool'),
+ format=dict(type='str', choices=['oci', 'v2s1', 'v2s2']),
+ remove_signatures=dict(type='bool'),
+ sign_by=dict(type='str'),
+ dest=dict(type='str', aliases=['destination'],),
+ transport=dict(
+ type='str',
+ choices=[
+ 'dir',
+ 'docker-archive',
+ 'docker-daemon',
+ 'oci-archive',
+ 'ostree',
+ ]
+ ),
+ ),
+ ),
+ ),
+ supports_check_mode=True,
+ required_together=(
+ ['username', 'password'],
+ ),
+ mutually_exclusive=(
+ ['auth_file', 'username'],
+ ['auth_file', 'password'],
+ ),
+ )
+
+ results = dict(
+ changed=False,
+ actions=[],
+ podman_actions=[],
+ image={},
+ stdout='',
+ )
+
+ PodmanImageManager(module, results)
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_image_info.py b/ansible_collections/containers/podman/plugins/modules/podman_image_info.py
new file mode 100644
index 000000000..d8af08814
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_image_info.py
@@ -0,0 +1,236 @@
+#!/usr/bin/python
+# Copyright (c) 2019 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+module: podman_image_info
+author:
+ - Sam Doran (@samdoran)
+short_description: Gather info about images using podman
+notes:
+ - Podman may required elevated privileges in order to run properly.
+description:
+ - Gather info about images using C(podman)
+options:
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the machine running C(podman)
+ default: 'podman'
+ type: str
+ name:
+ description:
+ - List of tags or UID to gather info about. If no name is given return info about all images.
+ type: list
+ elements: str
+
+'''
+
+EXAMPLES = r"""
+- name: Gather info for all images
+ containers.podman.podman_image_info:
+
+- name: Gather info on a specific image
+ containers.podman.podman_image_info:
+ name: nginx
+
+- name: Gather info on several images
+ containers.podman.podman_image_info:
+ name:
+ - redis
+ - quay.io/bitnami/wildfly
+"""
+
+RETURN = r"""
+images:
+ description: info from all or specified images
+ returned: always
+ type: dict
+ sample: [
+ {
+ "Annotations": {},
+ "Architecture": "amd64",
+ "Author": "",
+ "Comment": "from Bitnami with love",
+ "ContainerConfig": {
+ "Cmd": [
+ "nami",
+ "start",
+ "--foreground",
+ "wildfly"
+ ],
+ "Entrypoint": [
+ "/app-entrypoint.sh"
+ ],
+ "Env": [
+ "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "IMAGE_OS=debian-9",
+ "NAMI_VERSION=0.0.9-0",
+ "GPG_KEY_SERVERS_LIST=ha.pool.sks-keyservers.net \
+hkp://p80.pool.sks-keyservers.net:80 keyserver.ubuntu.com hkp://keyserver.ubuntu.com:80 pgp.mit.edu",
+ "TINI_VERSION=v0.13.2",
+ "TINI_GPG_KEY=595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7",
+ "GOSU_VERSION=1.10",
+ "GOSU_GPG_KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4",
+ "BITNAMI_IMAGE_VERSION=14.0.1-debian-9-r12",
+ "BITNAMI_APP_NAME=wildfly",
+ "WILDFLY_JAVA_HOME=",
+ "WILDFLY_JAVA_OPTS=",
+ "WILDFLY_MANAGEMENT_HTTP_PORT_NUMBER=9990",
+ "WILDFLY_PASSWORD=bitnami",
+ "WILDFLY_PUBLIC_CONSOLE=true",
+ "WILDFLY_SERVER_AJP_PORT_NUMBER=8009",
+ "WILDFLY_SERVER_HTTP_PORT_NUMBER=8080",
+ "WILDFLY_SERVER_INTERFACE=0.0.0.0",
+ "WILDFLY_USERNAME=user",
+ "WILDFLY_WILDFLY_HOME=/home/wildfly",
+ "WILDFLY_WILDFLY_OPTS=-Dwildfly.as.deployment.ondemand=false"
+ ],
+ "ExposedPorts": {
+ "8080/tcp": {},
+ "9990/tcp": {}
+ },
+ "Labels": {
+ "maintainer": "Bitnami <containers@bitnami.com>"
+ }
+ },
+ "Created": "2018-09-25T04:07:45.934395523Z",
+ "Digest": "sha256:5c7d8e2dd66dcf4a152a4032a1d3c5a33458c67e1c1335edd8d18d738892356b",
+ "GraphDriver": {
+ "Data": {
+ "LowerDir": "/var/lib/containers/storage/overlay/a9dbf5616cc16919a8ac0dfc60aff87a72b5be52994c4649fcc91a089a12931\
+f/diff:/var/lib/containers/storage/overlay/67129bd46022122a7d8b7acb490092af6c7ce244ce4fbd7d9e2d2b7f5979e090/diff:/var/lib/containers/storage/overlay/7c51242c\
+4c5db5c74afda76d7fdbeab6965d8b21804bb3fc597dee09c770b0ca/diff:/var/lib/containers/storage/overlay/f97315dc58a9c002ba0cabccb9933d4b0d2113733d204188c88d72f75569b57b/diff:/var/lib/containers/storage/overlay/1dbde2dd497ddde2b467727125b900958a051a72561e58d29abe3d660dcaa9a7/diff:/var/lib/containers/storage/overlay/4aad9d80f30c3f0608f58173558b7554d84dee4dc4479672926eca29f75e6e33/diff:/var/lib/containers/storage/overlay/6751fc9b6868254870c062d75a511543fc8cfda2ce6262f4945f107449219632/diff:/var/lib/containers/storage/overlay/a27034d79081347421dd24d7e9e776c18271cd9a6e51053cb39af4d3d9c400e8/diff:/var/lib/containers/storage/overlay/537cf0045ed9cd7989f7944e7393019c81b16c1799a2198d8348cd182665397f/diff:/var/lib/containers/storage/overlay/27578615c5ae352af4e8449862d61aaf5c11b105a7d5905af55bd01b0c656d6e/diff:/var/lib/containers/storage/overlay/566542742840fe3034b3596f7cb9e62a6274c95a69f368f9e713746f8712c0b6/diff",
+ "MergedDir": "/var/lib/containers/storage/overlay/72bb96d6\
+c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/merged",
+ "UpperDir": "/var/lib/containers/storage/overlay/72bb96d6c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/diff",
+ "WorkDir": "/var/lib/containers/storage/overlay/72bb96d6c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/work"
+ },
+ "Name": "overlay"
+ },
+ "Id": "bcacbdf7a119c0fa934661ca8af839e625ce6540d9ceb6827cdd389f823d49e0",
+ "Labels": {
+ "maintainer": "Bitnami <containers@bitnami.com>"
+ },
+ "ManifestType": "application/vnd.docker.distribution.manifest.v1+prettyjws",
+ "Os": "linux",
+ "Parent": "",
+ "RepoDigests": [
+ "quay.io/bitnami/wildfly@sha256:5c7d8e2dd66dcf4a152a4032a1d3c5a33458c67e1c1335edd8d18d738892356b"
+ ],
+ "RepoTags": [
+ "quay.io/bitnami/wildfly:latest"
+ ],
+ "RootFS": {
+ "Layers": [
+ "sha256:75391df2c87e076b0c2f72d20c95c57dc8be7ee684cc07273416cce622b43367",
+ "sha256:7dd303f041039bfe8f0833092673ac35f93137d10e0fbc4302021ea65ad57731",
+ "sha256:720d9edf0cd2a9bb56b88b80be9070dbfaad359514c70094c65066963fed485d",
+ "sha256:6a567ecbf97725501a634fcb486271999aa4591b633b4ae9932a46b40f5aaf47",
+ "sha256:59e9a6db8f178f3da868614564faabb2820cdfb69be32e63a4405d6f7772f68c",
+ "sha256:310a82ccb092cd650215ab375da8943d235a263af9a029b8ac26a281446c04db",
+ "sha256:36cb91cf4513543a8f0953fed785747ea18b675bc2677f3839889cfca0aac79e"
+ ],
+ "Type": "layers"
+ },
+ "Size": 569919342,
+ "User": "",
+ "Version": "17.06.0-ce",
+ "VirtualSize": 569919342
+ }
+ ]
+"""
+
+import json
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def image_exists(module, executable, name):
+ command = [executable, 'image', 'exists', name]
+ rc, out, err = module.run_command(command)
+ if rc == 1:
+ return False
+ elif 'Command "exists" not found' in err:
+ # The 'exists' test is available in podman >= 0.12.1
+ command = [executable, 'image', 'ls', '-q', name]
+ rc2, out2, err2 = module.run_command(command)
+ if rc2 != 0:
+ return False
+ return True
+
+
+def filter_invalid_names(module, executable, name):
+ valid_names = []
+ names = name
+ if not isinstance(name, list):
+ names = [name]
+
+ for name in names:
+ if image_exists(module, executable, name):
+ valid_names.append(name)
+
+ return valid_names
+
+
+def get_image_info(module, executable, name):
+ names = name
+ if not isinstance(name, list):
+ names = [name]
+
+ if len(names) > 0:
+ command = [executable, 'image', 'inspect']
+ command.extend(names)
+ rc, out, err = module.run_command(command)
+
+ if rc != 0:
+ module.fail_json(msg="Unable to gather info for '{0}': {1}".format(', '.join(names), err))
+ return out
+
+ else:
+ return json.dumps([])
+
+
+def get_all_image_info(module, executable):
+ command = [executable, 'image', 'ls', '-q']
+ rc, out, err = module.run_command(command)
+ out = out.strip()
+ if out:
+ name = out.split('\n')
+ res = get_image_info(module, executable, name)
+ return res
+ return json.dumps([])
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ name=dict(type='list', elements='str')
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.params['executable']
+ name = module.params.get('name')
+ executable = module.get_bin_path(executable, required=True)
+
+ if name:
+ valid_names = filter_invalid_names(module, executable, name)
+ results = json.loads(get_image_info(module, executable, valid_names))
+ else:
+ results = json.loads(get_all_image_info(module, executable))
+
+ results = dict(
+ changed=False,
+ images=results
+ )
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_import.py b/ansible_collections/containers/podman/plugins/modules/podman_import.py
new file mode 100644
index 000000000..5090b177c
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_import.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# Copyright (c) 2021, Sagi Shnaidman <sshnaidm@redhat.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_import
+short_description: Import Podman container from a tar file.
+author: Sagi Shnaidman (@sshnaidm)
+description:
+ - podman import imports a tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz)
+ and saves it as a filesystem image.
+options:
+ src:
+ description:
+ - Path to image file to load.
+ type: str
+ required: true
+ commit_message:
+ description:
+ - Set commit message for imported image
+ type: str
+ change:
+ description:
+ - Set changes as list of key-value pairs, see example.
+ type: list
+ elements: dict
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+requirements:
+ - "Podman installed on host"
+'''
+
+RETURN = '''
+image:
+ description: info from loaded image
+ returned: always
+ type: dict
+ sample: {
+ "Id": "cbc6d73c4d232db6e8441df96af81855f62c74157b5db80a1d5...",
+ "Digest": "sha256:8730c75be86a718929a658db4663d487e562d66762....",
+ "RepoTags": [],
+ "RepoDigests": [],
+ "Parent": "",
+ "Comment": "imported from tarball",
+ "Created": "2021-09-07T04:45:38.749977105+03:00",
+ "Config": {},
+ "Version": "",
+ "Author": "",
+ "Architecture": "amd64",
+ "Os": "linux",
+ "Size": 5882449,
+ "VirtualSize": 5882449,
+ "GraphDriver": {
+ "Name": "overlay",
+ "Data": {
+ "UpperDir": "/home/...34/diff",
+ "WorkDir": "/home/.../work"
+ }
+ },
+ "RootFS": {
+ "Type": "layers",
+ "Layers": [
+ "sha256:...."
+ ]
+ },
+ "Labels": null,
+ "Annotations": {},
+ "ManifestType": "application/vnd.oci.image.manifest.v1+json",
+ "User": "",
+ "History": [
+ {
+ "created": "2021-09-07T04:45:38.749977105+03:00",
+ "created_by": "/bin/sh -c #(nop) ADD file:091... in /",
+ "comment": "imported from tarball"
+ }
+ ],
+ "NamesHistory": null
+ }
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- containers.podman.podman_import:
+ src: /path/to/tar/file
+ change:
+ - "CMD": /bin/bash
+ - "User": root
+ commit_message: "Importing image"
+'''
+
+import json # noqa: E402
+from ansible.module_utils.basic import AnsibleModule # noqa: E402
+
+
+def load(module, executable):
+ changed = False
+ command = [executable, 'import']
+ if module.params['commit_message']:
+ command.extend(['--message', module.params['commit_message']])
+ if module.params['change']:
+ for change in module.params['change']:
+ command += ['--change', "=".join(list(change.items())[0])]
+ command += [module.params['src']]
+ changed = True
+ if module.check_mode:
+ return changed, '', '', '', command
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ module.fail_json(msg="Image loading failed: %s" % (err))
+ image_name_line = [i for i in out.splitlines() if 'sha256' in i][0]
+ image_name = image_name_line.split(":", maxsplit=1)[1].strip()
+ rc, out2, err2 = module.run_command([executable, 'image', 'inspect', image_name])
+ if rc != 0:
+ module.fail_json(msg="Image %s inspection failed: %s" % (image_name, err2))
+ try:
+ info = json.loads(out2)[0]
+ except Exception as e:
+ module.fail_json(msg="Could not parse JSON from image %s: %s" % (image_name, e))
+ return changed, out, err, info, command
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ src=dict(type='str', required=True),
+ commit_message=dict(type='str'),
+ change=dict(type='list', elements='dict'),
+ executable=dict(type='str', default='podman')
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.get_bin_path(module.params['executable'], required=True)
+ changed, out, err, image_info, command = load(module, executable)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ "image": image_info,
+ "podman_command": " ".join(command)
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_load.py b/ansible_collections/containers/podman/plugins/modules/podman_load.py
new file mode 100644
index 000000000..4fa7bde01
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_load.py
@@ -0,0 +1,199 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# Copyright (c) 2020, Sagi Shnaidman <sshnaidm@redhat.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_load
+short_description: Load image from a tar file.
+author: Sagi Shnaidman (@sshnaidm)
+description:
+ - podman load loads an image from either an oci-archive or a docker-archive stored
+ on the local machine into container storage.
+ podman load is used for loading from the archive generated by podman save,
+ that includes the image parent layers.
+options:
+ input:
+ description:
+ - Path to image file to load.
+ type: str
+ required: true
+ aliases:
+ - path
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+requirements:
+ - "Podman installed on host"
+'''
+
+RETURN = '''
+image:
+ description: info from loaded image
+ returned: always
+ type: dict
+ sample: [
+ {
+ "Annotations": {},
+ "Architecture": "amd64",
+ "Author": "",
+ "Comment": "from Bitnami with love",
+ "ContainerConfig": {
+ "Cmd": [
+ "nami",
+ "start",
+ "--foreground",
+ "wildfly"
+ ],
+ "Entrypoint": [
+ "/app-entrypoint.sh"
+ ],
+ "Env": [
+ "PATH=/opt/bitnami/java/bin:/opt/bitnami/wildfly/bin:/opt/bitnami/nami/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "IMAGE_OS=debian-9",
+ "NAMI_VERSION=0.0.9-0",
+ "GPG_KEY_SERVERS_LIST=ha.pool.sks-keyservers.net \
+hkp://p80.pool.sks-keyservers.net:80 keyserver.ubuntu.com hkp://keyserver.ubuntu.com:80 pgp.mit.edu",
+ "TINI_VERSION=v0.13.2",
+ "TINI_GPG_KEY=595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7",
+ "GOSU_VERSION=1.10",
+ "GOSU_GPG_KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4",
+ "BITNAMI_IMAGE_VERSION=14.0.1-debian-9-r12",
+ "BITNAMI_APP_NAME=wildfly",
+ "WILDFLY_JAVA_HOME=",
+ "WILDFLY_JAVA_OPTS=",
+ "WILDFLY_MANAGEMENT_HTTP_PORT_NUMBER=9990",
+ "WILDFLY_PASSWORD=bitnami",
+ "WILDFLY_PUBLIC_CONSOLE=true",
+ "WILDFLY_SERVER_AJP_PORT_NUMBER=8009",
+ "WILDFLY_SERVER_HTTP_PORT_NUMBER=8080",
+ "WILDFLY_SERVER_INTERFACE=0.0.0.0",
+ "WILDFLY_USERNAME=user",
+ "WILDFLY_WILDFLY_HOME=/home/wildfly",
+ "WILDFLY_WILDFLY_OPTS=-Dwildfly.as.deployment.ondemand=false"
+ ],
+ "ExposedPorts": {
+ "8080/tcp": {},
+ "9990/tcp": {}
+ },
+ "Labels": {
+ "maintainer": "Bitnami <containers@bitnami.com>"
+ }
+ },
+ "Created": "2018-09-25T04:07:45.934395523Z",
+ "Digest": "sha256:5c7d8e2dd66dcf4a152a4032a1d3c5a33458c67e1c1335edd8d18d738892356b",
+ "GraphDriver": {
+ "Data": {
+ "LowerDir": "/var/lib/containers/storage/overlay/a9dbf5616cc16919a8ac0dfc60aff87a72b5be52994c4649fcc91a089a12931\
+f/diff:/var/lib/containers/storage/overlay/67129bd46022122a7d8b7acb490092af6c7ce244ce4fbd7d9e2d2b7f5979e090/diff:/var/lib/containers/storage/overlay/7c51242c\
+4c5db5c74afda76d7fdbeab6965d8b21804bb3fc597dee09c770b0ca/diff:/var/lib/containers/storage/overlay/f97315dc58a9c002ba0cabccb9933d4b0d2113733d204188c88d72f75569b57b/diff:/var/lib/containers/storage/overlay/1dbde2dd497ddde2b467727125b900958a051a72561e58d29abe3d660dcaa9a7/diff:/var/lib/containers/storage/overlay/4aad9d80f30c3f0608f58173558b7554d84dee4dc4479672926eca29f75e6e33/diff:/var/lib/containers/storage/overlay/6751fc9b6868254870c062d75a511543fc8cfda2ce6262f4945f107449219632/diff:/var/lib/containers/storage/overlay/a27034d79081347421dd24d7e9e776c18271cd9a6e51053cb39af4d3d9c400e8/diff:/var/lib/containers/storage/overlay/537cf0045ed9cd7989f7944e7393019c81b16c1799a2198d8348cd182665397f/diff:/var/lib/containers/storage/overlay/27578615c5ae352af4e8449862d61aaf5c11b105a7d5905af55bd01b0c656d6e/diff:/var/lib/containers/storage/overlay/566542742840fe3034b3596f7cb9e62a6274c95a69f368f9e713746f8712c0b6/diff",
+ "MergedDir": "/var/lib/containers/storage/overlay/72bb96d6\
+c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/merged",
+ "UpperDir": "/var/lib/containers/storage/overlay/72bb96d6c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/diff",
+ "WorkDir": "/var/lib/containers/storage/overlay/72bb96d6c53ad57a0b1e44cab226a6251598accbead40b23fac89c19ad8c25ca/work"
+ },
+ "Name": "overlay"
+ },
+ "Id": "bcacbdf7a119c0fa934661ca8af839e625ce6540d9ceb6827cdd389f823d49e0",
+ "Labels": {
+ "maintainer": "Bitnami <containers@bitnami.com>"
+ },
+ "ManifestType": "application/vnd.docker.distribution.manifest.v1+prettyjws",
+ "Os": "linux",
+ "Parent": "",
+ "RepoDigests": [
+ "quay.io/bitnami/wildfly@sha256:5c7d8e2dd66dcf4a152a4032a1d3c5a33458c67e1c1335edd8d18d738892356b"
+ ],
+ "RepoTags": [
+ "quay.io/bitnami/wildfly:latest"
+ ],
+ "RootFS": {
+ "Layers": [
+ "sha256:75391df2c87e076b0c2f72d20c95c57dc8be7ee684cc07273416cce622b43367",
+ "sha256:7dd303f041039bfe8f0833092673ac35f93137d10e0fbc4302021ea65ad57731",
+ "sha256:720d9edf0cd2a9bb56b88b80be9070dbfaad359514c70094c65066963fed485d",
+ "sha256:6a567ecbf97725501a634fcb486271999aa4591b633b4ae9932a46b40f5aaf47",
+ "sha256:59e9a6db8f178f3da868614564faabb2820cdfb69be32e63a4405d6f7772f68c",
+ "sha256:310a82ccb092cd650215ab375da8943d235a263af9a029b8ac26a281446c04db",
+ "sha256:36cb91cf4513543a8f0953fed785747ea18b675bc2677f3839889cfca0aac79e"
+ ],
+ "Type": "layers"
+ },
+ "Size": 569919342,
+ "User": "",
+ "Version": "17.06.0-ce",
+ "VirtualSize": 569919342
+ }
+ ]
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- containers.podman.podman_load:
+ input: /path/to/tar/file
+'''
+
+import json # noqa: E402
+from ansible.module_utils.basic import AnsibleModule # noqa: E402
+
+
+def load(module, executable):
+ changed = False
+ command = [executable, 'load', '--input']
+ command.append(module.params['input'])
+ changed = True
+ if module.check_mode:
+ return changed, '', '', ''
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ module.fail_json(msg="Image loading failed: %s" % (err))
+ image_name_line = [i for i in out.splitlines() if 'Loaded image' in i][0]
+ # For Podman < 4.x
+ if 'Loaded image(s):' in image_name_line:
+ image_name = image_name_line.split("Loaded image(s): ")[1].split(',')[0].strip()
+ # For Podman > 4.x
+ elif 'Loaded image:' in image_name_line:
+ image_name = image_name_line.split("Loaded image: ")[1].strip()
+ else:
+ module.fail_json(msg="Not found images in %s" % image_name_line)
+ rc, out2, err2 = module.run_command([executable, 'image', 'inspect', image_name])
+ if rc != 0:
+ module.fail_json(msg="Image %s inspection failed: %s" % (image_name, err2))
+ try:
+ info = json.loads(out2)[0]
+ except Exception as e:
+ module.fail_json(msg="Could not parse JSON from image %s: %s" % (image_name, e))
+ return changed, out, err, info
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ input=dict(type='str', required=True, aliases=['path']),
+ executable=dict(type='str', default='podman')
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.get_bin_path(module.params['executable'], required=True)
+ changed, out, err, image_info = load(module, executable)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ "image": image_info,
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_login.py b/ansible_collections/containers/podman/plugins/modules/podman_login.py
new file mode 100644
index 000000000..be417c761
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_login.py
@@ -0,0 +1,184 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_login
+author:
+ - "Jason Hiatt (@jthiatt)"
+ - "Clemens Lange (@clelange)"
+ - "Michael Fox (@spmfox)"
+short_description: Login to a container registry using podman
+notes: []
+description:
+ - Login to a container registry server using the podman login command
+ If the registry is not specified, the first registry under
+ `[registries.search]` from `registries.conf `will be used. The path of
+ the authentication file can be overridden by the user by setting the
+ `authfile` flag. The default path used is
+ `${XDG_RUNTIME_DIR}/containers/auth.json`.
+requirements:
+ - "Podman installed on host"
+options:
+ authfile:
+ description:
+ - Path of the authentication file. Default is
+ ``${XDG_RUNTIME_DIR}/containers/auth.json``
+ You can also override the default path of the authentication
+ file by setting the ``REGISTRY_AUTH_FILE`` environment
+ variable. ``export REGISTRY_AUTH_FILE=path``
+ type: path
+ certdir:
+ description:
+ - Use certificates at path (*.crt, *.cert, *.key) to connect
+ to the registry. Default certificates directory
+ is /etc/containers/certs.d.
+ type: path
+ password:
+ description:
+ - Password for the registry server.
+ required: True
+ type: str
+ registry:
+ description:
+ - Registry server. If the registry is not specified,
+ the first registry under `[registries.search]` from
+ `registries.conf` will be used.
+ type: str
+ tlsverify:
+ description:
+ - Require HTTPS and verify certificates when
+ contacting registries. If explicitly set to true,
+ then TLS verification will be used. If set to false,
+ then TLS verification will not be used. If not specified,
+ TLS verification will be used unless the target registry
+ is listed as an insecure registry in registries.conf.
+ type: bool
+ username:
+ description:
+ - Username for the registry server.
+ required: True
+ type: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+'''
+
+EXAMPLES = r"""
+- name: Login to default registry and create ${XDG_RUNTIME_DIR}/containers/auth.json
+ containers.podman.podman_login:
+ username: user
+ password: 'p4ssw0rd'
+
+- name: Login to default registry and create ${XDG_RUNTIME_DIR}/containers/auth.json
+ containers.podman.podman_login:
+ username: user
+ password: 'p4ssw0rd'
+ registry: quay.io
+
+"""
+# noqa: F402
+
+import hashlib
+import os
+from ansible.module_utils.basic import AnsibleModule
+
+
+def login(module, executable, registry, authfile,
+ certdir, tlsverify, username, password):
+
+ command = [executable, 'login']
+ changed = False
+
+ if username:
+ command.extend(['--username', username])
+ if password:
+ command.extend(['--password', password])
+ if authfile:
+ command.extend(['--authfile', authfile])
+ authfile = os.path.expandvars(authfile)
+ else:
+ authfile = os.getenv('XDG_RUNTIME_DIR', '') + '/containers/auth.json'
+ if registry:
+ command.append(registry)
+ if certdir:
+ command.extend(['--cert-dir', certdir])
+ if tlsverify is not None:
+ if tlsverify:
+ command.append('--tls-verify')
+ else:
+ command.append('--tls-verify=False')
+ # Use a checksum to check if the auth JSON has changed
+ checksum = None
+ docker_authfile = os.path.expandvars('$HOME/.docker/config.json')
+ # podman falls back to ~/.docker/config.json if the default authfile doesn't exist
+ check_file = authfile if os.path.exists(authfile) else docker_authfile
+ if os.path.exists(check_file):
+ content = open(check_file, 'rb').read()
+ checksum = hashlib.sha256(content).hexdigest()
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ if 'Error: Not logged into' not in err:
+ module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err))
+ else:
+ # If the command is successful, we managed to login
+ changed = True
+ if 'Existing credentials are valid' in out:
+ changed = False
+ # If we have managed to calculate a checksum before, check if it has changed
+ # due to the login
+ if checksum:
+ content = open(check_file, 'rb').read()
+ new_checksum = hashlib.sha256(content).hexdigest()
+ if new_checksum == checksum:
+ changed = False
+ return changed, out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ registry=dict(type='str'),
+ authfile=dict(type='path'),
+ username=dict(type='str', required=True),
+ password=dict(type='str', required=True, no_log=True),
+ certdir=dict(type='path'),
+ tlsverify=dict(type='bool'),
+ ),
+ supports_check_mode=True,
+ required_together=(
+ ['username', 'password'],
+ ),
+ mutually_exclusive=(
+ ['certdir', 'tlsverify'],
+ ),
+ )
+
+ registry = module.params['registry']
+ authfile = module.params['authfile']
+ username = module.params['username']
+ password = module.params['password']
+ certdir = module.params['certdir']
+ tlsverify = module.params['tlsverify']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ changed, out, err = login(module, executable, registry, authfile,
+ certdir, tlsverify, username, password)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_login_info.py b/ansible_collections/containers/podman/plugins/modules/podman_login_info.py
new file mode 100644
index 000000000..739adb134
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_login_info.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r"""
+module: podman_login_info
+author:
+ - "Clemens Lange (@clelange)"
+version_added: '1.0.0'
+short_description: Return the logged-in user if any for a given registry
+notes: []
+description:
+ - Return the logged-in user if any for a given registry.
+requirements:
+ - "Podman installed on host"
+options:
+ registry:
+ description:
+ - Registry server.
+ type: str
+ required: true
+ authfile:
+ description:
+ - Path of the authentication file. Default is
+ ``${XDG_RUNTIME_DIR}/containers/auth.json``
+ (Not available for remote commands) You can also override the default
+ path of the authentication file by setting the ``REGISTRY_AUTH_FILE``
+ environment variable. ``export REGISTRY_AUTH_FILE=path``
+ type: path
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+"""
+
+EXAMPLES = r"""
+- name: Return the logged-in user for docker hub registry
+ containers.podman.podman_login_info:
+ registry: docker.io
+
+- name: Return the logged-in user for quay.io registry
+ containers.podman.podman_login_info:
+ registry: quay.io
+"""
+
+RETURN = r"""
+login:
+ description: Logged in user for a registry
+ returned: always
+ type: dict
+ sample: {
+ "logged_in": true,
+ "registry": "docker.io",
+ "username": "clelange",
+ }
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_login_info(module, executable, authfile, registry):
+ command = [executable, 'login', '--get-login']
+ result = dict(
+ registry=registry,
+ username='',
+ logged_in=False,
+ )
+ if authfile:
+ command.extend(['--authfile', authfile])
+ if registry:
+ command.append(registry)
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ if 'Error: not logged into' in err:
+ # The error message is e.g. 'Error: not logged into docker.io'
+ # Therefore get last word to extract registry name
+ result["registry"] = err.split()[-1]
+ err = ''
+ return result
+ module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err))
+ result["username"] = out.strip()
+ result["logged_in"] = True
+ return result
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ authfile=dict(type='path'),
+ registry=dict(type='str', required=True)
+ ),
+ supports_check_mode=True,
+ )
+
+ registry = module.params['registry']
+ authfile = module.params['authfile']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ inspect_results = get_login_info(module, executable, authfile, registry)
+
+ results = {
+ "changed": False,
+ "login": inspect_results,
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_logout.py b/ansible_collections/containers/podman/plugins/modules/podman_logout.py
new file mode 100644
index 000000000..d5816a9c0
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_logout.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_logout
+author:
+ - "Clemens Lange (@clelange)"
+short_description: Log out of a container registry using podman
+notes: []
+description:
+ - Log out of a container registry server using the podman logout command
+ by deleting the cached credentials stored in the `auth.json` file.
+ If the registry is not specified, the first registry under
+ `[registries.search]` from `registries.conf `will be used. The path of
+ the authentication file can be overridden by the user by setting the
+ `authfile` flag. The default path used is
+ `${XDG_RUNTIME_DIR}/containers/auth.json`.
+ All the cached credentials can be removed by setting the `all` flag.
+ Warning - podman will use credentials in `${HOME}/.docker/config.json`
+ to authenticate in case they are not found in the default `authfile`.
+ However, the logout command will only removed credentials in the
+ `authfile` specified.
+requirements:
+ - "Podman installed on host"
+options:
+ registry:
+ description:
+ - Registry server. If the registry is not specified,
+ the first registry under `[registries.search]` from
+ `registries.conf` will be used.
+ type: str
+ authfile:
+ description:
+ - Path of the authentication file. Default is
+ ``${XDG_RUNTIME_DIR}/containers/auth.json``
+ You can also override the default path of the authentication
+ file by setting the ``REGISTRY_AUTH_FILE`` environment
+ variable. ``export REGISTRY_AUTH_FILE=path``
+ type: path
+ all:
+ description:
+ - Remove the cached credentials for all registries in the auth file.
+ type: bool
+ ignore_docker_credentials:
+ description:
+ - Credentials created using other tools such as `docker login` are not
+ removed unless the corresponding `authfile` is explicitly specified.
+ Since podman also uses existing credentials in these files by default
+ (for docker e.g. `${HOME}/.docker/config.json`), module execution will
+ fail if a docker login exists for the registry specified in any
+ `authfile` is used by podman. This can be ignored by setting
+ `ignore_docker_credentials` to `true` - the credentials will be kept and
+ `changed` will be false.
+ This option cannot be used together with `all` since in this case
+ podman will not check for existing `authfiles` created by other tools.
+ type: bool
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+'''
+
+EXAMPLES = r"""
+- name: Log out of default registry
+ podman_logout:
+
+- name: Log out of quay.io
+ podman_logout:
+ registry: quay.io
+
+- name: Log out of all registries in auth file
+ podman_logout:
+ all: true
+
+- name: Log out of all registries in specified auth file
+ podman_logout:
+ authfile: $HOME/.docker/config.json
+ all: true
+"""
+# noqa: F402
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def logout(module, executable, registry, authfile, all_registries, ignore_docker_credentials):
+ command = [executable, 'logout']
+ changed = False
+ if authfile:
+ command.extend(['--authfile', authfile])
+ if registry:
+ command.append(registry)
+ if all_registries:
+ command.append("--all")
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ if 'Error: Not logged into' not in err:
+ module.fail_json(msg="Unable to gather info for %s: %s" % (registry, err))
+ else:
+ # If the command is successful, we managed to log out
+ # Mind: This also applied if --all flag is used, while in this case
+ # there is no check whether one has been logged into any registry
+ changed = True
+ if 'Existing credentials were established via' in out:
+ # The command will return successfully but not log out the user if the
+ # credentials were initially created using docker. Catch this behaviour:
+ if not ignore_docker_credentials:
+ module.fail_json(msg="Unable to log out %s: %s" % (registry or '', out))
+ else:
+ changed = False
+ return changed, out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ registry=dict(type='str'),
+ authfile=dict(type='path'),
+ all=dict(type='bool'),
+ ignore_docker_credentials=dict(type='bool'),
+ ),
+ supports_check_mode=True,
+ mutually_exclusive=(
+ ['registry', 'all'],
+ ['ignore_docker_credentials', 'all'],
+ ),
+ )
+
+ registry = module.params['registry']
+ authfile = module.params['authfile']
+ all_registries = module.params['all']
+ ignore_docker_credentials = module.params['ignore_docker_credentials']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ changed, out, err = logout(module, executable, registry, authfile,
+ all_registries, ignore_docker_credentials)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_network.py b/ansible_collections/containers/podman/plugins/modules/podman_network.py
new file mode 100644
index 000000000..846524b65
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_network.py
@@ -0,0 +1,673 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r"""
+module: podman_network
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.0.0'
+short_description: Manage podman networks
+notes: []
+description:
+ - Manage podman networks with podman network command.
+requirements:
+ - podman
+options:
+ name:
+ description:
+ - Name of the network
+ type: str
+ required: True
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+ disable_dns:
+ description:
+ - disable dns plugin (default "false")
+ type: bool
+ driver:
+ description:
+ - Driver to manage the network (default "bridge")
+ type: str
+ gateway:
+ description:
+ - IPv4 or IPv6 gateway for the subnet
+ type: str
+ internal:
+ description:
+ - Restrict external access from this network (default "false")
+ type: bool
+ ip_range:
+ description:
+ - Allocate container IP from range
+ type: str
+ ipv6:
+ description:
+ - Enable IPv6 (Dual Stack) networking. You must pass a IPv6 subnet.
+ The subnet option must be used with the ipv6 option.
+ type: bool
+ subnet:
+ description:
+ - Subnet in CIDR format
+ type: str
+ macvlan:
+ description:
+ - Create a Macvlan connection based on this device
+ type: str
+ opt:
+ description:
+ - Add network options. Currently 'vlan' and 'mtu' are supported.
+ type: dict
+ suboptions:
+ isolate:
+ description:
+ - This option isolates networks by blocking traffic between those
+ that have this option enabled.
+ type: bool
+ required: false
+ metric:
+ description:
+ - Sets the Route Metric for the default route created in every
+ container joined to this network.
+ Can only be used with the Netavark network backend.
+ type: int
+ required: false
+ mode:
+ description:
+ - This option sets the specified ip/macvlan mode on the interface.
+ type: str
+ required: false
+ mtu:
+ description:
+ - MTU size for bridge network interface.
+ type: int
+ required: false
+ parent:
+ description:
+ - The host device which should be used for the macvlan interface.
+ Defaults to the default route interface.
+ type: str
+ required: false
+ vlan:
+ description:
+ - VLAN tag for bridge which enables vlan_filtering.
+ type: int
+ required: false
+ debug:
+ description:
+ - Return additional information which can be helpful for investigations.
+ type: bool
+ default: False
+ state:
+ description:
+ - State of network, default 'present'
+ type: str
+ default: present
+ choices:
+ - present
+ - absent
+ recreate:
+ description:
+ - Recreate network even if exists.
+ type: bool
+ default: false
+"""
+
+EXAMPLES = r"""
+- name: Create a podman network
+ containers.podman.podman_network:
+ name: podman_network
+ become: true
+
+- name: Create internal podman network
+ containers.podman.podman_network:
+ name: podman_internal
+ internal: true
+ ip_range: 192.168.22.128/25
+ subnet: 192.168.22.0/24
+ gateway: 192.168.22.1
+ become: true
+"""
+
+RETURN = r"""
+network:
+ description: Facts from created or updated networks
+ returned: always
+ type: list
+ sample: [
+ {
+ "cniVersion": "0.4.0",
+ "name": "podman",
+ "plugins": [
+ {
+ "bridge": "cni-podman0",
+ "ipMasq": true,
+ "ipam": {
+ "ranges": [
+ [
+ {
+ "gateway": "10.88.0.1",
+ "subnet": "10.88.0.0/16"
+ }
+ ]
+ ],
+ "routes": [
+ {
+ "dst": "0.0.0.0/0"
+ }
+ ],
+ "type": "host-local"
+ },
+ "isGateway": true,
+ "type": "bridge"
+ },
+ {
+ "capabilities": {
+ "portMappings": true
+ },
+ "type": "portmap"
+ },
+ {
+ "backend": "iptables",
+ "type": "firewall"
+ }
+ ]
+ }
+ ]
+"""
+
+import json # noqa: F402
+try:
+ import ipaddress
+ HAS_IP_ADDRESS_MODULE = True
+except ImportError:
+ HAS_IP_ADDRESS_MODULE = False
+
+from ansible.module_utils.basic import AnsibleModule # noqa: F402
+from ansible.module_utils._text import to_bytes, to_native # noqa: F402
+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
+
+
+class PodmanNetworkModuleParams:
+ """Creates list of arguments for podman CLI command.
+
+ Arguments:
+ action {str} -- action type from 'create', 'delete'
+ 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 ['delete']:
+ return self._simple_action()
+ if self.action in ['create']:
+ return self._create_action()
+
+ def _simple_action(self):
+ if self.action == 'delete':
+ cmd = ['rm', '-f', self.params['name']]
+ return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
+
+ def _create_action(self):
+ cmd = [self.action, self.params['name']]
+ 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_gateway(self, c):
+ return c + ['--gateway', self.params['gateway']]
+
+ def addparam_driver(self, c):
+ return c + ['--driver', self.params['driver']]
+
+ def addparam_subnet(self, c):
+ return c + ['--subnet', self.params['subnet']]
+
+ def addparam_ip_range(self, c):
+ return c + ['--ip-range', self.params['ip_range']]
+
+ def addparam_ipv6(self, c):
+ return c + ['--ipv6=%s' % self.params['ipv6']]
+
+ def addparam_macvlan(self, c):
+ return c + ['--macvlan', self.params['macvlan']]
+
+ def addparam_internal(self, c):
+ return c + ['--internal=%s' % self.params['internal']]
+
+ def addparam_opt(self, c):
+ for opt in self.params['opt'].items():
+ if opt[1] is not None:
+ c += ['--opt',
+ b"=".join([to_bytes(k, errors='surrogate_or_strict')
+ for k in opt])]
+ return c
+
+ def addparam_disable_dns(self, c):
+ return c + ['--disable-dns=%s' % self.params['disable_dns']]
+
+
+class PodmanNetworkDefaults:
+ def __init__(self, module, podman_version):
+ self.module = module
+ self.version = podman_version
+ self.defaults = {
+ 'driver': 'bridge',
+ 'disable_dns': False,
+ 'internal': False,
+ 'ipv6': False
+ }
+
+ def default_dict(self):
+ # make here any changes to self.defaults related to podman version
+ return self.defaults
+
+
+class PodmanNetworkDiff:
+ def __init__(self, module, info, podman_version):
+ self.module = module
+ self.version = podman_version
+ self.default_dict = None
+ self.info = lower_keys(info)
+ self.params = self.defaultize()
+ self.diff = {'before': {}, 'after': {}}
+ self.non_idempotent = {}
+
+ def defaultize(self):
+ params_with_defaults = {}
+ self.default_dict = PodmanNetworkDefaults(
+ 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 diffparam_disable_dns(self):
+ # For v3 it's impossible to find out DNS settings.
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ before = not self.info.get('dns_enabled', True)
+ after = self.params['disable_dns']
+ return self._diff_update_and_compare('disable_dns', before, after)
+ before = after = self.params['disable_dns']
+ return self._diff_update_and_compare('disable_dns', before, after)
+
+ def diffparam_driver(self):
+ # Currently only bridge is supported
+ before = after = 'bridge'
+ return self._diff_update_and_compare('driver', before, after)
+
+ def diffparam_ipv6(self):
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ before = self.info.get('ipv6_enabled', False)
+ after = self.params['ipv6']
+ return self._diff_update_and_compare('ipv6', before, after)
+ before = after = ''
+ return self._diff_update_and_compare('ipv6', before, after)
+
+ def diffparam_gateway(self):
+ # Disable idempotency of subnet for v4, subnets are added automatically
+ # TODO(sshnaidm): check if it's still the issue in v5
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ return self._diff_update_and_compare('gateway', '', '')
+ try:
+ before = self.info['plugins'][0]['ipam']['ranges'][0][0]['gateway']
+ except (IndexError, KeyError):
+ before = ''
+ after = before
+ if self.params['gateway'] is not None:
+ after = self.params['gateway']
+ return self._diff_update_and_compare('gateway', before, after)
+
+ def diffparam_internal(self):
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ before = self.info.get('internal', False)
+ after = self.params['internal']
+ return self._diff_update_and_compare('internal', before, after)
+ try:
+ before = not self.info['plugins'][0]['isgateway']
+ except (IndexError, KeyError):
+ before = False
+ after = self.params['internal']
+ return self._diff_update_and_compare('internal', before, after)
+
+ def diffparam_ip_range(self):
+ # TODO(sshnaidm): implement IP to CIDR convert and vice versa
+ before = after = ''
+ return self._diff_update_and_compare('ip_range', before, after)
+
+ def diffparam_subnet(self):
+ # Disable idempotency of subnet for v4, subnets are added automatically
+ # TODO(sshnaidm): check if it's still the issue in v5
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ return self._diff_update_and_compare('subnet', '', '')
+ try:
+ before = self.info['plugins'][0]['ipam']['ranges'][0][0]['subnet']
+ except (IndexError, KeyError):
+ before = ''
+ after = before
+ if self.params['subnet'] is not None:
+ after = self.params['subnet']
+ if HAS_IP_ADDRESS_MODULE:
+ after = ipaddress.ip_network(after).compressed
+ return self._diff_update_and_compare('subnet', before, after)
+
+ def diffparam_macvlan(self):
+ before = after = ''
+ return self._diff_update_and_compare('macvlan', before, after)
+
+ def diffparam_opt(self):
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ vlan_before = self.info.get('options', {}).get('vlan')
+ else:
+ try:
+ vlan_before = self.info['plugins'][0].get('vlan')
+ except (IndexError, KeyError):
+ vlan_before = None
+ vlan_after = self.params['opt'].get('vlan') if self.params['opt'] else None
+ if vlan_before or vlan_after:
+ before, after = {'vlan': str(vlan_before)}, {'vlan': str(vlan_after)}
+ else:
+ before, after = {}, {}
+ if LooseVersion(self.version) >= LooseVersion('4.0.0'):
+ mtu_before = self.info.get('options', {}).get('mtu')
+ else:
+ try:
+ mtu_before = self.info['plugins'][0].get('mtu')
+ except (IndexError, KeyError):
+ mtu_before = None
+ mtu_after = self.params['opt'].get('mtu') if self.params['opt'] else None
+ if mtu_before or mtu_after:
+ before.update({'mtu': str(mtu_before)})
+ after.update({'mtu': str(mtu_after)})
+ return self._diff_update_and_compare('opt', before, after)
+
+ 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 PodmanNetwork:
+ """Perform network tasks.
+
+ Manages podman network, inspects it and checks its current state
+ """
+
+ def __init__(self, module, name):
+ """Initialize PodmanNetwork class.
+
+ Arguments:
+ module {obj} -- ansible module object
+ name {str} -- name of network
+ """
+
+ super(PodmanNetwork, self).__init__()
+ self.module = module
+ self.name = name
+ self.stdout, self.stderr = '', ''
+ self.info = self.get_info()
+ self.version = self._get_podman_version()
+ self.diff = {}
+ self.actions = []
+
+ @property
+ def exists(self):
+ """Check if network exists."""
+ return bool(self.info != {})
+
+ @property
+ def different(self):
+ """Check if network is different."""
+ diffcheck = PodmanNetworkDiff(
+ self.module,
+ self.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
+
+ def get_info(self):
+ """Inspect network and gather info about it."""
+ # pylint: disable=unused-variable
+ rc, out, err = self.module.run_command(
+ [self.module.params['executable'], b'network', b'inspect', self.name])
+ 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 network.
+
+ Arguments:
+ action {str} -- action to perform - create, stop, delete
+ """
+ b_command = PodmanNetworkModuleParams(action,
+ self.module.params,
+ self.version,
+ self.module,
+ ).construct_command_from_params()
+ full_cmd = " ".join([self.module.params['executable'], 'network']
+ + [to_native(i) for i in b_command])
+ self.module.log("PODMAN-NETWORK-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'network'] + b_command,
+ expand_user_and_vars=False)
+ self.stdout = out
+ self.stderr = err
+ if rc != 0:
+ self.module.fail_json(
+ msg="Can't %s network %s" % (action, self.name),
+ stdout=out, stderr=err)
+
+ def delete(self):
+ """Delete the network."""
+ self._perform_action('delete')
+
+ def create(self):
+ """Create the network."""
+ self._perform_action('create')
+
+ def recreate(self):
+ """Recreate the network."""
+ self.delete()
+ self.create()
+
+
+class PodmanNetworkManager:
+ """Module manager class.
+
+ Defines according to parameters what actions should be applied to network
+ """
+
+ def __init__(self, module):
+ """Initialize PodmanManager class.
+
+ Arguments:
+ module {obj} -- ansible module object
+ """
+
+ super(PodmanNetworkManager, self).__init__()
+
+ self.module = module
+ self.results = {
+ 'changed': False,
+ 'actions': [],
+ 'network': {},
+ }
+ 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.network = PodmanNetwork(self.module, self.name)
+
+ def update_network_result(self, changed=True):
+ """Inspect the current network, update results with last info, exit.
+
+ Keyword Arguments:
+ changed {bool} -- whether any action was performed
+ (default: {True})
+ """
+ facts = self.network.get_info() if changed else self.network.info
+ out, err = self.network.stdout, self.network.stderr
+ self.results.update({'changed': changed, 'network': facts,
+ 'podman_actions': self.network.actions},
+ stdout=out, stderr=err)
+ if self.network.diff:
+ self.results.update({'diff': self.network.diff})
+ if self.module.params['debug']:
+ self.results.update({'podman_version': self.network.version})
+ self.module.exit_json(**self.results)
+
+ def execute(self):
+ """Execute the desired action according to map of actions & states."""
+ states_map = {
+ 'present': self.make_present,
+ 'absent': self.make_absent,
+ }
+ process_action = states_map[self.state]
+ process_action()
+ self.module.fail_json(msg="Unexpected logic error happened, "
+ "please contact maintainers ASAP!")
+
+ def make_present(self):
+ """Run actions if desired state is 'started'."""
+ if not self.network.exists:
+ self.network.create()
+ self.results['actions'].append('created %s' % self.network.name)
+ self.update_network_result()
+ elif self.recreate or self.network.different:
+ self.network.recreate()
+ self.results['actions'].append('recreated %s' %
+ self.network.name)
+ self.update_network_result()
+ else:
+ self.update_network_result(changed=False)
+
+ def make_absent(self):
+ """Run actions if desired state is 'absent'."""
+ if not self.network.exists:
+ self.results.update({'changed': False})
+ elif self.network.exists:
+ self.network.delete()
+ self.results['actions'].append('deleted %s' % self.network.name)
+ self.results.update({'changed': True})
+ self.results.update({'network': {},
+ 'podman_actions': self.network.actions})
+ self.module.exit_json(**self.results)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ state=dict(type='str', default="present",
+ choices=['present', 'absent']),
+ name=dict(type='str', required=True),
+ disable_dns=dict(type='bool', required=False),
+ driver=dict(type='str', required=False),
+ gateway=dict(type='str', required=False),
+ internal=dict(type='bool', required=False),
+ ip_range=dict(type='str', required=False),
+ ipv6=dict(type='bool', required=False),
+ subnet=dict(type='str', required=False),
+ macvlan=dict(type='str', required=False),
+ opt=dict(type='dict', required=False,
+ options=dict(
+ isolate=dict(type='bool', required=False),
+ mtu=dict(type='int', required=False),
+ metric=dict(type='int', required=False),
+ mode=dict(type='str', required=False),
+ parent=dict(type='str', required=False),
+ vlan=dict(type='int', required=False),
+ )),
+ executable=dict(type='str', required=False, default='podman'),
+ debug=dict(type='bool', default=False),
+ recreate=dict(type='bool', default=False),
+ ),
+ required_by=dict( # for IP range and GW to set 'subnet' is required
+ ip_range=('subnet'),
+ gateway=('subnet'),
+ ))
+
+ PodmanNetworkManager(module).execute()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_network_info.py b/ansible_collections/containers/podman/plugins/modules/podman_network_info.py
new file mode 100644
index 000000000..a9e18cd4d
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_network_info.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r"""
+module: podman_network_info
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.0.0'
+short_description: Gather info about podman networks
+notes: []
+description:
+ - Gather info about podman networks with podman inspect command.
+requirements:
+ - "Podman installed on host"
+options:
+ name:
+ description:
+ - Name of the network
+ type: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+"""
+
+EXAMPLES = r"""
+- name: Gather info about all present networks
+ containers.podman.podman_network_info:
+
+- name: Gather info about specific network
+ containers.podman.podman_network_info:
+ name: podman
+"""
+
+RETURN = r"""
+networks:
+ description: Facts from all or specified networks
+ returned: always
+ type: list
+ sample: [
+ {
+ "cniVersion": "0.4.0",
+ "name": "podman",
+ "plugins": [
+ {
+ "bridge": "cni-podman0",
+ "ipMasq": true,
+ "ipam": {
+ "ranges": [
+ [
+ {
+ "gateway": "10.88.0.1",
+ "subnet": "10.88.0.0/16"
+ }
+ ]
+ ],
+ "routes": [
+ {
+ "dst": "0.0.0.0/0"
+ }
+ ],
+ "type": "host-local"
+ },
+ "isGateway": true,
+ "type": "bridge"
+ },
+ {
+ "capabilities": {
+ "portMappings": true
+ },
+ "type": "portmap"
+ },
+ {
+ "backend": "iptables",
+ "type": "firewall"
+ }
+ ]
+ }
+ ]
+"""
+
+import json
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_network_info(module, executable, name):
+ command = [executable, 'network', 'inspect']
+ if not name:
+ all_names = [executable, 'network', 'ls', '-q']
+ rc, out, err = module.run_command(all_names)
+ if rc != 0:
+ module.fail_json(msg="Unable to get list of networks: %s" % err)
+ name = out.split()
+ if not name:
+ return [], out, err
+ command += name
+ else:
+ command.append(name)
+ rc, out, err = module.run_command(command)
+ if rc != 0 or 'unable to find network configuration' in err:
+ module.fail_json(msg="Unable to gather info for %s: %s" % (name, err))
+ if not out or json.loads(out) is None:
+ return [], out, err
+ return json.loads(out), out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ name=dict(type='str')
+ ),
+ supports_check_mode=True,
+ )
+
+ name = module.params['name']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ inspect_results, out, err = get_network_info(module, executable, name)
+
+ results = {
+ "changed": False,
+ "networks": inspect_results,
+ "stderr": err
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_play.py b/ansible_collections/containers/podman/plugins/modules/podman_play.py
new file mode 100644
index 000000000..04a30441b
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_play.py
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+module: podman_play
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+short_description: Play kubernetes YAML file using podman
+notes: []
+description:
+ - The module reads in a structured file of Kubernetes YAML.
+ It will then recreate the pod and containers described in the YAML.
+requirements:
+ - "Podman installed on host"
+options:
+ executable:
+ description:
+ - Name of executable to run, by default 'podman'
+ type: str
+ default: podman
+ kube_file:
+ description:
+ - Path to file with YAML configuration for a Pod.
+ type: path
+ required: True
+ authfile:
+ description:
+ - Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json,
+ which is set using podman login. If the authorization state is not found there,
+ $HOME/.docker/config.json is checked, which is set using docker login.
+ Note - You can also override the default path of the authentication file
+ by setting the REGISTRY_AUTH_FILE environment variable. export REGISTRY_AUTH_FILE=path
+ type: path
+ cert_dir:
+ description:
+ - Use certificates at path (*.crt, *.cert, *.key) to connect to the registry.
+ Default certificates directory is /etc/containers/certs.d.
+ (This option is not available with the remote Podman client)
+ type: path
+ configmap:
+ description:
+ - Use Kubernetes configmap YAML at path to provide a source for environment
+ variable values within the containers of the pod.
+ Note - The configmap option can be used multiple times to pass multiple
+ Kubernetes configmap YAMLs
+ type: list
+ elements: path
+ seccomp_profile_root:
+ description:
+ - Directory path for seccomp profiles (default is "/var/lib/kubelet/seccomp").
+ This option is not available with the remote Podman client
+ type: path
+ username:
+ description:
+ - The username and password to use to authenticate with the registry if required.
+ type: str
+ password:
+ description:
+ - The username and password to use to authenticate with the registry if required.
+ type: str
+ log_driver:
+ description:
+ - Set logging driver for all created containers.
+ type: str
+ log_level:
+ description:
+ - Set logging level for podman calls. Log messages above specified level
+ ("debug"|"info"|"warn"|"error"|"fatal"|"panic") (default "error")
+ type: str
+ choices:
+ - debug
+ - info
+ - warn
+ - error
+ - fatal
+ - panic
+ network:
+ description:
+ - List of the names of CNI networks the pod should join.
+ type: list
+ elements: str
+ state:
+ description:
+ - Start the pod after creating it, or to leave it created only.
+ type: str
+ choices:
+ - created
+ - started
+ - absent
+ required: True
+ tls_verify:
+ description:
+ - Require HTTPS and verify certificates when contacting registries (default is true).
+ If explicitly set to true, then TLS verification will be used. If set to false,
+ then TLS verification will not be used. If not specified, TLS verification will be
+ used unless the target registry is listed as an insecure registry in registries.conf.
+ type: bool
+ debug:
+ description:
+ - Enable debug for the module.
+ type: bool
+ recreate:
+ description:
+ - If pod already exists, delete it and run the new one.
+ type: bool
+ quiet:
+ description:
+ - Hide image pulls logs from output.
+ type: bool
+ userns:
+ description:
+ - Set the user namespace mode for all the containers in a pod.
+ It defaults to the PODMAN_USERNS environment variable.
+ An empty value ("") means user namespaces are disabled.
+ required: false
+ type: str
+'''
+
+EXAMPLES = '''
+- name: Play kube file
+ containers.podman.podman_play:
+ kube_file: ~/kube.yaml
+ state: started
+
+'''
+import re # noqa: F402
+try:
+ import yaml
+ HAS_YAML = True
+except ImportError:
+ HAS_YAML = False
+
+from ansible.module_utils.basic import AnsibleModule # noqa: F402
+
+
+class PodmanKubeManagement:
+
+ def __init__(self, module, executable):
+ self.module = module
+ self.actions = []
+ self.executable = executable
+ self.command = [self.executable, 'play', 'kube']
+ creds = []
+ # pod_name = extract_pod_name(module.params['kube_file'])
+ if self.module.params['username']:
+ creds += [self.module.params['username']]
+ if self.module.params['password']:
+ creds += [self.module.params['password']]
+ creds = ":".join(creds)
+ self.command.extend(['--creds=%s' % creds])
+ if self.module.params['network']:
+ networks = ",".join(self.module.params['network'])
+ self.command.extend(['--network=%s' % networks])
+ if self.module.params['configmap']:
+ configmaps = ",".join(self.module.params['configmap'])
+ self.command.extend(['--configmap=%s' % configmaps])
+ start = self.module.params['state'] == 'started'
+ self.command.extend(['--start=%s' % str(start).lower()])
+ for arg, param in {
+ '--authfile': 'authfile',
+ '--cert-dir': 'cert_dir',
+ '--log-driver': 'log_driver',
+ '--seccomp-profile-root': 'seccomp_profile_root',
+ '--tls-verify': 'tls_verify',
+ '--log-level': 'log_level',
+ '--userns': 'userns',
+ '--quiet': 'quiet',
+ }.items():
+ if self.module.params[param] is not None:
+ self.command += ["%s=%s" % (arg, self.module.params[param])]
+ self.command += [self.module.params['kube_file']]
+
+ def _command_run(self, cmd):
+ rc, out, err = self.module.run_command(cmd)
+ self.actions.append(" ".join(cmd))
+ if self.module.params['debug']:
+ self.module.log('PODMAN-PLAY-KUBE command: %s' % " ".join(cmd))
+ self.module.log('PODMAN-PLAY-KUBE stdout: %s' % out)
+ self.module.log('PODMAN-PLAY-KUBE stderr: %s' % err)
+ self.module.log('PODMAN-PLAY-KUBE rc: %s' % rc)
+ return rc, out, err
+
+ def discover_pods(self):
+ pod_name = ''
+ if self.module.params['kube_file']:
+ if HAS_YAML:
+ with open(self.module.params['kube_file']) as f:
+ pod = yaml.safe_load(f)
+ if 'metadata' in pod:
+ pod_name = pod['metadata'].get('name')
+ else:
+ self.module.fail_json(
+ "No metadata in Kube file!\n%s" % pod)
+ else:
+ with open(self.module.params['kube_file']) as text:
+ # the following formats are matched for a kube name:
+ # should match name field within metadata (2 or 4 spaces in front of name)
+ # the name can be written without quotes, in single or double quotes
+ # the name can contain -_
+ re_pod_name = re.compile(r'^\s{2,4}name: ["|\']?(?P<pod_name>[\w|\-|\_]+)["|\']?', re.MULTILINE)
+ re_pod = re_pod_name.search(text.read())
+ if re_pod:
+ pod_name = re_pod.group(1)
+ if not pod_name:
+ self.module.fail_json("Deployment doesn't have a name!")
+ # Find all pods
+ all_pods = ''
+ # In case of one pod or replicasets
+ for name in ("name=%s$", "name=%s-pod-*"):
+ cmd = [self.executable,
+ "pod", "ps", "-q", "--filter", name % pod_name]
+ rc, out, err = self._command_run(cmd)
+ all_pods += out
+ ids = list(set([i for i in all_pods.splitlines() if i]))
+ return ids
+
+ def remove_associated_pods(self, pods):
+ changed = False
+ out_all, err_all = '', ''
+ # Delete all pods
+ for pod_id in pods:
+ rc, out, err = self._command_run(
+ [self.executable, "pod", "rm", "-f", pod_id])
+ if rc != 0:
+ self.module.fail_json("Can NOT delete Pod %s" % pod_id)
+ else:
+ changed = True
+ out_all += out
+ err_all += err
+ return changed, out_all, err_all
+
+ def pod_recreate(self):
+ pods = self.discover_pods()
+ self.remove_associated_pods(pods)
+ # Create a pod
+ rc, out, err = self._command_run(self.command)
+ if rc != 0:
+ self.module.fail_json("Can NOT create Pod! Error: %s" % err)
+ return out, err
+
+ def play(self):
+ rc, out, err = self._command_run(self.command)
+ if rc != 0 and 'pod already exists' in err:
+ if self.module.params['recreate']:
+ out, err = self.pod_recreate()
+ changed = True
+ else:
+ changed = False
+ err = "\n".join([
+ i for i in err.splitlines() if 'pod already exists' not in i])
+ elif rc != 0:
+ self.module.fail_json(msg="Output: %s\nError=%s" % (out, err))
+ else:
+ changed = True
+ return changed, out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ kube_file=dict(type='path', required=True),
+ authfile=dict(type='path'),
+ cert_dir=dict(type='path'),
+ configmap=dict(type='list', elements='path'),
+ seccomp_profile_root=dict(type='path'),
+ username=dict(type='str'),
+ password=dict(type='str', no_log=True),
+ log_driver=dict(type='str'),
+ network=dict(type='list', elements='str'),
+ state=dict(
+ type='str',
+ choices=['started', 'created', 'absent'],
+ required=True),
+ tls_verify=dict(type='bool'),
+ debug=dict(type='bool'),
+ quiet=dict(type='bool'),
+ recreate=dict(type='bool'),
+ userns=dict(type='str'),
+ log_level=dict(
+ type='str',
+ choices=["debug", "info", "warn", "error", "fatal", "panic"]),
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.get_bin_path(
+ module.params['executable'], required=True)
+ manage = PodmanKubeManagement(module, executable)
+ if module.params['state'] == 'absent':
+ pods = manage.discover_pods()
+ changed, out, err = manage.remove_associated_pods(pods)
+ else:
+ changed, out, err = manage.play()
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ "actions": manage.actions
+ }
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_pod.py b/ansible_collections/containers/podman/plugins/modules/podman_pod.py
new file mode 100644
index 000000000..ab475de99
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_pod.py
@@ -0,0 +1,415 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# flake8: noqa: E501
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: podman_pod
+short_description: Manage Podman pods
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.0.0'
+description:
+ - Manage podman pods.
+options:
+ state:
+ description:
+ - This variable is set for state
+ type: str
+ default: created
+ choices:
+ - created
+ - killed
+ - restarted
+ - absent
+ - started
+ - stopped
+ - paused
+ - unpaused
+ recreate:
+ description:
+ - Use with present and started states to force the re-creation of an
+ existing pod.
+ type: bool
+ default: False
+ add_host:
+ description:
+ - Add a host to the /etc/hosts file shared between all containers in the pod.
+ type: list
+ elements: str
+ required: false
+ cgroup_parent:
+ description:
+ - Path to cgroups under which the cgroup for the pod will be created. If the path
+ is not absolute, he path is considered to be relative to the cgroups path of the
+ init process. Cgroups will be created if they do not already exist.
+ type: str
+ required: false
+ cpus:
+ description:
+ - Set the total number of CPUs delegated to the pod.
+ Default is 0.000 which indicates that there is no limit on computation power.
+ required: false
+ type: str
+ cpuset_cpus:
+ description:
+ - Limit the CPUs to support execution. First CPU is numbered 0.
+ Unlike `cpus` this is of type string and parsed as a list of numbers. Format is 0-3,0,1
+ required: false
+ type: str
+ device:
+ description:
+ - Add a host device to the pod. Optional permissions parameter can be used to specify
+ device permissions. It is a combination of r for read, w for write, and m for mknod(2)
+ elements: str
+ required: false
+ type: list
+ device_read_bps:
+ description:
+ - Limit read rate (bytes per second) from a device (e.g. device-read-bps=/dev/sda:1mb)
+ elements: str
+ required: false
+ type: list
+ dns:
+ description:
+ - Set custom DNS servers in the /etc/resolv.conf file that will be shared between
+ all containers in the pod. A special option, "none" is allowed which disables
+ creation of /etc/resolv.conf for the pod.
+ type: list
+ elements: str
+ required: false
+ dns_opt:
+ description:
+ - Set custom DNS options in the /etc/resolv.conf file that will be shared between
+ all containers in the pod.
+ type: list
+ elements: str
+ required: false
+ dns_search:
+ description:
+ - Set custom DNS search domains in the /etc/resolv.conf file that will be shared
+ between all containers in the pod.
+ type: list
+ elements: str
+ required: false
+ generate_systemd:
+ description:
+ - Generate systemd unit file for container.
+ type: dict
+ default: {}
+ suboptions:
+ path:
+ description:
+ - Specify a path to the directory where unit files will be generated.
+ Required for this option. If it doesn't exist, the directory will be created.
+ type: str
+ required: false
+ restart_policy:
+ description:
+ - Specify a restart policy for the service. The restart-policy must be one of
+ "no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", or "always".
+ The default policy is "on-failure".
+ type: str
+ required: false
+ choices:
+ - 'no'
+ - 'on-success'
+ - 'on-failure'
+ - 'on-abnormal'
+ - 'on-watchdog'
+ - 'on-abort'
+ - 'always'
+ time:
+ description:
+ - Override the default stop timeout for the container with the given value.
+ type: int
+ required: false
+ no_header:
+ description:
+ - Do not generate the header including meta data such as the Podman version and the timestamp.
+ From podman version 3.1.0.
+ type: bool
+ default: false
+ names:
+ description:
+ - Use names of the containers for the start, stop, and description in the unit file.
+ Default is true.
+ type: bool
+ default: true
+ container_prefix:
+ description:
+ - Set the systemd unit name prefix for containers. The default is "container".
+ type: str
+ required: false
+ pod_prefix:
+ description:
+ - Set the systemd unit name prefix for pods. The default is "pod".
+ type: str
+ required: false
+ separator:
+ description:
+ - Set the systemd unit name separator between the name/id of a
+ container/pod and the prefix. The default is "-" (dash).
+ type: str
+ required: false
+ new:
+ description:
+ - Create containers and pods when the unit is started instead of
+ expecting them to exist. The default is "false".
+ Refer to podman-generate-systemd(1) for more information.
+ type: bool
+ default: false
+ after:
+ type: list
+ elements: str
+ required: false
+ description:
+ - Add the systemd unit after (After=) option, that ordering dependencies between the list of dependencies and this service.
+ wants:
+ type: list
+ elements: str
+ required: false
+ description:
+ - Add the systemd unit wants (Wants=) option, that this service is (weak) dependent on.
+ requires:
+ type: list
+ elements: str
+ required: false
+ description:
+ - Set the systemd unit requires (Requires=) option. Similar to wants, but declares a stronger requirement dependency.
+ gidmap:
+ description:
+ - GID map for the user namespace. Using this flag will run the container with
+ user namespace enabled. It conflicts with the `userns` and `subgidname` flags.
+ elements: str
+ required: false
+ type: list
+ hostname:
+ description:
+ - Set a hostname to the pod
+ type: str
+ required: false
+ infra:
+ description:
+ - Create an infra container and associate it with the pod. An infra container is
+ a lightweight container used to coordinate the shared kernel namespace of a pod.
+ Default is true.
+ type: bool
+ required: false
+ infra_conmon_pidfile:
+ description:
+ - Write the pid of the infra container's conmon process to a file. As conmon runs
+ in a separate process than Podman, this is necessary when using systemd to manage
+ Podman containers and pods.
+ type: str
+ required: false
+ infra_command:
+ description:
+ - The command that will be run to start the infra container. Default is "/pause".
+ type: str
+ required: false
+ infra_image:
+ description:
+ - The image that will be created for the infra container. Default is "k8s.gcr.io/pause:3.1".
+ type: str
+ required: false
+ infra_name:
+ description:
+ - The name that will be used for the pod's infra container.
+ type: str
+ required: false
+ ip:
+ description:
+ - Set a static IP for the pod's shared network.
+ type: str
+ required: false
+ label:
+ description:
+ - Add metadata to a pod, pass dictionary of label keys and values.
+ type: dict
+ required: false
+ label_file:
+ description:
+ - Read in a line delimited file of labels.
+ type: str
+ required: false
+ mac_address:
+ description:
+ - Set a static MAC address for the pod's shared network.
+ type: str
+ required: false
+ name:
+ description:
+ - Assign a name to the pod.
+ type: str
+ required: true
+ network:
+ description:
+ - Set network mode for the pod. Supported values are bridge (the default), host
+ (do not create a network namespace, all containers in the pod will use the host's
+ network), or a list of names of CNI networks to join.
+ type: list
+ elements: str
+ required: false
+ network_alias:
+ description:
+ - Add a network-scoped alias for the pod, setting the alias for all networks that the pod joins.
+ To set a name only for a specific network, use the alias option as described under the -`network` option.
+ Network aliases work only with the bridge networking mode.
+ This option can be specified multiple times.
+ elements: str
+ required: false
+ type: list
+ aliases:
+ - network_aliases
+ no_hosts:
+ description:
+ - Disable creation of /etc/hosts for the pod.
+ type: bool
+ required: false
+ pid:
+ description:
+ - Set the PID mode for the pod. The default is to create a private PID namespace
+ for the pod. Requires the PID namespace to be shared via `share` option.
+ required: false
+ type: str
+ pod_id_file:
+ description:
+ - Write the pod ID to the file.
+ type: str
+ required: false
+ publish:
+ description:
+ - Publish a port or range of ports from the pod to the host.
+ type: list
+ elements: str
+ required: false
+ aliases:
+ - ports
+ share:
+ description:
+ - A comma delimited list of kernel namespaces to share. If none or "" is specified,
+ no namespaces will be shared. The namespaces to choose from are ipc, net, pid,
+ user, uts.
+ type: str
+ required: false
+ subgidname:
+ description:
+ - Name for GID map from the /etc/subgid file. Using this flag will run the container
+ with user namespace enabled. This flag conflicts with `userns` and `gidmap`.
+ required: false
+ type: str
+ subuidname:
+ description:
+ - Name for UID map from the /etc/subuid file.
+ Using this flag will run the container with user namespace enabled.
+ This flag conflicts with `userns` and `uidmap`.
+ required: false
+ type: str
+ uidmap:
+ description:
+ - Run the container in a new user namespace using the supplied mapping.
+ This option conflicts with the `userns` and `subuidname` options.
+ This option provides a way to map host UIDs to container UIDs.
+ It can be passed several times to map different ranges.
+ elements: str
+ required: false
+ type: list
+ userns:
+ description:
+ - Set the user namespace mode for all the containers in a pod.
+ It defaults to the PODMAN_USERNS environment variable.
+ An empty value ("") means user namespaces are disabled.
+ required: false
+ type: str
+ volume:
+ description:
+ - Create a bind mount.
+ aliases:
+ - volumes
+ elements: str
+ required: false
+ type: list
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+ debug:
+ description:
+ - Return additional information which can be helpful for investigations.
+ type: bool
+ default: False
+
+requirements:
+ - "podman"
+
+'''
+
+RETURN = '''
+pod:
+ description: Pod inspection results for the given pod
+ built.
+ returned: always
+ type: dict
+ sample:
+ Config:
+ cgroupParent: /libpod_parent
+ created: '2020-06-14T15:16:12.230818767+03:00'
+ hostname: newpod
+ id: a5a5c6cdf8c72272fc5c33f787e8d7501e2fa0c1e92b2b602860defdafeeec58
+ infraConfig:
+ infraPortBindings: null
+ makeInfraContainer: true
+ labels: {}
+ lockID: 515
+ name: newpod
+ sharesCgroup: true
+ sharesIpc: true
+ sharesNet: true
+ sharesUts: true
+ Containers:
+ - id: dc70a947c7ae15198ec38b3c817587584085dee3919cbeb9969e3ab77ba10fd2
+ state: configured
+ State:
+ cgroupPath: /libpod_parent/a5a5c6cdf8c72272fc5c33f787e8d7501e2fa0c1e92b2b602860defdafeeec58
+ infraContainerID: dc70a947c7ae15198ec38b3c817587584085dee3919cbeb9969e3ab77ba10fd2
+ status: Created
+
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- podman_pod:
+ name: pod1
+ state: started
+ ports:
+ - "4444:5555"
+
+# Connect random port from localhost to port 80 on pod2
+- name: Connect random port from localhost to port 80 on pod2
+ containers.podman.podman_pod:
+ name: pod2
+ state: started
+ publish: "127.0.0.1::80"
+'''
+from ansible.module_utils.basic import AnsibleModule # noqa: F402
+from ..module_utils.podman.podman_pod_lib import PodmanPodManager # noqa: F402
+from ..module_utils.podman.podman_pod_lib import ARGUMENTS_SPEC_POD # noqa: F402
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=ARGUMENTS_SPEC_POD
+ )
+ results = PodmanPodManager(module, module.params).execute()
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_pod_info.py b/ansible_collections/containers/podman/plugins/modules/podman_pod_info.py
new file mode 100644
index 000000000..8b2a4bf06
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_pod_info.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r"""
+module: podman_pod_info
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.0.0'
+short_description: Gather info about podman pods
+notes: []
+description:
+ - Gather info about podman pods with podman inspect command.
+requirements:
+ - "Podman installed on host"
+options:
+ name:
+ description:
+ - Name of the pod
+ type: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+"""
+
+EXAMPLES = r"""
+- name: Gather info about all present pods
+ containers.podman.podman_pod_info:
+
+- name: Gather info about specific pods
+ containers.podman.podman_pod_info:
+ name: special_pod
+"""
+
+RETURN = r"""
+pods:
+ description: Facts from all or specified pods
+ returned: always
+ type: list
+ sample: [
+ {
+ "Config": {
+ "id": "d9cb6dbb0....",
+ "name": "pod1",
+ "hostname": "pod1host",
+ "labels": {
+ },
+ "cgroupParent": "/libpod_parent",
+ "sharesCgroup": true,
+ "sharesIpc": true,
+ "sharesNet": true,
+ "sharesUts": true,
+ "infraConfig": {
+ "makeInfraContainer": true,
+ "infraPortBindings": [
+ {
+ "hostPort": 7777,
+ "containerPort": 7111,
+ "protocol": "tcp",
+ "hostIP": ""
+ }
+ ]
+ },
+ "created": "2020-07-13T20:29:12.572282186+03:00",
+ "lockID": 682
+ },
+ "State": {
+ "cgroupPath": "/libpod_parent/d9cb6dbb0....",
+ "infraContainerID": "ad46737bf....",
+ "status": "Created"
+ },
+ "Containers": [
+ {
+ "id": "ad46737bf....",
+ "state": "configured"
+ }
+ ]
+ }
+ ]
+"""
+
+import json
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_pod_info(module, executable, name):
+ command = [executable, 'pod', 'inspect']
+ pods = [name]
+ result = []
+ errs = []
+ rcs = []
+ if not name:
+ all_names = [executable, 'pod', 'ls', '-q']
+ rc, out, err = module.run_command(all_names)
+ if rc != 0:
+ module.fail_json(msg="Unable to get list of pods: %s" % err)
+ name = out.split()
+ if not name:
+ return [], [err], [rc]
+ pods = name
+ for pod in pods:
+ rc, out, err = module.run_command(command + [pod])
+ errs.append(err.strip())
+ rcs += [rc]
+ if not out or json.loads(out) is None or not json.loads(out):
+ continue
+ result.append(json.loads(out))
+ return result, errs, rcs
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ name=dict(type='str')
+ ),
+ supports_check_mode=True,
+ )
+
+ name = module.params['name']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ inspect_results, errs, rcs = get_pod_info(module, executable, name)
+
+ if len(rcs) > 1 and 0 not in rcs:
+ module.fail_json(msg="Failed to inspect pods", stderr="\n".join(errs))
+
+ results = {
+ "changed": False,
+ "pods": inspect_results,
+ "stderr": "\n".join(errs),
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_prune.py b/ansible_collections/containers/podman/plugins/modules/podman_prune.py
new file mode 100644
index 000000000..ee4c68a93
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_prune.py
@@ -0,0 +1,252 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# Copyright (c) 2023, Roberto Alfieri <ralfieri@redhat.com>
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_prune
+author:
+ - 'Roberto Alfieri (@rebtoor)'
+version_added: '1.10.0'
+short_description: Allows to prune various podman objects
+notes: []
+description:
+ - Allows to run C(podman container prune), C(podman image prune), C(podman network prune),
+ C(podman volume prune) and C(podman system prune)
+requirements:
+ - 'Podman installed on host'
+options:
+ executable:
+ description:
+ - Podman binary.
+ type: str
+ default: podman
+ container:
+ description:
+ - Whether to prune containers.
+ type: bool
+ default: false
+ container_filters:
+ description:
+ - A dictionary of filter values used for selecting containers to delete.
+ - 'For example, C(until: 24h).'
+ - See L(the podman documentation,
+ https://docs.podman.io/en/latest/markdown/podman-container-prune.1.html#filter-filters)
+ for more information on possible filters.
+ type: dict
+ image:
+ description:
+ - Whether to prune images.
+ type: bool
+ default: false
+ image_filters:
+ description:
+ - A dictionary of filter values used for selecting images to delete.
+ - 'You can also use C(dangling_only: false) to delete dangling and non-dangling images or C(external: true)
+ to delete images even when they are used by external containers.'
+ - See L(the podman documentation,
+ https://docs.podman.io/en/latest/markdown/podman-image-prune.1.html#filter-filters)
+ for more information on possible filters.
+ type: dict
+ network:
+ description:
+ - Whether to prune networks.
+ type: bool
+ default: false
+ network_filters:
+ description:
+ - A dictionary of filter values used for selecting networks to delete.
+ - See L(the podman documentation,
+ https://docs.podman.io/en/latest/markdown/podman-network-prune.1.html#filter)
+ for more information on possible filters.
+ type: dict
+ system:
+ description:
+ - Wheter to prune unused pods, containers, image, networks and volume data
+ type: bool
+ default: false
+ system_all:
+ description:
+ - Wheter to prune all unused images, not only dangling images.
+ type: bool
+ default: false
+ system_volumes:
+ description:
+ - Wheter to prune volumes currently unused by any container.
+ type: bool
+ default: false
+ volume:
+ description:
+ - Whether to prune volumes.
+ type: bool
+ default: false
+ volume_filters:
+ description:
+ - A dictionary of filter values used for selecting volumes to delete.
+ - See L(the podman documentation,
+ https://docs.podman.io/en/latest/markdown/podman-volume-prune.1.html#filter)
+ for more information on possible filters.
+ type: dict
+'''
+
+EXAMPLES = r'''
+- name: Prune containers older than 24h
+ containers.podman.podman_prune:
+ containers: true
+ containers_filters:
+ # only consider containers created more than 24 hours ago
+ until: 24h
+
+- name: Prune everything
+ containers.podman.podman_prune:
+ system: true
+
+- name: Prune everything (including non-dangling images)
+ containers.podman.podman_prune:
+ system: true
+ system_all: true
+ system_volumes: true
+'''
+
+RETURN = r'''
+# containers
+containers:
+ description:
+ - List of IDs of deleted containers.
+ returned: I(containers) is C(true)
+ type: list
+ elements: str
+ sample: []
+
+# images
+images:
+ description:
+ - List of IDs of deleted images.
+ returned: I(images) is C(true)
+ type: list
+ elements: str
+ sample: []
+
+# networks
+networks:
+ description:
+ - List of IDs of deleted networks.
+ returned: I(networks) is C(true)
+ type: list
+ elements: str
+ sample: []
+
+# volumes
+volumes:
+ description:
+ - List of IDs of deleted volumes.
+ returned: I(volumes) is C(true)
+ type: list
+ elements: str
+ sample: []
+
+# system
+system:
+ description:
+ - List of ID of deleted containers, volumes, images, network and total reclaimed space
+ returned: I(system) is C(true)
+ type: list
+ elements: str
+ sample: []
+'''
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def filtersPrepare(target, filters):
+ filter_out = []
+ if target == 'system':
+ for system_filter in filters:
+ filter_out.append(filters[system_filter])
+ else:
+ for common_filter in filters:
+ if isinstance(filters[common_filter], dict):
+ dict_filters = filters[common_filter]
+ for single_filter in dict_filters:
+ filter_out.append('--filter={label}={key}={value}'.format(label=common_filter, key=single_filter,
+ value=dict_filters[single_filter]))
+ else:
+ if target == 'image' and (common_filter in ('dangling_only', 'external')):
+ if common_filter == 'dangling_only' and not filters['dangling_only']:
+ filter_out.append('-a')
+ if common_filter == 'external' and filters['external']:
+ filter_out.append('--external')
+ else:
+ filter_out.append('--filter={label}={value}'.format(label=common_filter,
+ value=filters[common_filter]))
+
+ return filter_out
+
+
+def podmanExec(module, target, filters, executable):
+ command = [executable, target, 'prune', '--force']
+ if filters is not None:
+ command.extend(filtersPrepare(target, filters))
+ rc, out, err = module.run_command(command)
+ changed = bool(out)
+
+ if rc != 0:
+ module.fail_json(
+ msg='Error executing prune on {target}: {err}'.format(target=target, err=err))
+
+ return {
+ "changed": changed,
+ target: list(filter(None, out.split('\n'))),
+ "errors": err
+ }
+
+
+def main():
+ results = dict()
+ module_args = dict(
+ container=dict(type='bool', default=False),
+ container_filters=dict(type='dict'),
+ image=dict(type='bool', default=False),
+ image_filters=dict(type='dict'),
+ network=dict(type='bool', default=False),
+ network_filters=dict(type='dict'),
+ volume=dict(type='bool', default=False),
+ volume_filters=dict(type='dict'),
+ system=dict(type='bool', default=False),
+ system_all=dict(type='bool', default=False),
+ system_volumes=dict(type='bool', default=False),
+ executable=dict(type='str', default='podman')
+ )
+
+ module = AnsibleModule(
+ argument_spec=module_args
+ )
+
+ executable = module.get_bin_path(
+ module.params['executable'], required=True)
+
+ for target, filters in (
+ ('container', 'container_filters'), ('image', 'image_filters'), ('network', 'network_filters'),
+ ('volume', 'volume_filters')):
+ if module.params[target]:
+ results[target] = podmanExec(module, target, module.params[filters], executable)
+
+ if module.params['system']:
+ target = 'system'
+ system_filters = {}
+ if module.params['system_all']:
+ system_filters['system_all'] = '--all'
+ if module.params['system_volumes']:
+ system_filters['system_volumes'] = '--volumes'
+ results[target] = podmanExec(module, target, system_filters, executable)
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_save.py b/ansible_collections/containers/podman/plugins/modules/podman_save.py
new file mode 100644
index 000000000..bc7ce252c
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_save.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# Copyright (c) 2020, Sagi Shnaidman <sshnaidm@redhat.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_save
+short_description: Saves podman image to tar file
+author: Sagi Shnaidman (@sshnaidm)
+description:
+ - podman save saves an image to either docker-archive, oci-archive, oci-dir
+ (directory with oci manifest type), or docker-dir (directory with v2s2 manifest type)
+ on the local machine, default is docker-archive.
+
+options:
+ image:
+ description:
+ - Image to save.
+ type: str
+ required: true
+ compress:
+ description:
+ - Compress tarball image layers when pushing to a directory using the 'dir' transport.
+ (default is same compression type, compressed or uncompressed, as source)
+ type: bool
+ dest:
+ description:
+ - Destination file to write image to.
+ type: str
+ required: true
+ aliases:
+ - path
+ format:
+ description:
+ - Save image to docker-archive, oci-archive (see containers-transports(5)), oci-dir
+ (oci transport), or docker-dir (dir transport with v2s2 manifest type).
+ type: str
+ choices:
+ - docker-archive
+ - oci-archive
+ - oci-dir
+ - docker-dir
+ multi_image_archive:
+ description:
+ - Allow for creating archives with more than one image. Additional names will be
+ interpreted as images instead of tags. Only supported for docker-archive.
+ type: bool
+ force:
+ description:
+ - Force saving to file even if it exists.
+ type: bool
+ default: True
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+requirements:
+ - "Podman installed on host"
+'''
+
+RETURN = '''
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- containers.podman.podman_save:
+ dest: /path/to/tar/file
+ compress: true
+ format: oci-dir
+'''
+
+import os # noqa: E402
+from ansible.module_utils.basic import AnsibleModule # noqa: E402
+from ..module_utils.podman.common import remove_file_or_dir # noqa: E402
+
+
+def save(module, executable):
+ changed = False
+ command = [executable, 'save']
+ cmd_args = {
+ 'compress': ['--compress'],
+ 'dest': ['-o=%s' % module.params['dest']],
+ 'format': ['--format=%s' % module.params['format']],
+ 'multi_image_archive': ['--multi-image-archive'],
+ }
+ for param in module.params:
+ if module.params[param] is not None and param in cmd_args:
+ command += cmd_args[param]
+ command.append(module.params['image'])
+ if module.params['force']:
+ dest = module.params['dest']
+ if os.path.exists(dest):
+ changed = True
+ if module.check_mode:
+ return changed, '', ''
+ try:
+ remove_file_or_dir(dest)
+ except Exception as e:
+ module.fail_json(msg="Error deleting %s path: %s" % (dest, e))
+ else:
+ changed = not os.path.exists(module.params['dest'])
+ if module.check_mode:
+ return changed, '', ''
+ rc, out, err = module.run_command(command)
+ if rc != 0:
+ module.fail_json(msg="Error: %s" % (err))
+ return changed, out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ image=dict(type='str', required=True),
+ compress=dict(type='bool'),
+ dest=dict(type='str', required=True, aliases=['path']),
+ format=dict(type='str', choices=['docker-archive', 'oci-archive', 'oci-dir', 'docker-dir']),
+ multi_image_archive=dict(type='bool'),
+ force=dict(type='bool', default=True),
+ executable=dict(type='str', default='podman')
+ ),
+ supports_check_mode=True,
+ )
+ if module.params['compress'] and module.params['format'] not in ['oci-dir', 'docker-dir']:
+ module.fail_json(msg="Compression is only supported for oci-dir and docker-dir format")
+
+ executable = module.get_bin_path(module.params['executable'], required=True)
+ changed, out, err = save(module, executable)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_secret.py b/ansible_collections/containers/podman/plugins/modules/podman_secret.py
new file mode 100644
index 000000000..fc8ec1f1d
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_secret.py
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+---
+module: podman_secret
+author:
+ - "Aliaksandr Mianzhynski (@amenzhinsky)"
+version_added: '1.7.0'
+short_description: Manage podman secrets
+notes: []
+description:
+ - Manage podman secrets
+requirements:
+ - podman
+options:
+ data:
+ description:
+ - The value of the secret. Required when C(state) is C(present).
+ type: str
+ driver:
+ description:
+ - Override default secrets driver, currently podman uses C(file)
+ which is unencrypted.
+ type: str
+ driver_opts:
+ description:
+ - Driver-specific key-value options.
+ type: dict
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ type: str
+ default: 'podman'
+ force:
+ description:
+ - Use it when C(state) is C(present) to remove and recreate an existing secret.
+ type: bool
+ default: false
+ skip_existing:
+ description:
+ - Use it when C(state) is C(present) and secret with the same name already exists.
+ If set to C(true), the secret will NOT be recreated and remains as is.
+ type: bool
+ default: false
+ name:
+ description:
+ - The name of the secret.
+ required: True
+ type: str
+ state:
+ description:
+ - Whether to create or remove the named secret.
+ type: str
+ default: present
+ choices:
+ - absent
+ - present
+'''
+
+EXAMPLES = r"""
+- name: Create secret
+ containers.podman.podman_secret:
+ state: present
+ name: mysecret
+ data: "my super secret content"
+
+- name: Create container that uses the secret
+ containers.podman.podman_container:
+ name: showmysecret
+ image: docker.io/alpine:3.14
+ secrets:
+ - mysecret
+ detach: false
+ command: cat /run/secrets/mysecret
+ register: container
+
+- name: Output secret data
+ debug:
+ msg: '{{ container.stdout }}'
+
+- name: Remove secret
+ containers.podman.podman_secret:
+ state: absent
+ name: mysecret
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def podman_secret_create(module, executable, name, data, force, skip,
+ driver, driver_opts):
+ if force:
+ module.run_command([executable, 'secret', 'rm', name])
+ if skip:
+ rc, out, err = module.run_command(
+ [executable, 'secret', 'ls', "--format", "{{.Name}}"])
+ if name in [i.strip() for i in out.splitlines()]:
+ return {
+ "changed": False,
+ }
+
+ cmd = [executable, 'secret', 'create']
+ if driver:
+ cmd.append('--driver')
+ cmd.append(driver)
+ if driver_opts:
+ cmd.append('--driver-opts')
+ cmd.append(",".join("=".join(i) for i in driver_opts.items()))
+ cmd.append(name)
+ cmd.append('-')
+
+ rc, out, err = module.run_command(cmd, data=data, binary_data=True)
+ if rc != 0:
+ module.fail_json(msg="Unable to create secret: %s" % err)
+
+ return {
+ "changed": True,
+ }
+
+
+def podman_secret_remove(module, executable, name):
+ changed = False
+ rc, out, err = module.run_command([executable, 'secret', 'rm', name])
+ if rc == 0:
+ changed = True
+ elif 'no such secret' in err:
+ pass
+ else:
+ module.fail_json(msg="Unable to remove secret: %s" % err)
+
+ return {
+ "changed": changed,
+ }
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ state=dict(type='str', default='present', choices=['absent', 'present']),
+ name=dict(type='str', required=True),
+ data=dict(type='str', no_log=True),
+ force=dict(type='bool', default=False),
+ skip_existing=dict(type='bool', default=False),
+ driver=dict(type='str'),
+ driver_opts=dict(type='dict'),
+ ),
+ )
+
+ state = module.params['state']
+ name = module.params['name']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ if state == 'present':
+ data = module.params['data']
+ if data is None:
+ raise Exception("'data' is required when 'state' is 'present'")
+ force = module.params['force']
+ skip = module.params['skip_existing']
+ driver = module.params['driver']
+ driver_opts = module.params['driver_opts']
+ results = podman_secret_create(module, executable,
+ name, data, force, skip,
+ driver, driver_opts)
+ else:
+ results = podman_secret_remove(module, executable, name)
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_tag.py b/ansible_collections/containers/podman/plugins/modules/podman_tag.py
new file mode 100644
index 000000000..39e799f6f
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_tag.py
@@ -0,0 +1,91 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# Copyright (c) 2021, Christian Bourque <@ocafebabe>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: podman_tag
+short_description: Add an additional name to a local image
+author: Christian Bourque (@ocafebabe)
+description:
+ - podman tag adds one or more additional names to locally-stored image.
+options:
+ image:
+ description:
+ - Image to tag.
+ type: str
+ required: true
+ target_names:
+ description:
+ - Additional names.
+ type: list
+ elements: str
+ required: true
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+requirements:
+ - "Podman installed on host"
+'''
+
+RETURN = '''
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- containers.podman.podman_tag:
+ image: docker.io/continuumio/miniconda3
+ target_names:
+ - miniconda3
+ - miniconda
+'''
+
+from ansible.module_utils.basic import AnsibleModule # noqa: E402
+
+
+def tag(module, executable):
+ changed = False
+ command = [executable, 'tag']
+ command.append(module.params['image'])
+ command.extend(module.params['target_names'])
+ if module.check_mode:
+ return changed, '', ''
+ rc, out, err = module.run_command(command)
+ if rc == 0:
+ changed = True
+ else:
+ module.fail_json(msg="Error tagging local image %s: %s" % (
+ module.params['image'], err))
+ return changed, out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ image=dict(type='str', required=True),
+ target_names=dict(type='list', elements='str', required=True),
+ executable=dict(type='str', default='podman')
+ ),
+ supports_check_mode=True,
+ )
+
+ executable = module.get_bin_path(module.params['executable'], required=True)
+ changed, out, err = tag(module, executable)
+
+ results = {
+ "changed": changed,
+ "stdout": out,
+ "stderr": err,
+ }
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_volume.py b/ansible_collections/containers/podman/plugins/modules/podman_volume.py
new file mode 100644
index 000000000..c533091e1
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_volume.py
@@ -0,0 +1,484 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# flake8: noqa: E501
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: podman_volume
+short_description: Manage Podman volumes
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+version_added: '1.1.0'
+description:
+ - Manage Podman volumes
+options:
+ state:
+ description:
+ - State of volume, default 'present'
+ type: str
+ default: present
+ choices:
+ - present
+ - absent
+ recreate:
+ description:
+ - Recreate volume even if exists.
+ type: bool
+ default: false
+ name:
+ description:
+ - Name of volume.
+ type: str
+ required: true
+ label:
+ description:
+ - Add metadata to a pod volume (e.g., label com.example.key=value).
+ type: dict
+ required: false
+ driver:
+ description:
+ - Specify volume driver name (default local).
+ type: str
+ required: false
+ options:
+ description:
+ - Set driver specific options. For example 'device=tpmfs', 'type=tmpfs'.
+ UID and GID idempotency is not supported due to changes in podman.
+ type: list
+ elements: str
+ required: false
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+ debug:
+ description:
+ - Return additional information which can be helpful for investigations.
+ type: bool
+ default: False
+
+requirements:
+ - "podman"
+
+'''
+
+RETURN = '''
+volume:
+ description: Volume inspection results if exists.
+ returned: always
+ type: dict
+ sample:
+ CreatedAt: '2020-06-05T16:38:55.277628769+03:00'
+ Driver: local
+ Labels:
+ key.com: value
+ key.org: value2
+ Mountpoint: /home/user/.local/share/containers/storage/volumes/test/_data
+ Name: test
+ Options: {}
+ Scope: local
+
+'''
+
+EXAMPLES = '''
+# What modules does for example
+- podman_volume:
+ state: present
+ name: volume1
+ label:
+ key: value
+ key2: value2
+ options:
+ - "device=/dev/loop1"
+ - "type=ext4"
+'''
+# noqa: F402
+import json # noqa: F402
+
+from ansible.module_utils.basic import AnsibleModule # noqa: F402
+from ansible.module_utils._text import to_bytes, to_native # noqa: F402
+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
+
+
+class PodmanVolumeModuleParams:
+ """Creates list of arguments for podman CLI command.
+
+ Arguments:
+ action {str} -- action type from 'create', 'delete'
+ 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 ['delete']:
+ return self._simple_action()
+ if self.action in ['create']:
+ return self._create_action()
+
+ def _simple_action(self):
+ if self.action == 'delete':
+ cmd = ['rm', '-f', self.params['name']]
+ return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
+
+ def _create_action(self):
+ cmd = [self.action, self.params['name']]
+ 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_label(self, c):
+ for label in self.params['label'].items():
+ c += ['--label', b'='.join(
+ [to_bytes(l, errors='surrogate_or_strict') for l in label])]
+ return c
+
+ def addparam_driver(self, c):
+ return c + ['--driver', self.params['driver']]
+
+ def addparam_options(self, c):
+ for opt in self.params['options']:
+ c += ['--opt', opt]
+ return c
+
+
+class PodmanVolumeDefaults:
+ def __init__(self, module, podman_version):
+ self.module = module
+ self.version = podman_version
+ self.defaults = {
+ 'driver': 'local',
+ 'label': {},
+ 'options': {}
+ }
+
+ def default_dict(self):
+ # make here any changes to self.defaults related to podman version
+ return self.defaults
+
+
+class PodmanVolumeDiff:
+ def __init__(self, module, info, podman_version):
+ self.module = module
+ self.version = podman_version
+ self.default_dict = None
+ self.info = lower_keys(info)
+ self.params = self.defaultize()
+ self.diff = {'before': {}, 'after': {}}
+ self.non_idempotent = {}
+
+ def defaultize(self):
+ params_with_defaults = {}
+ self.default_dict = PodmanVolumeDefaults(
+ 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 diffparam_label(self):
+ before = self.info['labels'] if 'labels' in self.info else {}
+ after = self.params['label']
+ return self._diff_update_and_compare('label', before, after)
+
+ def diffparam_driver(self):
+ before = self.info['driver']
+ after = self.params['driver']
+ return self._diff_update_and_compare('driver', before, after)
+
+ def diffparam_options(self):
+ before = self.info['options'] if 'options' in self.info else {}
+ # Removing GID and UID from options list
+ before.pop('uid', None)
+ before.pop('gid', None)
+ # Collecting all other options in the list
+ before = ["=".join((k, v)) for k, v in before.items()]
+ after = self.params['options']
+ # # For UID, GID
+ # if 'uid' in self.info or 'gid' in self.info:
+ # ids = []
+ # if 'uid' in self.info and self.info['uid']:
+ # before = [i for i in before if 'uid' not in i]
+ # before += ['uid=%s' % str(self.info['uid'])]
+ # if 'gid' in self.info and self.info['gid']:
+ # before = [i for i in before if 'gid' not in i]
+ # before += ['gid=%s' % str(self.info['gid'])]
+ # if self.params['options']:
+ # for opt in self.params['options']:
+ # if 'uid=' in opt or 'gid=' in opt:
+ # ids += opt.split("o=")[1].split(",")
+ # after = [i for i in after if 'gid' not in i and 'uid' not in i]
+ # after += ids
+ before, after = sorted(list(set(before))), sorted(list(set(after)))
+ return self._diff_update_and_compare('options', before, after)
+
+ 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
+ else:
+ 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 PodmanVolume:
+ """Perform volume tasks.
+
+ Manages podman volume, inspects it and checks its current state
+ """
+
+ def __init__(self, module, name):
+ """Initialize PodmanVolume class.
+
+ Arguments:
+ module {obj} -- ansible module object
+ name {str} -- name of volume
+ """
+
+ super(PodmanVolume, self).__init__()
+ self.module = module
+ self.name = name
+ self.stdout, self.stderr = '', ''
+ self.info = self.get_info()
+ self.version = self._get_podman_version()
+ self.diff = {}
+ self.actions = []
+
+ @property
+ def exists(self):
+ """Check if volume exists."""
+ return bool(self.info != {})
+
+ @property
+ def different(self):
+ """Check if volume is different."""
+ diffcheck = PodmanVolumeDiff(
+ self.module,
+ self.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
+
+ def get_info(self):
+ """Inspect volume and gather info about it."""
+ # pylint: disable=unused-variable
+ rc, out, err = self.module.run_command(
+ [self.module.params['executable'], b'volume', b'inspect', self.name])
+ 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 volume.
+
+ Arguments:
+ action {str} -- action to perform - create, stop, delete
+ """
+ b_command = PodmanVolumeModuleParams(action,
+ self.module.params,
+ self.version,
+ self.module,
+ ).construct_command_from_params()
+ full_cmd = " ".join([self.module.params['executable'], 'volume']
+ + [to_native(i) for i in b_command])
+ self.module.log("PODMAN-VOLUME-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'volume'] + b_command,
+ expand_user_and_vars=False)
+ self.stdout = out
+ self.stderr = err
+ if rc != 0:
+ self.module.fail_json(
+ msg="Can't %s volume %s" % (action, self.name),
+ stdout=out, stderr=err)
+
+ def delete(self):
+ """Delete the volume."""
+ self._perform_action('delete')
+
+ def create(self):
+ """Create the volume."""
+ self._perform_action('create')
+
+ def recreate(self):
+ """Recreate the volume."""
+ self.delete()
+ self.create()
+
+
+class PodmanVolumeManager:
+ """Module manager class.
+
+ Defines according to parameters what actions should be applied to volume
+ """
+
+ def __init__(self, module):
+ """Initialize PodmanManager class.
+
+ Arguments:
+ module {obj} -- ansible module object
+ """
+
+ super(PodmanVolumeManager, self).__init__()
+
+ self.module = module
+ self.results = {
+ 'changed': False,
+ 'actions': [],
+ 'volume': {},
+ }
+ 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.volume = PodmanVolume(self.module, self.name)
+
+ def update_volume_result(self, changed=True):
+ """Inspect the current volume, update results with last info, exit.
+
+ Keyword Arguments:
+ changed {bool} -- whether any action was performed
+ (default: {True})
+ """
+ facts = self.volume.get_info() if changed else self.volume.info
+ out, err = self.volume.stdout, self.volume.stderr
+ self.results.update({'changed': changed, 'volume': facts,
+ 'podman_actions': self.volume.actions},
+ stdout=out, stderr=err)
+ if self.volume.diff:
+ self.results.update({'diff': self.volume.diff})
+ if self.module.params['debug']:
+ self.results.update({'podman_version': self.volume.version})
+ self.module.exit_json(**self.results)
+
+ def execute(self):
+ """Execute the desired action according to map of actions & states."""
+ states_map = {
+ 'present': self.make_present,
+ 'absent': self.make_absent,
+ }
+ process_action = states_map[self.state]
+ process_action()
+ self.module.fail_json(msg="Unexpected logic error happened, "
+ "please contact maintainers ASAP!")
+
+ def make_present(self):
+ """Run actions if desired state is 'started'."""
+ if not self.volume.exists:
+ self.volume.create()
+ self.results['actions'].append('created %s' % self.volume.name)
+ self.update_volume_result()
+ elif self.recreate or self.volume.different:
+ self.volume.recreate()
+ self.results['actions'].append('recreated %s' %
+ self.volume.name)
+ self.update_volume_result()
+ else:
+ self.update_volume_result(changed=False)
+
+ def make_absent(self):
+ """Run actions if desired state is 'absent'."""
+ if not self.volume.exists:
+ self.results.update({'changed': False})
+ elif self.volume.exists:
+ self.volume.delete()
+ self.results['actions'].append('deleted %s' % self.volume.name)
+ self.results.update({'changed': True})
+ self.results.update({'volume': {},
+ 'podman_actions': self.volume.actions})
+ self.module.exit_json(**self.results)
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ state=dict(type='str', default="present",
+ choices=['present', 'absent']),
+ name=dict(type='str', required=True),
+ label=dict(type='dict', required=False),
+ driver=dict(type='str', required=False),
+ options=dict(type='list', elements='str', required=False),
+ recreate=dict(type='bool', default=False),
+ executable=dict(type='str', required=False, default='podman'),
+ debug=dict(type='bool', default=False),
+ ))
+
+ PodmanVolumeManager(module).execute()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/containers/podman/plugins/modules/podman_volume_info.py b/ansible_collections/containers/podman/plugins/modules/podman_volume_info.py
new file mode 100644
index 000000000..97b43b3ce
--- /dev/null
+++ b/ansible_collections/containers/podman/plugins/modules/podman_volume_info.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# Copyright (c) 2020 Red Hat
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+module: podman_volume_info
+author:
+ - "Sagi Shnaidman (@sshnaidm)"
+short_description: Gather info about podman volumes
+notes: []
+description:
+ - Gather info about podman volumes with podman inspect command.
+requirements:
+ - "Podman installed on host"
+options:
+ name:
+ description:
+ - Name of the volume
+ type: str
+ executable:
+ description:
+ - Path to C(podman) executable if it is not in the C($PATH) on the
+ machine running C(podman)
+ default: 'podman'
+ type: str
+'''
+
+EXAMPLES = r"""
+- name: Gather info about all present volumes
+ podman_volume_info:
+
+- name: Gather info about specific volume
+ podman_volume_info:
+ name: specific_volume
+"""
+
+RETURN = r"""
+volumes:
+ description: Facts from all or specified volumes
+ returned: always
+ type: list
+ sample: [
+ {
+ "name": "testvolume",
+ "labels": {},
+ "mountPoint": "/home/ansible/.local/share/testvolume/_data",
+ "driver": "local",
+ "options": {},
+ "scope": "local"
+ }
+ ]
+"""
+
+import json
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_volume_info(module, executable, name):
+ command = [executable, 'volume', 'inspect']
+ if name:
+ command.append(name)
+ else:
+ command.append("--all")
+ rc, out, err = module.run_command(command)
+ if rc != 0 or 'no such volume' in err:
+ module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all volumes', err))
+ if not out or json.loads(out) is None:
+ return [], out, err
+ return json.loads(out), out, err
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ executable=dict(type='str', default='podman'),
+ name=dict(type='str')
+ ),
+ supports_check_mode=True,
+ )
+
+ name = module.params['name']
+ executable = module.get_bin_path(module.params['executable'], required=True)
+
+ inspect_results, out, err = get_volume_info(module, executable, name)
+
+ results = {
+ "changed": False,
+ "volumes": inspect_results,
+ "stderr": err
+ }
+
+ module.exit_json(**results)
+
+
+if __name__ == '__main__':
+ main()