Browse Source

[SQUASH] backport tripleo-container-manage to stable/train

This is a squash of 32 commits to facilitate the backport of
tripleo-container-manage and its dependencies.

Introduce tripleo-container-manage role

This is a first ieration of the role, but there is still a long TODO,
that will come later in separated patches:
- Add molecule testing
- In podman.yaml, add cpuset_cpus with parity of what is in paunch
- Remove containers that are:
  - managed by tripleo-ansible (using the container_label flag)
  - not in the container-startup-config
- Print stdout when containers start as it was done with paunch

Story: 2006732
Task: 37165

Co-Authored-By: Kevin Carter <kecarter@redhat.com>
Co-Authored-By: Alex Schultz <aschultz@redhat.com>

Depends-On: https://review.opendev.org/#/c/702144/
Change-Id: I2f88caa8e1c230dfe846a8a0dd9f939b98992cd5
(cherry picked from commit a191a2d600)

tripleo-container-manage: set some defaults

Set defaults that are needed to use the role outside of THT more easily.

Change-Id: Id67cf06c85a2a6b50e6494b1a66f534ccb06c4a7
(cherry picked from commit 609d7895a1)

Move the filters plugin to the core plugins location

This change is a workaround for a zuul issue which moves the
nested ansible role plugin to the core plugins directory so
that it is not creating a gate conflict.

Change-Id: I9f959803381063502b4d15980b14c3416ffa153f
Signed-off-by: Kevin Carter <kecarter@redhat.com>
(cherry picked from commit e2719131db)

Revert "Workaround for ansible-lint installation failure"

Backport note: this is a second backport of the same patch
since now it includes the change in tripleo-container-manage
role that is being backported to stable/train.

This reverts the disabling of the ansible-lint test from
commit cffd4fc9d4 and updates
ansible-lint to the fixed version.

Included are fixes for ansible-lint test failures which
got merged as part of I2f88caa8e1c230dfe846a8a0dd9f939b98992cd5
while the lint test was disabled.

Change-Id: I37100f5e1764a5cd2cb8df82ae963e673ca0a8da
(cherry picked from commit 28e105c056)

tripleo-container-manage: few improvements

- Add and use variables to make the role more flexible:
  tripleo_container_manage_config,
  tripleo_container_manage_config_id
  tripleo_container_manage_debug
  tripleo_container_manage_config_pattern (and rename hashed_files var)

  With these vars, the role can pretty much be used outside of TripleO.

- Show logs of config data generation if debug is enabled

- Do not run the "podman exec" tasks in check mode

- Remove the dependency on the "step" variable

Change-Id: I28ee31b723f27c392f880676aaae9368906cf45f
(cherry picked from commit 9c69840640)

Adds new molecule testing for tripleo-container-manage

This test ensure the "create" part is working fine with an easy and
simple container. The container-create and default scenario have been
consoldated so its running one complete test for now.

Change-Id: I9139c7b63c15739a1a95d913acb1128af299ce97
Co-Authored-By: Emilien Macchi <emilien@redhat.com>
Signed-off-by: Kevin Carter <kecarter@redhat.com>
(cherry picked from commit 414f47cc32)

tripleo-container-manage: add check tasks to the molecule playbook

Verify that the "fedora" container exists and has the right infos.

Change-Id: If78a254564cff502b49769b0b401d2efdac8cb23
(cherry picked from commit c20ab42015)

Molecule job for testing plugins and modules

and remove ansible_facts from podman_container_info module
Change-Id: I0c768bc6168363fa3758562f9f053aa9ab85236b

(cherry picked from commit 2d42082737)

tripleo-container-manage - first support for idempotency

- Implements a new helper to figure out if the existing containers on
  the hosts need to be removed (and re-created later).
  The helper will remove the container if:
    - the container is managed by tripleo_ansible (+ other conditions later)
    - a running container isn't in the config
    - the container has no config_data Label
    - the config_data changed for the container

- Restart the systemd service for the container if the podman_container
  module reported as changed (note the podman_container isn't yet
  idempotent but we're working on it in a separated patch)

- Fix the healthcheck & timer systemd files to be attached to the right
  service (with tripleo_ prefix)

Story: #2006732
Task: #37163
Change-Id: I5081c918b47dcb9f3629a3649fdf33d17668c1ff
(cherry picked from commit 7e41e0642d)

tripleo-container-manage: include_tasks to speed things up

Change-Id: I79d48fef7552f72c8fc0ebbad35d98cfc618b114
(cherry picked from commit 57411934b7)

tripleo-container-manage: introduce concurrency

Co-Authored-By: Alex Schultz <aschultz@redhat.com>
Change-Id: I1e5e941558c492b050e4db542703e322707dbbbd
(cherry picked from commit 2f8f0fc027)

tripleo-container-manage: fix log_opt

Put the right variables to have proper logging.

Change-Id: I60a14721ed978dddbebfd28f830fb2c50f326ce0
(cherry picked from commit e7f71c352e)

tripleo-container-manage: some nits fixed

Change-Id: I4006db3f5e3092aefeb8a0e50819dda11931630b
(cherry picked from commit 73e49888f7)

tripleo-container-manage: fix exec check

Fix the task which check if the container where the exec happens is
actually running.

Also podman_containers needs to be refreshed at every start_order to get
new container informations status.
So moving podman_containers from main, and putting it where we need it:
- before processing the list of containers to delete
- when start_order playbook starts

Co-Authored-By: Sagi Shnaidman <sshnaidm@redhat.com>
Change-Id: I2afb71288208c8b97763caa832c94e06e1b9457c
(cherry picked from commit e68d4b42a2)

tripleo-container-manage: port paunch-services

paunch-services used to be useful for container start/stop ordering,
when on the host some containers are managed by Pacemaker and some
others by Paunch.

We need to keep that feature so we are now porting these scripts and
services into tripleo-ansible.

These files where managed by Paunch:
%{_libexecdir}/paunch-container-shutdown
%{_libexecdir}/paunch-start-podman-container
%{_unitdir}/paunch-container-shutdown.service
%{_presetdir}/91-paunch-container-shutdown.preset
%{_unitdir}/netns-placeholder.service
%{_presetdir}/91-netns-placeholder.preset

Now we handle them via Ansible now, and cleanup the Paunch version.
It creates the exact same files from:
https://github.com/rdo-packages/paunch-distgit

This feature is disabled by default and will be explicitely enabled by
THT later. Molecule tests enable it though for testing coverage.

Story: 2006732
Task: 37382
Change-Id: I4f79429baab50bc0199fb65fe84641908d83935d
(cherry picked from commit 6e76f444df00af9ab60de1a5c7a7c8fa1d7eed61)

tripleo-container-manage: use async

- Add a new filter 'haskey' which returns container data with a specific
  config key. The filter takes a list of dicts and returns the dicts
  which have a certain key given in parameter with 'attribute'.
  If 'reverse' is set to True, the returned list won't contains dicts
  which have the 'attribute' value.
  If 'any' is set to True, the returned list will match any value in the
  list of 'value' parameter which has to be a list.

- Make the exec and create playbook using ansible "async".
  (WIP is to re-add the podman_container.changed in systemd playbook).

Note: we don't manage Systemd resources with async, since it exposes
race conditions at the systemd level.

Change-Id: Ice92cd5f90039e685c171e9035f929349a67ff2c
(cherry picked from commit e26f817597cf9f5a08d2eb6d559d1f2cc1b7d031)

tripleo-container-manage: fix duplicated loop vars

So we don't have the Ansible warning saying the loop var is already used
for another loop.

Change-Id: Ie28857cb712d2133aad4a67ae6942fcbbb1a6aee
(cherry picked from commit 80e0476f78c654f3bd563efb62a2acdc96e6df86)

tripleo-container-manage: skip some tasks in check mode

Change-Id: I5e68fdb2d872c741f00a1a24bac33112ba630f69
(cherry picked from commit f506dd6994c398669ae0b20d36ecce18d0ffbc4b)

tripleo-container-manage: add no_log to podman create

So we don't leak confidential informations like passwords and others in
the logs.
Note that if debug is enabled, the infos are displayed in the ansible
logs.

Change-Id: I8473c9118dbce2b04eeb2c01bcc1e55232325a67
(cherry picked from commit ac8cff4ebbbb6964255b2eb45b9cfbfd5394ef65)

Improve molecule tests for tripleo-container-manage (systemd)

Change-Id: Id14ad4876bdf7dcc979d5ecf4fe2b4751c635b88
(cherry picked from commit 484faa7e8016781afa86c099032b511bf90ef4fb)

fix typo

Change-Id: I0a970ae73f01c6ff181a67367cf668091748fc9c
Signed-off-by: Kevin Carter <kecarter@redhat.com>
(cherry picked from commit 3720b41a7f10e937c265a710071ca03fedf2ec70)

tripleo-container-manage: restart systemd service if container changed

If a podman_container resource changed, it'll be added to a fact, that
later will be used when it comes to figure our if a systemd service
needs to be restarted or not.

It introduces a new filter: list_of_keys.
This filter takes in input a list of dictionaries and for each of them
it will add the key to list_of_keys and returns it.

Change-Id: I0285b006c015f6cd223615ebdad52286f7683a87
(cherry picked from commit 0a037c65f7804316d4b1c3f1753d4c35bc8fbfbb)

tripleo-container-manage: some improvements

- Improve logging in some tasks, to display what container is managed
- Use no_log for the tasks which leak all infos about containers
  (config_data). If debug is enabled, the logs will show all the
  config_data.
- Move the /etc/sysconfig/podman_drop_in tasks into shutdown.yaml so it
  runs once and it's staying close to the other tasks related to why we
  have this file (manage shutdown/start order when reboot).

Change-Id: I56896f92d58db900fd2ea06281d89f75e8d53a17
(cherry picked from commit 6743f7586343d826c068f82b13388ca170922810)

tripleo-container-manage: create ansible-managed dropin file

If a deployment has containers managed by tripleo-ansible and not
paunch, and an operator would try to run paunch on the host, we want to
send a warning because paunch will remove all containers managed by
tripleo-ansible and then redeploy the containers with managed_by=paunch.

We could change the default of manage_by in Paunch (currently
managed_by=paunch) to something else e.g. tripleo, which would also be
the managed_by of tripleo-ansible; so both paunch and tripleo-ansible
could be used on the same host. However, we decided that when
tripleo-ansible is used to deploy the containers, paunch could not be
used anymore on that host.

Therefore, we create a file that will be checked by paunch and if
present, paunch CLI will show a warning.

Change-Id: I722cb8faa3b7eee81b418da83451bf802351dd79
(cherry picked from commit db1b13c962fc74ec7be9981d8e6f6af237afd3f5)

tripleo-container-manage: remove no_log for create/exec tasks

1. Remove no_log for the create and exec tasks. It's always useful to
   see what is being run.

2. Enable podman_container debug, so we see what commands are being run.

Change-Id: If728788293dd64622cf95da840b60e271197e9a0
(cherry picked from commit 89e4adf2ae816244cc9b7b48e86b7ca5511f07d3)

Add missing ExecReload in container service unit file

It may happen that we want to just reload the container. Before this
patch, it was a "stop and start", while podman has the "podman kill"
available, accepting the HUP signal.

Doing so allows other automated tools to actually just "reload" the
container as we would do for a standard service.

Change-Id: I35eff80f7637b013d3a1a831289ec9b1e0f81431
(cherry picked from commit 2a2bed6f5cb9989d3544918c7e151ba168c7cd6f)

Introduce unit tests structure for tripleo-ansible filters

Change-Id: Ie2fea14d2cbfb2c0b78cdc3064df0a558fa28a4c
(cherry picked from commit f90a6d42b3f5a55d5269b2d88b30e6455f6b3287)

Fix case in filters when Labels is None

When Labels is None it's not managed by tripleo container,
then skip it.
Change-Id: Ib82c4d28c462abb3f1a5ccb7d5137ec6059b2665

(cherry picked from commit 1acc95211d018033858f15e33dd063fca1991d3b)

tripleo-container-manage: include more arguments for container exec

Container execs can be more complex than just a user and a command.
It can also use --env and --privileged.

- container_exec_cmd is a new filter that will help to build the
  container exec command from the container data.
- list_or_dict_arg is an utility taken from openstack/paunch which
  allows to build a command and its arguments with list or dict data.

This patch will allow the container exec to work fine when they have
environment variables, like it's the case for Keystone bootstrap.

Change-Id: I15e0b518936b37e26799dbda9677a248cf17ff3c
(cherry picked from commit 55d5363b4a1b4d1eceda9522c327f193d133c05b)

Add SyslogIdenfier to healthcheck systemd unit

Adding this new field will allow to filter all healthcheck logs using
the Idenfier value.

For instance, using journalctl, you would be able to run this:
`journalctl -t healthcheck_collectd'

It will also allow to get a dedicated file out of (r)syslog if needed.

Change-Id: Icdc5caf4cedc46291a807c39c0a31c74955a4a74
Closes-Bug: #1856573
(cherry picked from commit 49858c5265)

Allow to run tripleo-container-manage in check mode

Change-Id: I3350a43805b4a148f64de393716c26b0158fcff4
(cherry picked from commit 6513a4bed8)

tripleo-container-manage: fix config_data in Config/Labels

config_data should not contain the container name, just the actual
config data which is the value of the container_data dict.

It removes the add of start_order into the compare, this isn't necessary
and breaks idempotency for containers which don't have start_order in
their config_data.
Also adds more unit tests to cover all situations handled by the filter.

Change-Id: I0b64bf34c8f7498128b3b1fceb7c727f8544cec6
(cherry picked from commit 19c5d7e77e)

tripleo-container-manage: search container configs recursively

If the user doesn't override the default value for
tripleo_container_manage_config, which is set to
'/var/lib/tripleo-config/', to something deeper, we want Ansible to
recursively search for the container config until it finds it.

Change-Id: I7d23fec91ffb813f0ab6f11b85d811ef3897f9e0
(cherry picked from commit 205f7c9b0c)

Idempotency for podman_container

Introduce partial idepmpotency for running podman_container

- Replace all options by their defaults it they're not set
- Force lower case on all podman inspection data
- Add a class with methods for every parameter to check its value
and compare it with module argument
- Add check_mode support and podman_actions

Change-Id: I1ae93dff1e10a1a696bb171996a75a0db6c34fa3
(cherry picked from commit 1212544a28)

Fix case where there're no effective caps

When no effective caps in container, it should be list, not None
Change-Id: I5412edc844ad43223c4b3bda35662a7f7ee43f3a

(cherry picked from commit 3a6690c904)

Add idempotency for networks parameter in podman_container

Change-Id: Ib7235d1cb64ad0f42e7a0201008536e1a35bd696
(cherry picked from commit 25f8abbc5a)

use version parsing from distutils

use LooseVersion for version comparisons

Change-Id: I609920a96c725c49f1623f60f8295d89ae4f3141
(cherry picked from commit 8c83219fbb)

Improve idempotency for podman containers module

Fix some pep8 issues
Change-Id: If4233e57edeec10ccac965d61c78f30688cd5531

(cherry picked from commit 0609c16d10)

Improve idempotency for podman container module

Strip all registry values from "image" parameter in input.
Add this case to test.

Change-Id: I78656e48cf85a1a39f873ee40193765eecf02c56
(cherry picked from commit 87d9e9d9a6)
changes/49/701849/5
Emilien Macchi 4 months ago
parent
commit
ec4351c566
55 changed files with 3459 additions and 735 deletions
  1. +4
    -0
      .gitignore
  2. +3
    -0
      .stestr.conf
  3. +1
    -0
      ansible-requirements.txt
  4. +3
    -1
      bindep.txt
  5. +6
    -0
      doc/source/roles/role-tripleo-container-manage.rst
  6. +3
    -0
      test-requirements.txt
  7. +14
    -2
      tox.ini
  8. +227
    -0
      tripleo_ansible/ansible_plugins/filter/helpers.py
  9. +601
    -53
      tripleo_ansible/ansible_plugins/modules/podman_container.py
  10. +285
    -293
      tripleo_ansible/ansible_plugins/modules/podman_container_info.py
  11. +30
    -34
      tripleo_ansible/ansible_plugins/modules/podman_volume_info.py
  12. +33
    -0
      tripleo_ansible/ansible_plugins/tests/.yamllint
  13. +56
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_container/molecule.yml
  14. +393
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_container/playbook.yml
  15. +39
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_container/prepare.yml
  16. +56
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_container_info/molecule.yml
  17. +70
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_container_info/playbook.yml
  18. +22
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_container_info/prepare.yml
  19. +56
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_volume_info/molecule.yml
  20. +63
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_volume_info/playbook.yml
  21. +22
    -0
      tripleo_ansible/ansible_plugins/tests/molecule/podman_volume_info/prepare.yml
  22. +0
    -351
      tripleo_ansible/playbooks/podman_tests.yml
  23. +29
    -0
      tripleo_ansible/roles/tripleo-container-manage/defaults/main.yml
  24. +1
    -0
      tripleo_ansible/roles/tripleo-container-manage/files/91-netns-placeholder-preset
  25. +1
    -0
      tripleo_ansible/roles/tripleo-container-manage/files/91-tripleo-container-shutdown-preset
  26. +11
    -0
      tripleo_ansible/roles/tripleo-container-manage/files/netns-placeholder-service
  27. +19
    -0
      tripleo_ansible/roles/tripleo-container-manage/files/tripleo-container-shutdown
  28. +21
    -0
      tripleo_ansible/roles/tripleo-container-manage/files/tripleo-container-shutdown-service
  29. +83
    -0
      tripleo_ansible/roles/tripleo-container-manage/files/tripleo-start-podman-container
  30. +44
    -0
      tripleo_ansible/roles/tripleo-container-manage/meta/main.yml
  31. +56
    -0
      tripleo_ansible/roles/tripleo-container-manage/molecule/default/molecule.yml
  32. +62
    -0
      tripleo_ansible/roles/tripleo-container-manage/molecule/default/playbook.yml
  33. +40
    -0
      tripleo_ansible/roles/tripleo-container-manage/molecule/default/prepare.yml
  34. +29
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/container_running.yml
  35. +21
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/create.yml
  36. +27
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/delete.yml
  37. +95
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/main.yml
  38. +93
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/podman/create.yml
  39. +77
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/podman/delete.yml
  40. +44
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/podman/exec.yml
  41. +33
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/podman/manage.yml
  42. +27
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/podman/start_order.yml
  43. +74
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/podman/systemd.yml
  44. +114
    -0
      tripleo_ansible/roles/tripleo-container-manage/tasks/shutdown.yml
  45. +10
    -0
      tripleo_ansible/roles/tripleo-container-manage/templates/systemd-healthcheck.j2
  46. +23
    -0
      tripleo_ansible/roles/tripleo-container-manage/templates/systemd-service.j2
  47. +9
    -0
      tripleo_ansible/roles/tripleo-container-manage/templates/systemd-timer.j2
  48. +0
    -0
      tripleo_ansible/tests/__init__.py
  49. +21
    -0
      tripleo_ansible/tests/base.py
  50. +0
    -0
      tripleo_ansible/tests/plugins/__init__.py
  51. +0
    -0
      tripleo_ansible/tests/plugins/filter/__init__.py
  52. +376
    -0
      tripleo_ansible/tests/plugins/filter/test_helpers.py
  53. +1
    -0
      zuul.d/layout.yaml
  54. +19
    -0
      zuul.d/molecule.yaml
  55. +12
    -1
      zuul.d/playbooks/run.yml

+ 4
- 0
.gitignore View File

@@ -11,6 +11,10 @@ cover
nosetests.xml
.testrepository
.venv
.stestr
tripleo_ansible.egg-info/
__pycache__
build

# Editors
*~

+ 3
- 0
.stestr.conf View File

@@ -0,0 +1,3 @@
[DEFAULT]
test_path=${TEST_PATH:-./tripleo_ansible/tests/}
top_dir=./

+ 1
- 0
ansible-requirements.txt View File

@@ -0,0 +1 @@
ansible>=2.8

+ 3
- 1
bindep.txt View File

@@ -19,7 +19,9 @@ git [platform:rpm]
libffi-devel [platform:rpm]
openssl-devel [platform:rpm]
python-devel [platform:rpm !platform:rhel-8 !platform:centos-8]
python3-devel [platform:rpm !platform:rhel-7 !platform:centos-7]
python3-devel [platform:rpm !platform:rhel-7 !platform:centos-7]
PyYAML [platform:rpm !platform:rhel-8 !platform:centos-8]
python3-pyyaml [platform:rpm !platform:rhel-7 !platform:centos-7]
python2-dnf [platform:fedora]

# For SELinux

+ 6
- 0
doc/source/roles/role-tripleo-container-manage.rst View File

@@ -0,0 +1,6 @@
===============================
Role - tripleo-container-manage
===============================

.. ansibleautoplugin::
:role: tripleo_ansible/roles/tripleo-container-manage

+ 3
- 0
test-requirements.txt View File

@@ -1,2 +1,5 @@
pre-commit # MIT
netaddr # BSD
mock>=2.0.0 # BSD
stestr>=2.0.0 # Apache-2.0
oslotest>=3.2.0 # Apache-2.0

+ 14
- 2
tox.ini View File

@@ -26,8 +26,13 @@ setenv =
PYTHONWARNINGS=ignore:DEPRECATION::pip._internal.cli.base_command,ignore::UserWarning
PIP_DISABLE_PIP_VERSION_CHECK=1
sitepackages = True
deps = -r {toxinidir}/test-requirements.txt
whitelist_externals = bash
deps =
-r {toxinidir}/test-requirements.txt
-r {toxinidir}/ansible-requirements.txt
commands = stestr run {posargs}
whitelist_externals =
bash
tox

[testenv:bindep]
# Do not install any requirements. We want this to be fast and work even if
@@ -123,3 +128,10 @@ commands =
echo -e '\n\nNo molecule tests have been executed\nSee https://docs.openstack.org/tripleo-ansible/latest/contributing.html#local-testing-of-new-roles\n\n'; \
fi"
{[testenv:linters]commands}

[testenv:modules]
deps=
{[testenv:linters]deps}
commands =
bash -c "cd {toxinidir}/tripleo_ansible/ansible_plugins/tests; molecule test --all;"
{[testenv:linters]commands}

+ 227
- 0
tripleo_ansible/ansible_plugins/filter/helpers.py View File

@@ -0,0 +1,227 @@
#!/usr/bin/env python
# Copyright 2019 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import ast
import json
import six

from collections import OrderedDict
from operator import itemgetter


# cmp() doesn't exist on python3
if six.PY3:
def cmp(a, b):
return 0 if a == b else 1


class FilterModule(object):
def filters(self):
return {
'singledict': self.singledict,
'subsort': self.subsort,
'needs_delete': self.needs_delete,
'haskey': self.haskey,
'list_of_keys': self.list_of_keys,
'container_exec_cmd': self.container_exec_cmd
}

def subsort(self, dict_to_sort, attribute, null_value=0):
"""Sort a hash from a sub-element.

This filter will return an dictionary ordered by the attribute
part of each item.
"""
for k, v in dict_to_sort.items():
if attribute not in v:
dict_to_sort[k][attribute] = null_value

data = {}
for d in dict_to_sort.items():
if d[1][attribute] not in data:
data[d[1][attribute]] = []
data[d[1][attribute]].append({d[0]: d[1]})

sorted_list = sorted(
data.items(),
key=lambda x: x[0]
)
ordered_dict = {}
for o, v in sorted_list:
ordered_dict[o] = v
return ordered_dict

def singledict(self, list_to_convert):
"""Generate a single dictionary from a list of dictionaries.

This filter will return a single dictionary from a list of
dictionaries.
"""
return_dict = {}
for i in list_to_convert:
return_dict.update(i)
return return_dict

def needs_delete(self, container_infos, config, config_id):
"""Returns a list of containers which need to be removed.

This filter will check which containers need to be removed for these
reasons: no config_data, updated config_data or container not
part of the global config.
"""
to_delete = []
to_skip = []
installed_containers = []
for c in container_infos:
c_name = c['Name']
installed_containers.append(c_name)

# Don't delete containers not managed by tripleo-ansible
if (c['Config']['Labels'] is None
or c['Config']['Labels'].get(
'managed_by') != 'tripleo_ansible'):
to_skip += [c_name]
continue

# Only remove containers managed in this config_id
if (c['Config']['Labels'] is None
or c['Config']['Labels'].get('config_id') != config_id):
to_skip += [c_name]
continue

# Remove containers with no config_data
# e.g. broken config containers
if (c['Config']['Labels'] is not None
and 'config_data' not in c['Config']['Labels']):
to_delete += [c_name]
continue

# Remove containers managed by tripleo-ansible that aren't in
# config e.g. containers not needed anymore and removed by an
# upgrade. Note: we don't cleanup paunch-managed containers.
if c_name not in config:
to_delete += [c_name]
continue

for c_name, config_data in config.items():
# don't try to remove a container which doesn't exist
if c_name not in installed_containers:
continue

# already tagged to be removed
if c_name in to_delete:
continue

if c_name in to_skip:
continue

# Remove containers managed by tripleo-ansible when config_data
# changed. Since we already cleaned the containers not in config,
# this check needs to be in that loop.
# e.g. new TRIPLEO_CONFIG_HASH during a minor update
try:
c_facts = [c['Config']['Labels']['config_data']
for c in container_infos if c_name == c['Name']]
except KeyError:
continue

# Build c_facts so it can be compared later with config_data
c_facts = ast.literal_eval(c_facts[0]) if (
len(c_facts)) == 1 else dict()

if cmp(c_facts, config_data) != 0:
to_delete += [c_name]

return to_delete

def haskey(self, batched_container_data, attribute, value=None,
reverse=False, any=False):
"""Return container data with a specific config key.

This filter will take a list of dictionaries (batched_container_data)
and will return the dictionnaries which have a certain key given
in parameter with 'attribute'.
If reverse is set to True, the returned list won't contain dictionaries
which have the attribute.
If any is set to True, the returned list will match any value in
the list of values for "value" parameter which has to be a list.
"""
return_list = []
for container in batched_container_data:
for k, v in json.loads(json.dumps(container)).items():
if attribute in v and not reverse:
if value is None:
return_list.append({k: v})
else:
if isinstance(value, list) and any:
if v[attribute] in value:
return_list.append({k: v})
elif any:
raise TypeError("value has to be a list if any is "
"set to True.")
else:
if v[attribute] == value:
return_list.append({k: v})
if attribute not in v and reverse:
return_list.append({k: v})
return return_list

def list_of_keys(self, keys_to_list):
"""Return a list of keys from a list of dictionaries.

This filter takes in input a list of dictionaries and for each of them
it will add the key to list_of_keys and returns it.
"""
list_of_keys = []
for i in keys_to_list:
for k, v in i.items():
list_of_keys.append(k)
return list_of_keys

def list_or_dict_arg(self, data, cmd, key, arg):
"""Utility to build a command and its argument with list or dict data.

The key can be a dictionary or a list, the returned arguments will be
a list where each item is the argument name and the item data.
"""
if key not in data:
return
value = data[key]
if isinstance(value, dict):
for k, v in sorted(value.items()):
if v:
cmd.append('%s=%s=%s' % (arg, k, v))
elif k:
cmd.append('%s=%s' % (arg, k))
elif isinstance(value, list):
for v in value:
if v:
cmd.append('%s=%s' % (arg, v))

def container_exec_cmd(self, data, cli='podman'):
"""Return a list of all the arguments to execute a container exec.

This filter takes in input the container exec data and the cli name
to return the full command in a list of arguments that will be used
by Ansible command module.
"""
cmd = [cli, 'exec']
cmd.append('--user=%s' % data.get('user', 'root'))
if 'privileged' in data:
cmd.append('--privileged=%s' % str(data['privileged']).lower())
self.list_or_dict_arg(data, cmd, 'environment', '--env')
cmd.extend(data['command'])
return cmd

+ 601
- 53
tripleo_ansible/ansible_plugins/modules/podman_container.py View File

@@ -21,9 +21,10 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type

import json
from distutils.version import LooseVersion
import yaml

from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_bytes, to_native

ANSIBLE_METADATA = {
@@ -101,7 +102,7 @@ options:
(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: str
type: path
blkio_weight:
description:
- Block IO weight (relative weight) accepts a weight value between 10 and
@@ -128,7 +129,22 @@ options:
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
choices:
- default
- disabled
cidfile:
description:
- Write the container ID to the file
@@ -154,9 +170,17 @@ options:
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
- 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:
@@ -180,6 +204,11 @@ options:
- 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
@@ -190,27 +219,28 @@ options:
- 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: str
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: str
type: list
device_read_iops:
description:
- Limit read rate (IO per second) from a device
(e.g. device-read-iops /dev/sda:1000)
type: str
type: list
device_write_bps:
description:
- Limit write rate (bytes per second) to a device
(e.g. device-write-bps /dev/sda:1mb)
type: str
type: list
device_write_iops:
description:
- Limit write rate (IO per second) to a device
(e.g. device-write-iops /dev/sda:1000)
type: str
type: list
dns:
description:
- Set custom DNS servers
@@ -240,6 +270,11 @@ options:
description:
- Read in a line delimited file of environment variables
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
@@ -271,7 +306,7 @@ options:
group_add:
description:
- Add additional groups to run as
type: str
type: list
healthcheck:
description:
- Set or alter a healthcheck command for a container.
@@ -356,6 +391,7 @@ options:
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:
@@ -385,10 +421,12 @@ options:
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:
@@ -396,6 +434,7 @@ options:
(--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:
@@ -419,7 +458,8 @@ options:
* 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: str
type: list
elements: str
aliases:
- net
no_hosts:
@@ -512,7 +552,8 @@ options:
security_opt:
description:
- Security Options. For example security_opt "seccomp=unconfined"
type: str
type: list
elements: str
shm_size:
description:
- Size of /dev/shm. The format is <number><unit>. number must be greater
@@ -530,7 +571,7 @@ options:
stop_signal:
description:
- Signal to stop a container. Default is SIGTERM.
type: str
type: int
stop_timeout:
description:
- Timeout (in seconds) to stop a container. Default is 10.
@@ -813,9 +854,12 @@ class PodmanModuleParams:
params {dict} -- dictionary of module parameters

"""
def __init__(self, action, params):

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.
@@ -828,8 +872,8 @@ class PodmanModuleParams:
if self.action in ['create', 'run']:
cmd = [self.action, '--name', self.params['name']]
all_param_methods = [func for func in dir(self)
if callable(getattr(self, func)) and
func.startswith("addparam")]
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])
@@ -853,19 +897,26 @@ class PodmanModuleParams:
cmd = ['rm', '-f', self.params['name']]
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]

def addparam_detach(self, c):
return c + ['--detach=%s' % self.params['detach']]

def addparam_etc_hosts(self, c):
for host_ip in self.params['etc_hosts'].items():
c += ['--add-host', ':'.join(host_ip)]
return c
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_annotation(self, c):
for annotate in self.params['annotation'].items():
c += ['--annotation', '='.join(annotate)]
return c

def addparam_authfile(self, c):
return c + ['--authfile', self.params['authfile']]

def addparam_blkio_weight(self, c):
return c + ['--blkio-weight', self.params['blkio_weight']]

@@ -884,6 +935,14 @@ class PodmanModuleParams:
c += ['--cap-drop', cap_drop]
return c

def addparam_cgroups(self, c):
self.check_version('--cgroups', minv='1.6.0')
return c + ['--cgroups=%s' % self.params['cgroups']]

def addparam_cgroupns(self, c):
self.check_version('--cgroupns', minv='1.6.2')
return c + ['--cgroupns=%s' % self.params['cgroupns']]

def addparam_cgroup_parent(self, c):
return c + ['--cgroup-parent', self.params['cgroup_parent']]

@@ -896,6 +955,9 @@ class PodmanModuleParams:
def addparam_cpu_period(self, c):
return c + ['--cpu-period', self.params['cpu_period']]

def addparam_cpu_rt_period(self, c):
return c + ['--cpu-rt-period', self.params['cpu_rt_period']]

def addparam_cpu_rt_runtime(self, c):
return c + ['--cpu-rt-runtime', self.params['cpu_rt_runtime']]

@@ -911,23 +973,36 @@ class PodmanModuleParams:
def addparam_cpuset_mems(self, c):
return c + ['--cpuset-mems', self.params['cpuset_mems']]

def addparam_detach(self, c):
return c + ['--detach=%s' % self.params['detach']]

def addparam_detach_keys(self, c):
return c + ['--detach-keys', self.params['detach_keys']]

def addparam_device(self, c):
return c + ['--device', self.params['device']]
for dev in self.params['device']:
c += ['--device', dev]
return c

def addparam_device_read_bps(self, c):
return c + ['--device-read-bps', self.params['device_read_bps']]
for dev in self.params['device_read_bps']:
c += ['--device-read-bps', dev]
return c

def addparam_device_read_iops(self, c):
return c + ['--device-read-iops', self.params['device_read_iops']]
for dev in self.params['device_read_iops']:
c += ['--device-read-iops', dev]
return c

def addparam_device_write_bps(self, c):
return c + ['--device-write-bps', self.params['device_write_bps']]
for dev in self.params['device_write_bps']:
c += ['--device-write-bps', dev]
return c

def addparam_device_write_iops(self, c):
return c + ['--device-write-iops', self.params['device_write_iops']]
for dev in self.params['device_write_iops']:
c += ['--device-write-iops', dev]
return c

def addparam_dns(self, c):
return c + ['--dns', ','.join(self.params['dns'])]
@@ -951,6 +1026,15 @@ class PodmanModuleParams:
def addparam_env_file(self, c):
return c + ['--env-file', self.params['env_file']]

def addparam_env_host(self, c):
self.check_version('--env-host', minv='1.5.0')
return c + ['--env-host=%s' % self.params['env_host']]

def addparam_etc_hosts(self, c):
for host_ip in self.params['etc_hosts'].items():
c += ['--add-host', ':'.join(host_ip)]
return c

def addparam_expose(self, c):
for exp in self.params['expose']:
c += ['--expose', exp]
@@ -960,7 +1044,9 @@ class PodmanModuleParams:
return c + ['--gidmap', self.params['gidmap']]

def addparam_group_add(self, c):
return c + ['--group-add', self.params['group_add']]
for g in self.params['group_add']:
c += ['--group-add', g]
return c

def addparam_healthcheck(self, c):
return c + ['--healthcheck', self.params['healthcheck']]
@@ -1010,7 +1096,8 @@ class PodmanModuleParams:

def addparam_label(self, c):
for label in self.params['label'].items():
c += ['--label', '='.join(label)]
c += ['--label', b'='.join([to_bytes(l, errors='surrogate_or_strict')
for l in label])]
return c

def addparam_label_file(self, c):
@@ -1038,7 +1125,7 @@ class PodmanModuleParams:
return c + ['--mount', self.params['mount']]

def addparam_network(self, c):
return c + ['--network', self.params['network']]
return c + ['--network', ",".join(self.params['network'])]

def addparam_no_hosts(self, c):
return c + ['--no-hosts=%s' % self.params['no_hosts']]
@@ -1085,7 +1172,9 @@ class PodmanModuleParams:
return c + ['--rootfs=%s' % self.params['rootfs']]

def addparam_security_opt(self, c):
return c + ['--security-opt', self.params['security_opt']]
for secopt in self.params['security_opt']:
c += ['--security-opt', secopt]
return c

def addparam_shm_size(self, c):
return c + ['--shm-size', self.params['shm_size']]
@@ -1161,6 +1250,436 @@ class PodmanModuleParams:
return c + self.params['cmd_args']


class PodmanDefaults:
def __init__(self, module, podman_version):
self.module = module
self.version = podman_version
self.defaults = {
"blkio_weight": 0,
"cgroups": "default",
"cgroup_parent": "",
"cidfile": "",
"cpus": 0.0,
"cpu_shares": 0,
"cpu_quota": 0,
"cpu_period": 0,
"cpu_rt_runtime": 0,
"cpu_rt_period": 0,
"cpuset_cpus": "",
"cpuset_mems": "",
"detach": True,
"device": [],
"env_host": False,
"etc_hosts": {},
"group_add": [],
"ipc": "",
"kernelmemory": "0",
"log_driver": "k8s-file",
"memory": "0",
"memory_swap": "0",
"memory_reservation": "0",
# "memory_swappiness": -1,
"no_hosts": False,
# libpod issue with networks in inspection
"network": ["default"],
"oom_score_adj": 0,
"pid": "",
"privileged": False,
"rm": False,
"security_opt": [],
"stop_signal": 15,
"tty": False,
"user": "",
"uts": "",
"volume": [],
"workdir": "/",
}

def default_dict(self):
# make here any changes to self.defaults related to podman version
return self.defaults


class PodmanContainerDiff:
def __init__(self, module, info, podman_version):
self.module = module
self.version = podman_version
self.default_dict = None
self.info = yaml.safe_load(json.dumps(info).lower())
self.params = self.defaultize()
self.diff = {'before': {}, 'after': {}}
self.non_idempotent = {
'env_file',
'env_host',
"ulimit", # Defaults depend on user and platform, impossible to guess
}

def defaultize(self):
params_with_defaults = {}
self.default_dict = PodmanDefaults(
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_annotation(self):
before = self.info['config']['annotations'] or {}
after = before.copy()
if self.module.params['annotation'] is not None:
after.update(self.params['annotation'])
return self._diff_update_and_compare('annotation', before, after)

def diffparam_env_host(self):
# It's impossible to get from inspest, recreate it if not default
before = False
after = self.params['env_host']
return self._diff_update_and_compare('env_host', before, after)

def diffparam_blkio_weight(self):
before = self.info['hostconfig']['blkioweight']
after = self.params['blkio_weight']
return self._diff_update_and_compare('blkio_weight', before, after)

def diffparam_blkio_weight_device(self):
before = self.info['hostconfig']['blkioweightdevice']
if before == [] and self.module.params['blkio_weight_device'] is None:
after = []
else:
after = self.params['blkio_weight_device']
return self._diff_update_and_compare('blkio_weight_device', before, after)

def diffparam_cap_add(self):
before = self.info['effectivecaps'] or []
after = []
if self.module.params['cap_add'] is not None:
after += ["cap_" + i.lower()
for i in self.module.params['cap_add']]
after += before
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('cap_add', before, after)

def diffparam_cap_drop(self):
before = self.info['effectivecaps'] or []
after = before[:]
if self.module.params['cap_drop'] is not None:
for c in ["cap_" + i.lower() for i in self.module.params['cap_drop']]:
if c in after:
after.remove(c)
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('cap_drop', before, after)

def diffparam_cgroup_parent(self):
before = self.info['hostconfig']['cgroupparent']
after = self.params['cgroup_parent']
return self._diff_update_and_compare('cgroup_parent', before, after)

def diffparam_cgroups(self):
# Cgroups output is not supported in all versions
if 'cgroups' in self.info['hostconfig']:
before = self.info['hostconfig']['cgroups']
after = self.params['cgroups']
return self._diff_update_and_compare('cgroups', before, after)
return False

def diffparam_cidfile(self):
before = self.info['hostconfig']['containeridfile']
after = self.params['cidfile']
return self._diff_update_and_compare('cidfile', before, after)

def diffparam_command(self):
before = self.info['config']['cmd']
after = self.params['command']
if isinstance(after, str):
after = [i.lower() for i in after.split()]
elif isinstance(after, list):
after = [i.lower() for i in after]
return self._diff_update_and_compare('command', before, after)

def diffparam_conmon_pidfile(self):
before = self.info['conmonpidfile']
if self.module.params['conmon_pidfile'] is None:
after = before
else:
after = self.params['conmon_pidfile']
return self._diff_update_and_compare('conmon_pidfile', before, after)

def diffparam_cpu_period(self):
before = self.info['hostconfig']['cpuperiod']
after = self.params['cpu_period']
return self._diff_update_and_compare('cpu_period', before, after)

def diffparam_cpu_rt_period(self):
before = self.info['hostconfig']['cpurealtimeperiod']
after = self.params['cpu_rt_period']
return self._diff_update_and_compare('cpu_rt_period', before, after)

def diffparam_cpu_rt_runtime(self):
before = self.info['hostconfig']['cpurealtimeruntime']
after = self.params['cpu_rt_runtime']
return self._diff_update_and_compare('cpu_rt_runtime', before, after)

def diffparam_cpu_shares(self):
before = self.info['hostconfig']['cpushares']
after = self.params['cpu_shares']
return self._diff_update_and_compare('cpu_shares', before, after)

def diffparam_cpus(self):
before = int(self.info['hostconfig']['nanocpus']) / 1000000000
after = self.params['cpus']
return self._diff_update_and_compare('cpus', before, after)

def diffparam_cpuset_cpus(self):
before = self.info['hostconfig']['cpusetcpus']
after = self.params['cpuset_cpus']
return self._diff_update_and_compare('cpuset_cpus', before, after)

def diffparam_cpuset_mems(self):
before = self.info['hostconfig']['cpusetmems']
after = self.params['cpuset_mems']
return self._diff_update_and_compare('cpuset_mems', before, after)

def diffparam_device(self):
before = [":".join([i['pathonhost'], i['pathincontainer']])
for i in self.info['hostconfig']['devices']]
after = [":".join(i.split(":")[:2]) for i in self.params['device']]
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('devices', before, after)

def diffparam_device_read_bps(self):
before = self.info['hostconfig']['blkiodevicereadbps'] or []
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
after = self.params['device_read_bps'] or []
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('device_read_bps', before, after)

def diffparam_device_read_iops(self):
before = self.info['hostconfig']['blkiodevicereadiops'] or []
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
after = self.params['device_read_iops'] or []
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('device_read_iops', before, after)

def diffparam_device_write_bps(self):
before = self.info['hostconfig']['blkiodevicewritebps'] or []
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
after = self.params['device_write_bps'] or []
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('device_write_bps', before, after)

def diffparam_device_write_iops(self):
before = self.info['hostconfig']['blkiodevicewriteiops'] or []
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
after = self.params['device_write_iops'] or []
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('device_write_iops', before, after)

# Limited idempotency, it can't guess default values
def diffparam_env(self):
env_before = self.info['config']['env'] or {}
before = {i.split("=")[0]: i.split("=")[1] for i in env_before}
after = before.copy()
if self.params['env']:
after.update({
str(k).lower(): str(v).lower()
for k, v in self.params['env'].items()
})
return self._diff_update_and_compare('env', before, after)

def diffparam_etc_hosts(self):
if self.info['hostconfig']['extrahosts']:
before = dict([i.split(":") for i in self.info['hostconfig']['extrahosts']])
else:
before = {}
after = self.params['etc_hosts']
return self._diff_update_and_compare('etc_hosts', before, after)

def diffparam_group_add(self):
before = self.info['hostconfig']['groupadd']
after = self.params['group_add']
return self._diff_update_and_compare('group_add', before, after)

# Because of hostname is random generated, this parameter has partial idempotency only.
def diffparam_hostname(self):
before = self.info['config']['hostname']
after = self.params['hostname'] or before
return self._diff_update_and_compare('hostname', before, after)

def diffparam_image(self):
before = self.info['config']['image'].replace(
"docker.io/library/", "").replace(
"docker.io/", "").replace(
":latest", "")
after = self.params['image'].replace(
"docker.io/library/", "").replace(
"docker.io/", "").replace(
":latest", "")
return self._diff_update_and_compare('image', before, after)

def diffparam_ipc(self):
before = self.info['hostconfig']['ipcmode']
after = self.params['ipc']
return self._diff_update_and_compare('ipc', before, after)

def diffparam_label(self):
before = self.info['config']['labels'] or {}
after = before.copy()
if self.params['label']:
after.update({
str(k).lower(): str(v).lower()
for k, v in self.params['label'].items()
})
return self._diff_update_and_compare('label', before, after)

def diffparam_log_driver(self):
before = self.info['hostconfig']['logconfig']['type']
after = self.params['log_driver']
return self._diff_update_and_compare('log_driver', before, after)

# Parameter has limited idempotency, unable to guess the default log_path
def diffparam_log_opt(self):
before = self.info['logpath']
if self.module.params['log_opt'] in [None, '']:
after = before
else:
after = self.params['log_opt'].split("=")[1]
return self._diff_update_and_compare('log_opt', before, after)

def diffparam_memory(self):
before = str(self.info['hostconfig']['memory'])
after = self.params['memory']
return self._diff_update_and_compare('memory', before, after)

def diffparam_memory_swap(self):
# By default it's twice memory parameter
before = str(self.info['hostconfig']['memoryswap'])
after = self.params['memory_swap']
if (self.module.params['memory_swap'] is None
and self.params['memory'] != 0
and self.params['memory'].isdigit()):
after = str(int(self.params['memory']) * 2)
return self._diff_update_and_compare('memory_swap', before, after)

def diffparam_memory_reservation(self):
before = str(self.info['hostconfig']['memoryreservation'])
after = self.params['memory_reservation']
return self._diff_update_and_compare('memory_reservation', before, after)

def diffparam_network(self):
before = [self.info['hostconfig']['networkmode']]
after = self.params['network']
return self._diff_update_and_compare('network', before, after)

def diffparam_no_hosts(self):
before = not bool(self.info['hostspath'])
after = self.params['no_hosts']
if self.params['network'] == ['none']:
after = True
return self._diff_update_and_compare('no_hosts', before, after)

def diffparam_oom_score_adj(self):
before = self.info['hostconfig']['oomscoreadj']
after = self.params['oom_score_adj']
return self._diff_update_and_compare('oom_score_adj', before, after)

def diffparam_privileged(self):
before = self.info['hostconfig']['privileged']
after = self.params['privileged']
return self._diff_update_and_compare('privileged', before, after)

def diffparam_pid(self):
before = self.info['hostconfig']['pidmode']
after = self.params['pid']
return self._diff_update_and_compare('pid', before, after)

def diffparam_rm(self):
before = self.info['hostconfig']['autoremove']
after = self.params['rm']
return self._diff_update_and_compare('rm', before, after)

def diffparam_security_opt(self):
before = self.info['hostconfig']['securityopt']
after = self.params['security_opt']
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('security_opt', before, after)

def diffparam_stop_signal(self):
before = self.info['config']['stopsignal']
after = self.params['stop_signal']
return self._diff_update_and_compare('stop_signal', before, after)

def diffparam_tty(self):
before = self.info['config']['tty']
after = self.params['tty']
return self._diff_update_and_compare('tty', before, after)

def diffparam_user(self):
before = self.info['config']['user']
if self.module.params['user'] is None and before:
after = before
else:
after = self.params['user']
return self._diff_update_and_compare('user', before, after)

def diffparam_uts(self):
before = self.info['hostconfig']['utsmode']
after = self.params['uts']
return self._diff_update_and_compare('uts', before, after)

def diffparam_volume(self):
before = self.info['mounts']
if before:
volumes = []
for m in before:
if m['type'] == 'volume':
volumes.append([m['name'], m['destination']])
else:
volumes.append([m['source'], m['destination']])
before = [":".join(v) for v in volumes]
# Ignore volumes option for idempotency
after = [":".join(v.split(":")[:2]) for v in self.params['volume']]
before, after = sorted(list(set(before))), sorted(list(set(after)))
return self._diff_update_and_compare('volume', before, after)

def diffparam_volumes_from(self):
before = self.info['hostconfig']['volumesfrom'] or []
after = self.params['volumes_from'] or []
return self._diff_update_and_compare('volumes_from', before, after)

def diffparam_workdir(self):
before = self.info['config']['workingdir']
after = self.params['workdir']
return self._diff_update_and_compare('workdir', 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


def ensure_image_exists(module, image):
"""If image is passed, ensure it exists, if not - pull it or fail.

@@ -1205,6 +1724,9 @@ class PodmanContainer:
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):
@@ -1214,9 +1736,17 @@ class PodmanContainer:
@property
def different(self):
"""Check if container is different."""
# TODO(sshnaidm): implement difference calculation between input vars
# and current container to understand if we need to recreate it
return True
diffcheck = PodmanContainerDiff(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

@property
def running(self):
@@ -1234,6 +1764,13 @@ class PodmanContainer:
[self.module.params['executable'], b'container', b'inspect', self.name])
return json.loads(out)[0] if rc == 0 else {}

def _get_podman_version(self):
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 container.

@@ -1241,19 +1778,25 @@ class PodmanContainer:
action {str} -- action to perform - start, create, stop, run,
delete
"""
b_command = PodmanModuleParams(action, self.module.params
b_command = PodmanModuleParams(action,
self.module.params,
self.version,
self.module,
).construct_command_from_params()
self.module.log("PODMAN-CONTAINER-DEBUG: " +
"%s" % " ".join([to_native(i) for i in b_command]))
rc, out, err = self.module.run_command(
[self.module.params['executable'], b'container'] + b_command,
expand_user_and_vars=False)
self.stdout = out
self.stderr = err
if rc != 0:
self.module.fail_json(
msg="Can't %s container %s" % (action, self.name),
stdout=out, stderr=err)
full_cmd = " ".join([self.module.params['executable']]
+ [to_native(i) for i in b_command])
self.module.log("PODMAN-CONTAINER-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'container'] + b_command,
expand_user_and_vars=False)
self.stdout = out
self.stderr = err
if rc != 0:
self.module.fail_json(
msg="Can't %s container %s" % (action, self.name),
stdout=out, stderr=err)

def run(self):
"""Run the container."""
@@ -1326,11 +1869,15 @@ class PodmanManager:
changed {bool} -- whether any action was performed
(default: {True})
"""
facts = self.container.get_info()
facts = self.container.get_info() if changed else self.container.info
out, err = self.container.stdout, self.container.stderr
self.results.update({'changed': changed, 'container': facts,
'ansible_facts': {'podman_container': facts}},
stdout=out, stderr=err)
'podman_actions': self.container.actions},
stdout=out, stderr=err)
if self.container.diff:
self.results.update({'diff': self.container.diff})
if self.module.params['debug']:
self.results.update({'podman_version': self.container.version})
self.module.exit_json(**self.results)

def make_started(self):
@@ -1347,7 +1894,7 @@ class PodmanManager:
self.results['actions'].append('restarted %s' %
self.container.name)
self.update_container_result()
self.module.exit_json(**self.results)
self.update_container_result(changed=False)
elif not self.container.exists:
self.container.run()
self.results['actions'].append('started %s' % self.container.name)
@@ -1365,7 +1912,7 @@ class PodmanManager:
def make_stopped(self):
"""Run actions if desired state is 'stopped'."""
if not self.container.exists and not self.image:
self.module.fail_json(msg='Cannot create container when image' +
self.module.fail_json(msg='Cannot create container when image'
' is not specified!')
if not self.container.exists:
self.container.create()
@@ -1387,7 +1934,7 @@ class PodmanManager:
self.results['actions'].append('deleted %s' % self.container.name)
self.results.update({'changed': True})
self.results.update({'container': {},
'ansible_facts': {'podman_container': {}}})
'podman_actions': self.container.actions})
self.module.exit_json(**self.results)

def execute(self):
@@ -1400,7 +1947,7 @@ class PodmanManager:
}
process_action = states_map[self.state]
process_action()
self.module.fail_json(msg="Unexpected logic error happened, " +
self.module.fail_json(msg="Unexpected logic error happened, "
"please contact maintainers ASAP!")


@@ -1411,6 +1958,7 @@ def main():
['no_hosts', 'etc_hosts'],

),
supports_check_mode=True,
)
# work on input vars
if module.params['state'] in ['started', 'present'] and \

+ 285
- 293
tripleo_ansible/ansible_plugins/modules/podman_container_info.py View File

@@ -13,54 +13,43 @@
# License for the specific language governing permissions and limitations
# under the License.

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import, division, print_function
__metaclass__ = type

import json
import yaml

from ansible.module_utils.basic import AnsibleModule
import ansible.module_utils.six as six

six.add_metaclass(type)

ANSIBLE_METADATA = {
'metadata_version': '1.0',
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}

DOCUMENTATION = """
---
module: podman_container_info
author:
- Sagi Shnaidman (@sshnaidm)
- Emilien Macchi (@EmilienM)
version_added: '2.9'
version_added: '2.10'
short_description: Gather facts about containers using podman
notes:
- Podman may required elevated privileges in order to run properly.
- Podman may require elevated privileges in order to run properly.
description:
- Gather facts about containers using C(podman)
requirements:
- "Podman installed on host"
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 facts about. If no name is given
return facts about all containers.
type: list
elements: str

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 = """
- name: Gather facts for all containers
podman_container_info:
@@ -75,273 +64,274 @@ EXAMPLES = """
- redis
- web1
"""

RETURN = """
containers:
description: Facts from all or specificed containers
returned: always
type: dict
type: list
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": [
{
"Id": "c5c39f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f",
"Created": "2019-10-01T12:51:00.233106443Z",
"Path": "dumb-init",
"Args": [
"--single-child",
"--",
"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"
"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
}
},
"StopSignal": 15
},
"HostConfig": {
"Binds": [],
"ContainerIDFile": "",
"LogConfig": {
"Type": "k8s-file",
"Config": 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"
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "",
"MaximumRetryCount": 0
"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": ""
},
"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
"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"
],
"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
"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"
},
{
"Name": "RLIMIT_NPROC",
"Soft": 1048576,
"Hard": 1048576
}
],
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0
}
}
]
"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
from ansible.module_utils.basic import AnsibleModule


def get_containers_facts(module, executable, name):
if not name:
@@ -353,30 +343,32 @@ def get_containers_facts(module, executable, name):
command = [executable, 'container', 'inspect']
command.extend(name)
rc, out, err = module.run_command(command)
if not out or rc != 0:
if rc != 0:
module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all containers', 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=yaml.safe_load(DOCUMENTATION)['options'],
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)
name = module.params['name']
executable = module.get_bin_path(module.params['executable'], required=True)

inspect_results, out, err = get_containers_facts(module, executable, name)

results = dict(
changed=False,
containers=inspect_results,
ansible_facts=dict(podman_containers=inspect_results),
stdout=out,
stderr=err
)
results = {
"changed": False,
"containers": inspect_results,
"stderr": err
}

module.exit_json(**results)


+ 30
- 34
tripleo_ansible/ansible_plugins/modules/podman_volume_info.py View File

@@ -15,17 +15,11 @@
# License for the specific language governing permissions and limitations
# under the License.


from __future__ import absolute_import, division, print_function

import json
import yaml

from ansible.module_utils.basic import AnsibleModule

__metaclass__ = type

ANSIBLE_METADATA = {
'metadata_version': '1.0',
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
@@ -34,7 +28,7 @@ DOCUMENTATION = """
module: podman_volume_info
author:
- "Sagi Shnaidman (@sshnaidm)"
version_added: '2.9'
version_added: '2.10'
short_description: Gather info about podman volumes
notes: []
description:
@@ -65,21 +59,22 @@ RETURN = """
volumes:
description: Facts from all or specified volumes
returned: always
type: dict
sample:
[
{
"name": "testvolume",
"labels": {},
"mountPoint": "/home/ansible/.local/share/testvolume/_data",
"driver": "local",
"options": {},
"scope": "local"
}
]

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']
@@ -88,31 +83,32 @@ def get_volume_info(module, executable, name):
else:
command.append("--all")
rc, out, err = module.run_command(command)
if not out or rc != 0:
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=yaml.safe_load(DOCUMENTATION)['options'],
argument_spec=dict(
executable=dict(type='str', default='podman'),
name=dict(type='str')
),
supports_check_mode=True,
)

executable = module.params['executable']
name = module.params['name']