Compare commits

...

155 Commits
3.6.0 ... 3.8.4

Author SHA1 Message Date
Felix Fontein
f7276b152b Release 3.8.4. 2022-01-11 06:41:56 +01:00
Felix Fontein
5e503bfcc7 Prepare 3.8.4 release. 2022-01-10 23:06:20 +01:00
patchback[bot]
d2b4151b7a fix alternatives parsing when they are part of a group (#3976) (#4020)
* fix alternatives parsing when they are part of a group

* add changelog fragment

Co-authored-by: Guillaume Rousse <guillaume.rousse@renater.fr>
(cherry picked from commit a675afcba9)

Co-authored-by: Guillaume Rousse <guillomovitch@gmail.com>
2022-01-10 07:27:12 +01:00
patchback[bot]
57b73e0d5e Fix example code for flattened lookup (#4013) (#4015)
Co-authored-by: Lee Garrett <lgarrett@rocketjump.eu>
(cherry picked from commit d19ab93faf)

Co-authored-by: Lee Garrett <leegarrett@users.noreply.github.com>
2022-01-09 12:29:19 +01:00
patchback[bot]
5f47d47f27 Restrict PyNaCL to 1.4.x on RHEL8 when using Python 3.6 (#4006) (#4009)
* Restrict PyNaCL to 1.4.x on RHEL8 when using Python 3.6.

* Fix typo.

(cherry picked from commit 77a930cf6b)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-08 15:07:00 +01:00
patchback[bot]
7e2f20f482 Fix 2.9 unit tests (#4002) (#4004)
* Fix 2.9 unit tests.

* Another try.

(cherry picked from commit 26a91e811f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-08 12:40:05 +01:00
patchback[bot]
3023312334 Fix comment. (#3993) (#3994)
(cherry picked from commit a6a8cd02b6)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-06 15:10:46 +01:00
Felix Fontein
fe3de232f0 Fix BOTMETA and corresponding sanity test (#3989) (#3991)
* Fix BOTMETA and authors mistakes.

* Fix BOTMETA sanity test regex.

(cherry picked from commit 11205eefee)
2022-01-06 06:56:10 +01:00
patchback[bot]
2024dc37af Use vendored copy of distutils.version. (#3984) (#3986)
(cherry picked from commit cf7a33356c)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-05 22:05:06 +01:00
Felix Fontein
b8469a5c28 Re-enable snap tests (#3967) (#3982)
* Re-enable snap tests.

* Skip tests on RHEL 8.2 and 8.3.

* Refactor snap setup.

* Try to simplify setup.

(cherry picked from commit bb78d98f8f)
2022-01-05 18:12:03 +01:00
patchback[bot]
2a0ec9c572 Get rid of distutils.spawn and distutils.util (#3934) (#3973)
* Replace distutils.spawn.find_executable.

* Replace distutils.util.strtobool.

(cherry picked from commit 77b7b4f75b)

Co-authored-by: Felix Fontein <felix@fontein.de>
2022-01-04 07:27:02 +01:00
patchback[bot]
fa92f8efb0 slack - use UTF-8 charset in content-type header (#3933) (#3970)
* Use UTF-8 charset in content-type header

* Add changelog fragment

(cherry picked from commit a4ab85fd68)

Co-authored-by: bluikko <14869000+bluikko@users.noreply.github.com>
2022-01-03 19:43:47 +01:00
patchback[bot]
03e068e298 Restrict redis to < 4.1.0 for ansible-base 2.10. (#3955) (#3958)
(cherry picked from commit 3f2364574d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-12-27 21:17:06 +01:00
patchback[bot]
8f084ac065 fix scaleway_user_data (#3940) (#3953)
* fix  scaleway_user_data

scaleway_user_data put cloud-init valuer with 2 unexpected " (begin and end of value)

If Content-Type is not change , it's jsonify ( file module_utils/scaleway.py ligne 131 )

fix the probleme  when "Content-Type" is used instead of "Content-type"

* Create 3940_fix_contenttype_scaleway_user_data.yml

* Update changelogs/fragments/3940_fix_contenttype_scaleway_user_data.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 043b407412)

Co-authored-by: pmangin <96626847+pmangin@users.noreply.github.com>
2021-12-27 19:55:43 +01:00
Felix Fontein
e02568d28a Prepare for distutils.version being removed in Python 3.12 (#3936) (#3942)
* Prepare for distutils.version being removed in Python 2.12.

* Fix copy'n'paste error.

* Re-add Loose prefix.

* Fix Python version typos.

* Improve formulation.

* Move message into own line.

* Fix casing, now that the object is no longer called Version.

(cherry picked from commit a2f72be6c8)
2021-12-24 19:15:51 +01:00
patchback[bot]
73580d09e0 Fix filesystem tests (so they run on their own) (#3937) (#3938)
* Don't use loops for installing packages.

* Install util-linux-systemd on OpenSuSE so that findmnt is around.

(cherry picked from commit f34c454412)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-12-23 12:38:22 +01:00
Felix Fontein
093036a2ae Disable snap tests. (#3922) (#3924)
(cherry picked from commit 51838adf8c)
2021-12-20 10:59:11 +01:00
patchback[bot]
7f1de4869e Fix nrdp string arguments without an encoding (#3909) (#3911)
* Fix nrdp string arguments without an encoding

* added changelog fragment

Signed-off-by: Jesse Harris <zigford@gmail.com>

* Update changelogs/fragments/3909-nrdp_fix_string_args_without_encoding.yaml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 40ffd559ac)

Co-authored-by: Jesse Harris <zigford@gmail.com>
2021-12-17 22:13:50 +01:00
patchback[bot]
8df1b93531 jira - fixed 'body' dict key error (#3867) (#3913)
* fixed

* added changelog fragment

* improved fail output when placing JIRA API requests

* Update plugins/modules/web_infrastructure/jira.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit e6c773a4f3)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-12-17 22:13:21 +01:00
Felix Fontein
c055ea2bcc Next expected release is 3.8.4. 2021-12-14 07:57:25 +01:00
Felix Fontein
27c094a095 Release 3.8.3. 2021-12-14 07:06:14 +01:00
patchback[bot]
86a5b4f28c proxmox - fixing onboot parameter causing module failure when not defined (#3874) (#3901)
* fixing onboot parameter when not supplied

* adding changelog fragment

(cherry picked from commit 00a1152bb1)

Co-authored-by: Andrew Pantuso <ajpantuso@gmail.com>
2021-12-14 07:00:27 +01:00
patchback[bot]
4e14c429c7 Pass missing vlan-related options (flags, ingress, egress) to nmcli (#3896) (#3898)
* Pass missing vlan-related options (flags, ingress, egress) to nmcli

Signed-off-by: Jean-Francois Panisset <panisset@gmail.com>

* Follow style: comma on last parameter

Signed-off-by: Jean-Francois Panisset <panisset@gmail.com>

* PEP8 code style fix

Signed-off-by: Jean-Francois Panisset <panisset@gmail.com>

* add missing changelog fragment

Signed-off-by: Jean-Francois Panisset <panisset@gmail.com>
(cherry picked from commit 6cec2e2f58)

Co-authored-by: Jean-Francois Panisset <32653482+jfpanisset@users.noreply.github.com>
2021-12-13 21:59:32 +01:00
Felix Fontein
c34fb01462 Docs improvements. (#3893) (#3895)
(cherry picked from commit 59bbaeed77)
2021-12-12 11:46:34 +01:00
patchback[bot]
2086977af6 Update lxd connection to use all documented vars for options (#3798) (#3880)
* Update lxd connection to use documented vars

* Add a changelog fragment

* Add clarification to changelog description

* Shorten changelog fragment description

(cherry picked from commit 8f6866dba6)

Co-authored-by: Conner Crosby <conner@cavcrosby.tech>
2021-12-09 21:58:02 +01:00
patchback[bot]
587d221376 small docs update for timezone module (#3876) (#3877)
* small docs update for timezone module
fixes #3242

* Update plugins/modules/system/timezone.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit c14eafd63f)

Co-authored-by: Anatoly Pugachev <matorola@gmail.com>
2021-12-09 21:19:00 +01:00
Felix Fontein
5ed6b38477 Prepare 3.8.3 release. 2021-12-08 20:22:31 +01:00
patchback[bot]
2c0cfe4d16 update scaleway maintainers (#3472) (#3872)
* update scaleway maintainers

* Fix

* Fix sieben -> remyleone

Co-authored-by: scaleway-bot <github@scaleway.com>
(cherry picked from commit 80d650f60a)

Co-authored-by: Rémy Léone <remy.leone@gmail.com>
2021-12-08 20:21:02 +01:00
patchback[bot]
c344d20a9a interfaces_file: unit tests improved (#3863) (#3868)
* interfaces_file: fixed unit tests and added README, added test cases for #3862

* typo fix for interfaces_file unit tests README.md

Co-authored-by: Felix Fontein <felix@fontein.de>

* typo fix for interfaces_file unit tests README.md

Co-authored-by: Felix Fontein <felix@fontein.de>

* typo fix for interfaces_file unit tests README.md

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 0c828d9d01)

Co-authored-by: Roman Belyakovsky <ihryamzik@gmail.com>
2021-12-08 12:51:21 +01:00
patchback[bot]
53480b25c8 interfaces_file - fixed dup options bug (#3862) (#3865)
* interfaces_file - fixed dup options bug

* added changelog fragment

(cherry picked from commit 3dd5b0d343)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-12-08 05:54:44 +00:00
patchback[bot]
cfffaa5b6f Add stable-4 to nightly CI jobs; make stable-2 weekly. (#3852) (#3856)
(cherry picked from commit 727c9a4032)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-12-05 17:41:08 +01:00
patchback[bot]
726918930b opentelemetry: honour ignore errors (#3837) (#3846)
* opentelemetry: honour the ignore_errors

* fix-encoding-pragma

* Add changelog fragment

* opentelemetry: ignore produces unset span status

(cherry picked from commit ce6d0a749e)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-12-04 19:55:20 +01:00
patchback[bot]
614a84d0f2 pipx - fixed --include-apps bug (#3800) (#3817)
* pipx - fixed --include-apps bug

* added changelog fragment

* skipped freebsd for the last test

(cherry picked from commit bc619bcefc)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-11-30 08:32:48 +01:00
patchback[bot]
a11022e896 ansible_galaxy_install - minor documentation fix (#3804) (#3813)
* ansible_galaxy_install - minor documentation fix

* further adjustments

(cherry picked from commit 49bdc0f218)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-11-30 06:52:55 +01:00
Andrew Pantuso
491196937d Fixing ip address without mask bug (#3784) (#3810)
* change ip6 type to list of str and fix problem with setting addresses without netmask

* change ip6 type to list of str and fix problem with setting addresses without netmask

* Add changelog fragment

* add suggestions

* fix no mask using bug

* Make change independed from feature branch

(cherry picked from commit aae3ae1a8e)

Co-authored-by: Alex Groshev <38885591+haddystuff@users.noreply.github.com>
2021-11-30 06:07:32 +01:00
Felix Fontein
af7a6dc29f Next release will be 3.8.3. 2021-11-23 07:10:11 +01:00
Felix Fontein
16ffb4ba10 Release 3.8.2. 2021-11-23 05:54:00 +01:00
Felix Fontein
31c3865251 Prepare 3.8.2 release. 2021-11-23 05:53:21 +01:00
patchback[bot]
53e0bf8297 terraform: ensuring command options are applied during build_plan (#3726) (#3777)
* Fixes parameters missing in planned state

* Added new line at end of file

* Added changelog fragment for pr 3726

* Added changes mentioned by felixfontein

* Removed blank space for pep8 validation

* Update changelogs/fragments/3726-terraform-missing-parameters-planned-fix.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/cloud/misc/terraform.py

extend needs to be a list

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Thomas Arringe <thomas.arringe@fouredge.se>
Co-authored-by: Thomas Arringe <Thomas.Arringe@ica.se>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 946430e1fb)

Co-authored-by: egnirra <37709886+egnirra@users.noreply.github.com>
2021-11-23 05:51:17 +01:00
patchback[bot]
9b80b14956 Fix collection dependency installation in CI. (#3753) (#3755)
(cherry picked from commit 17b4c6972f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-19 06:59:47 +01:00
patchback[bot]
be763e6ed2 CI: Replace RHEL 8.4 by RHEL 8.5 for devel (#3747) (#3748)
* Replace RHEL 8.4 by RHEL 8.5 for devel.

* Install virtualenv.

* Revert "Install virtualenv."

This reverts commit 22ba0d074e.

* Just do another skip...

(cherry picked from commit 26c7995c82)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-17 22:30:00 +01:00
patchback[bot]
4375280497 Restrict redis version. (#3733) (#3735)
(cherry picked from commit bf7a954f00)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-16 07:31:25 +01:00
patchback[bot]
ebda14ba41 Enable counter_enabled.py to support batch mode (#3709) (#3731)
* Enable counter_enabled.py to support serial mode

Enable counter_enabled.py to support batch playbook executions using the serial tag in plays. Currently, the host counter gets reset at the beginning of every task. However, during batch executions we want it to keep track of the previous batch executions and print the host counter based on the previous runs. This proposal keeps track of how many servers have been updated in previous batches and starts the host counter at that tracked value.

```
- hosts: allthethings
  gather_facts: no
  serial:
    - 3
    - 15%
    - 20%
    - 35%
    - 55%
    - 90%
    - 100%
  tasks:
    - name: Ping Hello!
      ping:
        data: "Hello!!!!"
```

* Reset task counter on play start

Reset task counter on play start for batch mode playbook executions.

* Add changelog fragment

* change changelog fragment after feedback

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit f5b4dcc564)

Co-authored-by: Nabheet Sandhu <nabheet@users.noreply.github.com>
2021-11-15 22:13:12 +01:00
patchback[bot]
c16a5f3780 Allow LDAP search to run in check mode (#3667) (#3724)
* Allow ldap search to run in check mode always

* Fix indentions

* Remove Comments and Chg Fragment

* Update changelogs/fragments/3667-ldap_search.yml

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

Co-authored-by: Sebastian Trupiano <sebastian.trupiano@srpnet.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
(cherry picked from commit 115d435d2d)

Co-authored-by: sabman3 <sabman3@aol.com>
2021-11-13 15:27:43 +01:00
patchback[bot]
f6c1566924 Example command has wrong arg in redfish_command (#3711) (#3721)
Example command arg `boot_next` missing the underscore

(cherry picked from commit 4fe5d54b9e)

Co-authored-by: bluikko <14869000+bluikko@users.noreply.github.com>
2021-11-13 14:58:49 +01:00
patchback[bot]
bffed2fda5 Rework safety check on size arguments for when LV doesn't exist (#3681) (#3719)
* Rework safety check on size arguments for when LV doesn't exist

* Update changelogs/fragments/3681-lvol-fix-create.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Jake Reynolds <jake.reynolds@bidfx.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 352047314b)

Co-authored-by: jake2184 <jake2184@users.noreply.github.com>
2021-11-13 14:34:31 +01:00
Felix Fontein
440804fd62 Replace Bash codecov uploader by new Python codecov uploader. (#3713) (#3715)
ci_coverage

(cherry picked from commit 5948809162)
2021-11-13 13:22:11 +01:00
patchback[bot]
a915a4b7c5 BOTMETA.yml: add new maintainer to gitlab team (#3696) (#3704)
(cherry picked from commit 18a17acaa4)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2021-11-13 10:37:08 +01:00
patchback[bot]
ed69bde7a9 Fix dummy interface returning changed (#3625) (#3687)
* fix dummy interface bug

* fix dummy interface bug

* Update nmcli.py

* Update nmcli.py

* Update nmcli.py

* Update nmcli.py

* adding tests and requested conditional

* Fix pylint problems and remove 2 lines from previous version of bugfix

* Fix pep8 issue

* add changelog

* Update changelogs/fragments/3625-nmcli_false_changed_mtu_fix.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 85085bcd53)

Co-authored-by: Alex Groshev <38885591+haddystuff@users.noreply.github.com>
2021-11-10 08:00:30 +01:00
patchback[bot]
77700e7110 Fix docs issues. (#3682) (#3683)
(cherry picked from commit 146af089e9)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-09 20:27:28 +01:00
patchback[bot]
91d445ab35 Replace Fedora 33 with Fedora 35 for devel tests (#3674) (#3679)
* Replace Fedora 33 with Fedora 35 for devel tests.

* Skip Fedora 35 for reiserfs tests.

(cherry picked from commit fc99893f10)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-09 07:05:06 +01:00
patchback[bot]
19c2af03b7 Better handling of base64-encoded values in xattr module (#3675) (#3677)
* Fix exception in xattr module when existing extended attribute's value contains non-printable characters and the base64-encoded string contains a '=' sign

* Added changelog fragment for #3675

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2f0ae0408d)

Co-authored-by: sc-anssi <sc-anssi@users.noreply.github.com>
2021-11-09 06:29:01 +01:00
David Moreau Simard
58a5463ddb Fix urpmi typo in changelog (#3659)
The module is urpmi, not urmpi.
2021-11-02 19:06:49 +01:00
Felix Fontein
84941d0a7f Next expected release is 3.8.2. 2021-11-02 06:49:01 +01:00
Felix Fontein
87880da6da Release 3.8.1. 2021-11-02 06:14:23 +01:00
patchback[bot]
7acc0b897a Revert "Temporarily disable yaml callback tests. (#3651)" (#3657) (#3658)
This reverts commit 2324f350bc.

(cherry picked from commit 29af59822d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-11-01 19:19:01 +01:00
patchback[bot]
5174fc98d2 Fixed - TypeError: unexpected keyword argument (#3649) (#3654)
* Fixed - TypeError: unexpected keyword argument

- File proxmox_group_info.py creates the error "TypeError:
  get_group() got an unexpected keyword argument \'group\'\r\n'" if a
  group parameter is used.
  Issue is an argument naming conflict. After changing the argument
  name to 'groupid', as used in method ProxmoxGroupInfoAnsible::get_group,
  testing a Proxmox group name is working now.

* Changelog fragment added for #3649

changelog fragment for TypeError: unexpected keyword argument #3649

* Update changelogs/fragments/3649-proxmox_group_info_TypeError.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 0df41241dd)

Co-authored-by: hklausing <hklausing@users.noreply.github.com>
2021-10-31 20:35:43 +01:00
patchback[bot]
d9ad386a13 Temporarily disable yaml callback tests. (#3651) (#3652)
(cherry picked from commit 2324f350bc)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-31 18:08:17 +00:00
patchback[bot]
739719a3b1 opennebula: fix error message when renaming an image (#3626) (#3650)
While porting this module to make use of `pyone` I have overlooked one
attribute.  Luckily the error only occurs when trying to rename an image
to a name that has already been taken.

Instead of telling the user which image ID already uses that name, the
module failed with the following error (along with a huge backtrace):

    AttributeError: 'IMAGESub' object has no attribute 'id'

With this commit the error message is much more obvous again.

(cherry picked from commit b429c520f5)

Co-authored-by: Georg Gadinger <nilsding@nilsding.org>
2021-10-31 18:50:20 +01:00
patchback[bot]
311b618016 provide more fitting description for runner timeout (#3624) (#3645)
* provide more fitting description for runner timeout

* Update plugins/modules/source_control/gitlab/gitlab_runner.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Tim Herren <tim.herren@gmx.ch>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 96de25fc94)

Co-authored-by: nerrehmit <accounts+github@herren.id>
2021-10-30 17:04:18 +02:00
patchback[bot]
70820cab5d Fix CI (#3637) (#3641)
* Replace yaml.load with yaml.safe_load in unit tests.

* Remove no longer needed loader arg in two instances.

(cherry picked from commit 753df78877)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-30 11:22:18 +02:00
patchback[bot]
a75a12227f gitlab_project_members: improve project name matching (#3602) (#3636)
* Update gitlab_project_members.py

The actual search method doesn't accept path with namespace for project_name. If you have many project with same name, this module gitlab_project_members can't work.

* Update gitlab_project_members.py

* Update gitlab_project_members.py

* Update gitlab_project_members.py

* Create 3602-fix-gitlab_project_members-improve-search-method

* Rename 3602-fix-gitlab_project_members-improve-search-method to 3602-fix-gitlab_project_members-improve-search-method.yml

(cherry picked from commit cdfc4dcf49)

Co-authored-by: paytroff <93038288+paytroff@users.noreply.github.com>
2021-10-30 08:32:06 +02:00
patchback[bot]
6959847701 pipx - fixed bug in state=inject (#3611) (#3634)
* pipx - fixed bug in state=inject

* added changelog fragment

* copy/paste error in the integration test

* replaced injected package with simpler one

* testing force_lang = None

* disable UTF-8 emojis in pipx output

* better way to achieve the same outcome

* Adjsuted the changelog fragment

(cherry picked from commit 40ccd1501b)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-10-30 08:30:13 +02:00
patchback[bot]
ad93c40d40 fix gitlab_deploy_key task in check mode (#3622) (#3629)
fixes #3621

* running check mode used to accidentally delete the existing
  ssh key; change it so deletion is skipped in check mode

(cherry picked from commit 8ba7fd5d61)

Co-authored-by: Waldek Maleska <w.maleska@gmail.com>
2021-10-28 20:19:54 +02:00
Felix Fontein
5bfbd65115 Prepare 3.8.1 release. 2021-10-22 08:31:37 +02:00
patchback[bot]
71de1ee1d5 Fix exception in pkgin module when all packages are already installed (#3583) (#3596)
* Fix exception in pkgin module when all packages are already installed.

* Update plugins/modules/packaging/os/pkgin.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Add changelog fragment for #3583

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 86a22b5ed2)

Co-authored-by: Nate Coraor <nate@bx.psu.edu>
2021-10-22 08:07:10 +02:00
patchback[bot]
ad4efaeb31 Redfish: Do not set the boot source override mode if not provided by the user (#3581) (#3595)
* Redfish: Do not set the boot source override mode if not provided by the user

Signed-off-by: Mike Raineri <michael.raineri@dell.com>

* Corrected changelog file extension

Signed-off-by: Mike Raineri <michael.raineri@dell.com>

* Update changelogs/fragments/3509-redfish_utils-SetOneTimeBoot-mode-fix.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 481fc48e51)

Co-authored-by: Mike Raineri <michael.raineri@dell.com>
2021-10-22 07:41:09 +02:00
patchback[bot]
786ea68016 nmcli: Fix ipv6.dns not being recongnized as list (#3563) (#3593)
* nmcli: Fix ipv6.dns not being recongnized as list

There was a missing comma on the previous line.

* nmcli: Add changelog fragment for #3563

* nmcli: Update changelogs/fragments/3563-nmcli-ipv6_dns.yaml

Make the fix description more descriptive

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* nmcli: Remove ipv4.route-metric from list-typed properties

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* nmcli: Update fragment 3563 with ipv4.route-metric bug

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit e7e2ab94da)

Co-authored-by: Matyáš Kroupa <kroupa.matyas@gmail.com>
2021-10-21 08:22:55 +02:00
patchback[bot]
dd878f931f Redfish: perform manager network interface configuration even if property is missing (#3582) (#3591)
* Redfish: perform manager network interface configuration even if property is missing

Signed-off-by: Mike Raineri <michael.raineri@dell.com>

* Update changelogs/fragments/3404-redfish_utils-skip-manager-network-check.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 6580e7559b)

Co-authored-by: Mike Raineri <michael.raineri@dell.com>
2021-10-20 17:51:35 +02:00
patchback[bot]
8f03511d9c Remove non-working example. (#3571) (#3587)
(cherry picked from commit e8c37ca605)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-18 23:07:04 +02:00
patchback[bot]
004e6d06c3 Fixed typo in homebrew documentation (#3577) (#3585)
Fixed typo in community.general.homebrew documentation

(cherry picked from commit 02c534bb8e)

Co-authored-by: Premkumar Subramanian <prem_x87@outlook.com>
2021-10-18 23:04:16 +02:00
patchback[bot]
25f46caefb Misc doc issues (#3572) (#3580)
* Add names to tasks in oneview module examples

* Fix task name in github_webhook module example

* Fix trailing whitespace

* Add changelog fragment

* Remove changelog fragment

(cherry picked from commit 3731064368)

Co-authored-by: Vitaly Khabarov <vitkhab@users.noreply.github.com>
2021-10-18 14:09:34 +02:00
patchback[bot]
0a733c60ca Use correct FQCN. (#3573) (#3575)
(cherry picked from commit c3813d4533)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-17 18:25:50 +02:00
patchback[bot]
f006aa4cf6 Fix bug with returning results in IPA role (#3561) (#3568)
* Fix bug with returning results in IPA role

Fix #3560

* Add changelog

* Fix typo in changelog

* Update changelogs/fragments/3561-fix-ipa-host-var-detection.yml

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>

* Update changelogs/fragments/3561-fix-ipa-host-var-detection.yml

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>

* Update changelogs/fragments/3561-fix-ipa-host-var-detection.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 191d2e08bb)

Co-authored-by: Sergey <sshnaidm@users.noreply.github.com>
2021-10-16 21:08:01 +02:00
patchback[bot]
72e0d8c310 Remove centos8 for devel from CI. (#3565) (#3566)
(cherry picked from commit 249d490f10)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-16 09:27:55 +02:00
Felix Fontein
b96aaffeae Next expected release is 3.8.1. 2021-10-12 13:18:09 +02:00
Felix Fontein
5bd5de4281 Release 3.8.0. 2021-10-12 12:34:31 +02:00
patchback[bot]
4aebefcf9e Bugfix issue2692 logstash callbackmodule with no attribute options (#3530) (#3550)
* Update logstash.py

replacing _options with context.cliargs

* Create 2692-logstash-callback-plugin-replacing_options

logstash callback plugin replace _option with context.CLIARGS

* Rename 2692-logstash-callback-plugin-replacing_options to 2692-logstash-callback-plugin-replacing_options.yml

missed out the extenstion

* Update logstash.py

context imported

* Update 2692-logstash-callback-plugin-replacing_options.yml

dict to string

* Update changelogs/fragments/2692-logstash-callback-plugin-replacing_options.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 7038812465)

Co-authored-by: Anand Victor <anandvict@gmail.com>
2021-10-12 05:07:26 +00:00
patchback[bot]
62f9a5b0a9 Improve contributing instructions (#3541) (#3548)
* Improve contributing instructions.

* Update CONTRIBUTING.md

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit 316adebd68)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-11 23:21:41 +02:00
patchback[bot]
3d03eda99e keycloak_identity_provider: Fix mappers update (#3538) (#3546)
* set identityprovideralias by default

* refactor mappers change detection

* fix sanity check

* update tests

* add changelog fragment

* Update changelogs/fragments/3538-fix-keycloak-idp-mappers-change-detection.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit d44c85aa90)

Co-authored-by: Laurent Paumier <30328363+laurpaum@users.noreply.github.com>
2021-10-11 23:21:25 +02:00
patchback[bot]
c01ce10b4b terraform: add parallelism parameter (#3540) (#3547)
* terraform: add parallelism parameter

* terraform: add parallelism parameter version

(cherry picked from commit ed2c1e4ac9)

Co-authored-by: linxside <39219399+linxside@users.noreply.github.com>
2021-10-11 23:21:16 +02:00
patchback[bot]
16aa776c93 Add elastic callback plugin (#3380) (#3542)
* Add elastic callback plugin

* Capture task failures

* Catch errors and add UTs

* Skip 3.5< python versions and install dependency

* fix lint

* Fix linting

* Fix linting

* Add botmeta

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* It's not required

* As suggested in the code review OrderedDict has been added to the Python stdlib since version 2.7

* Update plugins/callback/elastic.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 905f4dcfa2)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-10-11 07:35:09 +02:00
patchback[bot]
d7d1659e34 Add new modules rundeck_job_run and rundeck_job_executions_info (#3521) (#3544)
* Add new module rundeck_job_run

* Add new module rundeck_job_executions_info

* Removed supports_check_mode

* Fix supports_check_mode

* Fix version_added

* Fixes for PR#3521

* Fix default value for loglevel in the doc

* Fix job_status_check loop

* Add proposed changes in PR#3521

* Add proposed changes in PR#3521

* Change executions_info output to executions

* Add rundeck integration tests

* Fix rundeck integration test

* Add more tests to rundeck integration tests

* Update job_options doc

* Add more tests to rundeck integration tests

* Add more examples to rundeck_job_run doc

* Add proposed fixes for PR#3521

* Add proposed fixes for PR#3521

* Fix job_options

* Add proposed changes for PR#3521

(cherry picked from commit 9772485d3c)

Co-authored-by: Phillipe Smith <phsmithcc@gmail.com>
2021-10-11 07:26:27 +02:00
patchback[bot]
5b9b99384f macports: add stdout and stderr to status (#3499) (#3500) (#3543)
* macports: add stdout and stderr to status (#3499)

* Add changelog fragment

* Update changelogs/fragments/3500-macports-add-stdout-and-stderr-to-status.yaml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Aoife Finch <aoife@finch.ink>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 8ece0d3609)

Co-authored-by: Aoife Finch <aoife.github@finch.ink>
2021-10-11 07:26:17 +02:00
Felix Fontein
f898279c8c Prepare 3.8.0 release. 2021-10-11 07:07:13 +02:00
patchback[bot]
2215c6d360 keycloak_role: quote role name in urls (#3536) (#3539)
* quote role name in urls

* add changelog fragment

* Update changelogs/fragments/3536-quote-role-name-in-url.yml

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* fix linefeeds

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit 9de01e04f2)

Co-authored-by: Laurent Paumier <30328363+laurpaum@users.noreply.github.com>
2021-10-09 12:08:05 +00:00
patchback[bot]
ca3948858a Fix shellcheck error. (#3531) (#3534)
(cherry picked from commit d1f820ed06)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-08 14:41:44 +00:00
patchback[bot]
f14e566cc7 Pkgng many packages one command (#3393) (#3529)
* pkgng: join package list into one command

Change the pkgng module so all packages being
installed (or upgraded) are acted on in one
command (per action). This will make installs
and upgrades a bit faster, because pkg will be
invoked fewer times per module run. More important,
module actions will be more atomic, making it less
likely that some packages are acted on because they
appear earlier in the argument list.

This change also improves the status reporting of
packages acted on, specifying the number of packages
for each action (install or upgrade).

* pkgng: make upgrade check lazily evaluated

Make upgrade_available an inner function so that the
if statement that checks whether installed packages
are up-to-date only runs the upgrade check on packages
that are already installed. This gets lazily evaluated
because of boolean operator short-circuiting:
https://docs.python.org/3.8/library/stdtypes.html#boolean-operations-and-or-not

Previously, the module would always check for upgrades,
even for not-installed packages, when running with
`state=latest`.

* pkgng: add changelog fragment

* pkgng: Apply changelog suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* pkgng: resolve pep8 style issue

Remove inline function. It's purpose would be confusing for
future maintainers, and someone refactoring it to a variable,
with good intentions, would introduce a performance regression.

Including the `query_update()` call in the if expression makes
the intent more legible and still ensures lazy evaluation of the
function call if the first `and` is `False`.

* pkgng: Fix changelog fragment syntax issue

Need to escape quotes so YAML doesn't eat them

Co-authored-by: Felix Fontein <felix@fontein.de>

* pkgng: Improve output message English grammar

Make word "package" plural only if reporting on more than one package

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 293c7a9fb3)

Co-authored-by: Ross Williams <ross@ross-williams.net>
2021-10-08 07:57:27 +02:00
patchback[bot]
a2c93f5e99 zypper_repository: Improve .repo file idempotency (#3474) (#3528)
* If repo option points to .repo file, download for later parsing

* Parse downloaded .repo file content (ini format)

* Validate downloaded file, map values to repodata, workaround to ignore old .repo related code

* Integration Test adjusted to install python package 'requests' first

* Revert "Integration Test adjusted to install python package 'requests' first"

This reverts commit 0d18352c2238d098831ba6d59b66e731fa8f0cd9.
Not allowed to introduce new dependencies at this point, module_utils usage required

* Remove python 'requests' dependency, using 'fetch_url' and 'to_text' from 'ansible.module_utils' instead

* Prefer alias (name) if given instead repo (url)

* If gpgkey was given in .repo file ensure key get automatically imported

* ConfigParser Import made Python2 compatible

* New .repo code moved below existing run-time parameters checks to keep previous logic

* Obsolete workaround removed

* two pylint/pep8 errors fixed

* name added to autorefresh assert

* Missing assert for 'Delete test repo' added

* name added to priority option assert

* name added to check repo is updated by url assert

* name added to check repo is updated by name assert

* name added to check add a repo by releasever assert

* name added to check remove added repo assert

* name added to check add a repo by basearch assert

* name added to check remove added repo #2 assert

* Bugfix to avoid 'KeyError' Exception in if statements

* Refactoring of configparser related code, usage of module_utils, py2 compatibility

* Removal of some leftover from earlier testing

* Integration tests for add/remove repositories by url to .repo file added

* Additional name added to list of test repos that has to be removed

* Test added to verify cleanup of local .repo file after removal via zypper

* Changelog fragment related to PR #3474 added

* yamllint error resolved

* Refactoring to reduce indentation and removal of else statements

* Integration tests added for loading .repo file from local path

* Test .repo file added

* Dependency to setup_remote_tmp_dir added

* New entry added to 'remove repositories added during test'

* Support for .repo file from local path

* Changelog: Ref to https://github.com/ansible-collections/community.general/issues/3466 added

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit f0fcb221cd)

Co-authored-by: Dominik Wombacher <dominik@wombacher.cc>
2021-10-08 07:56:34 +02:00
patchback[bot]
67a2abcab2 [opentelemetry][callback] add option to support enabling plugin in the CI (#3498) (#3524)
* [opentelemetry][callback] add option to support enabling plugin in the CI only

* [opentelemetry][callback] add changelog fragment

* Apply suggestions from code review

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* [opentelemetry][callback] use enable_from_environment

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* [opentelemetry] ensure the value is true otherwise the plugin is not enabled

* [opentelemetry][changelog] update entry with the new option

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 3a460751a4)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-10-07 22:16:28 +02:00
patchback[bot]
2e4864db7f [ufw] Insert or delete biased when deletion enabled - as for append or delete. (#3514) (#3525)
* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

* [ufw] Insert or delete biased when deletion enabled - as for append or delete.

(cherry picked from commit 80bb42325b)

Co-authored-by: Greg <greg-a-atkinson@users.noreply.github.com>
2021-10-07 22:15:40 +02:00
patchback[bot]
1f0b2a5173 [opentelemetry][callback] enrich stacktrace errors (#3496) (#3523)
* [opentelemetry][callback] refactor get_error_message and add UTs

* [opentelemetry][callback] enrich exception with msg, exception and stderr fields

* [opentelemetry] fix linting

* [opentelemetry][callback] add changelog fragment

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* [opentelemetry][callback] chore: remove comment

* [opentelemetry][callback] refactor tests

* [opentelemetry] refactor UTs

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit e22fff2b12)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-10-07 07:45:15 +02:00
patchback[bot]
25482000f0 lxd_container: improvements to the comments (#3520) (#3522)
* improvements to the comments

* next documentation improvements

Co-authored-by: Frank Dornheim <“dornheim@posteo.de@users.noreply.github.com”>
(cherry picked from commit 57e5f8c7be)

Co-authored-by: Frank Dornheim <524257+conloos@users.noreply.github.com>
2021-10-07 07:14:42 +02:00
patchback[bot]
c0f3aa14cf pipx - new module (#3507) (#3518)
* pipx - new module

* using python instead of python3

* removed ensure_path as it is unused

* ensuring we are running the same python as Ansible

* changed the last solution to adding a pipx_path parameter to the module, with a sensible default

* added docs for the new parameter

* changed param name to executable, and customized it for Darwin

* use executable if passed, otherwise use python -m pipx

* minor update

* added examples

* Update plugins/modules/packaging/language/pipx.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/packaging/language/pipx.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/packaging/language/pipx.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* tests names 324 -> 3.24.0

* ensure tox is uninstalled by the beginning of the test

* Renamed option+suggestions from PR

* improved idempotency

* fixed sanity

* fixed test

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit f1807d3323)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-10-06 07:30:19 +02:00
patchback[bot]
1ef104be61 Fix: gitlab_deploy_key idempotency (#3473) (#3512)
* Fix: gitlab_deploy_key idempotency

The module was not retrieving all the deploy keys leading to non
idempotency on projects with multiple deploy keys.
SEE: https://python-gitlab.readthedocs.io/en/stable/api-usage.html#pagination

* Update changelogs/fragments/3473-gitlab_deploy_key-fix_idempotency.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Jonathan Piron <jonathanpiron@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 0bc4518f3b)

Co-authored-by: Jonathan Piron <jonathan@piron.at>
2021-10-04 21:42:04 +02:00
patchback[bot]
773df88a41 Proxmox tasks module (#3226) (#3508)
* Started creating Unit tests for Proxmox Tasks module

* Tried really hard to get the mock to work

* unit tests  for the module

* Fixed symslink and permissions

* Suggested changes and more unit tests

* Fixed isFalse

* Apply suggestions from code review

* Update plugins/modules/cloud/misc/proxmox_tasks_info.py

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit e95f63b067)

Co-authored-by: Andreas Botzner <paginabianca66@gmail.com>
2021-10-03 22:37:41 +02:00
patchback[bot]
d77e256088 Fix OSX 10.11 CI runs (#3501) (#3504)
* Restrict to OSX 10.11 tests.

* See whether updating brew helps.

* Skip archive task for OSX.

* Refactor homebrew task to make changing the package name easier.

* Revert "See whether updating brew helps."

This reverts commit 8eceb9ef1f.

* Replace xz by gnu-tar.

* Uninstall first.

* Skip iso_extract task for OSX.

* Revert "Restrict to OSX 10.11 tests."

This reverts commit 81823d2f97.

* ci_complete

(cherry picked from commit 106856ed86)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-02 21:55:06 +02:00
patchback[bot]
2917389779 yaml callback: prevent plugin from modifying PyYAML (#3478) (#3494)
* Prevent yaml callback from modifying PyYAML.

* Fix changelog fragment.

* Update changelogs/fragments/3478-yaml-callback.yml

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>

Co-authored-by: Brian Scholer <1260690+briantist@users.noreply.github.com>
(cherry picked from commit 5895e50185)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-02 20:22:35 +02:00
patchback[bot]
59af80235b [opentelemetry] minor changes in the import, docs and exception (#3450) (#3491)
* OrderedDict has been added to the Python stdlib since version 2.7

* Docs: cosmetic change in the python lib

* See https://github.com/ansible/ansible/issues/75726

* Add changelog fragment

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 774b2f642b)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-10-01 15:08:00 +02:00
patchback[bot]
aec52198e3 Update redfish_info.py (#3485) (#3490)
* Update redfish_info.py

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Update plugins/modules/remote_management/redfish/redfish_info.py

* Update plugins/modules/remote_management/redfish/redfish_info.py

* Update plugins/modules/remote_management/redfish/redfish_info.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
(cherry picked from commit 316e1d6bf2)

Co-authored-by: Rasdva3 <34684695+Rasdva3@users.noreply.github.com>
2021-10-01 15:07:23 +02:00
patchback[bot]
cbe4490c9e devel dropped support for Python 2.6. (#3487) (#3488)
(cherry picked from commit 0d3c4de6a2)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-10-01 13:16:57 +02:00
patchback[bot]
9de059b44d fix structure xcc_redfish_command (#3479) (#3482)
* adhere to proper task structure

* add changelog fragment

* return code formatting to original

* remove unnecessary fragment

(cherry picked from commit a14392fab0)

Co-authored-by: Zach Biles <bile0026@users.noreply.github.com>
2021-09-30 17:24:44 +02:00
patchback[bot]
c72a23a5f1 Stick to community.crypto 1.x.y for ubuntu1604. (#3470) (#3477)
(cherry picked from commit 3fee872d58)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-29 20:53:25 +02:00
patchback[bot]
0b9d9c0fdb nmcli: amended the routing-rules4 key values as list (#3401) (#3467)
* Updated nmcli.py

Amended the routing-rules4 values as list. By this we could add the entries for "routing_rules4" in the form of a list .

* Update nmcli.py

Fixed typo in line #1701

* 3395-nmcli-needs-type.yml

routing_rules4 module argument is currently accepting only string elements. In order to accept multiple values, amended the type of routing_rules4 as list.

* nmcli: amended the routing-rules4 key values as list

routing_rules4 module argument is currently accepting only string elements. In the case of adding multiple entries to routing_rules4, we need to accept values as list.

* Added 3401-nmcli-needs-type.yml

routing_rules4 module argument is currently accepting only string elements. In the case of adding multiple entries to routing_rules4, we need to accept values as lists

* Amended type to 'minor_changes'

Amended type to 'minor_changes'  from 'bug_fixes'

* routing_rules4 to a list of element str

nmcli.py - routing_rules4 to a list of element str

* Update changelogs/fragments/3401-nmcli-needs-type.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* nmcli: allow routing-rules4 key values as list

* nmcli: amended the routing-rules4 key values as list

* nmcli: amended the routing-rules4 key values as list

* nmcli: amended the routing-rules4 key values as list

* test_nmcli: amended whitespaces

* Update 3401-nmcli-needs-type.yml

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 53fc2c477b)

Co-authored-by: Sreekanth H <65583280+sree0744@users.noreply.github.com>
2021-09-29 05:45:08 +00:00
patchback[bot]
67eaf9405f Copy the permissions along with file for jboss module (#3426) (#3469)
* Copy the permissions along with file for jboss module

Issue: The deployment file is copied with file content only. The file
permission is set to 440 and belongs to root user. When the
JBossI(Wildfly) server is running under non root account, it can't read
the deployment file.

With is fix, the correct permission can be set from previous task for JBoss
server to pickup the deployment file.

* Update changelogs/fragments/3426-copy-permissions-along-with-file-for-jboss-module.yml

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit 7cfdc2ce8c)

Co-authored-by: Pan Luo <xcompass@gmail.com>
2021-09-29 05:44:59 +00:00
patchback[bot]
5de05a6243 [PR #3462/845c4064 backport][stable-3] Enable ansibullbot notifications in issues and PRs (#3465)
* Enable ansibullbot notifications in issues and PRs (#3462)

The more recent version of Ansibullbot defaults notifications to false.
We need to set it to true so it can notify contributors and maintainers.

(cherry picked from commit 845c406419)

* Update botmeta sanity test to accept notifications.

(cherry picked from commit 71a655193c)

Co-authored-by: David Moreau Simard <moi@dmsimard.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-29 06:41:37 +02:00
patchback[bot]
46b4b9a6de Added OpenNebula inventory plugin (#810) (#3460)
* Added OpenNebula inventory plugin

Signed-off-by: Kristián Feldsam <feldsam@gmail.com>

* Apply suggestions from code review

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Removed matching inventory yaml files ending with "one"

Too general word

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Apply suggestions from code review

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Added BOTMETA

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Moved import

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Fix indentation problem

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Added group_by_labels, refactored so can be unit tested

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Added unit tests

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Removed blank line

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
(cherry picked from commit 806f1ea3c9)

Co-authored-by: Kristian Feldsam <feldsam@gmail.com>
2021-09-28 17:41:54 +02:00
patchback[bot]
10146aae1c Update CI matrix to include ansible-core's stable-2.12 branch (#3445) (#3459)
* Update CI matrix to include ansible-core's stable-2.12 branch.

* Adjust README.

* Fix stage names.

* Avoid requirements clash.

* Thin matrix for older Ansible versions.

(cherry picked from commit d96b85af9f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-28 16:04:46 +02:00
patchback[bot]
d2ec7053c5 Fix require_two_factor_authentication can't be null (#3453) (#3457)
* fix: require_two_factor_authentication default=false

* chore: add changelog fragment

* docs: require_two_factor_authentication default value

* fix: don't send require_two_factor_authentication null value

* chore: fix changelog

(cherry picked from commit 1bb2ff5128)

Co-authored-by: Chris Frage <git@sh0shin.org>
2021-09-27 22:11:54 +02:00
patchback[bot]
51fcacae08 Fix: GitLab API searches always return first found match (#3400) (#3448)
* fix: return correct group id
match only full_path or name

* chore: add changelog fragment

* fix: indentation multiple of four

* refactor: use two loops

* fix: typo of group id

* fix: changelog fragment

(cherry picked from commit b6b7601615)

Co-authored-by: Chris Frage <chris.frage@cancom.de>
2021-09-26 19:49:08 +02:00
patchback[bot]
29211b970c open-iscsi: adding mutual authentication support and updating authentication parameters description (#3422) (#3447)
* Adding mutual athentication support and changing doucumentation about authentication credentials

* Removing blank line with whitspaces

* Update plugins/modules/system/open_iscsi.py

Adding version_added to node_user_in parameter

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* Update plugins/modules/system/open_iscsi.py

adding version_added attibute to new parameter password_in

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* Update plugins/modules/system/open_iscsi.py

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* Adding changelog fragment for #3422

* Rename 3422-open-iscsi-mutual-authentication-support.yam to 3422-open-iscsi-mutual-authentication-support.yaml

* Update changelogs/fragments/3422-open-iscsi-mutual-authentication-support.yaml

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* Update changelogs/fragments/3422-open-iscsi-mutual-authentication-support.yaml

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit 43a9f09a17)

Co-authored-by: Ricardo Sanchez <84853324+ricsanfre@users.noreply.github.com>
2021-09-26 13:49:02 +02:00
patchback[bot]
5c1fa53558 Keycloak: Fix bug on keycloak_authentication, requirement not always updated (#3330) (#3446)
* Fix diff mode when updating authentication flow with keycloak_authentication module

* Update documentation of create_or_update_executions function (return tuple instead of dict)

* Fix: Update requirement when new exex created

* Add changelog fragment

* Update changelogs/fragments/3330-bugfix-keycloak-authentication-flow-requirements-not-set-correctly.yml.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/3330-bugfix-keycloak-authentication-flow-requirements-not-set-correctly.yml.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Edit requirement of sublow

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 147ca2fe66)

Co-authored-by: Gaetan2907 <48204380+Gaetan2907@users.noreply.github.com>
2021-09-26 13:48:48 +02:00
patchback[bot]
2348f3d439 Diable netcat conflict in zypper tests as one package seems to be no longer available. (#3438) (#3442)
(cherry picked from commit 3715b6ef46)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-25 17:44:53 +02:00
patchback[bot]
46a051d168 grpcio takes 7 minutes to build on Python 3.10 - make sure it's not installed indirectly. (#3435) (#3437)
(cherry picked from commit d0563e34a6)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-25 17:07:26 +02:00
patchback[bot]
b2212bc8ef Fix CI (#3430) (#3433)
* Restrict to unit tests with devel (to be reverted later).

* Restrict lxml for Python 2.6.

* Revert "Restrict to unit tests with devel (to be reverted later)."

This reverts commit d0d87a8a0f.

(cherry picked from commit 935348ae78)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-25 09:24:01 +02:00
Felix Fontein
e05e3aed67 Prepare ansible-core devel branch version bump that is planned for later today.
(cherry picked from commit 0d2bcf545e)
2021-09-24 18:47:20 +02:00
patchback[bot]
a13541299e mail: adding capability to specify ehlo hostname (#3425) (#3427)
* Adding capability to specify ehlo hostname

* Fixing default for ehlohost

the CI did not like "None" as default in the documentation,
judging from the rest of the definitions omitting that seems to be right

* Update plugins/modules/notification/mail.py

Proper spelling in documentation

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* Adding Changelog Fragment

* Update changelogs/fragments/3425-mail_add_configurable_ehlo_hostname.yml

Proper phrasing in changelog

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* integration-test for ehlohost parameter

* proper description, increased async-time

changed body of ehlohost-mail to reflect ehlohost
increased async from 30 to 45 as CI failed because
smtp was already down.

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit ae6cbc2d82)

Co-authored-by: Hartwig Hauschild <hardy+github@foxxnet.de>
2021-09-24 07:41:21 +02:00
Felix Fontein
221067e708 Next expected release is 3.8.0. 2021-09-21 18:49:12 +02:00
Felix Fontein
db6458bd93 Release 3.7.0. 2021-09-21 18:09:31 +02:00
patchback[bot]
f342243fb0 Update README.md (#3402) (#3415)
* Update README.md

* Update README.md

Co-authored-by: Felix Fontein <felix@fontein.de>

* Fix the link

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit e1cdad3537)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2021-09-21 12:16:28 +02:00
Felix Fontein
37f2b06c3c Fix file location. 2021-09-21 09:49:38 +02:00
Felix Fontein
0a8a41966d Prepare 3.7.0 release. 2021-09-21 09:49:00 +02:00
patchback[bot]
263c5ba9de Make ready for split-controller testing in ansible-core (#3345) (#3412)
* Accept context/ in aliases.

* Mark ansible_galaxy_install test as context/controller.

* Fix interfaces_file test.

ci_complete

* Install pyone dependency.

ci_complete

(cherry picked from commit 98d071f61e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-21 09:03:28 +02:00
patchback[bot]
cfc28a3f6a lxd_container: do not ignore volatile configs by option (#3331) (#3411)
* do not ignore volatile - fix #2466

* remove unnecessary checks

* fix ansible-tests sanity

* fix access to the option: ignore_volatile_options

* add fragment

* fix quotation marks error

* fix review findings

* fix sanity error

* fix description - in yaml examples use true not True
comment out the deprecate message

* remove "default=True" at ignore_volatile_options
fix spelling

* fix sanity error that ignore_volatile_options have no default value

* undo changes. add defaut value and remove keyword at dict.get()

* fix sanity error

* small spelling fixes

* change text for fragment

* Fix description sentence.

Co-authored-by: Frank Dornheim <“dornheim@posteo.de@users.noreply.github.com”>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 8e7d49c1c6)

Co-authored-by: Frank Dornheim <524257+conloos@users.noreply.github.com>
2021-09-20 21:40:55 +02:00
patchback[bot]
b495035923 Added redis_data module (#3230) (#3410)
* Added redis_set module

Added redis_set module and unit tests for the new module.
Applied suggested changes and removed redis_del in favor of state
option.
Also added redis utility class that handles connection.

* Typos, added version and BOTMETA

* Fixed import error checking

* Fixed Unit tests

* Docfix and return consistency

* Added Check Mode

* Update plugins/modules/database/misc/redis_data.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 424af85929)

Co-authored-by: Andreas Botzner <paginabianca66@gmail.com>
2021-09-20 20:31:07 +02:00
patchback[bot]
d4637e9b1c kernel_blacklist - revamped the module (#3329) (#3409)
* kernel_blacklist - revamped the module

* file default and setting "lines" when file does not exist

* added changelog fragment

* added change in arg_spec to the documentation block

* Update plugins/modules/system/kernel_blacklist.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/kernel_blacklist.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/system/kernel_blacklist.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* fixed: initialize self.pattern before self.var.is_blacklisted

* File writing recoded

* added try/finally for the file

* multiple changes:

- fixed case when last line of the existing file has no newline char
- added integration tests

* PR: integration test now using remote_tmp_dir

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2ad7ed4f83)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-09-20 19:58:05 +02:00
patchback[bot]
7842dc0dea Add keycloak_user_federation module (#3340) (#3408)
* new module

* fix unit tests

* fix documentation

* more fixes

* fix linefeeds

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* use true/false instead of True/False

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* fix result content + rename variable

* urlencode parameters

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2589e9a030)

Co-authored-by: Laurent Paumier <30328363+laurpaum@users.noreply.github.com>
2021-09-20 19:50:03 +02:00
patchback[bot]
314a0bc553 openbsd_pkg: Fix KeyError (#3336) (#3407)
If package installation has an error after the package is install (e.g.
when running tags), then there will be output on stderr and the fallback
regex will match; however, since pkg_add exited non-zero, changed is
never added as a key to the dictionary. As a result the code at the end
of main that checks if anything has changed raises a KeyError.

(cherry picked from commit 02d0e3d286)

Co-authored-by: Matthew Martin <phy1729@gmail.com>
2021-09-20 19:49:52 +02:00
patchback[bot]
34b7876e4f Add require_two_factor_authentication property to gitlab group (#3367) (#3399)
* feat: add require_two_factor_authentication property

* chore: add changelog fragment

* chore: add gitlab_group test tasks

* chore: add gitlab tests

* chore: add gitlab group tests

* docs: apply suggestions

* fix: removing default value

(cherry picked from commit 9ce1009643)

Co-authored-by: Chris Frage <git@sh0shin.org>
2021-09-20 07:18:43 +02:00
patchback[bot]
65c10de630 Add gitlab_project CI/CD properties (#3379) (#3398)
* feat: add ci_config_path property

* feat: add shared_runners_enabled property

* chore: add changelog fragment

* docs: add missing dot

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* docs: ci_config_path change description

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
(cherry picked from commit e48f9fdf74)

Co-authored-by: Chris Frage <git@sh0shin.org>
2021-09-20 07:18:34 +02:00
patchback[bot]
a86f31ac0f Interfaces_file - improvements (#3328) (#3396)
* pythonific!! no camel cases, bitte

* simplified iface attributes parsing

* some improvements, passing tests

* simplified set_interface_option()

* further simplifications

* remove unreachable stmt

* pythonified a file open

* added changelog fragment

* adjustment per PR

* PR: fixed the auto- case

* PR: added testcase and chglog frag for the misleading change report

* extra line removed

* integration is not destructive

(cherry picked from commit 7aae8d5386)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-09-19 14:09:07 +02:00
patchback[bot]
bc82fe36be Icinga2 inventory plugin (#3202) (#3392)
* Added Icinga2 inventory plugin

* Added Icinga2 inventory plugin

* Linting

* Added tests

* Linting

* Linting

* Added tests

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Added tests for icinga2 inventory

* Resolved reviews and added host filters

* Linting

* Fixing yaml for example and updating tests

* Updating test data

* Fixing pep8 indentations

* Missed copywriting

* Missed copywriting

* Updated documentation grammar

* Removing Cacheable class and cleanup

* Update plugins/inventory/icinga2.py

* Update plugins/inventory/icinga2.py

* Bump version number

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/inventory/icinga2.py

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 8ab96d9533)

Co-authored-by: Cliff Hults <BongoEADGC6@users.noreply.github.com>
2021-09-18 15:34:27 +02:00
patchback[bot]
7c810a6186 Get behavior of gitlab_project_members to the one of gitlab_group_members (#3319) (#3383)
* Initial change to get behaviour of gitlab_project_members like the new gitlab_group_members

* added changelog

* linter: removed trainling whitespaces

* Update plugins/modules/source_control/gitlab/gitlab_project_members.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/source_control/gitlab/gitlab_project_members.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/source_control/gitlab/gitlab_project_members.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/source_control/gitlab/gitlab_project_members.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/source_control/gitlab/gitlab_project_members.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* requested changes

* linter fixes

* undoing formatting changes to existing code

Co-authored-by: Max-Florian Bidlingmaier <Max.Bidlingmaier@konsolan.de>
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Max Bidlingmaier <Max-Florian.Bidlingmaier@sap.com>
(cherry picked from commit 7a2efb4775)

Co-authored-by: Max Bidlingmaier <maks@konsolan.de>
2021-09-17 06:59:08 +02:00
patchback[bot]
9d468fb078 redfish_utils: adding "Id" to the add user function (#3343) (#3382)
* Adding "Id" to the add user function

Some implementations of Redfish (e.g. the one in Cisco's CIMC) seem to require the id of the new user for account creation.
I'm not that firm with Python but lines 982 and 983 should fix it.

* changed indention

* created changelog fragment

* Update changelogs/fragments/3343-redfish_utils-addUser-userId.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update change type

* supplemented the description of the ID parameter

* Update plugins/modules/remote_management/redfish/redfish_command.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 331f5bdf24)

Co-authored-by: Patrick Pfurtscheller <57419021+PfurtschellerP@users.noreply.github.com>
2021-09-16 22:36:27 +02:00
patchback[bot]
2c79d42eb4 opentelemetry callback: context propagation and error exception (#3378) (#3381)
* opentelemetry callback: context propagation and error exception

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 06345839c6)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-09-16 22:34:12 +02:00
patchback[bot]
d95c3a738f Install nios test requirements. (#3375) (#3377)
(cherry picked from commit b20fc7a7c3)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-15 08:09:37 +02:00
patchback[bot]
839880d711 Add opentelemetry callback plugin (#3091) (#3373)
* Add opentelemetry callback plugin

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Formatting (text), booleans and renamed env variables

* This should be done in a future release

* Remove insecure in favour of the OTEL env variable. Add descriptions

* Use OpenTelemetrySource

* Move generate_distributed_traces

* Move update_span_data and set_span_attribute

* Move finish_task

* Move start_task

* Refactor to support UTs

* Add first UT

* Fix codestyle

* opentelemetry callback entry in the botmeta

* Fix linting

* Fix signature

* Mock methods

* Use MagicMock

* Mock the methods

* UT for transform_to_boolean_or_default

* Fix linting

* Set test data

* Mock _time_ns

* Exclude tests for python <= 3.6

* Remove obsoleted setup task type configuration

* Remove unused docs

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Fix docs

* unrequired logic that was originally took from https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/junit.py\#L226

* Use raise_from for the required dependencies

* Fix linting

* Add requirements for the UTs

* add missing dependency for the opentelemetry plugin in the UTs

* Add ANSIBLE_ prefix for the ansible specific options

* Add more context in the docs and remove duplicated docs

* As suggested in the code review

* Verify if the OTEL env variables for the endpoint were set

* Fix docs typo

* Fix linting

* Revert "Fix linting"

This reverts commit 3a54c827c5472553a6baf5598bc76a0f63f020c1.

* Revert "Verify if the OTEL env variables for the endpoint were set"

This reverts commit cab9d8648899c28c0345745690c4ec7a41f7e680.

* Remove console_output as suggested

* Apply suggestions from code review

Co-authored-by: flowerysong <junk+github@flowerysong.com>

* Delegate the definition of OTEL_EXPORTER_OTLP_INSECURE to the user

* Move definitions above, close to the class that uses them

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: flowerysong <junk+github@flowerysong.com>
(cherry picked from commit 517570a64f)

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2021-09-15 07:20:36 +02:00
patchback[bot]
bc0edf7d55 tss: add option for token authorization (#3327) (#3372)
* Added token parameter for AccessTokenAuthorizer

Parameters username and password are not required anymore because of
this.

* Added changelog fragments

* Apply suggestions from code review

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* token authorizer is prioritized

token authorizer is prioritized when token parameter is set

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* domain optional if token not provided

* Updated examples

- `base_url` is required everywhere
- examples for user, name + domain authorization included
- token authorization included

* Update 3327-tss-token-authorization.yml

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit dc8d076a25)

Co-authored-by: Martin Vician <vician@users.noreply.github.com>
2021-09-14 19:49:51 +02:00
patchback[bot]
68458fd8aa Added additional maintainer for the dsv and tss plugins (#3368) (#3369)
(cherry picked from commit 4e39a4b825)

Co-authored-by: Ricky White <ricky@whitelionmedia.com>
2021-09-13 21:35:20 +02:00
patchback[bot]
4aa70ab48f gitlab_runner: Support project-scoped runners registration (#2971) (#3364)
* support project-scoped gitlab.com runners registration

* rename glproject variable to gitlab_project

* update version

* Update plugins/modules/source_control/gitlab/gitlab_runner.py

Co-authored-by: Raphaël Droz <raphael.droz+floss@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 0a5db85ad5)

Co-authored-by: Raphaël Droz <raphael.droz@gmail.com>
2021-09-13 07:35:35 +02:00
patchback[bot]
739210c6b9 pids - refactor module to make version-based behavior consistent (#3315) (#3365)
* Initial commit

* Adding changelog fragment

* Further refactoring

* Fixing bad copy/paste and adding task for psutil >= 5.7.0 install

* Inverting psutil installation order to reduce duplication

* Optimizing regex compilation

(cherry picked from commit 118c040879)

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
2021-09-13 07:35:26 +02:00
patchback[bot]
8c23d0e345 New filter plugin - unicode_normalization (#3359) (#3362)
* Initial commit

* Adding maintainer in BOTMETA

* Adding changelog fragment

* Updating filter_guide

* Applying initial review suggestions

(cherry picked from commit 29e4066944)

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
2021-09-12 14:40:02 +02:00
patchback[bot]
cde4a1a099 Add ipaselinuxusermaporder option to the ipa_config module (#3178) (#3361)
(cherry picked from commit 612543919e)

Co-authored-by: Roy Lenferink <lenferinkroy@gmail.com>
2021-09-12 12:39:44 +00:00
patchback[bot]
10b3381f21 Adds few more gitlab group options (#3248) (#3360)
* Adds few more gitlab group options

* Update plugins/modules/source_control/gitlab/gitlab_group.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Removes default for new string options

* Removes default from argument_spec

* Adds changelog fragment

* Update plugins/modules/source_control/gitlab/gitlab_group.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/source_control/gitlab/gitlab_group.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/source_control/gitlab/gitlab_group.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Maik Schueller <maik.schueller@sap.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 58d018ebbd)

Co-authored-by: mkschuel <77283980+mkschuel@users.noreply.github.com>
2021-09-10 23:59:16 +02:00
patchback[bot]
0ccd52b63a Adding new maintainer (#3349) (#3355)
(cherry picked from commit 05fe587a3e)

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
2021-09-09 08:28:47 -04:00
patchback[bot]
c76e598d61 Improve CI (#3348) (#3351)
* Remove superfluous test.

* Use remote_temp_dir instead of output_dir on remote.

* Read certificate from correct place.

* Adjust more places.

* Fix boolean.

* Improve cryptography setup.

* Fix java_keystore changes.

* Need to copy binary from remote.

* Use correct Python for serve script.

* Sleep before downloading.

* Use correct Python interpreter.

* Avoid failing shebang test.

* Fix permission error with macOS 11.1.

* Avoid shebang trouble.

(cherry picked from commit 7c43cc3faa)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-09 08:10:12 +02:00
patchback[bot]
ad3efa9719 Adds redis_data_info module (#3227) (#3346)
* Added redis_data_info module

Added:
  - redis_data_info module and suggested 'exists' return flag.
  - module_utils for redis with a base class that handles database connections.
  - inhereited unit tests and added some new ones for the exit flag

* Docfix and sanity

* typo

* Suggested doc changes and ssl option

* TLS and validate_certs fix

* Set support_check_mode for info plugin

* Docfix and import errors

* Redis versioning Fix

* version bump and append fixes

(cherry picked from commit 6b207bce4c)

Co-authored-by: Andreas Botzner <paginabianca66@gmail.com>
2021-09-08 13:37:11 +02:00
patchback[bot]
b9c8d2bee5 django_manage - split params (#3334) (#3342)
* django_manage - fix fixures

* docs formatting adjustments

* param apps also in need of splitting

* oops, the splitted version was not being properly added to the command args

* added changelog fragment

* check for None

* moving to shlex.split()

* Update changelogs/fragments/3334-django_manage-split-params.yaml

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit dd25c0d3bf)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-09-07 12:40:33 +02:00
patchback[bot]
2c167547f6 Fix default value of new option. (#3338) (#3341)
(cherry picked from commit a20862797e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-07 06:16:30 +02:00
patchback[bot]
73de447489 zpool_facts - pythonification (#3332) (#3339)
* pythonification

* added changelog fragment

* adjustment per PR

(cherry picked from commit 0f9311c3d9)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-09-06 20:27:45 +02:00
patchback[bot]
134f6132ce nmcli: Support GSM connections (#3313) (#3335)
* nmcli: Support GSM connections

* Add GSM support
* Add GSM unit test

* nmcli: Add changelog fragment

* nmcli: Fix GSM unit test

* Fix copy-paste error in test_gsm_mod

* nmcli: Fix yaml formatting

* nmcli: Fix yaml formatting

* nmcli: Fix typeerror

* type must be str not string

* nmcli: Fix gsm_show_output

* gsm.username did not match input

* nmcli: Fix gsm_show_output

* doublechecked generated output with test-client

* nmcli: GSM fix unit test

* Removed `mocked_gsm_connection_unchanged`

* Revert "nmcli: GSM fix unit test"

This reverts commit 2d112b779a.

* nmcli: gsm fix unit test

* Add needed output to `TESTCASE_GSM_SHOW_OUTPUT`
* Move `mocked_gsm_connection_unchanged`to sort correctly

* nmcli: gsm fix _compare_conn_params

* Strip double-qoutes of gsm.apn if exist

* nmcli: GSM apply suggestions from code review

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* nmcli: GSM: Fix documentation

* Shorten too long lines

* nmcli: GSM apply suggestions from code review

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* nmcli: GSM add version

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 76317d1f64)

Co-authored-by: Manuel Gayer <85677493+nm-mga@users.noreply.github.com>
2021-09-06 18:43:49 +02:00
patchback[bot]
f229c800da redfish: clean etag of quotes before patch (#3296) (#3326)
* Some vendors surround header etag with quotes, which need to be cleaned before sending a patch

* Minor change fragment

* Add etag strip quote option

* Rebase

* Cleanup fragment

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/remote_management/redfish/redfish_command.py

Co-authored-by: Felix Fontein <felix@fontein.de>

* Description update

* Update plugins/modules/remote_management/redfish/redfish_config.py

Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Kyle Williams <kyle.williams@thetradedesk.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 3502f3b486)

Co-authored-by: Kyle Williams <36274986+kyle-williams-1@users.noreply.github.com>
2021-09-02 06:30:17 +02:00
patchback[bot]
52a0970ef8 Fix copr integration tests (#3237) (#3325)
Fixes: #2084
(cherry picked from commit 7c493eb4e5)

Co-authored-by: Silvie Chlupova <33493796+schlupov@users.noreply.github.com>
2021-09-02 06:30:05 +02:00
patchback[bot]
11e0797650 Fix documentation bugs. (#3321) (#3322)
(cherry picked from commit c121e8685f)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-09-01 07:47:41 +02:00
Felix Fontein
e6bbbac6a0 Next expected release is 3.7.0. 2021-09-01 07:09:44 +02:00
383 changed files with 12156 additions and 1408 deletions

View File

@@ -24,14 +24,15 @@ schedules:
always: true
branches:
include:
- stable-2
- stable-3
- stable-4
- cron: 0 11 * * 0
displayName: Weekly (old stable branches)
always: true
branches:
include:
- stable-1
- stable-2
variables:
- name: checkoutPath
@@ -68,6 +69,19 @@ stages:
- test: 3
- test: 4
- test: extra
- stage: Sanity_2_12
displayName: Sanity 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.12/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_11
displayName: Sanity 2.11
dependsOn: []
@@ -117,7 +131,6 @@ stages:
nameFormat: Python {0}
testFormat: devel/units/{0}/1
targets:
- test: 2.6
- test: 2.7
- test: 3.5
- test: 3.6
@@ -125,6 +138,22 @@ stages:
- test: 3.8
- test: 3.9
- test: '3.10'
- stage: Units_2_12
displayName: Units 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.12/units/{0}/1
targets:
- test: 2.6
- test: 2.7
- test: 3.5
- test: 3.6
- test: 3.7
- test: 3.8
- test: '3.10'
- stage: Units_2_11
displayName: Units 2.11
dependsOn: []
@@ -150,13 +179,8 @@ stages:
nameFormat: Python {0}
testFormat: 2.10/units/{0}/1
targets:
- test: 2.6
- test: 2.7
- test: 3.5
- test: 3.6
- test: 3.7
- test: 3.8
- test: 3.9
- stage: Units_2_9
displayName: Units 2.9
dependsOn: []
@@ -186,8 +210,8 @@ stages:
test: macos/11.1
- name: RHEL 7.9
test: rhel/7.9
- name: RHEL 8.4
test: rhel/8.4
- name: RHEL 8.5
test: rhel/8.5
- name: FreeBSD 12.2
test: freebsd/12.2
- name: FreeBSD 13.0
@@ -196,6 +220,23 @@ stages:
- 1
- 2
- 3
- stage: Remote_2_12
displayName: Remote 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.12/{0}
targets:
- name: macOS 11.1
test: macos/11.1
- name: RHEL 8.4
test: rhel/8.4
- name: FreeBSD 13.0
test: freebsd/13.0
groups:
- 1
- 2
- stage: Remote_2_11
displayName: Remote 2.11
dependsOn: []
@@ -204,8 +245,6 @@ stages:
parameters:
testFormat: 2.11/{0}
targets:
- name: macOS 11.1
test: macos/11.1
- name: RHEL 7.9
test: rhel/7.9
- name: RHEL 8.3
@@ -227,14 +266,6 @@ stages:
test: osx/10.11
- name: macOS 10.15
test: macos/10.15
- name: macOS 11.1
test: macos/11.1
- name: RHEL 7.8
test: rhel/7.8
- name: RHEL 8.2
test: rhel/8.2
- name: FreeBSD 12.1
test: freebsd/12.1
groups:
- 1
- 2
@@ -248,6 +279,8 @@ stages:
targets:
- name: RHEL 8.2
test: rhel/8.2
- name: RHEL 7.8
test: rhel/7.8
- name: FreeBSD 12.0
test: freebsd/12.0
groups:
@@ -263,16 +296,12 @@ stages:
parameters:
testFormat: devel/linux/{0}
targets:
- name: CentOS 6
test: centos6
- name: CentOS 7
test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 33
test: fedora33
- name: Fedora 34
test: fedora34
- name: Fedora 35
test: fedora35
- name: openSUSE 15 py2
test: opensuse15py2
- name: openSUSE 15 py3
@@ -285,6 +314,28 @@ stages:
- 1
- 2
- 3
- stage: Docker_2_12
displayName: Docker 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.12/linux/{0}
targets:
- name: CentOS 6
test: centos6
- name: CentOS 8
test: centos8
- name: Fedora 34
test: fedora34
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 20.04
test: ubuntu2004
groups:
- 1
- 2
- 3
- stage: Docker_2_11
displayName: Docker 2.11
dependsOn: []
@@ -293,14 +344,12 @@ stages:
parameters:
testFormat: 2.11/linux/{0}
targets:
- name: CentOS 8
test: centos8
- name: CentOS 7
test: centos7
- name: Fedora 33
test: fedora33
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 20.04
test: ubuntu2004
- name: openSUSE 15 py2
test: opensuse15py2
groups:
- 2
- 3
@@ -312,12 +361,8 @@ stages:
parameters:
testFormat: 2.10/linux/{0}
targets:
- name: CentOS 8
test: centos8
- name: Fedora 32
test: fedora32
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 16.04
test: ubuntu1604
groups:
@@ -331,8 +376,6 @@ stages:
parameters:
testFormat: 2.9/linux/{0}
targets:
- name: CentOS 8
test: centos8
- name: Fedora 31
test: fedora31
- name: openSUSE 15 py3
@@ -350,6 +393,17 @@ stages:
parameters:
nameFormat: Python {0}
testFormat: devel/cloud/{0}/1
targets:
- test: 2.7
- test: 3.9
- stage: Cloud_2_12
displayName: Cloud 2.12
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.12/cloud/{0}/1
targets:
- test: 3.8
- stage: Cloud_2_11
@@ -361,7 +415,6 @@ stages:
nameFormat: Python {0}
testFormat: 2.11/cloud/{0}/1
targets:
- test: 2.7
- test: 3.6
- stage: Cloud_2_10
displayName: Cloud 2.10
@@ -372,7 +425,7 @@ stages:
nameFormat: Python {0}
testFormat: 2.10/cloud/{0}/1
targets:
- test: 3.6
- test: 3.5
- stage: Cloud_2_9
displayName: Cloud 2.9
dependsOn: []
@@ -382,7 +435,7 @@ stages:
nameFormat: Python {0}
testFormat: 2.9/cloud/{0}/1
targets:
- test: 3.6
- test: 2.7
- stage: Summary
condition: succeededOrFailed()
dependsOn:
@@ -390,21 +443,26 @@ stages:
- Sanity_2_9
- Sanity_2_10
- Sanity_2_11
- Sanity_2_12
- Units_devel
- Units_2_9
- Units_2_10
- Units_2_11
- Units_2_12
- Remote_devel
- Remote_2_9
- Remote_2_10
- Remote_2_11
- Remote_2_12
- Docker_devel
- Docker_2_9
- Docker_2_10
- Docker_2_11
- Docker_2_12
- Cloud_devel
- Cloud_2_9
- Cloud_2_10
- Cloud_2_11
- Cloud_2_12
jobs:
- template: templates/coverage.yml

View File

@@ -11,7 +11,7 @@ mkdir "${agent_temp_directory}/coverage/"
options=(--venv --venv-system-site-packages --color -v)
ansible-test coverage combine --export "${agent_temp_directory}/coverage/" "${options[@]}"
ansible-test coverage combine --group-by command --export "${agent_temp_directory}/coverage/" "${options[@]}"
if ansible-test coverage analyze targets generate --help >/dev/null 2>&1; then
# Only analyze coverage if the installed version of ansible-test supports it.

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python
"""
Upload code coverage reports to codecov.io.
Multiple coverage files from multiple languages are accepted and aggregated after upload.
Python coverage, as well as PowerShell and Python stubs can all be uploaded.
"""
import argparse
import dataclasses
import pathlib
import shutil
import subprocess
import tempfile
import typing as t
import urllib.request
@dataclasses.dataclass(frozen=True)
class CoverageFile:
name: str
path: pathlib.Path
flags: t.List[str]
@dataclasses.dataclass(frozen=True)
class Args:
dry_run: bool
path: pathlib.Path
def parse_args() -> Args:
parser = argparse.ArgumentParser()
parser.add_argument('-n', '--dry-run', action='store_true')
parser.add_argument('path', type=pathlib.Path)
args = parser.parse_args()
# Store arguments in a typed dataclass
fields = dataclasses.fields(Args)
kwargs = {field.name: getattr(args, field.name) for field in fields}
return Args(**kwargs)
def process_files(directory: pathlib.Path) -> t.Tuple[CoverageFile, ...]:
processed = []
for file in directory.joinpath('reports').glob('coverage*.xml'):
name = file.stem.replace('coverage=', '')
# Get flags from name
flags = name.replace('-powershell', '').split('=') # Drop '-powershell' suffix
flags = [flag if not flag.startswith('stub') else flag.split('-')[0] for flag in flags] # Remove "-01" from stub files
processed.append(CoverageFile(name, file, flags))
return tuple(processed)
def upload_files(codecov_bin: pathlib.Path, files: t.Tuple[CoverageFile, ...], dry_run: bool = False) -> None:
for file in files:
cmd = [
str(codecov_bin),
'--name', file.name,
'--file', str(file.path),
]
for flag in file.flags:
cmd.extend(['--flags', flag])
if dry_run:
print(f'DRY-RUN: Would run command: {cmd}')
continue
subprocess.run(cmd, check=True)
def download_file(url: str, dest: pathlib.Path, flags: int, dry_run: bool = False) -> None:
if dry_run:
print(f'DRY-RUN: Would download {url} to {dest} and set mode to {flags:o}')
return
with urllib.request.urlopen(url) as resp:
with dest.open('w+b') as f:
# Read data in chunks rather than all at once
shutil.copyfileobj(resp, f, 64 * 1024)
dest.chmod(flags)
def main():
args = parse_args()
url = 'https://ansible-ci-files.s3.amazonaws.com/codecov/linux/codecov'
with tempfile.TemporaryDirectory(prefix='codecov-') as tmpdir:
codecov_bin = pathlib.Path(tmpdir) / 'codecov'
download_file(url, codecov_bin, 0o755, args.dry_run)
files = process_files(args.path)
upload_files(codecov_bin, files, args.dry_run)
if __name__ == '__main__':
main()

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
# Upload code coverage reports to codecov.io.
# Multiple coverage files from multiple languages are accepted and aggregated after upload.
# Python coverage, as well as PowerShell and Python stubs can all be uploaded.
set -o pipefail -eu
output_path="$1"
curl --silent --show-error https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh
for file in "${output_path}"/reports/coverage*.xml; do
name="${file}"
name="${name##*/}" # remove path
name="${name##coverage=}" # remove 'coverage=' prefix if present
name="${name%.xml}" # remove '.xml' suffix
bash codecov.sh \
-f "${file}" \
-n "${name}" \
-X coveragepy \
-X gcov \
-X fix \
-X search \
-X xcode \
|| echo "Failed to upload code coverage report to codecov.io: ${file}"
done

View File

@@ -12,4 +12,4 @@ if ! ansible-test --help >/dev/null 2>&1; then
pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
fi
ansible-test coverage xml --stub --venv --venv-system-site-packages --color -v
ansible-test coverage xml --group-by command --stub --venv --venv-system-site-packages --color -v

View File

@@ -33,7 +33,7 @@ jobs:
summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
displayName: Publish to Azure Pipelines
condition: gt(variables.coverageFileCount, 0)
- bash: .azure-pipelines/scripts/publish-codecov.sh "$(outputPath)"
- bash: .azure-pipelines/scripts/publish-codecov.py "$(outputPath)"
displayName: Publish to codecov.io
condition: gt(variables.coverageFileCount, 0)
continueOnError: true

53
.github/BOTMETA.yml vendored
View File

@@ -1,7 +1,8 @@
notifications: true
automerge: true
files:
plugins/:
supershipit: quidame Ajpantuso
supershipit: quidame
changelogs/: {}
changelogs/fragments/:
support: community
@@ -48,6 +49,9 @@ files:
maintainers: dagwieers
$callbacks/diy.py:
maintainers: theque5t
$callbacks/elastic.py:
maintainers: v1v
keywords: apm observability
$callbacks/hipchat.py: {}
$callbacks/jabber.py: {}
$callbacks/loganalytics.py:
@@ -62,6 +66,9 @@ files:
$callbacks/nrdp.py:
maintainers: rverchere
$callbacks/null.py: {}
$callbacks/opentelemetry.py:
maintainers: v1v
keywords: opentelemetry observability
$callbacks/say.py:
notify: chris-short
maintainers: $team_macos
@@ -131,6 +138,8 @@ files:
$filters/random_mac.py: {}
$filters/time.py:
maintainers: resmo
$filters/unicode_normalize.py:
maintainers: Ajpantuso
$filters/version_sort.py:
maintainers: ericzolf
$inventories/:
@@ -147,9 +156,15 @@ files:
maintainers: conloos
$inventories/nmap.py: {}
$inventories/online.py:
maintainers: sieben
maintainers: remyleone
$inventories/opennebula.py:
maintainers: feldsam
labels: cloud opennebula
keywords: opennebula dynamic inventory script
$inventories/proxmox.py:
maintainers: $team_virt ilijamt
$inventories/icinga2.py:
maintainers: BongoEADGC6
$inventories/scaleway.py:
maintainers: $team_scaleway
labels: cloud scaleway
@@ -173,7 +188,7 @@ files:
$lookups/dnstxt.py:
maintainers: jpmens
$lookups/dsv.py:
maintainers: amigus
maintainers: amigus endlesstrax
$lookups/etcd3.py:
maintainers: eric-belhomme
$lookups/etcd.py:
@@ -209,7 +224,7 @@ files:
maintainers: $team_ansible_core jpmens
$lookups/shelvefile.py: {}
$lookups/tss.py:
maintainers: amigus
maintainers: amigus endlesstrax
$module_utils/:
labels: module_utils
$module_utils/gitlab.py:
@@ -305,6 +320,10 @@ files:
$modules/cloud/misc/proxmox_kvm.py:
maintainers: helldorado
ignore: skvidal
$modules/cloud/misc/proxmox_nic.py:
maintainers: Kogelvis
$modules/cloud/misc/proxmox_tasks_info:
maintainers: paginabianca
$modules/cloud/misc/proxmox_template.py:
maintainers: UnderGreen
ignore: skvidal
@@ -324,7 +343,7 @@ files:
$modules/cloud/oneandone/:
maintainers: aajdinov edevenport
$modules/cloud/online/:
maintainers: sieben
maintainers: remyleone
$modules/cloud/opennebula/:
maintainers: $team_opennebula
$modules/cloud/opennebula/one_host.py:
@@ -394,11 +413,11 @@ files:
$modules/cloud/scaleway/scaleway_ip_info.py:
maintainers: Spredzy
$modules/cloud/scaleway/scaleway_organization_info.py:
maintainers: sieben Spredzy
maintainers: Spredzy
$modules/cloud/scaleway/scaleway_security_group.py:
maintainers: DenBeke
$modules/cloud/scaleway/scaleway_security_group_info.py:
maintainers: sieben Spredzy
maintainers: Spredzy
$modules/cloud/scaleway/scaleway_security_group_rule.py:
maintainers: DenBeke
$modules/cloud/scaleway/scaleway_server_info.py:
@@ -458,6 +477,10 @@ files:
maintainers: slok
$modules/database/misc/redis_info.py:
maintainers: levonet
$modules/database/misc/redis_data_info.py:
maintainers: paginabianca
$modules/database/misc/redis_data.py:
maintainers: paginabianca
$modules/database/misc/riak.py:
maintainers: drewkerrigan jsmartin
$modules/database/mssql/mssql_db.py:
@@ -512,6 +535,8 @@ files:
maintainers: kris2kris
$modules/identity/keycloak/keycloak_role.py:
maintainers: laurpaum
$modules/identity/keycloak/keycloak_user_federation.py:
maintainers: laurpaum
$modules/identity/onepassword_info.py:
maintainers: Rylon
$modules/identity/opendj/opendj_backendprop.py:
@@ -740,6 +765,8 @@ files:
ignore: jle64
$modules/packaging/language/pip_package_info.py:
maintainers: bcoca matburt maxamillion
$modules/packaging/language/pipx.py:
maintainers: russoz
$modules/packaging/language/yarn.py:
maintainers: chrishoffman verkaufer
$modules/packaging/os/apk.py:
@@ -891,6 +918,10 @@ files:
$modules/remote_management/manageiq/:
labels: manageiq
maintainers: $team_manageiq
$modules/remote_management/manageiq/manageiq_alert_profiles.py:
maintainers: elad661
$modules/remote_management/manageiq/manageiq_alerts.py:
maintainers: elad661
$modules/remote_management/manageiq/manageiq_group.py:
maintainers: evertmulder
$modules/remote_management/manageiq/manageiq_tenant.py:
@@ -1140,6 +1171,10 @@ files:
maintainers: nerzhul
$modules/web_infrastructure/rundeck_project.py:
maintainers: nerzhul
$modules/web_infrastructure/rundeck_job_run.py:
maintainers: phsmith
$modules/web_infrastructure/rundeck_job_executions_info.py:
maintainers: phsmith
$modules/web_infrastructure/sophos_utm/:
maintainers: $team_e_spirit
keywords: sophos utm
@@ -1194,7 +1229,7 @@ macros:
team_cyberark_conjur: jvanderhoof ryanprior
team_e_spirit: MatrixCrawler getjack
team_flatpak: JayKayy oolongbrothers
team_gitlab: Lunik Shaps dj-wasabi marwatk waheedi zanssa scodeman metanovii
team_gitlab: Lunik Shaps dj-wasabi marwatk waheedi zanssa scodeman metanovii sh0shin
team_hpux: bcoca davx8342
team_huawei: QijunPan TommyLike edisonxiang freesky-edward hwDCN niuzhenguo xuxiaowei0512 yanzhangi zengchen1024 zhongjun2
team_ipa: Akasurde Nosmoht fxfitz justchris1
@@ -1209,7 +1244,7 @@ macros:
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
team_redfish: mraineri tomasg2012 xmadsen renxulei
team_rhn: FlossWare alikins barnabycourt vritant
team_scaleway: QuentinBrosse abarbare jerome-quere kindermoumoute remyleone sieben
team_scaleway: remyleone abarbare
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
team_virt: joshainglis karmab tleguern Thulium-Drake Ajpantuso

View File

@@ -6,6 +6,244 @@ Community General Release Notes
This changelog describes changes after version 2.0.0.
v3.8.4
======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- Various modules and plugins - use vendored version of ``distutils.version`` instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.general/pull/3936).
- alternatives - fix output parsing for alternatives groups (https://github.com/ansible-collections/community.general/pull/3976).
- jail connection plugin - replace deprecated ``distutils.spawn.find_executable`` with Ansible's ``get_bin_path`` to find the executable (https://github.com/ansible-collections/community.general/pull/3934).
- jira - fixed bug where module returns error related to dictionary key ``body`` (https://github.com/ansible-collections/community.general/issues/3419).
- lxd connection plugin - replace deprecated ``distutils.spawn.find_executable`` with Ansible's ``get_bin_path`` to find the ``lxc`` executable (https://github.com/ansible-collections/community.general/pull/3934).
- nrdp callback plugin - fix error ``string arguments without an encoding`` (https://github.com/ansible-collections/community.general/issues/3903).
- passwordstore lookup plugin - replace deprecated ``distutils.util.strtobool`` with Ansible's ``convert_bool.boolean`` to interpret values for the ``create``, ``returnall``, ``overwrite``, 'backup``, and ``nosymbols`` options (https://github.com/ansible-collections/community.general/pull/3934).
- say callback plugin - replace deprecated ``distutils.spawn.find_executable`` with Ansible's ``get_bin_path`` to find the ``say`` resp. ``espeak`` executables (https://github.com/ansible-collections/community.general/pull/3934).
- scaleway_user_data - fix double-quote added where no double-quote is needed to user data in scaleway's server (``Content-type`` -> ``Content-Type``) (https://github.com/ansible-collections/community.general/pull/3940).
- slack - add ``charset`` to HTTP headers to avoid Slack API warning (https://github.com/ansible-collections/community.general/issues/3932).
- zone connection plugin - replace deprecated ``distutils.spawn.find_executable`` with Ansible's ``get_bin_path`` to find the executable (https://github.com/ansible-collections/community.general/pull/3934).
v3.8.3
======
Release Summary
---------------
Regular bugfix release.
Minor Changes
-------------
- lxd connection plugin - make sure that ``ansible_lxd_host``, ``ansible_executable``, and ``ansible_lxd_executable`` work (https://github.com/ansible-collections/community.general/pull/3798).
Bugfixes
--------
- interfaces_file - fixed the check for existing option in interface (https://github.com/ansible-collections/community.general/issues/3841).
- nmcli - fix returning "changed" when no mask set for IPv4 or IPv6 addresses on task rerun (https://github.com/ansible-collections/community.general/issues/3768).
- nmcli - pass ``flags``, ``ingress``, ``egress`` params to ``nmcli`` (https://github.com/ansible-collections/community.general/issues/1086).
- opentelemetry_plugin - honour ``ignore_errors`` when a task has failed instead of reporting an error (https://github.com/ansible-collections/community.general/pull/3837).
- pipx - passes the correct command line option ``--include-apps`` (https://github.com/ansible-collections/community.general/issues/3791).
- proxmox - fixed ``onboot`` parameter causing module failures when undefined (https://github.com/ansible-collections/community.general/issues/3844).
v3.8.2
======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- counter_enabled callback plugin - fix output to correctly display host and task counters in serial mode (https://github.com/ansible-collections/community.general/pull/3709).
- ldap_search - allow it to be used even in check mode (https://github.com/ansible-collections/community.general/issues/3619).
- lvol - allows logical volumes to be created with certain size arguments prefixed with ``+`` to preserve behavior of older versions of this module (https://github.com/ansible-collections/community.general/issues/3665).
- nmcli - fixed falsely reported changed status when ``mtu`` is omitted with ``dummy`` connections (https://github.com/ansible-collections/community.general/issues/3612, https://github.com/ansible-collections/community.general/pull/3625).
- terraform - fix command options being ignored during planned/plan in function ``build_plan`` such as ``lock`` or ``lock_timeout`` (https://github.com/ansible-collections/community.general/issues/3707, https://github.com/ansible-collections/community.general/pull/3726).
- xattr - fix exception caused by ``_run_xattr()`` raising a ``ValueError`` due to a mishandling of base64-encoded value (https://github.com/ansible-collections/community.general/issues/3673).
v3.8.1
======
Release Summary
---------------
Regular bugfix release.
Bugfixes
--------
- gitlab_deploy_key - fix the SSH Deploy Key being deleted accidentally while running task in check mode (https://github.com/ansible-collections/community.general/issues/3621, https://github.com/ansible-collections/community.general/pull/3622).
- gitlab_project_members - ``get_project_id`` return the project id by matching ``full_path`` or ``name`` (https://github.com/ansible-collections/community.general/pull/3602).
- ipa_* modules - fix environment fallback for ``ipa_host`` option (https://github.com/ansible-collections/community.general/issues/3560).
- nmcli - fixed ``dns6`` option handling so that it is treated as a list internally (https://github.com/ansible-collections/community.general/pull/3563).
- nmcli - fixed ``ipv4.route-metric`` being in properties of type list (https://github.com/ansible-collections/community.general/pull/3563).
- one_image - fix error message when renaming an image (https://github.com/ansible-collections/community.general/pull/3626).
- pipx - ``state=inject`` was failing to parse the list of injected packages (https://github.com/ansible-collections/community.general/pull/3611).
- pipx - set environment variable ``USE_EMOJI=0`` to prevent errors in platforms that do not support ``UTF-8`` (https://github.com/ansible-collections/community.general/pull/3611).
- pkgin - Fix exception encountered when all packages are already installed (https://github.com/ansible-collections/community.general/pull/3583).
- proxmox_group_info - fix module crash if a ``group`` parameter is used (https://github.com/ansible-collections/community.general/pull/3649).
- redfish_utils module utils - do not attempt to change the boot source override mode if not specified by the user (https://github.com/ansible-collections/community.general/issues/3509/).
- redfish_utils module utils - if a manager network property is not specified in the service, attempt to change the requested settings (https://github.com/ansible-collections/community.general/issues/3404/).
v3.8.0
======
Release Summary
---------------
Regular feature and bugfix release. Please note that this is the last minor 3.x.0 release; afterwards there will only be bugfix releases 3.8.y.
Minor Changes
-------------
- mail - added the ``ehlohost`` parameter which allows for manual override of the host used in SMTP EHLO (https://github.com/ansible-collections/community.general/pull/3425).
- nmcli - the option ``routing_rules4`` can now be specified as a list of strings, instead of as a single string (https://github.com/ansible-collections/community.general/issues/3401).
- open-iscsi - adding support for mutual authentication between target and initiator (https://github.com/ansible-collections/community.general/pull/3422).
- opentelemetry callback plugin - added option ``enable_from_environment`` to support enabling the plugin only if the given environment variable exists and it is set to true (https://github.com/ansible-collections/community.general/pull/3498).
- opentelemetry callback plugin - enriched the stacktrace information with the ``message``, ``exception`` and ``stderr`` fields from the failed task (https://github.com/ansible-collections/community.general/pull/3496).
- pkgng - packages being installed (or upgraded) are acted on in one command (per action) (https://github.com/ansible-collections/community.general/issues/2265).
- pkgng - status message specifies number of packages installed and/or upgraded separately. Previously, all changes were reported as one count of packages "added" (https://github.com/ansible-collections/community.general/pull/3393).
- terraform - add ``parallelism`` parameter (https://github.com/ansible-collections/community.general/pull/3540).
- ufw - if ``delete=true`` and ``insert`` option is present, then ``insert`` is now ignored rather than failing with a syntax error (https://github.com/ansible-collections/community.general/pull/3514).
Bugfixes
--------
- gitlab_deploy_key - fix idempotency on projects with multiple deploy keys (https://github.com/ansible-collections/community.general/pull/3473).
- gitlab_group - avoid passing wrong value for ``require_two_factor_authentication`` on creation when the option has not been specified (https://github.com/ansible-collections/community.general/pull/3453).
- gitlab_group_members - ``get_group_id`` return the group ID by matching ``full_path``, ``path`` or ``name`` (https://github.com/ansible-collections/community.general/pull/3400).
- jboss - fix the deployment file permission issue when Jboss server is running under non-root user. The deployment file is copied with file content only. The file permission is set to ``440`` and belongs to root user. When the JBoss ``WildFly`` server is running under non-root user, it is unable to read the deployment file (https://github.com/ansible-collections/community.general/pull/3426).
- keycloak_authentication - fix bug, the requirement was always on ``DISABLED`` when creating a new authentication flow (https://github.com/ansible-collections/community.general/pull/3330).
- keycloak_identity_provider - fix change detection when updating identity provider mappers (https://github.com/ansible-collections/community.general/pull/3538, https://github.com/ansible-collections/community.general/issues/3537).
- keycloak_role - quote role name when used in URL path to avoid errors when role names contain special characters (https://github.com/ansible-collections/community.general/issues/3535, https://github.com/ansible-collections/community.general/pull/3536).
- logstash callback plugin - replace ``_option`` with ``context.CLIARGS`` to fix the plugin on ansible-base and ansible-core (https://github.com/ansible-collections/community.general/issues/2692).
- macports - add ``stdout`` and ``stderr`` to return values (https://github.com/ansible-collections/community.general/issues/3499).
- opentelemetry callback plugin - validated the task result exception without crashing. Also simplifying code a bit (https://github.com/ansible-collections/community.general/pull/3450, https://github.com/ansible/ansible/issues/75726).
- yaml callback plugin - avoid modifying PyYAML so that other plugins using it on the controller, like the ``to_yaml`` filter, do not produce different output (https://github.com/ansible-collections/community.general/issues/3471, https://github.com/ansible-collections/community.general/pull/3478).
- zypper_repository - when an URL to a .repo file was provided in option ``repo=`` and ``state=present`` only the first run was successful, future runs failed due to missing checks prior starting zypper. Usage of ``state=absent`` in combination with a .repo file was not working either (https://github.com/ansible-collections/community.general/issues/1791, https://github.com/ansible-collections/community.general/issues/3466).
New Plugins
-----------
Callback
~~~~~~~~
- elastic - Create distributed traces for each Ansible task in Elastic APM
Inventory
~~~~~~~~~
- opennebula - OpenNebula inventory source
New Modules
-----------
Cloud
~~~~~
misc
^^^^
- proxmox_tasks_info - Retrieve information about one or more Proxmox VE tasks
Packaging
~~~~~~~~~
language
^^^^^^^^
- pipx - Manages applications installed with pipx
Web Infrastructure
~~~~~~~~~~~~~~~~~~
- rundeck_job_executions_info - Query executions for a Rundeck job
- rundeck_job_run - Run a Rundeck job
v3.7.0
======
Release Summary
---------------
Regular feature and bugfix release.
Minor Changes
-------------
- gitlab_group - add new options ``project_creation_level``, ``auto_devops_enabled``, ``subgroup_creation_level`` (https://github.com/ansible-collections/community.general/pull/3248).
- gitlab_group - add new property ``require_two_factor_authentication`` (https://github.com/ansible-collections/community.general/pull/3367).
- gitlab_project - add new properties ``ci_config_path`` and ``shared_runners_enabled`` (https://github.com/ansible-collections/community.general/pull/3379).
- gitlab_project_members - ``gitlab_user`` can now also be a list of users (https://github.com/ansible-collections/community.general/pull/3319).
- gitlab_project_members - added functionality to set all members exactly as given (https://github.com/ansible-collections/community.general/pull/3319).
- gitlab_runner - support project-scoped gitlab.com runners registration (https://github.com/ansible-collections/community.general/pull/634).
- interfaces_file - minor refactor (https://github.com/ansible-collections/community.general/pull/3328).
- ipa_config - add ``ipaselinuxusermaporder`` option to set the SELinux user map order (https://github.com/ansible-collections/community.general/pull/3178).
- kernel_blacklist - revamped the module using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/3329).
- lxd_container - add ``ignore_volatile_options`` option which allows to disable the behavior that the module ignores options starting with ``volatile.`` (https://github.com/ansible-collections/community.general/pull/3331).
- nmcli - add ``gsm`` support (https://github.com/ansible-collections/community.general/pull/3313).
- pids - refactor to add support for older ``psutil`` versions to the ``pattern`` option (https://github.com/ansible-collections/community.general/pull/3315).
- redfish_command and redfish_config and redfish_utils module utils - add parameter to strip etag of quotes before patch, since some vendors do not properly ``If-Match`` etag with quotes (https://github.com/ansible-collections/community.general/pull/3296).
- tss lookup plugin - added ``token`` parameter for token authorization; ``username`` and ``password`` are optional when ``token`` is provided (https://github.com/ansible-collections/community.general/pull/3327).
- zpool_facts - minor refactoring (https://github.com/ansible-collections/community.general/pull/3332).
Bugfixes
--------
- copr - fix chroot naming issues, ``centos-stream`` changed naming to ``centos-stream-<number>`` (for exmaple ``centos-stream-8``) (https://github.com/ansible-collections/community.general/issues/2084, https://github.com/ansible-collections/community.general/pull/3237).
- django_manage - parameters ``apps`` and ``fixtures`` are now splitted instead of being used as a single argument (https://github.com/ansible-collections/community.general/issues/3333).
- interfaces_file - no longer reporting change when none happened (https://github.com/ansible-collections/community.general/pull/3328).
- linode inventory plugin - fix default value of new option ``ip_style`` (https://github.com/ansible-collections/community.general/issues/3337).
- openbsd_pkg - fix crash from ``KeyError`` exception when package installs, but ``pkg_add`` returns with a non-zero exit code (https://github.com/ansible-collections/community.general/pull/3336).
- redfish_utils module utils - if given, add account ID of user that should be created to HTTP request (https://github.com/ansible-collections/community.general/pull/3343/).
New Plugins
-----------
Callback
~~~~~~~~
- opentelemetry - Create distributed traces with OpenTelemetry
Filter
~~~~~~
- unicode_normalize - Normalizes unicode strings to facilitate comparison of characters with normalized forms
Inventory
~~~~~~~~~
- icinga2 - Icinga2 inventory source
New Modules
-----------
Database
~~~~~~~~
misc
^^^^
- redis_data - Set key value pairs in Redis
- redis_data_info - Get value of key in Redis database
Identity
~~~~~~~~
keycloak
^^^^^^^^
- keycloak_user_federation - Allows administration of Keycloak user federations via Keycloak API
v3.6.0
======
@@ -725,7 +963,7 @@ Deprecated Features
- puppet - deprecated undocumented parameter ``show_diff``, will be removed in 7.0.0. (https://github.com/ansible-collections/community.general/pull/1927).
- runit - unused parameter ``dist`` marked for deprecation (https://github.com/ansible-collections/community.general/pull/1830).
- slackpkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- urmpi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- urpmi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- xbps - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- xfconf - returning output as facts is deprecated, this will be removed in community.general 4.0.0. Please register the task output in a variable and use it instead. You can already switch to the new behavior now by using the new ``disable_facts`` option (https://github.com/ansible-collections/community.general/pull/1747).

View File

@@ -26,6 +26,7 @@ Also, consider taking up a valuable, reviewed, but abandoned pull request which
* Do not squash your commits and force-push to your branch if not needed. Reviews of your pull request are much easier with individual commits to comprehend the pull request history. All commits of your pull request branch will be squashed into one commit by GitHub upon merge.
* Do not add merge commits to your PR. The bot will complain and you will have to rebase ([instructions for rebasing](https://docs.ansible.com/ansible/latest/dev_guide/developing_rebasing.html)) to remove them before your PR can be merged. To avoid that git automatically does merges during pulls, you can configure it to do rebases instead by running `git config pull.rebase true` inside the respository checkout.
* Make sure your PR includes a [changelog fragment](https://docs.ansible.com/ansible/devel/community/development_process.html#changelogs-how-to). (You must not include a fragment for new modules or new plugins, except for test and filter plugins. Also you shouldn't include one for docs-only changes. If you're not sure, simply don't include one, we'll tell you whether one is needed or not :) )
* Avoid reformatting unrelated parts of the codebase in your PR. These types of changes will likely be requested for reversion, create additional work for reviewers, and may cause approval to be delayed.
You can also read [our Quick-start development guide](https://github.com/ansible/community-docs/blob/main/create_pr_quick_start_guide.rst).
@@ -42,7 +43,12 @@ Creating new modules and plugins requires a bit more work than other Pull Reques
1. Please make sure that your new module or plugin is of interest to a larger audience. Very specialized modules or plugins that
can only be used by very few people should better be added to more specialized collections.
2. When creating a new module or plugin, please make sure that you follow various guidelines:
2. Please do not add more than one plugin/module in one PR, especially if it is the first plugin/module you are contributing.
That makes it easier for reviewers, and increases the chance that your PR will get merged. If you plan to contribute a group
of plugins/modules (say, more than a module and a corresponding ``_info`` module), please mention that in the first PR. In
such cases, you also have to think whether it is better to publish the group of plugins/modules in a new collection.
3. When creating a new module or plugin, please make sure that you follow various guidelines:
- Follow [development conventions](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_best_practices.html);
- Follow [documentation standards](https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html) and
@@ -52,7 +58,7 @@ Creating new modules and plugins requires a bit more work than other Pull Reques
- Make sure that new plugins and modules have tests (unit tests, integration tests, or both); it is preferable to have some tests
which run in CI.
3. For modules and action plugins, make sure to create your module/plugin in the correct subdirectory, and create a symbolic link
4. For modules and action plugins, make sure to create your module/plugin in the correct subdirectory, and create a symbolic link
from `plugins/modules/` respectively `plugins/action/` to the actual module/plugin code. (Other plugin types should not use
subdirectories.)
@@ -60,7 +66,7 @@ Creating new modules and plugins requires a bit more work than other Pull Reques
(`DOCUMENTATION`, `EXAMPLES` and `RETURN`). The module must have the same name and directory path in `plugins/modules/`
than the action plugin has in `plugins/action/`.
4. Make sure to add a BOTMETA entry for your new module/plugin in `.github/BOTMETA.yml`. Search for other plugins/modules in the
5. Make sure to add a BOTMETA entry for your new module/plugin in `.github/BOTMETA.yml`. Search for other plugins/modules in the
same directory to see how entries could look. You should list all authors either as `maintainers` or under `ignore`. People
listed as `maintainers` will be pinged for new issues and PRs that modify the module/plugin or its tests.

View File

@@ -17,7 +17,7 @@ If you encounter abusive behavior violating the [Ansible Code of Conduct](https:
## Tested with Ansible
Tested with the current Ansible 2.9, ansible-base 2.10 and ansible-core 2.11 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
Tested with the current Ansible 2.9, ansible-base 2.10, ansible-core 2.11, ansible-core 2.12 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
## External requirements
@@ -76,7 +76,21 @@ Also for some notes specific to this collection see [our CONTRIBUTING documentat
See [here](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#testing-collections).
### Communication
## Collection maintenance
To learn how to maintain / become a maintainer of this collection, refer to:
* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/main/commit-rights.md).
* [Maintainer guidelines](https://github.com/ansible/community-docs/blob/main/maintaining.rst).
It is necessary for maintainers of this collection to be subscribed to:
* The collection itself (the `Watch` button → `All Activity` in the upper right corner of the repository's homepage).
* The "Changes Impacting Collection Contributors and Maintainers" [issue](https://github.com/ansible-collections/overview/issues/45).
They also should be subscribed to Ansible's [The Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn).
## Communication
We announce important development changes and releases through Ansible's [The Bullhorn newsletter](https://eepurl.com/gZmiEP). If you are a collection developer, be sure you are subscribed.
@@ -86,16 +100,11 @@ We take part in the global quarterly [Ansible Contributor Summit](https://github
For more information about communities, meetings and agendas see [Community Wiki](https://github.com/ansible/community/wiki/Community).
For more information about communication, refer to the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
For more information about communication, refer to Ansible's the [Communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
### Publishing New Version
## Publishing New Version
Basic instructions without release branches:
1. Create `changelogs/fragments/<version>.yml` with `release_summary:` section (which must be a string, not a list).
2. Run `antsibull-changelog release --collection-flatmap yes`
3. Make sure `CHANGELOG.rst` and `changelogs/changelog.yaml` are added to git, and the deleted fragments have been removed.
4. Tag the commit with `<version>`. Push changes and tag to the main repository.
See the [Releasing guidelines](https://github.com/ansible/community-docs/blob/main/releasing_collections.rst) to learn how to release this collection.
## Release notes
@@ -103,10 +112,10 @@ See the [changelog](https://github.com/ansible-collections/community.general/blo
## Roadmap
See [this issue](https://github.com/ansible-collections/community.general/issues/582) for information on releasing, versioning and deprecation.
In general, we plan to release a major version every six months, and minor versions every two months. Major versions can contain breaking changes, while minor versions only contain new features and bugfixes.
See [this issue](https://github.com/ansible-collections/community.general/issues/582) for information on releasing, versioning, and deprecation.
## More information
- [Ansible Collection overview](https://github.com/ansible-collections/overview)

View File

@@ -286,7 +286,7 @@ releases:
- runit - unused parameter ``dist`` marked for deprecation (https://github.com/ansible-collections/community.general/pull/1830).
- slackpkg - deprecated invalid parameter alias ``update-cache``, will be removed
in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- urmpi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``,
- urpmi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``,
will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- xbps - deprecated invalid parameter alias ``update-cache``, will be removed
in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
@@ -1747,3 +1747,322 @@ releases:
name: keycloak_identity_provider
namespace: identity.keycloak
release_date: '2021-08-31'
3.7.0:
changes:
bugfixes:
- copr - fix chroot naming issues, ``centos-stream`` changed naming to ``centos-stream-<number>``
(for exmaple ``centos-stream-8``) (https://github.com/ansible-collections/community.general/issues/2084,
https://github.com/ansible-collections/community.general/pull/3237).
- django_manage - parameters ``apps`` and ``fixtures`` are now splitted instead
of being used as a single argument (https://github.com/ansible-collections/community.general/issues/3333).
- interfaces_file - no longer reporting change when none happened (https://github.com/ansible-collections/community.general/pull/3328).
- linode inventory plugin - fix default value of new option ``ip_style`` (https://github.com/ansible-collections/community.general/issues/3337).
- openbsd_pkg - fix crash from ``KeyError`` exception when package installs,
but ``pkg_add`` returns with a non-zero exit code (https://github.com/ansible-collections/community.general/pull/3336).
- redfish_utils module utils - if given, add account ID of user that should
be created to HTTP request (https://github.com/ansible-collections/community.general/pull/3343/).
minor_changes:
- gitlab_group - add new options ``project_creation_level``, ``auto_devops_enabled``,
``subgroup_creation_level`` (https://github.com/ansible-collections/community.general/pull/3248).
- gitlab_group - add new property ``require_two_factor_authentication`` (https://github.com/ansible-collections/community.general/pull/3367).
- gitlab_project - add new properties ``ci_config_path`` and ``shared_runners_enabled``
(https://github.com/ansible-collections/community.general/pull/3379).
- gitlab_project_members - ``gitlab_user`` can now also be a list of users (https://github.com/ansible-collections/community.general/pull/3319).
- gitlab_project_members - added functionality to set all members exactly as
given (https://github.com/ansible-collections/community.general/pull/3319).
- gitlab_runner - support project-scoped gitlab.com runners registration (https://github.com/ansible-collections/community.general/pull/634).
- interfaces_file - minor refactor (https://github.com/ansible-collections/community.general/pull/3328).
- ipa_config - add ``ipaselinuxusermaporder`` option to set the SELinux user
map order (https://github.com/ansible-collections/community.general/pull/3178).
- kernel_blacklist - revamped the module using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/3329).
- lxd_container - add ``ignore_volatile_options`` option which allows to disable
the behavior that the module ignores options starting with ``volatile.`` (https://github.com/ansible-collections/community.general/pull/3331).
- nmcli - add ``gsm`` support (https://github.com/ansible-collections/community.general/pull/3313).
- pids - refactor to add support for older ``psutil`` versions to the ``pattern``
option (https://github.com/ansible-collections/community.general/pull/3315).
- redfish_command and redfish_config and redfish_utils module utils - add parameter
to strip etag of quotes before patch, since some vendors do not properly ``If-Match``
etag with quotes (https://github.com/ansible-collections/community.general/pull/3296).
- tss lookup plugin - added ``token`` parameter for token authorization; ``username``
and ``password`` are optional when ``token`` is provided (https://github.com/ansible-collections/community.general/pull/3327).
- zpool_facts - minor refactoring (https://github.com/ansible-collections/community.general/pull/3332).
release_summary: Regular feature and bugfix release.
fragments:
- 3.7.0.yml
- 3178-add-ipaselinuxusermaporder-to-ipa-config-module.yml
- 3237-copr-fix_chroot_naming.yml
- 3248-adds-few-more-gitlab-group-options.yml
- 3296-clean-etag.yaml
- 3313-nmcli-add_gsm_support.yml
- 3315-pids-refactor.yml
- 3319-gitlab_project_members_enhancement.yml
- 3327-tss-token-authorization.yml
- 3328-interfaces_file-improvements.yaml
- 3329-kernel_blacklist-improvements.yaml
- 3331-do_not_ignore_volatile_configs_by_option.yml
- 3332-zpool_facts-pythonify.yaml
- 3334-django_manage-split-params.yaml
- 3336-openbsd_pkg-fix-KeyError.yml
- 3337-linode-fix.yml
- 3343-redfish_utils-addUser-userId.yml
- 3359-add-unicode_normalize-filter.yml
- 3367-add-require_two_factor_authentication-property-to-gitlab-group.yml
- 3379-gitlab_project-ci_cd_properties.yml
- 634-gitlab_project_runners.yaml
modules:
- description: Allows administration of Keycloak user federations via Keycloak
API
name: keycloak_user_federation
namespace: identity.keycloak
- description: Set key value pairs in Redis
name: redis_data
namespace: database.misc
- description: Get value of key in Redis database
name: redis_data_info
namespace: database.misc
plugins:
callback:
- description: Create distributed traces with OpenTelemetry
name: opentelemetry
namespace: null
filter:
- description: Normalizes unicode strings to facilitate comparison of characters
with normalized forms
name: unicode_normalize
namespace: null
inventory:
- description: Icinga2 inventory source
name: icinga2
namespace: null
release_date: '2021-09-21'
3.8.0:
changes:
bugfixes:
- gitlab_deploy_key - fix idempotency on projects with multiple deploy keys
(https://github.com/ansible-collections/community.general/pull/3473).
- gitlab_group - avoid passing wrong value for ``require_two_factor_authentication``
on creation when the option has not been specified (https://github.com/ansible-collections/community.general/pull/3453).
- gitlab_group_members - ``get_group_id`` return the group ID by matching ``full_path``,
``path`` or ``name`` (https://github.com/ansible-collections/community.general/pull/3400).
- jboss - fix the deployment file permission issue when Jboss server is running
under non-root user. The deployment file is copied with file content only.
The file permission is set to ``440`` and belongs to root user. When the JBoss
``WildFly`` server is running under non-root user, it is unable to read the
deployment file (https://github.com/ansible-collections/community.general/pull/3426).
- keycloak_authentication - fix bug, the requirement was always on ``DISABLED``
when creating a new authentication flow (https://github.com/ansible-collections/community.general/pull/3330).
- keycloak_identity_provider - fix change detection when updating identity provider
mappers (https://github.com/ansible-collections/community.general/pull/3538,
https://github.com/ansible-collections/community.general/issues/3537).
- keycloak_role - quote role name when used in URL path to avoid errors when
role names contain special characters (https://github.com/ansible-collections/community.general/issues/3535,
https://github.com/ansible-collections/community.general/pull/3536).
- logstash callback plugin - replace ``_option`` with ``context.CLIARGS`` to
fix the plugin on ansible-base and ansible-core (https://github.com/ansible-collections/community.general/issues/2692).
- macports - add ``stdout`` and ``stderr`` to return values (https://github.com/ansible-collections/community.general/issues/3499).
- opentelemetry callback plugin - validated the task result exception without
crashing. Also simplifying code a bit (https://github.com/ansible-collections/community.general/pull/3450,
https://github.com/ansible/ansible/issues/75726).
- yaml callback plugin - avoid modifying PyYAML so that other plugins using
it on the controller, like the ``to_yaml`` filter, do not produce different
output (https://github.com/ansible-collections/community.general/issues/3471,
https://github.com/ansible-collections/community.general/pull/3478).
- zypper_repository - when an URL to a .repo file was provided in option ``repo=``
and ``state=present`` only the first run was successful, future runs failed
due to missing checks prior starting zypper. Usage of ``state=absent`` in
combination with a .repo file was not working either (https://github.com/ansible-collections/community.general/issues/1791,
https://github.com/ansible-collections/community.general/issues/3466).
minor_changes:
- mail - added the ``ehlohost`` parameter which allows for manual override of
the host used in SMTP EHLO (https://github.com/ansible-collections/community.general/pull/3425).
- nmcli - the option ``routing_rules4`` can now be specified as a list of strings,
instead of as a single string (https://github.com/ansible-collections/community.general/issues/3401).
- open-iscsi - adding support for mutual authentication between target and initiator
(https://github.com/ansible-collections/community.general/pull/3422).
- opentelemetry callback plugin - added option ``enable_from_environment`` to
support enabling the plugin only if the given environment variable exists
and it is set to true (https://github.com/ansible-collections/community.general/pull/3498).
- opentelemetry callback plugin - enriched the stacktrace information with the
``message``, ``exception`` and ``stderr`` fields from the failed task (https://github.com/ansible-collections/community.general/pull/3496).
- pkgng - packages being installed (or upgraded) are acted on in one command
(per action) (https://github.com/ansible-collections/community.general/issues/2265).
- pkgng - status message specifies number of packages installed and/or upgraded
separately. Previously, all changes were reported as one count of packages
"added" (https://github.com/ansible-collections/community.general/pull/3393).
- terraform - add ``parallelism`` parameter (https://github.com/ansible-collections/community.general/pull/3540).
- ufw - if ``delete=true`` and ``insert`` option is present, then ``insert``
is now ignored rather than failing with a syntax error (https://github.com/ansible-collections/community.general/pull/3514).
release_summary: Regular feature and bugfix release. Please note that this is
the last minor 3.x.0 release; afterwards there will only be bugfix releases
3.8.y.
fragments:
- 2692-logstash-callback-plugin-replacing_options.yml
- 3.8.0.yml
- 3330-bugfix-keycloak-authentication-flow-requirements-not-set-correctly.yml.yml
- 3393-pkgng-many_packages_one_command.yml
- 3400-fix-gitLab-api-searches-always-return-first-found-match-3386.yml
- 3401-nmcli-needs-type.yml
- 3422-open-iscsi-mutual-authentication-support.yaml
- 3425-mail_add_configurable_ehlo_hostname.yml
- 3426-copy-permissions-along-with-file-for-jboss-module.yml
- 3450-callback_opentelemetry-exception_handling.yml
- 3453-fix-gitlab_group-require_two_factor_authentication-cant_be_null.yml
- 3473-gitlab_deploy_key-fix_idempotency.yml
- 3474-zypper_repository_improve_repo_file_idempotency.yml
- 3478-yaml-callback.yml
- 3496-callback_opentelemetry-enrich_stacktraces.yml
- 3498-callback_opentelemetry-only_in_ci.yml
- 3500-macports-add-stdout-and-stderr-to-status.yaml
- 3514-ufw_insert_or_delete_biased_when_deletion_enabled.yml
- 3536-quote-role-name-in-url.yml
- 3538-fix-keycloak-idp-mappers-change-detection.yml
- 3540-terraform_add_parallelism_parameter.yml
modules:
- description: Manages applications installed with pipx
name: pipx
namespace: packaging.language
- description: Retrieve information about one or more Proxmox VE tasks
name: proxmox_tasks_info
namespace: cloud.misc
- description: Query executions for a Rundeck job
name: rundeck_job_executions_info
namespace: web_infrastructure
- description: Run a Rundeck job
name: rundeck_job_run
namespace: web_infrastructure
plugins:
callback:
- description: Create distributed traces for each Ansible task in Elastic APM
name: elastic
namespace: null
inventory:
- description: OpenNebula inventory source
name: opennebula
namespace: null
release_date: '2021-10-12'
3.8.1:
changes:
bugfixes:
- gitlab_deploy_key - fix the SSH Deploy Key being deleted accidentally while
running task in check mode (https://github.com/ansible-collections/community.general/issues/3621,
https://github.com/ansible-collections/community.general/pull/3622).
- gitlab_project_members - ``get_project_id`` return the project id by matching
``full_path`` or ``name`` (https://github.com/ansible-collections/community.general/pull/3602).
- ipa_* modules - fix environment fallback for ``ipa_host`` option (https://github.com/ansible-collections/community.general/issues/3560).
- nmcli - fixed ``dns6`` option handling so that it is treated as a list internally
(https://github.com/ansible-collections/community.general/pull/3563).
- nmcli - fixed ``ipv4.route-metric`` being in properties of type list (https://github.com/ansible-collections/community.general/pull/3563).
- one_image - fix error message when renaming an image (https://github.com/ansible-collections/community.general/pull/3626).
- pipx - ``state=inject`` was failing to parse the list of injected packages
(https://github.com/ansible-collections/community.general/pull/3611).
- pipx - set environment variable ``USE_EMOJI=0`` to prevent errors in platforms
that do not support ``UTF-8`` (https://github.com/ansible-collections/community.general/pull/3611).
- pkgin - Fix exception encountered when all packages are already installed
(https://github.com/ansible-collections/community.general/pull/3583).
- proxmox_group_info - fix module crash if a ``group`` parameter is used (https://github.com/ansible-collections/community.general/pull/3649).
- redfish_utils module utils - do not attempt to change the boot source override
mode if not specified by the user (https://github.com/ansible-collections/community.general/issues/3509/).
- redfish_utils module utils - if a manager network property is not specified
in the service, attempt to change the requested settings (https://github.com/ansible-collections/community.general/issues/3404/).
release_summary: Regular bugfix release.
fragments:
- 3.8.1.yml
- 3404-redfish_utils-skip-manager-network-check.yml
- 3509-redfish_utils-SetOneTimeBoot-mode-fix.yml
- 3561-fix-ipa-host-var-detection.yml
- 3563-nmcli-ipv6_dns.yaml
- 3583-fix-pkgin-exception.yml
- 3602-fix-gitlab_project_members-improve-search-method.yml
- 3611-pipx-fix-inject.yml
- 3622-fix-gitlab-deploy-key-check-mode.yml
- 3626-fix-one_image-error.yml
- 3649-proxmox_group_info_TypeError.yml
release_date: '2021-11-02'
3.8.2:
changes:
bugfixes:
- counter_enabled callback plugin - fix output to correctly display host and
task counters in serial mode (https://github.com/ansible-collections/community.general/pull/3709).
- ldap_search - allow it to be used even in check mode (https://github.com/ansible-collections/community.general/issues/3619).
- lvol - allows logical volumes to be created with certain size arguments prefixed
with ``+`` to preserve behavior of older versions of this module (https://github.com/ansible-collections/community.general/issues/3665).
- nmcli - fixed falsely reported changed status when ``mtu`` is omitted with
``dummy`` connections (https://github.com/ansible-collections/community.general/issues/3612,
https://github.com/ansible-collections/community.general/pull/3625).
- terraform - fix command options being ignored during planned/plan in function
``build_plan`` such as ``lock`` or ``lock_timeout`` (https://github.com/ansible-collections/community.general/issues/3707,
https://github.com/ansible-collections/community.general/pull/3726).
- xattr - fix exception caused by ``_run_xattr()`` raising a ``ValueError``
due to a mishandling of base64-encoded value (https://github.com/ansible-collections/community.general/issues/3673).
release_summary: Regular bugfix release.
fragments:
- 3.8.2.yml
- 3625-nmcli_false_changed_mtu_fix.yml
- 3667-ldap_search.yml
- 3675-xattr-handle-base64-values.yml
- 3681-lvol-fix-create.yml
- 3709-support-batch-mode.yml
- 3726-terraform-missing-parameters-planned-fix.yml
release_date: '2021-11-23'
3.8.3:
changes:
bugfixes:
- interfaces_file - fixed the check for existing option in interface (https://github.com/ansible-collections/community.general/issues/3841).
- nmcli - fix returning "changed" when no mask set for IPv4 or IPv6 addresses
on task rerun (https://github.com/ansible-collections/community.general/issues/3768).
- nmcli - pass ``flags``, ``ingress``, ``egress`` params to ``nmcli`` (https://github.com/ansible-collections/community.general/issues/1086).
- opentelemetry_plugin - honour ``ignore_errors`` when a task has failed instead
of reporting an error (https://github.com/ansible-collections/community.general/pull/3837).
- pipx - passes the correct command line option ``--include-apps`` (https://github.com/ansible-collections/community.general/issues/3791).
- proxmox - fixed ``onboot`` parameter causing module failures when undefined
(https://github.com/ansible-collections/community.general/issues/3844).
minor_changes:
- lxd connection plugin - make sure that ``ansible_lxd_host``, ``ansible_executable``,
and ``ansible_lxd_executable`` work (https://github.com/ansible-collections/community.general/pull/3798).
release_summary: Regular bugfix release.
fragments:
- 3.8.3.yml
- 3768-nmcli_fix_changed_when_no_mask_set.yml
- 3798-fix-lxd-connection-option-vars-support.yml
- 3800-pipx-include-apps.yaml
- 3837-opentelemetry_plugin-honour_ignore_errors.yaml
- 3862-interfaces-file-fix-dup-option.yaml
- 3874-proxmox-fix-onboot-param.yml
- 3896-nmcli_vlan_missing_options.yaml
release_date: '2021-12-14'
3.8.4:
changes:
bugfixes:
- Various modules and plugins - use vendored version of ``distutils.version``
instead of the deprecated Python standard library ``distutils`` (https://github.com/ansible-collections/community.general/pull/3936).
- alternatives - fix output parsing for alternatives groups (https://github.com/ansible-collections/community.general/pull/3976).
- jail connection plugin - replace deprecated ``distutils.spawn.find_executable``
with Ansible's ``get_bin_path`` to find the executable (https://github.com/ansible-collections/community.general/pull/3934).
- jira - fixed bug where module returns error related to dictionary key ``body``
(https://github.com/ansible-collections/community.general/issues/3419).
- lxd connection plugin - replace deprecated ``distutils.spawn.find_executable``
with Ansible's ``get_bin_path`` to find the ``lxc`` executable (https://github.com/ansible-collections/community.general/pull/3934).
- nrdp callback plugin - fix error ``string arguments without an encoding``
(https://github.com/ansible-collections/community.general/issues/3903).
- passwordstore lookup plugin - replace deprecated ``distutils.util.strtobool``
with Ansible's ``convert_bool.boolean`` to interpret values for the ``create``,
``returnall``, ``overwrite``, 'backup``, and ``nosymbols`` options (https://github.com/ansible-collections/community.general/pull/3934).
- say callback plugin - replace deprecated ``distutils.spawn.find_executable``
with Ansible's ``get_bin_path`` to find the ``say`` resp. ``espeak`` executables
(https://github.com/ansible-collections/community.general/pull/3934).
- scaleway_user_data - fix double-quote added where no double-quote is needed
to user data in scaleway's server (``Content-type`` -> ``Content-Type``) (https://github.com/ansible-collections/community.general/pull/3940).
- slack - add ``charset`` to HTTP headers to avoid Slack API warning (https://github.com/ansible-collections/community.general/issues/3932).
- zone connection plugin - replace deprecated ``distutils.spawn.find_executable``
with Ansible's ``get_bin_path`` to find the executable (https://github.com/ansible-collections/community.general/pull/3934).
release_summary: Regular bugfix release.
fragments:
- 3.8.4.yml
- 3867-jira-fix-body.yaml
- 3909-nrdp_fix_string_args_without_encoding.yaml
- 3933-slack-charset-header.yaml
- 3934-distutils.yml
- 3936-distutils.version.yml
- 3940_fix_contenttype_scaleway_user_data.yml
- 3976-fix-alternatives-parsing.yml
release_date: '2022-01-11'

View File

@@ -69,5 +69,6 @@ Individuals who have been asked to become a part of this group have generally be
| ------------------- | -------------------- | ------------------ | -------------------- |
| Alexei Znamensky | russoz | russoz | |
| Andrew Klychkov | andersson007 | andersson007_ | |
| Andrew Pantuso | Ajpantuso | ajpantuso | |
| Felix Fontein | felixfontein | felixfontein | |
| John R Barker | gundalow | gundalow | |

View File

@@ -751,3 +751,34 @@ To extract ports from all clusters with name containing 'server1':
server_name_query: "domain.server[?contains(name,'server1')].port"
.. note:: while using ``starts_with`` and ``contains``, you have to use `` to_json | from_json `` filter for correct parsing of data structure.
Working with Unicode
---------------------
`Unicode <https://unicode.org/main.html>`_ makes it possible to produce two strings which may be visually equivalent, but are comprised of distinctly different characters/character sequences. To address this ``Unicode`` defines `normalization forms <https://unicode.org/reports/tr15/>`_ which avoid these distinctions by choosing a unique character sequence for a given visual representation.
You can use the ``community.general.unicode_normalize`` filter to normalize ``Unicode`` strings within your playbooks.
.. code-block:: yaml+jinja
- name: Compare Unicode representations
debug:
msg: "{{ with_combining_character | community.general.unicode_normalize == without_combining_character }}"
vars:
with_combining_character: "{{ 'Mayagu\u0308ez' }}"
without_combining_character: Mayagüez
This produces:
.. code-block:: ansible-output
TASK [Compare Unicode representations] ********************************************************
ok: [localhost] => {
"msg": true
}
The ``community.general.unicode_normalize`` filter accepts a keyword argument to select the ``Unicode`` form used to normalize the input string.
:form: One of ``'NFC'`` (default), ``'NFD'``, ``'NFKC'``, or ``'NFKD'``. See the `Unicode reference <https://unicode.org/reports/tr15/>`_ for more information.
.. versionadded:: 3.7.0

View File

@@ -1,6 +1,6 @@
namespace: community
name: general
version: 3.6.0
version: 3.8.4
readme: README.md
authors:
- Ansible (https://github.com/ansible)

View File

@@ -45,6 +45,8 @@ class CallbackModule(CallbackBase):
_task_total = 0
_host_counter = 1
_host_total = 0
_current_batch_total = 0
_previous_batch_total = 0
def __init__(self):
super(CallbackModule, self).__init__()
@@ -76,8 +78,11 @@ class CallbackModule(CallbackBase):
self._display.banner(msg)
self._play = play
self._previous_batch_total = self._current_batch_total
self._current_batch_total = self._previous_batch_total + len(self._all_vars()['vars']['ansible_play_batch'])
self._host_total = len(self._all_vars()['vars']['ansible_play_hosts_all'])
self._task_total = len(self._play.get_tasks()[0])
self._task_counter = 1
def v2_playbook_on_stats(self, stats):
self._display.banner("PLAY RECAP")
@@ -145,7 +150,7 @@ class CallbackModule(CallbackBase):
path = task.get_path()
if path:
self._display.display("task path: %s" % path, color=C.COLOR_DEBUG)
self._host_counter = 0
self._host_counter = self._previous_batch_total
self._task_counter += 1
def v2_runner_on_ok(self, result):

408
plugins/callback/elastic.py Normal file
View File

@@ -0,0 +1,408 @@
# (C) 2021, Victor Martinez <VictorMartinezRubio@gmail.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 = '''
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
name: elastic
type: notification
short_description: Create distributed traces for each Ansible task in Elastic APM
version_added: 3.8.0
description:
- This callback creates distributed traces for each Ansible task in Elastic APM.
- You can configure the plugin with environment variables.
- See U(https://www.elastic.co/guide/en/apm/agent/python/current/configuration.html).
options:
hide_task_arguments:
default: false
type: bool
description:
- Hide the arguments for a task.
env:
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
apm_service_name:
default: ansible
type: str
description:
- The service name resource attribute.
env:
- name: ELASTIC_APM_SERVICE_NAME
apm_server_url:
type: str
description:
- Use the APM server and its environment variables.
env:
- name: ELASTIC_APM_SERVER_URL
apm_secret_token:
type: str
description:
- Use the APM server token
env:
- name: ELASTIC_APM_SECRET_TOKEN
apm_api_key:
type: str
description:
- Use the APM API key
env:
- name: ELASTIC_APM_API_KEY
apm_verify_server_cert:
default: true
type: bool
description:
- Verifies the SSL certificate if an HTTPS connection.
env:
- name: ELASTIC_APM_VERIFY_SERVER_CERT
traceparent:
type: str
description:
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
env:
- name: TRACEPARENT
requirements:
- elastic-apm (Python library)
'''
EXAMPLES = '''
examples: |
Enable the plugin in ansible.cfg:
[defaults]
callbacks_enabled = community.general.elastic
Set the environment variable:
export ELASTIC_APM_SERVER_URL=<your APM server URL)>
export ELASTIC_APM_SERVICE_NAME=your_service_name
export ELASTIC_APM_API_KEY=your_APM_API_KEY
'''
import getpass
import socket
import time
import uuid
from collections import OrderedDict
from os.path import basename
from ansible.errors import AnsibleError, AnsibleRuntimeError
from ansible.module_utils.six import raise_from
from ansible.plugins.callback import CallbackBase
try:
from elasticapm import Client, capture_span, trace_parent_from_string, instrument, label
except ImportError as imp_exc:
ELASTIC_LIBRARY_IMPORT_ERROR = imp_exc
else:
ELASTIC_LIBRARY_IMPORT_ERROR = None
class TaskData:
"""
Data about an individual task.
"""
def __init__(self, uuid, name, path, play, action, args):
self.uuid = uuid
self.name = name
self.path = path
self.play = play
self.host_data = OrderedDict()
self.start = time.time()
self.action = action
self.args = args
def add_host(self, host):
if host.uuid in self.host_data:
if host.status == 'included':
# concatenate task include output from multiple items
host.result = '%s\n%s' % (self.host_data[host.uuid].result, host.result)
else:
return
self.host_data[host.uuid] = host
class HostData:
"""
Data about an individual host.
"""
def __init__(self, uuid, name, status, result):
self.uuid = uuid
self.name = name
self.status = status
self.result = result
self.finish = time.time()
class ElasticSource(object):
def __init__(self, display):
self.ansible_playbook = ""
self.ansible_version = None
self.session = str(uuid.uuid4())
self.host = socket.gethostname()
try:
self.ip_address = socket.gethostbyname(socket.gethostname())
except Exception as e:
self.ip_address = None
self.user = getpass.getuser()
self._display = display
def start_task(self, tasks_data, hide_task_arguments, play_name, task):
""" record the start of a task for one or more hosts """
uuid = task._uuid
if uuid in tasks_data:
return
name = task.get_name().strip()
path = task.get_path()
action = task.action
args = None
if not task.no_log and not hide_task_arguments:
args = ', '.join(('%s=%s' % a for a in task.args.items()))
tasks_data[uuid] = TaskData(uuid, name, path, play_name, action, args)
def finish_task(self, tasks_data, status, result):
""" record the results of a task for a single host """
task_uuid = result._task._uuid
if hasattr(result, '_host') and result._host is not None:
host_uuid = result._host._uuid
host_name = result._host.name
else:
host_uuid = 'include'
host_name = 'include'
task = tasks_data[task_uuid]
if self.ansible_version is None and result._task_fields['args'].get('_ansible_version'):
self.ansible_version = result._task_fields['args'].get('_ansible_version')
task.add_host(HostData(host_uuid, host_name, status, result))
def generate_distributed_traces(self, tasks_data, status, end_time, traceparent, apm_service_name,
apm_server_url, apm_verify_server_cert, apm_secret_token, apm_api_key):
""" generate distributed traces from the collected TaskData and HostData """
tasks = []
parent_start_time = None
for task_uuid, task in tasks_data.items():
if parent_start_time is None:
parent_start_time = task.start
tasks.append(task)
apm_cli = self.init_apm_client(apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key)
if apm_cli:
instrument() # Only call this once, as early as possible.
if traceparent:
parent = trace_parent_from_string(traceparent)
apm_cli.begin_transaction("Session", trace_parent=parent, start=parent_start_time)
else:
apm_cli.begin_transaction("Session", start=parent_start_time)
# Populate trace metadata attributes
if self.ansible_version is not None:
label(ansible_version=self.ansible_version)
label(ansible_session=self.session, ansible_host_name=self.host, ansible_host_user=self.user)
if self.ip_address is not None:
label(ansible_host_ip=self.ip_address)
for task_data in tasks:
for host_uuid, host_data in task_data.host_data.items():
self.create_span_data(apm_cli, task_data, host_data)
apm_cli.end_transaction(name=__name__, result=status, duration=end_time - parent_start_time)
def create_span_data(self, apm_cli, task_data, host_data):
""" create the span with the given TaskData and HostData """
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
message = "success"
status = "success"
if host_data.status == 'included':
rc = 0
else:
res = host_data.result._result
rc = res.get('rc', 0)
if host_data.status == 'failed':
if res.get('exception') is not None:
message = res['exception'].strip().split('\n')[-1]
elif 'msg' in res:
message = res['msg']
else:
message = 'failed'
status = "failure"
elif host_data.status == 'skipped':
if 'skip_reason' in res:
message = res['skip_reason']
else:
message = 'skipped'
status = "unknown"
with capture_span(task_data.name,
start=task_data.start,
span_type="ansible.task.run",
duration=host_data.finish - task_data.start,
labels={"ansible.task.args": task_data.args,
"ansible.task.message": message,
"ansible.task.module": task_data.action,
"ansible.task.name": name,
"ansible.task.result": rc,
"ansible.task.host.name": host_data.name,
"ansible.task.host.status": host_data.status}) as span:
span.outcome = status
if 'failure' in status:
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, message))
apm_cli.capture_exception(exc_info=(type(exception), exception, exception.__traceback__), handled=True)
def init_apm_client(self, apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key):
if apm_server_url:
return Client(service_name=apm_service_name,
server_url=apm_server_url,
verify_server_cert=False,
secret_token=apm_secret_token,
api_key=apm_api_key,
use_elastic_traceparent_header=True,
debug=True)
class CallbackModule(CallbackBase):
"""
This callback creates distributed traces with Elastic APM.
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'notification'
CALLBACK_NAME = 'community.general.elastic'
CALLBACK_NEEDS_ENABLED = True
def __init__(self, display=None):
super(CallbackModule, self).__init__(display=display)
self.hide_task_arguments = None
self.apm_service_name = None
self.ansible_playbook = None
self.traceparent = False
self.play_name = None
self.tasks_data = None
self.errors = 0
self.disabled = False
if ELASTIC_LIBRARY_IMPORT_ERROR:
raise_from(
AnsibleError('The `elastic-apm` must be installed to use this plugin'),
ELASTIC_LIBRARY_IMPORT_ERROR)
self.tasks_data = OrderedDict()
self.elastic = ElasticSource(display=self._display)
def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys,
var_options=var_options,
direct=direct)
self.hide_task_arguments = self.get_option('hide_task_arguments')
self.apm_service_name = self.get_option('apm_service_name')
if not self.apm_service_name:
self.apm_service_name = 'ansible'
self.apm_server_url = self.get_option('apm_server_url')
self.apm_secret_token = self.get_option('apm_secret_token')
self.apm_api_key = self.get_option('apm_api_key')
self.apm_verify_server_cert = self.get_option('apm_verify_server_cert')
self.traceparent = self.get_option('traceparent')
def v2_playbook_on_start(self, playbook):
self.ansible_playbook = basename(playbook._file_name)
def v2_playbook_on_play_start(self, play):
self.play_name = play.get_name()
def v2_runner_on_no_hosts(self, task):
self.elastic.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_playbook_on_task_start(self, task, is_conditional):
self.elastic.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_playbook_on_cleanup_task_start(self, task):
self.elastic.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_playbook_on_handler_task_start(self, task):
self.elastic.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_runner_on_failed(self, result, ignore_errors=False):
self.errors += 1
self.elastic.finish_task(
self.tasks_data,
'failed',
result
)
def v2_runner_on_ok(self, result):
self.elastic.finish_task(
self.tasks_data,
'ok',
result
)
def v2_runner_on_skipped(self, result):
self.elastic.finish_task(
self.tasks_data,
'skipped',
result
)
def v2_playbook_on_include(self, included_file):
self.elastic.finish_task(
self.tasks_data,
'included',
included_file
)
def v2_playbook_on_stats(self, stats):
if self.errors == 0:
status = "success"
else:
status = "failure"
self.elastic.generate_distributed_traces(
self.tasks_data,
status,
time.time(),
self.traceparent,
self.apm_service_name,
self.apm_server_url,
self.apm_verify_server_cert,
self.apm_secret_token,
self.apm_api_key
)
def v2_runner_on_async_failed(self, result, **kwargs):
self.errors += 1

View File

@@ -94,6 +94,7 @@ ansible.cfg: |
import os
import json
from ansible import context
import socket
import uuid
import logging
@@ -152,11 +153,11 @@ class CallbackModule(CallbackBase):
self.base_data['ansible_pre_command_output'] = os.popen(
self.ls_pre_command).read()
if self._options is not None:
self.base_data['ansible_checkmode'] = self._options.check
self.base_data['ansible_tags'] = self._options.tags
self.base_data['ansible_skip_tags'] = self._options.skip_tags
self.base_data['inventory'] = self._options.inventory
if context.CLIARGS is not None:
self.base_data['ansible_checkmode'] = context.CLIARGS.get('check')
self.base_data['ansible_tags'] = context.CLIARGS.get('tags')
self.base_data['ansible_skip_tags'] = context.CLIARGS.get('skip_tags')
self.base_data['inventory'] = context.CLIARGS.get('inventory')
def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)

View File

@@ -70,6 +70,7 @@ import os
import json
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.common.text.converters import to_bytes
from ansible.module_utils.urls import open_url
from ansible.plugins.callback import CallbackBase
@@ -143,7 +144,7 @@ class CallbackModule(CallbackBase):
body = {
'cmd': 'submitcheck',
'token': self.token,
'XMLDATA': bytes(xmldata)
'XMLDATA': to_bytes(xmldata)
}
try:

View File

@@ -0,0 +1,445 @@
# -*- coding: utf-8 -*-
# (C) 2021, Victor Martinez <VictorMartinezRubio@gmail.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 = '''
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
name: opentelemetry
type: notification
short_description: Create distributed traces with OpenTelemetry
version_added: 3.7.0
description:
- This callback creates distributed traces for each Ansible task with OpenTelemetry.
- You can configure the OpenTelemetry exporter and SDK with environment variables.
- See U(https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html).
- See U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#opentelemetry-sdk-environment-variables).
options:
hide_task_arguments:
default: false
type: bool
description:
- Hide the arguments for a task.
env:
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
enable_from_environment:
type: str
description:
- Whether to enable this callback only if the given environment variable exists and it is set to C(true).
- This is handy when you use Configuration as Code and want to send distributed traces
if running in the CI rather when running Ansible locally.
- For such, it evaluates the given I(enable_from_environment) value as environment variable
and if set to true this plugin will be enabled.
env:
- name: ANSIBLE_OPENTELEMETRY_ENABLE_FROM_ENVIRONMENT
version_added: 3.8.0
otel_service_name:
default: ansible
type: str
description:
- The service name resource attribute.
env:
- name: OTEL_SERVICE_NAME
traceparent:
default: None
type: str
description:
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
env:
- name: TRACEPARENT
requirements:
- opentelemetry-api (Python library)
- opentelemetry-exporter-otlp (Python library)
- opentelemetry-sdk (Python library)
'''
EXAMPLES = '''
examples: |
Enable the plugin in ansible.cfg:
[defaults]
callbacks_enabled = community.general.opentelemetry
Set the environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT=<your endpoint (OTLP/HTTP)>
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer your_otel_token"
export OTEL_SERVICE_NAME=your_service_name
'''
import getpass
import os
import socket
import sys
import time
import uuid
from collections import OrderedDict
from os.path import basename
from ansible.errors import AnsibleError
from ansible.module_utils.six import raise_from
from ansible.plugins.callback import CallbackBase
try:
from opentelemetry import trace
from opentelemetry.trace import SpanKind
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.trace.status import Status, StatusCode
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
ConsoleSpanExporter,
SimpleSpanProcessor,
BatchSpanProcessor
)
from opentelemetry.util._time import _time_ns
except ImportError as imp_exc:
OTEL_LIBRARY_IMPORT_ERROR = imp_exc
else:
OTEL_LIBRARY_IMPORT_ERROR = None
class TaskData:
"""
Data about an individual task.
"""
def __init__(self, uuid, name, path, play, action, args):
self.uuid = uuid
self.name = name
self.path = path
self.play = play
self.host_data = OrderedDict()
if sys.version_info >= (3, 7):
self.start = time.time_ns()
else:
self.start = _time_ns()
self.action = action
self.args = args
def add_host(self, host):
if host.uuid in self.host_data:
if host.status == 'included':
# concatenate task include output from multiple items
host.result = '%s\n%s' % (self.host_data[host.uuid].result, host.result)
else:
return
self.host_data[host.uuid] = host
class HostData:
"""
Data about an individual host.
"""
def __init__(self, uuid, name, status, result):
self.uuid = uuid
self.name = name
self.status = status
self.result = result
if sys.version_info >= (3, 7):
self.finish = time.time_ns()
else:
self.finish = _time_ns()
class OpenTelemetrySource(object):
def __init__(self, display):
self.ansible_playbook = ""
self.ansible_version = None
self.session = str(uuid.uuid4())
self.host = socket.gethostname()
try:
self.ip_address = socket.gethostbyname(socket.gethostname())
except Exception as e:
self.ip_address = None
self.user = getpass.getuser()
self._display = display
def traceparent_context(self, traceparent):
carrier = dict()
carrier['traceparent'] = traceparent
return TraceContextTextMapPropagator().extract(carrier=carrier)
def start_task(self, tasks_data, hide_task_arguments, play_name, task):
""" record the start of a task for one or more hosts """
uuid = task._uuid
if uuid in tasks_data:
return
name = task.get_name().strip()
path = task.get_path()
action = task.action
args = None
if not task.no_log and not hide_task_arguments:
args = ', '.join(('%s=%s' % a for a in task.args.items()))
tasks_data[uuid] = TaskData(uuid, name, path, play_name, action, args)
def finish_task(self, tasks_data, status, result):
""" record the results of a task for a single host """
task_uuid = result._task._uuid
if hasattr(result, '_host') and result._host is not None:
host_uuid = result._host._uuid
host_name = result._host.name
else:
host_uuid = 'include'
host_name = 'include'
task = tasks_data[task_uuid]
if self.ansible_version is None and result._task_fields['args'].get('_ansible_version'):
self.ansible_version = result._task_fields['args'].get('_ansible_version')
task.add_host(HostData(host_uuid, host_name, status, result))
def generate_distributed_traces(self, otel_service_name, ansible_playbook, tasks_data, status, traceparent):
""" generate distributed traces from the collected TaskData and HostData """
tasks = []
parent_start_time = None
for task_uuid, task in tasks_data.items():
if parent_start_time is None:
parent_start_time = task.start
tasks.append(task)
trace.set_tracer_provider(
TracerProvider(
resource=Resource.create({SERVICE_NAME: otel_service_name})
)
)
processor = BatchSpanProcessor(OTLPSpanExporter())
trace.get_tracer_provider().add_span_processor(processor)
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span(ansible_playbook, context=self.traceparent_context(traceparent),
start_time=parent_start_time, kind=SpanKind.SERVER) as parent:
parent.set_status(status)
# Populate trace metadata attributes
if self.ansible_version is not None:
parent.set_attribute("ansible.version", self.ansible_version)
parent.set_attribute("ansible.session", self.session)
parent.set_attribute("ansible.host.name", self.host)
if self.ip_address is not None:
parent.set_attribute("ansible.host.ip", self.ip_address)
parent.set_attribute("ansible.host.user", self.user)
for task in tasks:
for host_uuid, host_data in task.host_data.items():
with tracer.start_as_current_span(task.name, start_time=task.start, end_on_exit=False) as span:
self.update_span_data(task, host_data, span)
def update_span_data(self, task_data, host_data, span):
""" update the span with the given TaskData and HostData """
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
message = 'success'
status = Status(status_code=StatusCode.OK)
if host_data.status == 'included':
rc = 0
else:
res = host_data.result._result
rc = res.get('rc', 0)
if host_data.status == 'failed':
message = self.get_error_message(res)
status = Status(status_code=StatusCode.ERROR, description=message)
# Record an exception with the task message
span.record_exception(BaseException(self.enrich_error_message(res)))
elif host_data.status == 'skipped':
if 'skip_reason' in res:
message = res['skip_reason']
else:
message = 'skipped'
status = Status(status_code=StatusCode.UNSET)
elif host_data.status == 'ignored':
status = Status(status_code=StatusCode.UNSET)
span.set_status(status)
self.set_span_attribute(span, "ansible.task.args", task_data.args)
self.set_span_attribute(span, "ansible.task.module", task_data.action)
self.set_span_attribute(span, "ansible.task.message", message)
self.set_span_attribute(span, "ansible.task.name", name)
self.set_span_attribute(span, "ansible.task.result", rc)
self.set_span_attribute(span, "ansible.task.host.name", host_data.name)
self.set_span_attribute(span, "ansible.task.host.status", host_data.status)
span.end(end_time=host_data.finish)
def set_span_attribute(self, span, attributeName, attributeValue):
""" update the span attribute with the given attribute and value if not None """
if span is None and self._display is not None:
self._display.warning('span object is None. Please double check if that is expected.')
else:
if attributeValue is not None:
span.set_attribute(attributeName, attributeValue)
@staticmethod
def get_error_message(result):
if result.get('exception') is not None:
return OpenTelemetrySource._last_line(result['exception'])
return result.get('msg', 'failed')
@staticmethod
def _last_line(text):
lines = text.strip().split('\n')
return lines[-1]
@staticmethod
def enrich_error_message(result):
message = result.get('msg', 'failed')
exception = result.get('exception')
stderr = result.get('stderr')
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
class CallbackModule(CallbackBase):
"""
This callback creates distributed traces.
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'notification'
CALLBACK_NAME = 'community.general.opentelemetry'
CALLBACK_NEEDS_ENABLED = True
def __init__(self, display=None):
super(CallbackModule, self).__init__(display=display)
self.hide_task_arguments = None
self.otel_service_name = None
self.ansible_playbook = None
self.play_name = None
self.tasks_data = None
self.errors = 0
self.disabled = False
self.traceparent = False
if OTEL_LIBRARY_IMPORT_ERROR:
raise_from(
AnsibleError('The `opentelemetry-api`, `opentelemetry-exporter-otlp` or `opentelemetry-sdk` must be installed to use this plugin'),
OTEL_LIBRARY_IMPORT_ERROR)
self.tasks_data = OrderedDict()
self.opentelemetry = OpenTelemetrySource(display=self._display)
def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys,
var_options=var_options,
direct=direct)
environment_variable = self.get_option('enable_from_environment')
if environment_variable is not None and os.environ.get(environment_variable, 'false').lower() != 'true':
self.disabled = True
self._display.warning("The `enable_from_environment` option has been set and {0} is not enabled. "
"Disabling the `opentelemetry` callback plugin.".format(environment_variable))
self.hide_task_arguments = self.get_option('hide_task_arguments')
self.otel_service_name = self.get_option('otel_service_name')
if not self.otel_service_name:
self.otel_service_name = 'ansible'
# See https://github.com/open-telemetry/opentelemetry-specification/issues/740
self.traceparent = self.get_option('traceparent')
def v2_playbook_on_start(self, playbook):
self.ansible_playbook = basename(playbook._file_name)
def v2_playbook_on_play_start(self, play):
self.play_name = play.get_name()
def v2_runner_on_no_hosts(self, task):
self.opentelemetry.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_playbook_on_task_start(self, task, is_conditional):
self.opentelemetry.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_playbook_on_cleanup_task_start(self, task):
self.opentelemetry.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_playbook_on_handler_task_start(self, task):
self.opentelemetry.start_task(
self.tasks_data,
self.hide_task_arguments,
self.play_name,
task
)
def v2_runner_on_failed(self, result, ignore_errors=False):
if ignore_errors:
status = 'ignored'
else:
status = 'failed'
self.errors += 1
self.opentelemetry.finish_task(
self.tasks_data,
status,
result
)
def v2_runner_on_ok(self, result):
self.opentelemetry.finish_task(
self.tasks_data,
'ok',
result
)
def v2_runner_on_skipped(self, result):
self.opentelemetry.finish_task(
self.tasks_data,
'skipped',
result
)
def v2_playbook_on_include(self, included_file):
self.opentelemetry.finish_task(
self.tasks_data,
'included',
included_file
)
def v2_playbook_on_stats(self, stats):
if self.errors == 0:
status = Status(status_code=StatusCode.OK)
else:
status = Status(status_code=StatusCode.ERROR)
self.opentelemetry.generate_distributed_traces(
self.otel_service_name,
self.ansible_playbook,
self.tasks_data,
status,
self.traceparent
)
def v2_runner_on_async_failed(self, result, **kwargs):
self.errors += 1

View File

@@ -21,11 +21,11 @@ DOCUMENTATION = '''
- In 2.8, this callback has been renamed from C(osx_say) into M(community.general.say).
'''
import distutils.spawn
import platform
import subprocess
import os
from ansible.module_utils.common.process import get_bin_path
from ansible.plugins.callback import CallbackBase
@@ -47,21 +47,24 @@ class CallbackModule(CallbackBase):
self.HAPPY_VOICE = None
self.LASER_VOICE = None
self.synthesizer = distutils.spawn.find_executable('say')
if not self.synthesizer:
self.synthesizer = distutils.spawn.find_executable('espeak')
if self.synthesizer:
try:
self.synthesizer = get_bin_path('say')
if platform.system() != 'Darwin':
# 'say' binary available, it might be GNUstep tool which doesn't support 'voice' parameter
self._display.warning("'say' executable found but system is '%s': ignoring voice parameter" % platform.system())
else:
self.FAILED_VOICE = 'Zarvox'
self.REGULAR_VOICE = 'Trinoids'
self.HAPPY_VOICE = 'Cellos'
self.LASER_VOICE = 'Princess'
except ValueError:
try:
self.synthesizer = get_bin_path('espeak')
self.FAILED_VOICE = 'klatt'
self.HAPPY_VOICE = 'f5'
self.LASER_VOICE = 'whisper'
elif platform.system() != 'Darwin':
# 'say' binary available, it might be GNUstep tool which doesn't support 'voice' parameter
self._display.warning("'say' executable found but system is '%s': ignoring voice parameter" % platform.system())
else:
self.FAILED_VOICE = 'Zarvox'
self.REGULAR_VOICE = 'Trinoids'
self.HAPPY_VOICE = 'Cellos'
self.LASER_VOICE = 'Princess'
except ValueError:
self.synthesizer = None
# plugin disable itself if say is not present
# ansible will not call any callback if disabled is set to True

View File

@@ -42,28 +42,29 @@ def should_use_block(value):
return False
def my_represent_scalar(self, tag, value, style=None):
"""Uses block style for multi-line strings"""
if style is None:
if should_use_block(value):
style = '|'
# we care more about readable than accuracy, so...
# ...no trailing space
value = value.rstrip()
# ...and non-printable characters
value = ''.join(x for x in value if x in string.printable or ord(x) >= 0xA0)
# ...tabs prevent blocks from expanding
value = value.expandtabs()
# ...and odd bits of whitespace
value = re.sub(r'[\x0b\x0c\r]', '', value)
# ...as does trailing space
value = re.sub(r' +\n', '\n', value)
else:
style = self.default_style
node = yaml.representer.ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
class MyDumper(AnsibleDumper):
def represent_scalar(self, tag, value, style=None):
"""Uses block style for multi-line strings"""
if style is None:
if should_use_block(value):
style = '|'
# we care more about readable than accuracy, so...
# ...no trailing space
value = value.rstrip()
# ...and non-printable characters
value = ''.join(x for x in value if x in string.printable or ord(x) >= 0xA0)
# ...tabs prevent blocks from expanding
value = value.expandtabs()
# ...and odd bits of whitespace
value = re.sub(r'[\x0b\x0c\r]', '', value)
# ...as does trailing space
value = re.sub(r' +\n', '\n', value)
else:
style = self.default_style
node = yaml.representer.ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
class CallbackModule(Default):
@@ -79,7 +80,6 @@ class CallbackModule(Default):
def __init__(self):
super(CallbackModule, self).__init__()
yaml.representer.BaseRepresenter.represent_scalar = my_represent_scalar
def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False):
if result.get('_ansible_no_log', False):
@@ -121,7 +121,7 @@ class CallbackModule(Default):
if abridged_result:
dumped += '\n'
dumped += to_text(yaml.dump(abridged_result, allow_unicode=True, width=1000, Dumper=AnsibleDumper, default_flow_style=False))
dumped += to_text(yaml.dump(abridged_result, allow_unicode=True, width=1000, Dumper=MyDumper, default_flow_style=False))
# indent by a couple of spaces
dumped = '\n '.join(dumped.split('\n')).rstrip()

View File

@@ -31,7 +31,6 @@ DOCUMENTATION = '''
- name: ansible_jail_user
'''
import distutils.spawn
import os
import os.path
import subprocess
@@ -39,6 +38,7 @@ import traceback
from ansible.errors import AnsibleError
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.common.process import get_bin_path
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.plugins.connection import ConnectionBase, BUFSIZE
from ansible.utils.display import Display
@@ -75,10 +75,10 @@ class Connection(ConnectionBase):
@staticmethod
def _search_executable(executable):
cmd = distutils.spawn.find_executable(executable)
if not cmd:
try:
return get_bin_path(executable)
except ValueError:
raise AnsibleError("%s command not found in PATH" % executable)
return cmd
def list_jails(self):
p = subprocess.Popen([self.jls_cmd, '-q', 'name'],

View File

@@ -43,10 +43,10 @@ DOCUMENTATION = '''
'''
import os
from distutils.spawn import find_executable
from subprocess import Popen, PIPE
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
from ansible.module_utils.common.process import get_bin_path
from ansible.module_utils.common.text.converters import to_bytes, to_text
from ansible.plugins.connection import ConnectionBase
@@ -62,9 +62,9 @@ class Connection(ConnectionBase):
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
self._host = self._play_context.remote_addr
self._lxc_cmd = find_executable("lxc")
if not self._lxc_cmd:
try:
self._lxc_cmd = get_bin_path("lxc")
except ValueError:
raise AnsibleError("lxc command not found in PATH")
if self._play_context.remote_user is not None and self._play_context.remote_user != 'root':
@@ -89,9 +89,9 @@ class Connection(ConnectionBase):
local_cmd.extend(["--project", self.get_option("project")])
local_cmd.extend([
"exec",
"%s:%s" % (self.get_option("remote"), self._host),
"%s:%s" % (self.get_option("remote"), self.get_option("remote_addr")),
"--",
self._play_context.executable, "-c", cmd
self.get_option("executable"), "-c", cmd
])
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
@@ -126,7 +126,7 @@ class Connection(ConnectionBase):
local_cmd.extend([
"file", "push",
in_path,
"%s:%s/%s" % (self.get_option("remote"), self._host, out_path)
"%s:%s/%s" % (self.get_option("remote"), self.get_option("remote_addr"), out_path)
])
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
@@ -145,7 +145,7 @@ class Connection(ConnectionBase):
local_cmd.extend(["--project", self.get_option("project")])
local_cmd.extend([
"file", "pull",
"%s:%s/%s" % (self.get_option("remote"), self._host, in_path),
"%s:%s/%s" % (self.get_option("remote"), self.get_option("remote_addr"), in_path),
out_path
])

View File

@@ -26,7 +26,6 @@ DOCUMENTATION = '''
- name: ansible_zone_host
'''
import distutils.spawn
import os
import os.path
import subprocess
@@ -34,6 +33,7 @@ import traceback
from ansible.errors import AnsibleError
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.common.process import get_bin_path
from ansible.module_utils.common.text.converters import to_bytes
from ansible.plugins.connection import ConnectionBase, BUFSIZE
from ansible.utils.display import Display
@@ -64,10 +64,10 @@ class Connection(ConnectionBase):
@staticmethod
def _search_executable(executable):
cmd = distutils.spawn.find_executable(executable)
if not cmd:
try:
return get_bin_path(executable)
except ValueError:
raise AnsibleError("%s command not found in PATH" % executable)
return cmd
def list_zones(self):
process = subprocess.Popen([self.zoneadm_cmd, 'list', '-ip'],

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andreas Botzner <andreas at botzner dot 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
class ModuleDocFragment(object):
# Common parameters for Redis modules
DOCUMENTATION = r'''
options:
login_host:
description:
- Specify the target host running the database.
default: localhost
type: str
login_port:
description:
- Specify the port to connect to.
default: 6379
type: int
login_user:
description:
- Specify the user to authenticate with.
- Requires L(redis,https://pypi.org/project/redis) >= 3.4.0.
type: str
login_password:
description:
- Specify the password to authenticate with.
- Usually not used when target is localhost.
type: str
tls:
description:
- Specify whether or not to use TLS for the connection.
type: bool
default: true
validate_certs:
description:
- Specify whether or not to validate TLS certificates.
- This should only be turned off for personally controlled sites or with
C(localhost) as target.
type: bool
default: true
ca_certs:
description:
- Path to root certificates file. If not set and I(tls) is
set to C(true), certifi ca-certificates will be used.
type: str
requirements: [ "redis", "certifi" ]
notes:
- Requires the C(redis) Python package on the remote host. You can
install it with pip (C(pip install redis)) or with a package manager.
Information on the library can be found at U(https://github.com/andymccurdy/redis-py).
'''

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Phillipe Smith <phsmithcc@gmail.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
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r'''
options:
url:
type: str
description:
- Rundeck instance URL.
required: true
api_version:
type: int
description:
- Rundeck API version to be used.
- API version must be at least 14.
default: 39
api_token:
type: str
description:
- Rundeck User API Token.
required: true
'''

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@gmail.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
from unicodedata import normalize
from ansible.errors import AnsibleFilterError, AnsibleFilterTypeError
from ansible.module_utils.six import text_type
def unicode_normalize(data, form='NFC'):
"""Applies normalization to 'unicode' strings.
Args:
data: A unicode string piped into the Jinja filter
form: One of ('NFC', 'NFD', 'NFKC', 'NFKD').
See https://docs.python.org/3/library/unicodedata.html#unicodedata.normalize for more information.
Returns:
A normalized unicode string of the specified 'form'.
"""
if not isinstance(data, text_type):
raise AnsibleFilterTypeError("%s is not a valid input type" % type(data))
if form not in ('NFC', 'NFD', 'NFKC', 'NFKD'):
raise AnsibleFilterError("%s is not a valid form" % form)
return normalize(form, data)
class FilterModule(object):
def filters(self):
return {
'unicode_normalize': unicode_normalize,
}

View File

@@ -5,7 +5,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
def version_sort(value, reverse=False):

View File

@@ -68,7 +68,6 @@ user: ansible-tester
password: secure
'''
from distutils.version import LooseVersion
import socket
from ansible.errors import AnsibleError

View File

@@ -0,0 +1,222 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Cliff Hults <cliff.hlts@gmail.com>
# Copyright (c) 2021 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 = '''
name: icinga2
short_description: Icinga2 inventory source
version_added: 3.7.0
author:
- Cliff Hults (@BongoEADGC6) <cliff.hults@gmail.com>
description:
- Get inventory hosts from the Icinga2 API.
- "Uses a configuration file as an inventory source, it must end in
C(.icinga2.yml) or C(.icinga2.yaml)."
options:
plugin:
description: Name of the plugin.
required: true
type: string
choices: ['community.general.icinga2']
url:
description: Root URL of Icinga2 API.
type: string
required: true
user:
description: Username to query the API.
type: string
required: true
password:
description: Password to query the API.
type: string
required: true
host_filter:
description: An Icinga2 API valid host filter.
type: string
required: false
validate_certs:
description: Enables or disables SSL certificate verification.
type: boolean
default: true
'''
EXAMPLES = r'''
# my.icinga2.yml
plugin: community.general.icinga2
url: http://localhost:5665
user: ansible
password: secure
host_filter: \"linux-servers\" in host.groups
validate_certs: false
'''
import json
from ansible.errors import AnsibleParserError
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
from ansible.module_utils.urls import open_url
class InventoryModule(BaseInventoryPlugin, Constructable):
''' Host inventory parser for ansible using Icinga2 as source. '''
NAME = 'community.general.icinga2'
def __init__(self):
super(InventoryModule, self).__init__()
# from config
self.icinga2_url = None
self.icinga2_user = None
self.icinga2_password = None
self.ssl_verify = None
self.host_filter = None
self.cache_key = None
self.use_cache = None
def verify_file(self, path):
valid = False
if super(InventoryModule, self).verify_file(path):
if path.endswith(('icinga2.yaml', 'icinga2.yml')):
valid = True
else:
self.display.vvv('Skipping due to inventory source not ending in "icinga2.yaml" nor "icinga2.yml"')
return valid
def _api_connect(self):
self.headers = {
'User-Agent': "ansible-icinga2-inv",
'Accept': "application/json",
}
api_status_url = self.icinga2_url + "/status"
request_args = {
'headers': self.headers,
'url_username': self.icinga2_user,
'url_password': self.icinga2_password,
'validate_certs': self.ssl_verify
}
open_url(api_status_url, **request_args)
def _post_request(self, request_url, data=None):
self.display.vvv("Requested URL: %s" % request_url)
request_args = {
'headers': self.headers,
'url_username': self.icinga2_user,
'url_password': self.icinga2_password,
'validate_certs': self.ssl_verify
}
if data is not None:
request_args['data'] = json.dumps(data)
self.display.vvv("Request Args: %s" % request_args)
response = open_url(request_url, **request_args)
response_body = response.read()
json_data = json.loads(response_body.decode('utf-8'))
if 200 <= response.status <= 299:
return json_data
if response.status == 404 and json_data['status'] == "No objects found.":
raise AnsibleParserError(
"API returned no data -- Response: %s - %s"
% (response.status, json_data['status']))
if response.status == 401:
raise AnsibleParserError(
"API was unable to complete query -- Response: %s - %s"
% (response.status, json_data['status']))
if response.status == 500:
raise AnsibleParserError(
"API Response - %s - %s"
% (json_data['status'], json_data['errors']))
raise AnsibleParserError(
"Unexpected data returned - %s - %s"
% (json_data['status'], json_data['errors']))
def _query_hosts(self, hosts=None, attrs=None, joins=None, host_filter=None):
query_hosts_url = "{0}/objects/hosts".format(self.icinga2_url)
self.headers['X-HTTP-Method-Override'] = 'GET'
data_dict = dict()
if hosts:
data_dict['hosts'] = hosts
if attrs is not None:
data_dict['attrs'] = attrs
if joins is not None:
data_dict['joins'] = joins
if host_filter is not None:
data_dict['filter'] = host_filter.replace("\\\"", "\"")
self.display.vvv(host_filter)
host_dict = self._post_request(query_hosts_url, data_dict)
return host_dict['results']
def get_inventory_from_icinga(self):
"""Query for all hosts """
self.display.vvv("Querying Icinga2 for inventory")
query_args = {
"attrs": ["address", "state_type", "state", "groups"],
}
if self.host_filter is not None:
query_args['host_filter'] = self.host_filter
# Icinga2 API Call
results_json = self._query_hosts(**query_args)
# Manipulate returned API data to Ansible inventory spec
ansible_inv = self._convert_inv(results_json)
return ansible_inv
def _populate(self):
groups = self._to_json(self.get_inventory_from_icinga())
return groups
def _to_json(self, in_dict):
"""Convert dictionary to JSON"""
return json.dumps(in_dict, sort_keys=True, indent=2)
def _convert_inv(self, json_data):
"""Convert Icinga2 API data to JSON format for Ansible"""
groups_dict = {"_meta": {"hostvars": {}}}
for entry in json_data:
host_name = entry['name']
host_attrs = entry['attrs']
if host_attrs['state'] == 0:
host_attrs['state'] = 'on'
else:
host_attrs['state'] = 'off'
host_groups = host_attrs['groups']
host_addr = host_attrs['address']
self.inventory.add_host(host_addr)
for group in host_groups:
if group not in self.inventory.groups.keys():
self.inventory.add_group(group)
self.inventory.add_child(group, host_addr)
self.inventory.set_variable(host_addr, 'address', host_addr)
self.inventory.set_variable(host_addr, 'hostname', host_name)
self.inventory.set_variable(host_addr, 'state',
host_attrs['state'])
self.inventory.set_variable(host_addr, 'state_type',
host_attrs['state_type'])
return groups_dict
def parse(self, inventory, loader, path, cache=True):
super(InventoryModule, self).parse(inventory, loader, path)
# read config from file, this sets 'options'
self._read_config_data(path)
# Store the options from the YAML file
self.icinga2_url = self.get_option('url').rstrip('/') + '/v1'
self.icinga2_user = self.get_option('user')
self.icinga2_password = self.get_option('password')
self.ssl_verify = self.get_option('validate_certs')
self.host_filter = self.get_option('host_filter')
# Not currently enabled
# self.cache_key = self.get_cache_key(path)
# self.use_cache = cache and self.get_option('cache')
# Test connection to API
self._api_connect()
# Call our internal helper to populate the dynamic inventory
self._populate()

View File

@@ -29,8 +29,7 @@ DOCUMENTATION = r'''
ip_style:
description: Populate hostvars with all information available from the Linode APIv4.
type: string
default:
- plain
default: plain
choices:
- plain
- api

View File

@@ -8,7 +8,7 @@ __metaclass__ = type
DOCUMENTATION = r'''
name: online
author:
- Remy Leone (@sieben)
- Remy Leone (@remyleone)
short_description: Scaleway (previously Online SAS or Online.net) inventory source
description:
- Get inventory hosts from Scaleway (previously Online SAS or Online.net).

View File

@@ -0,0 +1,239 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, FELDSAM s.r.o. - FeldHost™ <support@feldhost.cz>
# 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'''
name: opennebula
author:
- Kristian Feldsam (@feldsam)
short_description: OpenNebula inventory source
version_added: "3.8.0"
extends_documentation_fragment:
- constructed
description:
- Get inventory hosts from OpenNebula cloud.
- Uses an YAML configuration file ending with either I(opennebula.yml) or I(opennebula.yaml)
to set parameter values.
- Uses I(api_authfile), C(~/.one/one_auth), or C(ONE_AUTH) pointing to a OpenNebula credentials file.
options:
plugin:
description: Token that ensures this is a source file for the 'opennebula' plugin.
type: string
required: true
choices: [ community.general.opennebula ]
api_url:
description:
- URL of the OpenNebula RPC server.
- It is recommended to use HTTPS so that the username/password are not
transferred over the network unencrypted.
- If not set then the value of the C(ONE_URL) environment variable is used.
env:
- name: ONE_URL
required: True
type: string
api_username:
description:
- Name of the user to login into the OpenNebula RPC server. If not set
then the value of the C(ONE_USERNAME) environment variable is used.
env:
- name: ONE_USERNAME
type: string
api_password:
description:
- Password or a token of the user to login into OpenNebula RPC server.
- If not set, the value of the C(ONE_PASSWORD) environment variable is used.
env:
- name: ONE_PASSWORD
required: False
type: string
api_authfile:
description:
- If both I(api_username) or I(api_password) are not set, then it will try
authenticate with ONE auth file. Default path is C(~/.one/one_auth).
- Set environment variable C(ONE_AUTH) to override this path.
env:
- name: ONE_AUTH
required: False
type: string
hostname:
description: Field to match the hostname. Note C(v4_first_ip) corresponds to the first IPv4 found on VM.
type: string
default: v4_first_ip
choices:
- v4_first_ip
- v6_first_ip
- name
filter_by_label:
description: Only return servers filtered by this label.
type: string
group_by_labels:
description: Create host groups by vm labels
type: bool
default: True
'''
EXAMPLES = r'''
# inventory_opennebula.yml file in YAML format
# Example command line: ansible-inventory --list -i inventory_opennebula.yml
# Pass a label filter to the API
plugin: community.general.opennebula
api_url: https://opennebula:2633/RPC2
filter_by_label: Cache
'''
try:
import pyone
HAS_PYONE = True
except ImportError:
HAS_PYONE = False
from ansible.errors import AnsibleError
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
from ansible.module_utils._text import to_native
from collections import namedtuple
import os
class InventoryModule(BaseInventoryPlugin, Constructable):
NAME = 'community.general.opennebula'
def verify_file(self, path):
valid = False
if super(InventoryModule, self).verify_file(path):
if path.endswith(('opennebula.yaml', 'opennebula.yml')):
valid = True
return valid
def _get_connection_info(self):
url = self.get_option('api_url')
username = self.get_option('api_username')
password = self.get_option('api_password')
authfile = self.get_option('api_authfile')
if not username and not password:
if authfile is None:
authfile = os.path.join(os.environ.get("HOME"), ".one", "one_auth")
try:
with open(authfile, "r") as fp:
authstring = fp.read().rstrip()
username, password = authstring.split(":")
except (OSError, IOError):
raise AnsibleError("Could not find or read ONE_AUTH file at '{e}'".format(e=authfile))
except Exception:
raise AnsibleError("Error occurs when reading ONE_AUTH file at '{e}'".format(e=authfile))
auth_params = namedtuple('auth', ('url', 'username', 'password'))
return auth_params(url=url, username=username, password=password)
def _get_vm_ipv4(self, vm):
nic = vm.TEMPLATE.get('NIC')
if isinstance(nic, dict):
nic = [nic]
for net in nic:
return net['IP']
return False
def _get_vm_ipv6(self, vm):
nic = vm.TEMPLATE.get('NIC')
if isinstance(nic, dict):
nic = [nic]
for net in nic:
if net.get('IP6_GLOBAL'):
return net['IP6_GLOBAL']
return False
def _get_vm_pool(self):
auth = self._get_connection_info()
if not (auth.username and auth.password):
raise AnsibleError('API Credentials missing. Check OpenNebula inventory file.')
else:
one_client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
# get hosts (VMs)
try:
vm_pool = one_client.vmpool.infoextended(-2, -1, -1, 3)
except Exception as e:
raise AnsibleError("Something happened during XML-RPC call: {e}".format(e=to_native(e)))
return vm_pool
def _retrieve_servers(self, label_filter=None):
vm_pool = self._get_vm_pool()
result = []
# iterate over hosts
for vm in vm_pool.VM:
server = vm.USER_TEMPLATE
labels = []
if vm.USER_TEMPLATE.get('LABELS'):
labels = [s for s in vm.USER_TEMPLATE.get('LABELS') if s == ',' or s == '-' or s.isalnum() or s.isspace()]
labels = ''.join(labels)
labels = labels.replace(' ', '_')
labels = labels.replace('-', '_')
labels = labels.split(',')
# filter by label
if label_filter is not None:
if label_filter not in labels:
continue
server['name'] = vm.NAME
server['LABELS'] = labels
server['v4_first_ip'] = self._get_vm_ipv4(vm)
server['v6_first_ip'] = self._get_vm_ipv6(vm)
result.append(server)
return result
def _populate(self):
hostname_preference = self.get_option('hostname')
group_by_labels = self.get_option('group_by_labels')
# Add a top group 'one'
self.inventory.add_group(group='all')
filter_by_label = self.get_option('filter_by_label')
for server in self._retrieve_servers(filter_by_label):
# check for labels
if group_by_labels and server['LABELS']:
for label in server['LABELS']:
self.inventory.add_group(group=label)
self.inventory.add_host(host=server['name'], group=label)
self.inventory.add_host(host=server['name'], group='all')
for attribute, value in server.items():
self.inventory.set_variable(server['name'], attribute, value)
if hostname_preference != 'name':
self.inventory.set_variable(server['name'], 'ansible_host', server[hostname_preference])
if server.get('SSH_PORT'):
self.inventory.set_variable(server['name'], 'ansible_port', server['SSH_PORT'])
def parse(self, inventory, loader, path, cache=True):
if not HAS_PYONE:
raise AnsibleError('OpenNebula Inventory plugin requires pyone to work!')
super(InventoryModule, self).parse(inventory, loader, path)
self._read_config_data(path=path)
self._populate()

View File

@@ -119,12 +119,13 @@ compose:
import re
from ansible.module_utils.common._collections_compat import MutableMapping
from distutils.version import LooseVersion
from ansible.errors import AnsibleError
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
# 3rd party imports
try:
import requests

View File

@@ -9,7 +9,7 @@ __metaclass__ = type
DOCUMENTATION = r'''
name: scaleway
author:
- Remy Leone (@sieben)
- Remy Leone (@remyleone)
short_description: Scaleway inventory source
description:
- Get inventory hosts from Scaleway.

View File

@@ -93,7 +93,7 @@ DOCUMENTATION = '''
environment variable and keep I(endpoints), I(host), and I(port) unused.
seealso:
- module: community.general.etcd3
- ref: etcd_lookup
- ref: ansible_collections.community.general.etcd_lookup
description: The etcd v2 lookup.
requirements:

View File

@@ -23,7 +23,7 @@ DOCUMENTATION = '''
EXAMPLES = """
- name: "'unnest' all elements into single list"
ansible.builtin.debug:
msg: "all in one list {{lookup('community.general.flattened', [1,2,3,[5,6]], [a,b,c], [[5,6,1,3], [34,a,b,c]])}}"
msg: "all in one list {{lookup('community.general.flattened', [1,2,3,[5,6]], ['a','b','c'], [[5,6,1,3], [34,'a','b','c']])}}"
"""
RETURN = """

View File

@@ -141,9 +141,9 @@ import time
import yaml
from distutils import util
from ansible.errors import AnsibleError, AnsibleAssertionError
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.utils.display import Display
from ansible.utils.encrypt import random_password
from ansible.plugins.lookup import LookupBase
@@ -211,7 +211,7 @@ class LookupModule(LookupBase):
try:
for key in ['create', 'returnall', 'overwrite', 'backup', 'nosymbols']:
if not isinstance(self.paramvals[key], bool):
self.paramvals[key] = util.strtobool(self.paramvals[key])
self.paramvals[key] = boolean(self.paramvals[key])
except (ValueError, AssertionError) as e:
raise AnsibleError(e)
if self.paramvals['missing'] not in ['error', 'warn', 'create', 'empty']:

View File

@@ -36,19 +36,20 @@ options:
ini:
- section: tss_lookup
key: username
required: true
password:
description: The password associated with the supplied username.
description:
- The password associated with the supplied username.
- Required when I(token) is not provided.
env:
- name: TSS_PASSWORD
ini:
- section: tss_lookup
key: password
required: true
domain:
default: ""
description:
- The domain with which to request the OAuth2 Access Grant.
- Optional when I(token) is not provided.
- Requires C(python-tss-sdk) version 1.0.0 or greater.
env:
- name: TSS_DOMAIN
@@ -57,6 +58,17 @@ options:
key: domain
required: false
version_added: 3.6.0
token:
description:
- Existing token for Thycotic authorizer.
- If provided, I(username) and I(password) are not needed.
- Requires C(python-tss-sdk) version 1.0.0 or greater.
env:
- name: TSS_TOKEN
ini:
- section: tss_lookup
key: token
version_added: 3.7.0
api_path_uri:
default: /api/v1
description: The path to append to the base URL to form a valid REST
@@ -83,18 +95,6 @@ _list:
"""
EXAMPLES = r"""
- hosts: localhost
vars:
secret: "{{ lookup('community.general.tss', 1) }}"
tasks:
- ansible.builtin.debug:
msg: >
the password is {{
(secret['items']
| items2dict(key_name='slug',
value_name='itemValue'))['password']
}}
- hosts: localhost
vars:
secret: >-
@@ -116,10 +116,39 @@ EXAMPLES = r"""
value_name='itemValue'))['password']
}}
- hosts: localhost
vars:
secret: >-
{{
lookup(
'community.general.tss',
102,
base_url='https://secretserver.domain.com/SecretServer/',
username='user.name',
password='password',
domain='domain'
)
}}
tasks:
- ansible.builtin.debug:
msg: >
the password is {{
(secret['items']
| items2dict(key_name='slug',
value_name='itemValue'))['password']
}}
- hosts: localhost
vars:
secret_password: >-
{{ ((lookup('community.general.tss', 1) | from_json).get('items') | items2dict(key_name='slug', value_name='itemValue'))['password'] }}"
{{
((lookup(
'community.general.tss',
102,
base_url='https://secretserver.domain.com/SecretServer/',
token='thycotic_access_token',
) | from_json).get('items') | items2dict(key_name='slug', value_name='itemValue'))['password']
}}
tasks:
- ansible.builtin.debug:
msg: the password is {{ secret_password }}
@@ -142,12 +171,13 @@ except ImportError:
HAS_TSS_SDK = False
try:
from thycotic.secrets.server import PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer
from thycotic.secrets.server import PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer
HAS_TSS_AUTHORIZER = True
except ImportError:
PasswordGrantAuthorizer = None
DomainPasswordGrantAuthorizer = None
AccessTokenAuthorizer = None
HAS_TSS_AUTHORIZER = False
@@ -209,6 +239,11 @@ class TSSClientV1(TSSClient):
@staticmethod
def _get_authorizer(**server_parameters):
if server_parameters.get("token"):
return AccessTokenAuthorizer(
server_parameters["token"],
)
if server_parameters.get("domain"):
return DomainPasswordGrantAuthorizer(
server_parameters["base_url"],
@@ -238,6 +273,7 @@ class LookupModule(LookupBase):
username=self.get_option("username"),
password=self.get_option("password"),
domain=self.get_option("domain"),
token=self.get_option("token"),
api_path_uri=self.get_option("api_path_uri"),
token_path_uri=self.get_option("token_path_uri"),
)

View File

@@ -0,0 +1,343 @@
# Vendored copy of distutils/version.py from CPython 3.9.5
#
# Implements multiple version numbering conventions for the
# Python Module Distribution Utilities.
#
# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
#
"""Provides classes to represent module version numbers (one class for
each style of version numbering). There are currently two such classes
implemented: StrictVersion and LooseVersion.
Every version number class implements the following interface:
* the 'parse' method takes a string and parses it to some internal
representation; if the string is an invalid version number,
'parse' raises a ValueError exception
* the class constructor takes an optional string argument which,
if supplied, is passed to 'parse'
* __str__ reconstructs the string that was passed to 'parse' (or
an equivalent string -- ie. one that will generate an equivalent
version number instance)
* __repr__ generates Python code to recreate the version number instance
* _cmp compares the current instance with either another instance
of the same class or a string (which will be parsed to an instance
of the same class, thus must follow the same rules)
"""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import re
try:
RE_FLAGS = re.VERBOSE | re.ASCII
except AttributeError:
RE_FLAGS = re.VERBOSE
class Version:
"""Abstract base class for version numbering classes. Just provides
constructor (__init__) and reproducer (__repr__), because those
seem to be the same for all version numbering classes; and route
rich comparisons to _cmp.
"""
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def __repr__(self):
return "%s ('%s')" % (self.__class__.__name__, str(self))
def __eq__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c == 0
def __lt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c < 0
def __le__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c <= 0
def __gt__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c > 0
def __ge__(self, other):
c = self._cmp(other)
if c is NotImplemented:
return c
return c >= 0
# Interface for version-number classes -- must be implemented
# by the following classes (the concrete ones -- Version should
# be treated as an abstract class).
# __init__ (string) - create and take same action as 'parse'
# (string parameter is optional)
# parse (string) - convert a string representation to whatever
# internal representation is appropriate for
# this style of version numbering
# __str__ (self) - convert back to a string; should be very similar
# (if not identical to) the string supplied to parse
# __repr__ (self) - generate Python code to recreate
# the instance
# _cmp (self, other) - compare two version numbers ('other' may
# be an unparsed version string, or another
# instance of your version class)
class StrictVersion(Version):
"""Version numbering for anal retentives and software idealists.
Implements the standard interface for version number classes as
described above. A version number consists of two or three
dot-separated numeric components, with an optional "pre-release" tag
on the end. The pre-release tag consists of the letter 'a' or 'b'
followed by a number. If the numeric components of two version
numbers are equal, then one with a pre-release tag will always
be deemed earlier (lesser) than one without.
The following are valid version numbers (shown in the order that
would be obtained by sorting according to the supplied cmp function):
0.4 0.4.0 (these two are equivalent)
0.4.1
0.5a1
0.5b3
0.5
0.9.6
1.0
1.0.4a3
1.0.4b1
1.0.4
The following are examples of invalid version numbers:
1
2.7.2.2
1.3.a4
1.3pl1
1.3c4
The rationale for this version numbering system will be explained
in the distutils documentation.
"""
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
RE_FLAGS)
def parse(self, vstring):
match = self.version_re.match(vstring)
if not match:
raise ValueError("invalid version number '%s'" % vstring)
(major, minor, patch, prerelease, prerelease_num) = \
match.group(1, 2, 4, 5, 6)
if patch:
self.version = tuple(map(int, [major, minor, patch]))
else:
self.version = tuple(map(int, [major, minor])) + (0,)
if prerelease:
self.prerelease = (prerelease[0], int(prerelease_num))
else:
self.prerelease = None
def __str__(self):
if self.version[2] == 0:
vstring = '.'.join(map(str, self.version[0:2]))
else:
vstring = '.'.join(map(str, self.version))
if self.prerelease:
vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
return vstring
def _cmp(self, other):
if isinstance(other, str):
other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
return NotImplemented
if self.version != other.version:
# numeric versions don't match
# prerelease stuff doesn't matter
if self.version < other.version:
return -1
else:
return 1
# have to compare prerelease
# case 1: neither has prerelease; they're equal
# case 2: self has prerelease, other doesn't; other is greater
# case 3: self doesn't have prerelease, other does: self is greater
# case 4: both have prerelease: must compare them!
if (not self.prerelease and not other.prerelease):
return 0
elif (self.prerelease and not other.prerelease):
return -1
elif (not self.prerelease and other.prerelease):
return 1
elif (self.prerelease and other.prerelease):
if self.prerelease == other.prerelease:
return 0
elif self.prerelease < other.prerelease:
return -1
else:
return 1
else:
raise AssertionError("never get here")
# end class StrictVersion
# The rules according to Greg Stein:
# 1) a version number has 1 or more numbers separated by a period or by
# sequences of letters. If only periods, then these are compared
# left-to-right to determine an ordering.
# 2) sequences of letters are part of the tuple for comparison and are
# compared lexicographically
# 3) recognize the numeric components may have leading zeroes
#
# The LooseVersion class below implements these rules: a version number
# string is split up into a tuple of integer and string components, and
# comparison is a simple tuple comparison. This means that version
# numbers behave in a predictable and obvious way, but a way that might
# not necessarily be how people *want* version numbers to behave. There
# wouldn't be a problem if people could stick to purely numeric version
# numbers: just split on period and compare the numbers as tuples.
# However, people insist on putting letters into their version numbers;
# the most common purpose seems to be:
# - indicating a "pre-release" version
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
# - indicating a post-release patch ('p', 'pl', 'patch')
# but of course this can't cover all version number schemes, and there's
# no way to know what a programmer means without asking him.
#
# The problem is what to do with letters (and other non-numeric
# characters) in a version number. The current implementation does the
# obvious and predictable thing: keep them as strings and compare
# lexically within a tuple comparison. This has the desired effect if
# an appended letter sequence implies something "post-release":
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
#
# However, if letters in a version number imply a pre-release version,
# the "obvious" thing isn't correct. Eg. you would expect that
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
# implemented here, this just isn't so.
#
# Two possible solutions come to mind. The first is to tie the
# comparison algorithm to a particular set of semantic rules, as has
# been done in the StrictVersion class above. This works great as long
# as everyone can go along with bondage and discipline. Hopefully a
# (large) subset of Python module programmers will agree that the
# particular flavour of bondage and discipline provided by StrictVersion
# provides enough benefit to be worth using, and will submit their
# version numbering scheme to its domination. The free-thinking
# anarchists in the lot will never give in, though, and something needs
# to be done to accommodate them.
#
# Perhaps a "moderately strict" version class could be implemented that
# lets almost anything slide (syntactically), and makes some heuristic
# assumptions about non-digits in version number strings. This could
# sink into special-case-hell, though; if I was as talented and
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
# just as happy dealing with things like "2g6" and "1.13++". I don't
# think I'm smart enough to do it right though.
#
# In any case, I've coded the test suite for this module (see
# ../test/test_version.py) specifically to fail on things like comparing
# "1.2a2" and "1.2". That's not because the *code* is doing anything
# wrong, it's because the simple, obvious design doesn't match my
# complicated, hairy expectations for real-world version numbers. It
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
# the Right Thing" (ie. the code matches the conception). But I'd rather
# have a conception that matches common notions about version numbers.
class LooseVersion(Version):
"""Version numbering for anarchists and software realists.
Implements the standard interface for version number classes as
described above. A version number consists of a series of numbers,
separated by either periods or strings of letters. When comparing
version numbers, the numeric components will be compared
numerically, and the alphabetic components lexically. The following
are all valid version numbers, in no particular order:
1.5.1
1.5.2b2
161
3.10a
8.02
3.4j
1996.07.12
3.2.pl0
3.1.1.6
2g6
11g
0.960923
2.2beta29
1.13++
5.5.kw
2.0b1pl0
In fact, there is no such thing as an invalid version number under
this scheme; the rules for comparison are simple and predictable,
but may not always give the results you want (for some definition
of "want").
"""
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def parse(self, vstring):
# I've given up on thinking I can reconstruct the version string
# from the parsed tuple -- so I just store the string here for
# use by __str__
self.vstring = vstring
components = [x for x in self.component_re.split(vstring) if x and x != '.']
for i, obj in enumerate(components):
try:
components[i] = int(obj)
except ValueError:
pass
self.version = components
def __str__(self):
return self.vstring
def __repr__(self):
return "LooseVersion ('%s')" % str(self)
def _cmp(self, other):
if isinstance(other, str):
other = LooseVersion(other)
elif not isinstance(other, LooseVersion):
return NotImplemented
if self.version == other.version:
return 0
if self.version < other.version:
return -1
if self.version > other.version:
return 1
# end class LooseVersion

View File

@@ -8,12 +8,13 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
from distutils.version import StrictVersion
from ansible.module_utils.basic import missing_required_lib
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
try:
from urllib import quote_plus # Python 2.X
except ImportError:
@@ -90,7 +91,7 @@ def gitlabAuthentication(module):
# python-gitlab library remove support for username/password authentication since 1.13.0
# Changelog : https://github.com/python-gitlab/python-gitlab/releases/tag/v1.13.0
# This condition allow to still support older version of the python-gitlab library
if StrictVersion(gitlab.__version__) < StrictVersion("1.13.0"):
if LooseVersion(gitlab.__version__) < LooseVersion("1.13.0"):
gitlab_instance = gitlab.Gitlab(url=gitlab_url, ssl_verify=validate_certs, email=gitlab_user, password=gitlab_password,
private_token=gitlab_token, api_version=4)
else:

View File

@@ -83,6 +83,9 @@ URL_IDENTITY_PROVIDER = "{url}/admin/realms/{realm}/identity-provider/instances/
URL_IDENTITY_PROVIDER_MAPPERS = "{url}/admin/realms/{realm}/identity-provider/instances/{alias}/mappers"
URL_IDENTITY_PROVIDER_MAPPER = "{url}/admin/realms/{realm}/identity-provider/instances/{alias}/mappers/{id}"
URL_COMPONENTS = "{url}/admin/realms/{realm}/components"
URL_COMPONENT = "{url}/admin/realms/{realm}/components/{id}"
def keycloak_argument_spec():
"""
@@ -1028,7 +1031,7 @@ class KeycloakAPI(object):
:param name: Name of the role to fetch.
:param realm: Realm in which the role resides; default 'master'.
"""
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=name)
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name))
try:
return json.loads(to_native(open_url(role_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
@@ -1062,7 +1065,7 @@ class KeycloakAPI(object):
:param rolerep: A RoleRepresentation of the updated role.
:return HTTPResponse object on success
"""
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=rolerep['name'])
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(rolerep['name']))
try:
return open_url(role_url, method='PUT', headers=self.restheaders,
data=json.dumps(rolerep), validate_certs=self.validate_certs)
@@ -1076,7 +1079,7 @@ class KeycloakAPI(object):
:param name: The name of the role.
:param realm: The realm in which this role resides, default "master".
"""
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=name)
role_url = URL_REALM_ROLE.format(url=self.baseurl, realm=realm, name=quote(name))
try:
return open_url(role_url, method='DELETE', headers=self.restheaders,
validate_certs=self.validate_certs)
@@ -1119,7 +1122,7 @@ class KeycloakAPI(object):
if cid is None:
self.module.fail_json(msg='Could not find client %s in realm %s'
% (clientid, realm))
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=name)
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name))
try:
return json.loads(to_native(open_url(role_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
@@ -1165,7 +1168,7 @@ class KeycloakAPI(object):
if cid is None:
self.module.fail_json(msg='Could not find client %s in realm %s'
% (clientid, realm))
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=rolerep['name'])
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(rolerep['name']))
try:
return open_url(role_url, method='PUT', headers=self.restheaders,
data=json.dumps(rolerep), validate_certs=self.validate_certs)
@@ -1184,7 +1187,7 @@ class KeycloakAPI(object):
if cid is None:
self.module.fail_json(msg='Could not find client %s in realm %s'
% (clientid, realm))
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=name)
role_url = URL_CLIENT_ROLE.format(url=self.baseurl, realm=realm, id=cid, name=quote(name))
try:
return open_url(role_url, method='DELETE', headers=self.restheaders,
validate_certs=self.validate_certs)
@@ -1601,3 +1604,93 @@ class KeycloakAPI(object):
except Exception as e:
self.module.fail_json(msg='Unable to delete mapper %s for identity provider %s in realm %s: %s'
% (mid, alias, realm, str(e)))
def get_components(self, filter=None, realm='master'):
""" Fetch representations for components in a realm
:param realm: realm to be queried
:param filter: search filter
:return: list of representations for components
"""
comps_url = URL_COMPONENTS.format(url=self.baseurl, realm=realm)
if filter is not None:
comps_url += '?%s' % filter
try:
return json.loads(to_native(open_url(comps_url, method='GET', headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except ValueError as e:
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain list of components for realm %s: %s'
% (realm, str(e)))
except Exception as e:
self.module.fail_json(msg='Could not obtain list of components for realm %s: %s'
% (realm, str(e)))
def get_component(self, cid, realm='master'):
""" Fetch component representation from a realm using its cid.
If the component does not exist, None is returned.
:param cid: Unique ID of the component to fetch.
:param realm: Realm in which the component resides; default 'master'.
"""
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
try:
return json.loads(to_native(open_url(comp_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except HTTPError as e:
if e.code == 404:
return None
else:
self.module.fail_json(msg='Could not fetch component %s in realm %s: %s'
% (cid, realm, str(e)))
except Exception as e:
self.module.fail_json(msg='Could not fetch component %s in realm %s: %s'
% (cid, realm, str(e)))
def create_component(self, comprep, realm='master'):
""" Create an component.
:param comprep: Component representation of the component to be created.
:param realm: Realm in which this component resides, default "master".
:return: Component representation of the created component
"""
comps_url = URL_COMPONENTS.format(url=self.baseurl, realm=realm)
try:
resp = open_url(comps_url, method='POST', headers=self.restheaders,
data=json.dumps(comprep), validate_certs=self.validate_certs)
comp_url = resp.getheader('Location')
if comp_url is None:
self.module.fail_json(msg='Could not create component in realm %s: %s'
% (realm, 'unexpected response'))
return json.loads(to_native(open_url(comp_url, method="GET", headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except Exception as e:
self.module.fail_json(msg='Could not create component in realm %s: %s'
% (realm, str(e)))
def update_component(self, comprep, realm='master'):
""" Update an existing component.
:param comprep: Component representation of the component to be updated.
:param realm: Realm in which this component resides, default "master".
:return HTTPResponse object on success
"""
cid = comprep.get('id')
if cid is None:
self.module.fail_json(msg='Cannot update component without id')
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
try:
return open_url(comp_url, method='PUT', headers=self.restheaders,
data=json.dumps(comprep), validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not update component %s in realm %s: %s'
% (cid, realm, str(e)))
def delete_component(self, cid, realm='master'):
""" Delete an component.
:param cid: Unique ID of the component.
:param realm: Realm in which this component resides, default "master".
"""
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
try:
return open_url(comp_url, method='DELETE', headers=self.restheaders,
validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Unable to delete component %s in realm %s: %s'
% (cid, realm, str(e)))

View File

@@ -9,7 +9,8 @@ __metaclass__ = type
import traceback
from ansible.module_utils.basic import missing_required_lib
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:

View File

@@ -31,6 +31,7 @@ def _env_then_dns_fallback(*args, **kwargs):
result = env_fallback(*args, **kwargs)
if result == '':
raise AnsibleFallbackNotFound
return result
except AnsibleFallbackNotFound:
# If no host was given, we try to guess it from IPA.
# The ipa-ca entry is a standard entry that IPA will have set for

View File

@@ -54,6 +54,17 @@ def proxmox_to_ansible_bool(value):
return True if value == 1 else False
def ansible_to_proxmox_bool(value):
'''Convert Ansible representation of a boolean to be proxmox-friendly'''
if value is None:
return None
if not isinstance(value, bool):
raise ValueError("%s must be of type bool not %s" % (value, type(value)))
return 1 if value else 0
class ProxmoxAnsible(object):
"""Base class for Proxmox modules"""
def __init__(self, module):

View File

@@ -29,7 +29,7 @@ FAIL_MSG = 'Issuing a data modification command without specifying the '\
class RedfishUtils(object):
def __init__(self, creds, root_uri, timeout, module, resource_id=None,
data_modification=False):
data_modification=False, strip_etag_quotes=False):
self.root_uri = root_uri
self.creds = creds
self.timeout = timeout
@@ -37,6 +37,7 @@ class RedfishUtils(object):
self.service_root = '/redfish/v1/'
self.resource_id = resource_id
self.data_modification = data_modification
self.strip_etag_quotes = strip_etag_quotes
self._init_session()
def _auth_params(self, headers):
@@ -121,6 +122,8 @@ class RedfishUtils(object):
if not etag:
etag = r['data'].get('@odata.etag')
if etag:
if self.strip_etag_quotes:
etag = etag.strip('"')
req_headers['If-Match'] = etag
username, password, basic_auth = self._auth_params(req_headers)
try:
@@ -976,6 +979,8 @@ class RedfishUtils(object):
payload['Password'] = user.get('account_password')
if user.get('account_roleid'):
payload['RoleId'] = user.get('account_roleid')
if user.get('account_id'):
payload['Id'] = user.get('account_id')
response = self.post_request(self.root_uri + self.accounts_uri, payload)
if not response['ret']:
@@ -1600,9 +1605,6 @@ class RedfishUtils(object):
cur_boot_next = boot.get('BootNext')
cur_override_mode = boot.get('BootSourceOverrideMode')
if not boot_override_mode:
boot_override_mode = cur_override_mode
if override_enabled == 'Disabled':
payload = {
'Boot': {
@@ -1638,16 +1640,18 @@ class RedfishUtils(object):
}
}
else:
if cur_enabled == override_enabled and target == bootdevice and cur_override_mode == boot_override_mode:
if (cur_enabled == override_enabled and target == bootdevice and
(cur_override_mode == boot_override_mode or not boot_override_mode)):
# If properties are already set, no changes needed
return {'ret': True, 'changed': False}
payload = {
'Boot': {
'BootSourceOverrideEnabled': override_enabled,
'BootSourceOverrideMode': boot_override_mode,
'BootSourceOverrideTarget': bootdevice
}
}
if boot_override_mode:
payload['Boot']['BootSourceOverrideMode'] = boot_override_mode
response = self.patch_request(self.root_uri + self.systems_uri, payload)
if response['ret'] is False:
@@ -2757,7 +2761,9 @@ class RedfishUtils(object):
if isinstance(set_value, dict):
for subprop in payload[property].keys():
if subprop not in target_ethernet_current_setting[property]:
return {'ret': False, 'msg': "Sub-property %s in nic_config is invalid" % subprop}
# Not configured already; need to apply the request
need_change = True
break
sub_set_value = payload[property][subprop]
sub_cur_value = target_ethernet_current_setting[property][subprop]
if sub_set_value != sub_cur_value:
@@ -2771,7 +2777,9 @@ class RedfishUtils(object):
for i in range(len(set_value)):
for subprop in payload[property][i].keys():
if subprop not in target_ethernet_current_setting[property][i]:
return {'ret': False, 'msg': "Sub-property %s in nic_config is invalid" % subprop}
# Not configured already; need to apply the request
need_change = True
break
sub_set_value = payload[property][i][subprop]
sub_cur_value = target_ethernet_current_setting[property][i][subprop]
if sub_set_value != sub_cur_value:

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2021, Andreas Botzner <andreas at botzner dot 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
from ansible.module_utils.basic import missing_required_lib
__metaclass__ = type
import traceback
REDIS_IMP_ERR = None
try:
from redis import Redis
from redis import __version__ as redis_version
HAS_REDIS_PACKAGE = True
except ImportError:
REDIS_IMP_ERR = traceback.format_exc()
HAS_REDIS_PACKAGE = False
try:
import certifi
HAS_CERTIFI_PACKAGE = True
except ImportError:
CERTIFI_IMPORT_ERROR = traceback.format_exc()
HAS_CERTIFI_PACKAGE = False
def fail_imports(module):
errors = []
traceback = []
if not HAS_REDIS_PACKAGE:
errors.append(missing_required_lib('redis'))
traceback.append(REDIS_IMP_ERR)
if not HAS_CERTIFI_PACKAGE:
errors.append(missing_required_lib('certifi'))
traceback.append(CERTIFI_IMPORT_ERROR)
if errors:
module.fail_json(errors=errors, traceback='\n'.join(traceback))
def redis_auth_argument_spec():
return dict(
login_host=dict(type='str',
default='localhost',),
login_user=dict(type='str'),
login_password=dict(type='str',
no_log=True
),
login_port=dict(type='int', default=6379),
tls=dict(type='bool',
default=True),
validate_certs=dict(type='bool',
default=True
),
ca_certs=dict(type='str')
)
class RedisAnsible(object):
'''Base class for Redis module'''
def __init__(self, module):
self.module = module
self.connection = self._connect()
def _connect(self):
login_host = self.module.params['login_host']
login_user = self.module.params['login_user']
login_password = self.module.params['login_password']
login_port = self.module.params['login_port']
tls = self.module.params['tls']
validate_certs = 'required' if self.module.params['validate_certs'] else None
ca_certs = self.module.params['ca_certs']
if tls and ca_certs is None:
ca_certs = str(certifi.where())
if tuple(map(int, redis_version.split('.'))) < (3, 4, 0) and login_user is not None:
self.module.fail_json(
msg='The option `username` in only supported with redis >= 3.4.0.')
params = {'host': login_host,
'port': login_port,
'password': login_password,
'ssl_ca_certs': ca_certs,
'ssl_cert_reqs': validate_certs,
'ssl': tls}
if login_user is not None:
params['username'] = login_user
try:
return Redis(**params)
except Exception as e:
self.module.fail_json(msg='{0}'.format(str(e)))
return None

View File

@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Phillipe Smith <phsmithcc@gmail.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
import json
from ansible.module_utils.urls import fetch_url, url_argument_spec
from ansible.module_utils.common.text.converters import to_native
def api_argument_spec():
'''
Creates an argument spec that can be used with any module
that will be requesting content via Rundeck API
'''
api_argument_spec = url_argument_spec()
api_argument_spec.update(dict(
url=dict(required=True, type="str"),
api_version=dict(type="int", default=39),
api_token=dict(required=True, type="str", no_log=True)
))
return api_argument_spec
def api_request(module, endpoint, data=None, method="GET"):
"""Manages Rundeck API requests via HTTP(S)
:arg module: The AnsibleModule (used to get url, api_version, api_token, etc).
:arg endpoint: The API endpoint to be used.
:kwarg data: The data to be sent (in case of POST/PUT).
:kwarg method: "POST", "PUT", etc.
:returns: A tuple of (**response**, **info**). Use ``response.read()`` to read the data.
The **info** contains the 'status' and other meta data. When a HttpError (status >= 400)
occurred then ``info['body']`` contains the error response data::
Example::
data={...}
resp, info = fetch_url(module,
"http://rundeck.example.org",
data=module.jsonify(data),
method="POST")
status_code = info["status"]
body = resp.read()
if status_code >= 400 :
body = info['body']
"""
response, info = fetch_url(
module=module,
url="%s/api/%s/%s" % (
module.params["url"],
module.params["api_version"],
endpoint
),
data=json.dumps(data),
method=method,
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"X-Rundeck-Auth-Token": module.params["api_token"]
}
)
if info["status"] == 403:
module.fail_json(msg="Token authorization failed",
execution_info=json.loads(info["body"]))
if info["status"] == 409:
module.fail_json(msg="Job executions limit reached",
execution_info=json.loads(info["body"]))
elif info["status"] >= 500:
module.fail_json(msg="Rundeck API error",
execution_info=json.loads(info["body"]))
try:
content = response.read()
json_response = json.loads(content)
return json_response, info
except AttributeError as error:
module.fail_json(msg="Rundeck API request error",
exception=to_native(error),
execution_info=info)
except ValueError as error:
module.fail_json(
msg="No valid JSON response",
exception=to_native(error),
execution_info=content
)

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""Provide version object to compare version numbers."""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
# Once we drop support for Ansible 2.9, ansible-base 2.10, and ansible-core 2.11, we can
# remove the _version.py file, and replace the following import by
#
# from ansible.module_utils.compat.version import LooseVersion
from ._version import LooseVersion

View File

@@ -120,7 +120,7 @@ __version__ = '${version}'
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:

View File

@@ -161,7 +161,8 @@ __version__ = '${version}'
import json
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:

View File

@@ -89,7 +89,8 @@ __version__ = '${version}'
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:
@@ -132,8 +133,7 @@ class ClcBlueprintPackage:
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
if not REQUESTS_FOUND:
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
if requests.__version__ and LooseVersion(
requests.__version__) < LooseVersion('2.5.0'):
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
self.module.fail_json(
msg='requests library version should be >= 2.5.0')

View File

@@ -162,7 +162,8 @@ import os
import traceback
from ansible.module_utils.six.moves.urllib.parse import urlparse
from time import sleep
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:
@@ -203,8 +204,7 @@ class ClcFirewallPolicy:
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
if not REQUESTS_FOUND:
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
if requests.__version__ and LooseVersion(
requests.__version__) < LooseVersion('2.5.0'):
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
self.module.fail_json(
msg='requests library version should be >= 2.5.0')

View File

@@ -207,7 +207,8 @@ __version__ = '${version}'
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:

View File

@@ -210,7 +210,8 @@ import json
import os
import traceback
from time import sleep
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:
@@ -255,8 +256,7 @@ class ClcLoadBalancer:
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
if not REQUESTS_FOUND:
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
if requests.__version__ and LooseVersion(
requests.__version__) < LooseVersion('2.5.0'):
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
self.module.fail_json(
msg='requests library version should be >= 2.5.0')

View File

@@ -311,7 +311,8 @@ __version__ = '${version}'
import json
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:
@@ -355,8 +356,7 @@ class ClcModifyServer:
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
if not REQUESTS_FOUND:
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
if requests.__version__ and LooseVersion(
requests.__version__) < LooseVersion('2.5.0'):
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
self.module.fail_json(
msg='requests library version should be >= 2.5.0')

View File

@@ -117,7 +117,8 @@ __version__ = '${version}'
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:

View File

@@ -433,7 +433,8 @@ import json
import os
import time
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:
@@ -478,8 +479,7 @@ class ClcServer:
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
if not REQUESTS_FOUND:
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
if requests.__version__ and LooseVersion(
requests.__version__) < LooseVersion('2.5.0'):
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
self.module.fail_json(
msg='requests library version should be >= 2.5.0')

View File

@@ -101,7 +101,8 @@ __version__ = '${version}'
import os
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:
@@ -145,8 +146,7 @@ class ClcSnapshot:
self.module.fail_json(msg=missing_required_lib('clc-sdk'), exception=CLC_IMP_ERR)
if not REQUESTS_FOUND:
self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR)
if requests.__version__ and LooseVersion(
requests.__version__) < LooseVersion('2.5.0'):
if requests.__version__ and LooseVersion(requests.__version__) < LooseVersion('2.5.0'):
self.module.fail_json(
msg='requests library version should be >= 2.5.0')

View File

@@ -23,40 +23,48 @@ options:
required: true
architecture:
description:
- The architecture for the container (e.g. "x86_64" or "i686").
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1)
- 'The architecture for the container (for example C(x86_64) or C(i686)).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).'
type: str
required: false
config:
description:
- 'The config for the container (e.g. {"limits.cpu": "2"}).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1)'
- If the container already exists and its "config" value in metadata
obtained from
GET /1.0/containers/<name>
- 'The config for the container (for example C({"limits.cpu": "2"})).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).'
- If the container already exists and its "config" values in metadata
obtained from GET /1.0/containers/<name>
U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#10containersname)
are different, they this module tries to apply the configurations.
- The key starts with 'volatile.' are ignored for this comparison.
- Not all config values are supported to apply the existing container.
Maybe you need to delete and recreate a container.
are different, this module tries to apply the configurations.
- The keys starting with C(volatile.) are ignored for this comparison when I(ignore_volatile_options=true).
type: dict
required: false
ignore_volatile_options:
description:
- If set to C(true), options starting with C(volatile.) are ignored. As a result,
they are reapplied for each execution.
- This default behavior can be changed by setting this option to C(false).
- The default value C(true) will be deprecated in community.general 4.0.0,
and will change to C(false) in community.general 5.0.0.
type: bool
default: true
required: false
version_added: 3.7.0
profiles:
description:
- Profile to be used by the container
- Profile to be used by the container.
type: list
elements: str
devices:
description:
- 'The devices for the container
(e.g. { "rootfs": { "path": "/dev/kvm", "type": "unix-char" }).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1)'
(for example C({ "rootfs": { "path": "/dev/kvm", "type": "unix-char" }})).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).'
type: dict
required: false
ephemeral:
description:
- Whether or not the container is ephemeral (e.g. true or false).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1)
- Whether or not the container is ephemeral (for example C(true) or C(false)).
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1).
required: false
type: bool
source:
@@ -68,7 +76,7 @@ options:
"protocol": "lxd",
"alias": "ubuntu/xenial/amd64" }).'
- 'See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1) for complete API documentation.'
- 'Note that C(protocol) accepts two choices: C(lxd) or C(simplestreams)'
- 'Note that C(protocol) accepts two choices: C(lxd) or C(simplestreams).'
required: false
type: dict
state:
@@ -144,10 +152,10 @@ options:
trust_password:
description:
- The client trusted password.
- You need to set this password on the LXD server before
running this module using the following command.
lxc config set core.trust_password <some random password>
See U(https://www.stgraber.org/2016/04/18/lxd-api-direct-interaction/)
- 'You need to set this password on the LXD server before
running this module using the following command:
C(lxc config set core.trust_password <some random password>).
See U(https://www.stgraber.org/2016/04/18/lxd-api-direct-interaction/).'
- If trust_password is set, this module send a request for
authentication before sending any requests.
required: false
@@ -176,6 +184,7 @@ EXAMPLES = '''
- name: Create a started container
community.general.lxd_container:
name: mycontainer
ignore_volatile_options: true
state: started
source:
type: image
@@ -209,6 +218,7 @@ EXAMPLES = '''
- name: Create a started container
community.general.lxd_container:
name: mycontainer
ignore_volatile_options: true
state: started
source:
type: image
@@ -279,6 +289,7 @@ EXAMPLES = '''
- name: Create LXD container
community.general.lxd_container:
name: new-container-1
ignore_volatile_options: true
state: started
source:
type: image
@@ -289,6 +300,7 @@ EXAMPLES = '''
- name: Create container on another node
community.general.lxd_container:
name: new-container-2
ignore_volatile_options: true
state: started
source:
type: image
@@ -557,7 +569,7 @@ class LXDContainerManagement(object):
def _needs_to_change_container_config(self, key):
if key not in self.config:
return False
if key == 'config':
if key == 'config' and self.ignore_volatile_options: # the old behavior is to ignore configurations by keyword "volatile"
old_configs = dict((k, v) for k, v in self.old_container_json['metadata'][key].items() if not k.startswith('volatile.'))
for k, v in self.config['config'].items():
if k not in old_configs:
@@ -565,6 +577,14 @@ class LXDContainerManagement(object):
if old_configs[k] != v:
return True
return False
elif key == 'config': # next default behavior
old_configs = dict((k, v) for k, v in self.old_container_json['metadata'][key].items())
for k, v in self.config['config'].items():
if k not in old_configs:
return True
if old_configs[k] != v:
return True
return False
else:
old_configs = self.old_container_json['metadata'][key]
return self.config[key] != old_configs
@@ -606,6 +626,7 @@ class LXDContainerManagement(object):
try:
if self.trust_password is not None:
self.client.authenticate(self.trust_password)
self.ignore_volatile_options = self.module.params.get('ignore_volatile_options')
self.old_container_json = self._get_container_json()
self.old_state = self._container_json_to_module_state(self.old_container_json)
@@ -651,6 +672,10 @@ def main():
config=dict(
type='dict',
),
ignore_volatile_options=dict(
type='bool',
default=True
),
devices=dict(
type='dict',
),
@@ -703,7 +728,13 @@ def main():
),
supports_check_mode=False,
)
# if module.params['ignore_volatile_options'] is None:
# module.params['ignore_volatile_options'] = True
# module.deprecate(
# 'If the keyword "volatile" is used in a playbook in the config section, a
# "changed" message will appear with every run, even without a change to the playbook.
# This will change in the future.
# Please test your scripts by "ignore_volatile_options: false"', version='5.0.0', collection_name='community.general')
lxd_manage = LXDContainerManagement(module=module)
lxd_manage.run()

View File

@@ -363,7 +363,8 @@ EXAMPLES = r'''
import time
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
try:
from proxmoxer import ProxmoxAPI
@@ -374,6 +375,10 @@ except ImportError:
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.general.plugins.module_utils.proxmox import (
ansible_to_proxmox_bool
)
VZ_TYPE = None
@@ -627,14 +632,14 @@ def main():
netif=module.params['netif'],
mounts=module.params['mounts'],
ip_address=module.params['ip_address'],
onboot=int(module.params['onboot']),
onboot=ansible_to_proxmox_bool(module.params['onboot']),
cpuunits=module.params['cpuunits'],
nameserver=module.params['nameserver'],
searchdomain=module.params['searchdomain'],
force=int(module.params['force']),
force=ansible_to_proxmox_bool(module.params['force']),
pubkey=module.params['pubkey'],
features=",".join(module.params['features']) if module.params['features'] is not None else None,
unprivileged=int(module.params['unprivileged']),
unprivileged=ansible_to_proxmox_bool(module.params['unprivileged']),
description=module.params['description'],
hookscript=module.params['hookscript'])

View File

@@ -131,7 +131,7 @@ def main():
group = module.params['group']
if group:
groups = [proxmox.get_group(group=group)]
groups = [proxmox.get_group(groupid=group)]
else:
groups = proxmox.get_groups()
result['proxmox_groups'] = [group.group for group in groups]

View File

@@ -761,9 +761,10 @@ msg:
import re
import time
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.six.moves.urllib.parse import quote
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
try:
from proxmoxer import ProxmoxAPI
HAS_PROXMOXER = True

View File

@@ -0,0 +1,186 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andreas Botzner (@paginabianca) <andreas at botzner dot 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: proxmox_tasks_info
short_description: Retrieve information about one or more Proxmox VE tasks
version_added: 3.8.0
description:
- Retrieve information about one or more Proxmox VE tasks.
author: 'Andreas Botzner (@paginabianca) <andreas at botzner dot com>'
options:
node:
description:
- Node where to get tasks.
required: true
type: str
task:
description:
- Return specific task.
aliases: ['upid', 'name']
type: str
extends_documentation_fragment:
- community.general.proxmox.documentation
'''
EXAMPLES = '''
- name: List tasks on node01
community.general.proxmox_task_info:
api_host: proxmoxhost
api_user: root@pam
api_password: '{{ password | default(omit) }}'
api_token_id: '{{ token_id | default(omit) }}'
api_token_secret: '{{ token_secret | default(omit) }}'
node: node01
register: result
- name: Retrieve information about specific tasks on node01
community.general.proxmox_task_info:
api_host: proxmoxhost
api_user: root@pam
api_password: '{{ password | default(omit) }}'
api_token_id: '{{ token_id | default(omit) }}'
api_token_secret: '{{ token_secret | default(omit) }}'
task: 'UPID:node01:00003263:16167ACE:621EE230:srvreload:networking:root@pam:'
node: node01
register: proxmox_tasks
'''
RETURN = '''
proxmox_tasks:
description: List of tasks.
returned: on success
type: list
elements: dict
contains:
id:
description: ID of the task.
returned: on success
type: str
node:
description: Node name.
returned: on success
type: str
pid:
description: PID of the task.
returned: on success
type: int
pstart:
description: pastart of the task.
returned: on success
type: int
starttime:
description: Starting time of the task.
returned: on success
type: int
type:
description: Type of the task.
returned: on success
type: str
upid:
description: UPID of the task.
returned: on success
type: str
user:
description: User that owns the task.
returned: on success
type: str
endtime:
description: Endtime of the task.
returned: on success, can be absent
type: int
status:
description: Status of the task.
returned: on success, can be absent
type: str
failed:
description: If the task failed.
returned: when status is defined
type: bool
msg:
description: Short message.
returned: on failure
type: str
sample: 'Task: UPID:xyz:xyz does not exist on node: proxmoxnode'
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.community.general.plugins.module_utils.proxmox import (
proxmox_auth_argument_spec, ProxmoxAnsible, HAS_PROXMOXER, PROXMOXER_IMP_ERR)
class ProxmoxTaskInfoAnsible(ProxmoxAnsible):
def get_task(self, upid, node):
tasks = self.get_tasks(node)
for task in tasks:
if task.info['upid'] == upid:
return [task]
def get_tasks(self, node):
tasks = self.proxmox_api.nodes(node).tasks.get()
return [ProxmoxTask(task) for task in tasks]
class ProxmoxTask:
def __init__(self, task):
self.info = dict()
for k, v in task.items():
if k == 'status' and isinstance(v, str):
self.info[k] = v
if v != 'OK':
self.info['failed'] = True
else:
self.info[k] = v
def proxmox_task_info_argument_spec():
return dict(
task=dict(type='str', aliases=['upid', 'name'], required=False),
node=dict(type='str', required=True),
)
def main():
module_args = proxmox_auth_argument_spec()
task_info_args = proxmox_task_info_argument_spec()
module_args.update(task_info_args)
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret'),
('api_user', 'api_password')],
required_one_of=[('api_password', 'api_token_id')],
supports_check_mode=True)
result = dict(changed=False)
if not HAS_PROXMOXER:
module.fail_json(msg=missing_required_lib(
'proxmoxer'), exception=PROXMOXER_IMP_ERR)
proxmox = ProxmoxTaskInfoAnsible(module)
upid = module.params['task']
node = module.params['node']
if upid:
tasks = proxmox.get_task(upid=upid, node=node)
else:
tasks = proxmox.get_tasks(node=node)
if tasks is not None:
result['proxmox_tasks'] = [task.info for task in tasks]
module.exit_json(**result)
else:
result['msg'] = 'Task: {0} does not exist on node: {1}.'.format(
upid, node)
module.fail_json(**result)
if __name__ == '__main__':
main()

View File

@@ -137,6 +137,11 @@ options:
type: bool
default: false
version_added: '3.3.0'
parallelism:
description:
- Restrict concurrent operations when Terraform applies the plan.
type: int
version_added: '3.8.0'
notes:
- To just run a `terraform plan`, use check mode.
requirements: [ "terraform" ]
@@ -225,11 +230,12 @@ command:
import os
import json
import tempfile
from distutils.version import LooseVersion
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
module = None
@@ -314,11 +320,25 @@ def remove_workspace(bin_path, project_path, workspace):
_workspace_cmd(bin_path, project_path, 'delete', workspace)
def build_plan(command, project_path, variables_args, state_file, targets, state, plan_path=None):
def build_plan(command, project_path, variables_args, state_file, targets, state, apply_args, plan_path=None):
if plan_path is None:
f, plan_path = tempfile.mkstemp(suffix='.tfplan')
plan_command = [command[0], 'plan', '-input=false', '-no-color', '-detailed-exitcode', '-out', plan_path]
local_command = command.copy()
plan_command = [command[0], 'plan']
if state == "planned":
for c in local_command[1:]:
plan_command.append(c)
if state == "present":
for a in apply_args:
local_command.remove(a)
for c in local_command[1:]:
plan_command.append(c)
plan_command.extend(['-input=false', '-no-color', '-detailed-exitcode', '-out', plan_path])
for t in targets:
plan_command.extend(['-target', t])
@@ -363,6 +383,7 @@ def main():
init_reconfigure=dict(type='bool', default=False),
overwrite_init=dict(type='bool', default=True),
check_destroy=dict(type='bool', default=False),
parallelism=dict(type='int'),
),
required_if=[('state', 'planned', ['plan_file'])],
supports_check_mode=True,
@@ -415,6 +436,9 @@ def main():
elif state == 'absent':
command.extend(DESTROY_ARGS)
if state == 'present' and module.params.get('parallelism') is not None:
command.append('-parallelism=%d' % module.params.get('parallelism'))
variables_args = []
for k, v in variables.items():
variables_args.extend([
@@ -452,7 +476,7 @@ def main():
module.fail_json(msg='Could not find plan_file "{0}", check the path and try again.'.format(plan_file))
else:
plan_file, needs_application, out, err, command = build_plan(command, project_path, variables_args, state_file,
module.params.get('targets'), state, plan_file)
module.params.get('targets'), state, APPLY_ARGS, plan_file)
if state == 'present' and check_destroy and '- destroy' in out:
module.fail_json(msg="Aborting command because it would destroy some resources. "
"Consider switching the 'check_destroy' to false to suppress this error")

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the servers.
- U(https://www.online.net/en/dedicated-server)
author:
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.online

View File

@@ -12,7 +12,7 @@ short_description: Gather information about Online user.
description:
- Gather information about the user.
author:
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.online
'''

View File

@@ -306,7 +306,7 @@ def rename_image(module, client, image, new_name):
tmp_image = get_image_by_name(module, client, new_name)
if tmp_image:
module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.id))
module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.ID))
if not module.check_mode:
client.image.rename(image.ID, new_name)

View File

@@ -97,7 +97,7 @@ EXAMPLES = '''
register: my_volume
'''
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
try:
import pyrax

View File

@@ -16,7 +16,7 @@ DOCUMENTATION = '''
---
module: scaleway_compute
short_description: Scaleway compute management module
author: Remy Leone (@sieben)
author: Remy Leone (@remyleone)
description:
- "This module manages compute instances on Scaleway."
extends_documentation_fragment:

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway images available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.scaleway

View File

@@ -13,7 +13,7 @@ DOCUMENTATION = '''
---
module: scaleway_ip
short_description: Scaleway IP management module
author: Remy Leone (@sieben)
author: Remy Leone (@remyleone)
description:
- This module manages IP on Scaleway account
U(https://developer.scaleway.com)

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway ips available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.scaleway

View File

@@ -16,7 +16,7 @@ DOCUMENTATION = '''
---
module: scaleway_lb
short_description: Scaleway load-balancer management module
author: Remy Leone (@sieben)
author: Remy Leone (@remyleone)
description:
- "This module manages load-balancers on Scaleway."
extends_documentation_fragment:

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway organizations available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
options:
api_url:
description:

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway security groups available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
options:
region:
type: str

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway servers available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.scaleway

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway snapshot available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.scaleway

View File

@@ -16,7 +16,7 @@ DOCUMENTATION = '''
---
module: scaleway_sshkey
short_description: Scaleway SSH keys management module
author: Remy Leone (@sieben)
author: Remy Leone (@remyleone)
description:
- This module manages SSH keys on Scaleway account
U(https://developer.scaleway.com)

View File

@@ -16,7 +16,7 @@ DOCUMENTATION = '''
---
module: scaleway_user_data
short_description: Scaleway user_data management module
author: Remy Leone (@sieben)
author: Remy Leone (@remyleone)
description:
- "This module manages user_data on compute instances on Scaleway."
- "It can be used to configure cloud-init for instance"
@@ -75,7 +75,7 @@ def patch_user_data(compute_api, server_id, key, value):
compute_api.module.debug("Starting patching user_data attributes")
path = "servers/%s/user_data/%s" % (server_id, key)
response = compute_api.patch(path=path, data=value, headers={"Content-type": "text/plain"})
response = compute_api.patch(path=path, data=value, headers={"Content-Type": "text/plain"})
if not response.ok:
msg = 'Error during user_data patching: %s %s' % (response.status_code, response.body)
compute_api.module.fail_json(msg=msg)

View File

@@ -15,7 +15,7 @@ description:
- Gather information about the Scaleway volumes available.
author:
- "Yanis Guenane (@Spredzy)"
- "Remy Leone (@sieben)"
- "Remy Leone (@remyleone)"
extends_documentation_fragment:
- community.general.scaleway

View File

@@ -84,7 +84,7 @@ options:
description:
- the address to advertise that the service will be listening on.
This value will be passed as the I(address) parameter to Consul's
U(/v1/agent/service/register) API method, so refer to the Consul API
C(/v1/agent/service/register) API method, so refer to the Consul API
documentation for further details.
tags:
type: list

View File

@@ -117,9 +117,10 @@ state:
'''
import os
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
PACKAGE_STATE_MAP = dict(
present="--install",

View File

@@ -0,0 +1,249 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andreas Botzner <andreas at botzner dot 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 = '''
---
module: redis_data
short_description: Set key value pairs in Redis
version_added: 3.7.0
description:
- Set key value pairs in Redis database.
author: "Andreas Botzner (@paginabianca)"
options:
key:
description:
- Database key.
required: true
type: str
value:
description:
- Value that key should be set to.
required: false
type: str
expiration:
description:
- Expiration time in milliseconds.
Setting this flag will always result in a change in the database.
required: false
type: int
non_existing:
description:
- Only set key if it does not already exist.
required: false
type: bool
existing:
description:
- Only set key if it already exists.
required: false
type: bool
keep_ttl:
description:
- Retain the time to live associated with the key.
required: false
type: bool
state:
description:
- State of the key.
default: present
type: str
choices:
- present
- absent
extends_documentation_fragment:
- community.general.redis.documentation
seealso:
- module: community.general.redis_data_info
- module: community.general.redis
'''
EXAMPLES = '''
- name: Set key foo=bar on localhost with no username
community.general.redis_data:
login_host: localhost
login_password: supersecret
key: foo
value: bar
state: present
- name: Set key foo=bar if non existing with expiration of 30s
community.general.redis_data:
login_host: localhost
login_password: supersecret
key: foo
value: bar
non_existing: true
expiration: 30000
state: present
- name: Set key foo=bar if existing and keep current TTL
community.general.redis_data:
login_host: localhost
login_password: supersecret
key: foo
value: bar
existing: true
keep_ttl: true
- name: Set key foo=bar on redishost with custom ca-cert file
community.general.redis_data:
login_host: redishost
login_password: supersecret
login_user: someuser
validate_certs: true
ssl_ca_certs: /path/to/ca/certs
key: foo
value: bar
- name: Delete key foo on localhost with no username
community.general.redis_data:
login_host: localhost
login_password: supersecret
key: foo
state: absent
'''
RETURN = '''
old_value:
description: Value of key before setting.
returned: on_success if state is C(present) and key exists in database.
type: str
sample: 'old_value_of_key'
value:
description: Value key was set to.
returned: on success if state is C(present).
type: str
sample: 'new_value_of_key'
msg:
description: A short message.
returned: always
type: str
sample: 'Set key: foo to bar'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.redis import (
fail_imports, redis_auth_argument_spec, RedisAnsible)
def main():
redis_auth_args = redis_auth_argument_spec()
module_args = dict(
key=dict(type='str', required=True, no_log=False),
value=dict(type='str', required=False),
expiration=dict(type='int', required=False),
non_existing=dict(type='bool', required=False),
existing=dict(type='bool', required=False),
keep_ttl=dict(type='bool', required=False),
state=dict(type='str', default='present',
choices=['present', 'absent']),
)
module_args.update(redis_auth_args)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
required_if=[('state', 'present', ('value',))],
mutually_exclusive=[['non_existing', 'existing'],
['keep_ttl', 'expiration']],)
fail_imports(module)
redis = RedisAnsible(module)
key = module.params['key']
value = module.params['value']
px = module.params['expiration']
nx = module.params['non_existing']
xx = module.params['existing']
keepttl = module.params['keep_ttl']
state = module.params['state']
set_args = {'name': key, 'value': value, 'px': px,
'nx': nx, 'xx': xx, 'keepttl': keepttl}
result = {'changed': False}
old_value = None
try:
old_value = redis.connection.get(key)
except Exception as e:
msg = 'Failed to get value of key: {0} with exception: {1}'.format(
key, str(e))
result['msg'] = msg
module.fail_json(**result)
if state == 'absent':
if module.check_mode:
if old_value is None:
msg = 'Key: {0} not present'.format(key)
result['msg'] = msg
module.exit_json(**result)
else:
msg = 'Deleted key: {0}'.format(key)
result['msg'] = msg
module.exit_json(**result)
try:
ret = redis.connection.delete(key)
if ret == 0:
msg = 'Key: {0} not present'.format(key)
result['msg'] = msg
module.exit_json(**result)
else:
msg = 'Deleted key: {0}'.format(key)
result['msg'] = msg
result['changed'] = True
module.exit_json(**result)
except Exception as e:
msg = 'Failed to delete key: {0} with exception: {1}'.format(
key, str(e))
result['msg'] = msg
module.fail_json(**result)
old_value = None
try:
old_value = redis.connection.get(key)
except Exception as e:
msg = 'Failed to get value of key: {0} with exception: {1}'.format(
key, str(e))
result['msg'] = msg
module.fail_json(**result)
result['old_value'] = old_value
if old_value == value and keepttl is not False and px is None:
msg = 'Key {0} already has desired value'.format(key)
result['msg'] = msg
result['value'] = value
module.exit_json(**result)
if module.check_mode:
result['msg'] = 'Set key: {0}'.format(key)
result['value'] = value
module.exit_json(**result)
try:
ret = redis.connection.set(**set_args)
if ret is None:
if nx:
msg = 'Could not set key: {0}. Key already present.'.format(
key)
else:
msg = 'Could not set key: {0}. Key not present.'.format(key)
result['msg'] = msg
module.fail_json(**result)
msg = 'Set key: {0}'.format(key)
result['msg'] = msg
result['changed'] = True
result['value'] = value
module.exit_json(**result)
except Exception as e:
msg = 'Failed to set key: {0} with exception: {2}'.format(key, str(e))
result['msg'] = msg
module.fail_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,112 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andreas Botzner <andreas at botzner dot 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 = '''
---
module: redis_data_info
short_description: Get value of key in Redis database
version_added: 3.7.0
description:
- Get value of keys in Redis database.
author: "Andreas Botzner (@paginabianca)"
options:
key:
description:
- Database key.
type: str
required: true
extends_documentation_fragment:
- community.general.redis
seealso:
- module: community.general.redis_data
- module: community.general.redis_info
- module: community.general.redis
'''
EXAMPLES = '''
- name: Get key foo=bar from loalhost with no username
community.general.redis_data_info:
login_host: localhost
login_password: supersecret
key: foo
- name: Get key foo=bar on redishost with custom ca-cert file
community.general.redis_data_info:
login_host: redishost
login_password: supersecret
login_user: somuser
validate_certs: true
ssl_ca_certs: /path/to/ca/certs
key: foo
'''
RETURN = '''
exists:
description: If they key exists in the database.
returned: on success
type: bool
value:
description: Value key was set to.
returned: if existing
type: str
sample: 'value_of_some_key'
msg:
description: A short message.
returned: always
type: str
sample: 'Got key: foo with value: bar'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.redis import (
fail_imports, redis_auth_argument_spec, RedisAnsible)
def main():
redis_auth_args = redis_auth_argument_spec()
module_args = dict(
key=dict(type='str', required=True, no_log=False),
)
module_args.update(redis_auth_args)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,
)
fail_imports(module)
redis = RedisAnsible(module)
key = module.params['key']
result = {'changed': False}
value = None
try:
value = redis.connection.get(key)
except Exception as e:
msg = 'Failed to get value of key "{0}" with exception: {1}'.format(
key, str(e))
result['msg'] = msg
module.fail_json(**result)
if value is None:
msg = 'Key "{0}" does not exist in database'.format(key)
result['exists'] = False
else:
msg = 'Got key "{0}"'.format(key)
result['value'] = value
result['exists'] = True
result['msg'] = msg
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -158,7 +158,7 @@ def _run_xattr(module, cmd, check_rc=True):
if line.startswith('#') or line == '':
pass
elif '=' in line:
(key, val) = line.split('=')
(key, val) = line.split('=', 1)
result[key] = val.strip('"')
else:
result[line] = ''

View File

@@ -356,9 +356,10 @@ import os
import re
import traceback
from distutils.version import LooseVersion
from io import BytesIO
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
LXML_IMP_ERR = None
try:
from lxml import etree, objectify

View File

@@ -72,6 +72,12 @@ options:
aliases: ["searchtimelimit"]
type: int
version_added: '2.5.0'
ipaselinuxusermaporder:
description: The SELinux user map order (order in increasing priority of SELinux users).
aliases: ["selinuxusermaporder"]
type: list
elements: str
version_added: '3.7.0'
ipauserauthtype:
description: The authentication type to use by default.
aliases: ["userauthtype"]
@@ -181,6 +187,18 @@ EXAMPLES = r'''
ipa_host: localhost
ipa_user: admin
ipa_pass: supersecret
- name: Ensure the SELinux user map order is set
community.general.ipa_config:
ipaselinuxusermaporder:
- "guest_u:s0"
- "xguest_u:s0"
- "user_u:s0"
- "staff_u:s0-s0:c0.c1023"
- "unconfined_u:s0-s0:c0.c1023"
ipa_host: localhost
ipa_user: admin
ipa_pass: supersecret
'''
RETURN = r'''
@@ -213,8 +231,8 @@ def get_config_dict(ipaconfigstring=None, ipadefaultloginshell=None,
ipagroupsearchfields=None, ipahomesrootdir=None,
ipakrbauthzdata=None, ipamaxusernamelength=None,
ipapwdexpadvnotify=None, ipasearchrecordslimit=None,
ipasearchtimelimit=None, ipauserauthtype=None,
ipausersearchfields=None):
ipasearchtimelimit=None, ipaselinuxusermaporder=None,
ipauserauthtype=None, ipausersearchfields=None):
config = {}
if ipaconfigstring is not None:
config['ipaconfigstring'] = ipaconfigstring
@@ -238,6 +256,8 @@ def get_config_dict(ipaconfigstring=None, ipadefaultloginshell=None,
config['ipasearchrecordslimit'] = str(ipasearchrecordslimit)
if ipasearchtimelimit is not None:
config['ipasearchtimelimit'] = str(ipasearchtimelimit)
if ipaselinuxusermaporder is not None:
config['ipaselinuxusermaporder'] = '$'.join(ipaselinuxusermaporder)
if ipauserauthtype is not None:
config['ipauserauthtype'] = ipauserauthtype
if ipausersearchfields is not None:
@@ -263,6 +283,7 @@ def ensure(module, client):
ipapwdexpadvnotify=module.params.get('ipapwdexpadvnotify'),
ipasearchrecordslimit=module.params.get('ipasearchrecordslimit'),
ipasearchtimelimit=module.params.get('ipasearchtimelimit'),
ipaselinuxusermaporder=module.params.get('ipaselinuxusermaporder'),
ipauserauthtype=module.params.get('ipauserauthtype'),
ipausersearchfields=module.params.get('ipausersearchfields'),
)
@@ -304,6 +325,8 @@ def main():
ipapwdexpadvnotify=dict(type='int', aliases=['pwdexpadvnotify']),
ipasearchrecordslimit=dict(type='int', aliases=['searchrecordslimit']),
ipasearchtimelimit=dict(type='int', aliases=['searchtimelimit']),
ipaselinuxusermaporder=dict(type='list', elements='str',
aliases=['selinuxusermaporder']),
ipauserauthtype=dict(type='list', elements='str',
aliases=['userauthtype'],
choices=["password", "radius", "otp", "pkinit",

View File

@@ -74,11 +74,12 @@ subca:
type: dict
'''
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.ipa import IPAClient, ipa_argument_spec
from ansible.module_utils.common.text.converters import to_native
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
class SubCAIPAClient(IPAClient):
def __init__(self, module, host, port, protocol):

View File

@@ -195,7 +195,6 @@ def create_or_update_executions(kc, config, realm='master'):
:param kc: Keycloak API access.
:param config: Representation of the authentication flow including it's executions.
:param realm: Realm
:return: True if executions have been modified. False otherwise.
:return: tuple (changed, dict(before, after)
WHERE
bool changed indicates if changes have been made
@@ -235,10 +234,14 @@ def create_or_update_executions(kc, config, realm='master'):
elif new_exec["providerId"] is not None:
kc.create_execution(new_exec, flowAlias=flow_alias_parent, realm=realm)
exec_found = True
exec_index = new_exec_index
id_to_update = kc.get_executions_representation(config, realm=realm)[exec_index]["id"]
after += str(new_exec) + '\n'
elif new_exec["displayName"] is not None:
kc.create_subflow(new_exec["displayName"], flow_alias_parent, realm=realm)
exec_found = True
exec_index = new_exec_index
id_to_update = kc.get_executions_representation(config, realm=realm)[exec_index]["id"]
after += str(new_exec) + '\n'
if exec_found:
changed = True

View File

@@ -295,6 +295,20 @@ EXAMPLES = '''
clientAuthMethod: client_secret_post
clientId: my-client
clientSecret: secret
syncMode: FORCE
mappers:
- name: first_name
identityProviderMapper: oidc-user-attribute-idp-mapper
config:
claim: first_name
user.attribute: first_name
syncMode: INHERIT
- name: last_name
identityProviderMapper: oidc-user-attribute-idp-mapper
config:
claim: last_name
user.attribute: last_name
syncMode: INHERIT
- name: Create SAML identity provider, authentication with credentials
community.general.keycloak_identity_provider:
@@ -313,6 +327,14 @@ EXAMPLES = '''
singleSignOnServiceUrl: https://idp.example.com/login
wantAuthnRequestsSigned: true
wantAssertionsSigned: true
mappers:
- name: roles
identityProviderMapper: saml-user-attribute-idp-mapper
config:
user.attribute: roles
attribute.friendly.name: User Roles
attribute.name: roles
syncMode: INHERIT
'''
RETURN = '''
@@ -400,15 +422,15 @@ end_state:
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
from copy import deepcopy
def sanitize(idp):
result = idp.copy()
if 'config' in result:
result['config'] = sanitize(result['config'])
if 'clientSecret' in result:
result['clientSecret'] = '**********'
return result
idpcopy = deepcopy(idp)
if 'config' in idpcopy:
if 'clientSecret' in idpcopy['config']:
idpcopy['clientSecret'] = '**********'
return idpcopy
def get_identity_provider_with_mappers(kc, alias, realm):
@@ -493,18 +515,29 @@ def main():
changeset[camel(param)] = new_param_value
# special handling of mappers list to allow change detection
changeset['mappers'] = before_idp.get('mappers', list())
if module.params.get('mappers') is not None:
for new_mapper in module.params.get('mappers'):
old_mapper = next((x for x in changeset['mappers'] if x['name'] == new_mapper['name']), None)
new_mapper = dict((k, v) for k, v in new_mapper.items() if new_mapper[k] is not None)
if old_mapper is not None:
old_mapper.update(new_mapper)
for change in module.params['mappers']:
change = dict((k, v) for k, v in change.items() if change[k] is not None)
if change.get('id') is None and change.get('name') is None:
module.fail_json(msg='Either `name` or `id` has to be specified on each mapper.')
if before_idp == dict():
old_mapper = dict()
elif change.get('id') is not None:
old_mapper = kc.get_identity_provider_mapper(change['id'], alias, realm)
if old_mapper is None:
old_mapper = dict()
else:
found = [x for x in kc.get_identity_provider_mappers(alias, realm) if x['name'] == change['name']]
if len(found) == 1:
old_mapper = found[0]
else:
old_mapper = dict()
new_mapper = old_mapper.copy()
new_mapper.update(change)
if new_mapper != old_mapper:
if changeset.get('mappers') is None:
changeset['mappers'] = list()
changeset['mappers'].append(new_mapper)
# remove mappers if not present in module params
changeset['mappers'] = [x for x in changeset['mappers']
if [y for y in module.params.get('mappers', []) if y['name'] == x['name']] != []]
# prepare the new representation
updated_idp = before_idp.copy()
@@ -538,6 +571,8 @@ def main():
mappers = updated_idp.pop('mappers', [])
kc.create_identity_provider(updated_idp, realm)
for mapper in mappers:
if mapper.get('identityProviderAlias') is None:
mapper['identityProviderAlias'] = alias
kc.create_identity_provider_mapper(mapper, alias, realm)
after_idp = get_identity_provider_with_mappers(kc, alias, realm)
@@ -572,6 +607,8 @@ def main():
if mapper.get('id') is not None:
kc.update_identity_provider_mapper(mapper, alias, realm)
else:
if mapper.get('identityProviderAlias') is None:
mapper['identityProviderAlias'] = alias
kc.create_identity_provider_mapper(mapper, alias, realm)
for mapper in [x for x in before_idp['mappers']
if [y for y in updated_mappers if y["name"] == x['name']] == []]:

View File

@@ -0,0 +1,979 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 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: keycloak_user_federation
short_description: Allows administration of Keycloak user federations via Keycloak API
version_added: 3.7.0
description:
- This module allows you to add, remove or modify Keycloak user federations via the Keycloak REST API.
It requires access to the REST API via OpenID Connect; the user connecting and the client being
used must have the requisite access rights. In a default Keycloak installation, admin-cli
and an admin user would work, as would a separate client definition with the scope tailored
to your needs and a user having the expected roles.
- The names of module options are snake_cased versions of the camelCase ones found in the
Keycloak API and its documentation at U(https://www.keycloak.org/docs-api/15.0/rest-api/index.html).
options:
state:
description:
- State of the user federation.
- On C(present), the user federation will be created if it does not yet exist, or updated with
the parameters you provide.
- On C(absent), the user federation will be removed if it exists.
default: 'present'
type: str
choices:
- present
- absent
realm:
description:
- The Keycloak realm under which this user federation resides.
default: 'master'
type: str
id:
description:
- The unique ID for this user federation. If left empty, the user federation will be searched
by its I(name).
type: str
name:
description:
- Display name of provider when linked in admin console.
type: str
provider_id:
description:
- Provider for this user federation.
aliases:
- providerId
type: str
choices:
- ldap
- kerberos
provider_type:
description:
- Component type for user federation (only supported value is C(org.keycloak.storage.UserStorageProvider)).
aliases:
- providerType
default: org.keycloak.storage.UserStorageProvider
type: str
parent_id:
description:
- Unique ID for the parent of this user federation. Realm ID will be automatically used if left blank.
aliases:
- parentId
type: str
config:
description:
- Dict specifying the configuration options for the provider; the contents differ depending on
the value of I(provider_id). Examples are given below for C(ldap) and C(kerberos). It is easiest
to obtain valid config values by dumping an already-existing user federation configuration
through check-mode in the I(existing) field.
type: dict
suboptions:
enabled:
description:
- Enable/disable this user federation.
default: true
type: bool
priority:
description:
- Priority of provider when doing a user lookup. Lowest first.
default: 0
type: int
importEnabled:
description:
- If C(true), LDAP users will be imported into Keycloak DB and synced by the configured
sync policies.
default: true
type: bool
editMode:
description:
- C(READ_ONLY) is a read-only LDAP store. C(WRITABLE) means data will be synced back to LDAP
on demand. C(UNSYNCED) means user data will be imported, but not synced back to LDAP.
type: str
choices:
- READ_ONLY
- WRITABLE
- UNSYNCED
syncRegistrations:
description:
- Should newly created users be created within LDAP store? Priority effects which
provider is chosen to sync the new user.
default: false
type: bool
vendor:
description:
- LDAP vendor (provider).
type: str
usernameLDAPAttribute:
description:
- Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server
vendors it can be C(uid). For Active directory it can be C(sAMAccountName) or C(cn).
The attribute should be filled for all LDAP user records you want to import from
LDAP to Keycloak.
type: str
rdnLDAPAttribute:
description:
- Name of LDAP attribute, which is used as RDN (top attribute) of typical user DN.
Usually it's the same as Username LDAP attribute, however it is not required. For
example for Active directory, it is common to use C(cn) as RDN attribute when
username attribute might be C(sAMAccountName).
type: str
uuidLDAPAttribute:
description:
- Name of LDAP attribute, which is used as unique object identifier (UUID) for objects
in LDAP. For many LDAP server vendors, it is C(entryUUID); however some are different.
For example for Active directory it should be C(objectGUID). If your LDAP server does
not support the notion of UUID, you can use any other attribute that is supposed to
be unique among LDAP users in tree.
type: str
userObjectClasses:
description:
- All values of LDAP objectClass attribute for users in LDAP divided by comma.
For example C(inetOrgPerson, organizationalPerson). Newly created Keycloak users
will be written to LDAP with all those object classes and existing LDAP user records
are found just if they contain all those object classes.
type: str
connectionUrl:
description:
- Connection URL to your LDAP server.
type: str
usersDn:
description:
- Full DN of LDAP tree where your users are. This DN is the parent of LDAP users.
type: str
customUserSearchFilter:
description:
- Additional LDAP Filter for filtering searched users. Leave this empty if you don't
need additional filter.
type: str
searchScope:
description:
- For one level, the search applies only for users in the DNs specified by User DNs.
For subtree, the search applies to the whole subtree. See LDAP documentation for
more details
default: '1'
type: str
choices:
- '1'
- '2'
authType:
description:
- Type of the Authentication method used during LDAP Bind operation. It is used in
most of the requests sent to the LDAP server.
default: 'none'
type: str
choices:
- none
- simple
bindDn:
description:
- DN of LDAP user which will be used by Keycloak to access LDAP server.
type: str
bindCredential:
description:
- Password of LDAP admin.
type: str
startTls:
description:
- Encrypts the connection to LDAP using STARTTLS, which will disable connection pooling.
default: false
type: bool
usePasswordModifyExtendedOp:
description:
- Use the LDAPv3 Password Modify Extended Operation (RFC-3062). The password modify
extended operation usually requires that LDAP user already has password in the LDAP
server. So when this is used with 'Sync Registrations', it can be good to add also
'Hardcoded LDAP attribute mapper' with randomly generated initial password.
default: false
type: bool
validatePasswordPolicy:
description:
- Determines if Keycloak should validate the password with the realm password policy
before updating it.
default: false
type: bool
trustEmail:
description:
- If enabled, email provided by this provider is not verified even if verification is
enabled for the realm.
default: false
type: bool
useTruststoreSpi:
description:
- Specifies whether LDAP connection will use the truststore SPI with the truststore
configured in standalone.xml/domain.xml. C(Always) means that it will always use it.
C(Never) means that it will not use it. C(Only for ldaps) means that it will use if
your connection URL use ldaps. Note even if standalone.xml/domain.xml is not
configured, the default Java cacerts or certificate specified by
C(javax.net.ssl.trustStore) property will be used.
default: ldapsOnly
type: str
choices:
- always
- ldapsOnly
- never
connectionTimeout:
description:
- LDAP Connection Timeout in milliseconds.
type: int
readTimeout:
description:
- LDAP Read Timeout in milliseconds. This timeout applies for LDAP read operations.
type: int
pagination:
description:
- Does the LDAP server support pagination.
default: true
type: bool
connectionPooling:
description:
- Determines if Keycloak should use connection pooling for accessing LDAP server.
default: true
type: bool
connectionPoolingAuthentication:
description:
- A list of space-separated authentication types of connections that may be pooled.
type: str
choices:
- none
- simple
- DIGEST-MD5
connectionPoolingDebug:
description:
- A string that indicates the level of debug output to produce. Example valid values are
C(fine) (trace connection creation and removal) and C(all) (all debugging information).
type: str
connectionPoolingInitSize:
description:
- The number of connections per connection identity to create when initially creating a
connection for the identity.
type: int
connectionPoolingMaxSize:
description:
- The maximum number of connections per connection identity that can be maintained
concurrently.
type: int
connectionPoolingPrefSize:
description:
- The preferred number of connections per connection identity that should be maintained
concurrently.
type: int
connectionPoolingProtocol:
description:
- A list of space-separated protocol types of connections that may be pooled.
Valid types are C(plain) and C(ssl).
type: str
connectionPoolingTimeout:
description:
- The number of milliseconds that an idle connection may remain in the pool without
being closed and removed from the pool.
type: int
allowKerberosAuthentication:
description:
- Enable/disable HTTP authentication of users with SPNEGO/Kerberos tokens. The data
about authenticated users will be provisioned from this LDAP server.
default: false
type: bool
kerberosRealm:
description:
- Name of kerberos realm.
type: str
serverPrincipal:
description:
- Full name of server principal for HTTP service including server and domain name. For
example C(HTTP/host.foo.org@FOO.ORG). Use C(*) to accept any service principal in the
KeyTab file.
type: str
keyTab:
description:
- Location of Kerberos KeyTab file containing the credentials of server principal. For
example C(/etc/krb5.keytab).
type: str
debug:
description:
- Enable/disable debug logging to standard output for Krb5LoginModule.
type: bool
useKerberosForPasswordAuthentication:
description:
- Use Kerberos login module for authenticate username/password against Kerberos server
instead of authenticating against LDAP server with Directory Service API.
default: false
type: bool
allowPasswordAuthentication:
description:
- Enable/disable possibility of username/password authentication against Kerberos database.
type: bool
batchSizeForSync:
description:
- Count of LDAP users to be imported from LDAP to Keycloak within a single transaction.
default: 1000
type: int
fullSyncPeriod:
description:
- Period for full synchronization in seconds.
default: -1
type: int
changedSyncPeriod:
description:
- Period for synchronization of changed or newly created LDAP users in seconds.
default: -1
type: int
updateProfileFirstLogin:
description:
- Update profile on first login.
type: bool
cachePolicy:
description:
- Cache Policy for this storage provider.
type: str
default: 'DEFAULT'
choices:
- DEFAULT
- EVICT_DAILY
- EVICT_WEEKLY
- MAX_LIFESPAN
- NO_CACHE
evictionDay:
description:
- Day of the week the entry will become invalid on.
type: str
evictionHour:
description:
- Hour of day the entry will become invalid on.
type: str
evictionMinute:
description:
- Minute of day the entry will become invalid on.
type: str
maxLifespan:
description:
- Max lifespan of cache entry in milliseconds.
type: int
mappers:
description:
- A list of dicts defining mappers associated with this Identity Provider.
type: list
elements: dict
suboptions:
id:
description:
- Unique ID of this mapper.
type: str
name:
description:
- Name of the mapper. If no ID is given, the mapper will be searched by name.
type: str
parentId:
description:
- Unique ID for the parent of this mapper. ID of the user federation will automatically
be used if left blank.
type: str
providerId:
description:
- The mapper type for this mapper (for instance C(user-attribute-ldap-mapper)).
type: str
providerType:
description:
- Component type for this mapper (only supported value is C(org.keycloak.storage.ldap.mappers.LDAPStorageMapper)).
type: str
config:
description:
- Dict specifying the configuration options for the mapper; the contents differ
depending on the value of I(identityProviderMapper).
type: dict
extends_documentation_fragment:
- community.general.keycloak
author:
- Laurent Paumier (@laurpaum)
'''
EXAMPLES = '''
- name: Create LDAP user federation
community.general.keycloak_user_federation:
auth_keycloak_url: https://keycloak.example.com/auth
auth_realm: master
auth_username: admin
auth_password: password
realm: my-realm
name: my-ldap
state: present
provider_id: ldap
provider_type: org.keycloak.storage.UserStorageProvider
config:
priority: 0
enabled: true
cachePolicy: DEFAULT
batchSizeForSync: 1000
editMode: READ_ONLY
importEnabled: true
syncRegistrations: false
vendor: other
usernameLDAPAttribute: uid
rdnLDAPAttribute: uid
uuidLDAPAttribute: entryUUID
userObjectClasses: inetOrgPerson, organizationalPerson
connectionUrl: ldaps://ldap.example.com:636
usersDn: ou=Users,dc=example,dc=com
authType: simple
bindDn: cn=directory reader
bindCredential: password
searchScope: 1
validatePasswordPolicy: false
trustEmail: false
useTruststoreSpi: ldapsOnly
connectionPooling: true
pagination: true
allowKerberosAuthentication: false
debug: false
useKerberosForPasswordAuthentication: false
mappers:
- name: "full name"
providerId: "full-name-ldap-mapper"
providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
config:
ldap.full.name.attribute: cn
read.only: true
write.only: false
- name: Create Kerberos user federation
community.general.keycloak_user_federation:
auth_keycloak_url: https://keycloak.example.com/auth
auth_realm: master
auth_username: admin
auth_password: password
realm: my-realm
name: my-kerberos
state: present
provider_id: kerberos
provider_type: org.keycloak.storage.UserStorageProvider
config:
priority: 0
enabled: true
cachePolicy: DEFAULT
kerberosRealm: EXAMPLE.COM
serverPrincipal: HTTP/host.example.com@EXAMPLE.COM
keyTab: keytab
allowPasswordAuthentication: false
updateProfileFirstLogin: false
- name: Delete user federation
community.general.keycloak_user_federation:
auth_keycloak_url: https://keycloak.example.com/auth
auth_realm: master
auth_username: admin
auth_password: password
realm: my-realm
name: my-federation
state: absent
'''
RETURN = '''
msg:
description: Message as to what action was taken.
returned: always
type: str
sample: "No changes required to user federation 164bb483-c613-482e-80fe-7f1431308799."
proposed:
description: Representation of proposed changes to user federation.
returned: always
type: dict
sample: {
"config": {
"allowKerberosAuthentication": "false",
"authType": "simple",
"batchSizeForSync": "1000",
"bindCredential": "**********",
"bindDn": "cn=directory reader",
"cachePolicy": "DEFAULT",
"connectionPooling": "true",
"connectionUrl": "ldaps://ldap.example.com:636",
"debug": "false",
"editMode": "READ_ONLY",
"enabled": "true",
"importEnabled": "true",
"pagination": "true",
"priority": "0",
"rdnLDAPAttribute": "uid",
"searchScope": "1",
"syncRegistrations": "false",
"trustEmail": "false",
"useKerberosForPasswordAuthentication": "false",
"useTruststoreSpi": "ldapsOnly",
"userObjectClasses": "inetOrgPerson, organizationalPerson",
"usernameLDAPAttribute": "uid",
"usersDn": "ou=Users,dc=example,dc=com",
"uuidLDAPAttribute": "entryUUID",
"validatePasswordPolicy": "false",
"vendor": "other"
},
"name": "ldap",
"providerId": "ldap",
"providerType": "org.keycloak.storage.UserStorageProvider"
}
existing:
description: Representation of existing user federation.
returned: always
type: dict
sample: {
"config": {
"allowKerberosAuthentication": "false",
"authType": "simple",
"batchSizeForSync": "1000",
"bindCredential": "**********",
"bindDn": "cn=directory reader",
"cachePolicy": "DEFAULT",
"changedSyncPeriod": "-1",
"connectionPooling": "true",
"connectionUrl": "ldaps://ldap.example.com:636",
"debug": "false",
"editMode": "READ_ONLY",
"enabled": "true",
"fullSyncPeriod": "-1",
"importEnabled": "true",
"pagination": "true",
"priority": "0",
"rdnLDAPAttribute": "uid",
"searchScope": "1",
"syncRegistrations": "false",
"trustEmail": "false",
"useKerberosForPasswordAuthentication": "false",
"useTruststoreSpi": "ldapsOnly",
"userObjectClasses": "inetOrgPerson, organizationalPerson",
"usernameLDAPAttribute": "uid",
"usersDn": "ou=Users,dc=example,dc=com",
"uuidLDAPAttribute": "entryUUID",
"validatePasswordPolicy": "false",
"vendor": "other"
},
"id": "01122837-9047-4ae4-8ca0-6e2e891a765f",
"mappers": [
{
"config": {
"always.read.value.from.ldap": "false",
"is.mandatory.in.ldap": "false",
"ldap.attribute": "mail",
"read.only": "true",
"user.model.attribute": "email"
},
"id": "17d60ce2-2d44-4c2c-8b1f-1fba601b9a9f",
"name": "email",
"parentId": "01122837-9047-4ae4-8ca0-6e2e891a765f",
"providerId": "user-attribute-ldap-mapper",
"providerType": "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
}
],
"name": "myfed",
"parentId": "myrealm",
"providerId": "ldap",
"providerType": "org.keycloak.storage.UserStorageProvider"
}
end_state:
description: Representation of user federation after module execution.
returned: always
type: dict
sample: {
"config": {
"allowPasswordAuthentication": "false",
"cachePolicy": "DEFAULT",
"enabled": "true",
"kerberosRealm": "EXAMPLE.COM",
"keyTab": "/etc/krb5.keytab",
"priority": "0",
"serverPrincipal": "HTTP/host.example.com@EXAMPLE.COM",
"updateProfileFirstLogin": "false"
},
"id": "cf52ae4f-4471-4435-a0cf-bb620cadc122",
"mappers": [],
"name": "kerberos",
"parentId": "myrealm",
"providerId": "kerberos",
"providerType": "org.keycloak.storage.UserStorageProvider"
}
'''
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
keycloak_argument_spec, get_token, KeycloakError
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six.moves.urllib.parse import urlencode
from copy import deepcopy
def sanitize(comp):
compcopy = deepcopy(comp)
if 'config' in compcopy:
compcopy['config'] = dict((k, v[0]) for k, v in compcopy['config'].items())
if 'bindCredential' in compcopy['config']:
compcopy['config']['bindCredential'] = '**********'
if 'mappers' in compcopy:
for mapper in compcopy['mappers']:
if 'config' in mapper:
mapper['config'] = dict((k, v[0]) for k, v in mapper['config'].items())
return compcopy
def main():
"""
Module execution
:return:
"""
argument_spec = keycloak_argument_spec()
config_spec = dict(
allowKerberosAuthentication=dict(type='bool', default=False),
allowPasswordAuthentication=dict(type='bool'),
authType=dict(type='str', choices=['none', 'simple'], default='none'),
batchSizeForSync=dict(type='int', default=1000),
bindCredential=dict(type='str', no_log=True),
bindDn=dict(type='str'),
cachePolicy=dict(type='str', choices=['DEFAULT', 'EVICT_DAILY', 'EVICT_WEEKLY', 'MAX_LIFESPAN', 'NO_CACHE'], default='DEFAULT'),
changedSyncPeriod=dict(type='int', default=-1),
connectionPooling=dict(type='bool', default=True),
connectionPoolingAuthentication=dict(type='str', choices=['none', 'simple', 'DIGEST-MD5']),
connectionPoolingDebug=dict(type='str'),
connectionPoolingInitSize=dict(type='int'),
connectionPoolingMaxSize=dict(type='int'),
connectionPoolingPrefSize=dict(type='int'),
connectionPoolingProtocol=dict(type='str'),
connectionPoolingTimeout=dict(type='int'),
connectionTimeout=dict(type='int'),
connectionUrl=dict(type='str'),
customUserSearchFilter=dict(type='str'),
debug=dict(type='bool'),
editMode=dict(type='str', choices=['READ_ONLY', 'WRITABLE', 'UNSYNCED']),
enabled=dict(type='bool', default=True),
evictionDay=dict(type='str'),
evictionHour=dict(type='str'),
evictionMinute=dict(type='str'),
fullSyncPeriod=dict(type='int', default=-1),
importEnabled=dict(type='bool', default=True),
kerberosRealm=dict(type='str'),
keyTab=dict(type='str', no_log=False),
maxLifespan=dict(type='int'),
pagination=dict(type='bool', default=True),
priority=dict(type='int', default=0),
rdnLDAPAttribute=dict(type='str'),
readTimeout=dict(type='int'),
searchScope=dict(type='str', choices=['1', '2'], default='1'),
serverPrincipal=dict(type='str'),
startTls=dict(type='bool', default=False),
syncRegistrations=dict(type='bool', default=False),
trustEmail=dict(type='bool', default=False),
updateProfileFirstLogin=dict(type='bool'),
useKerberosForPasswordAuthentication=dict(type='bool', default=False),
usePasswordModifyExtendedOp=dict(type='bool', default=False, no_log=False),
useTruststoreSpi=dict(type='str', choices=['always', 'ldapsOnly', 'never'], default='ldapsOnly'),
userObjectClasses=dict(type='str'),
usernameLDAPAttribute=dict(type='str'),
usersDn=dict(type='str'),
uuidLDAPAttribute=dict(type='str'),
validatePasswordPolicy=dict(type='bool', default=False),
vendor=dict(type='str'),
)
mapper_spec = dict(
id=dict(type='str'),
name=dict(type='str'),
parentId=dict(type='str'),
providerId=dict(type='str'),
providerType=dict(type='str'),
config=dict(type='dict'),
)
meta_args = dict(
config=dict(type='dict', options=config_spec),
state=dict(type='str', default='present', choices=['present', 'absent']),
realm=dict(type='str', default='master'),
id=dict(type='str'),
name=dict(type='str'),
provider_id=dict(type='str', aliases=['providerId'], choices=['ldap', 'kerberos']),
provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'),
parent_id=dict(type='str', aliases=['parentId']),
mappers=dict(type='list', elements='dict', options=mapper_spec),
)
argument_spec.update(meta_args)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=([['id', 'name'],
['token', 'auth_realm', 'auth_username', 'auth_password']]),
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={})
# Obtain access token, initialize API
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
realm = module.params.get('realm')
state = module.params.get('state')
config = module.params.get('config')
mappers = module.params.get('mappers')
cid = module.params.get('id')
name = module.params.get('name')
# Keycloak API expects config parameters to be arrays containing a single string element
if config is not None:
module.params['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v])
for k, v in config.items() if config[k] is not None)
if mappers is not None:
for mapper in mappers:
if mapper.get('config') is not None:
mapper['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v])
for k, v in mapper['config'].items() if mapper['config'][k] is not None)
# convert module parameters to client representation parameters (if they belong in there)
comp_params = [x for x in module.params
if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and
module.params.get(x) is not None]
# does the user federation already exist?
if cid is None:
found = kc.get_components(urlencode(dict(type='org.keycloak.storage.UserStorageProvider', parent=realm, name=name)), realm)
if len(found) > 1:
module.fail_json(msg='No ID given and found multiple user federations with name `{name}`. Cannot continue.'.format(name=name))
before_comp = next(iter(found), None)
if before_comp is not None:
cid = before_comp['id']
else:
before_comp = kc.get_component(cid, realm)
if before_comp is None:
before_comp = dict()
# if user federation exists, get associated mappers
if cid is not None:
before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name'))
# build a changeset
changeset = dict()
for param in comp_params:
new_param_value = module.params.get(param)
old_value = before_comp[camel(param)] if camel(param) in before_comp else None
if param == 'mappers':
new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value]
if new_param_value != old_value:
changeset[camel(param)] = new_param_value
# special handling of mappers list to allow change detection
if module.params.get('mappers') is not None:
if module.params['provider_id'] == 'kerberos':
module.fail_json(msg='Cannot configure mappers for Kerberos federations.')
for change in module.params['mappers']:
change = dict((k, v) for k, v in change.items() if change[k] is not None)
if change.get('id') is None and change.get('name') is None:
module.fail_json(msg='Either `name` or `id` has to be specified on each mapper.')
if cid is None:
old_mapper = dict()
elif change.get('id') is not None:
old_mapper = kc.get_component(change['id'], realm)
if old_mapper is None:
old_mapper = dict()
else:
found = kc.get_components(urlencode(dict(parent=cid, name=change['name'])), realm)
if len(found) > 1:
module.fail_json(msg='Found multiple mappers with name `{name}`. Cannot continue.'.format(name=change['name']))
if len(found) == 1:
old_mapper = found[0]
else:
old_mapper = dict()
new_mapper = old_mapper.copy()
new_mapper.update(change)
if new_mapper != old_mapper:
if changeset.get('mappers') is None:
changeset['mappers'] = list()
changeset['mappers'].append(new_mapper)
# prepare the new representation
updated_comp = before_comp.copy()
updated_comp.update(changeset)
result['proposed'] = sanitize(changeset)
result['existing'] = sanitize(before_comp)
# if before_comp is none, the user federation doesn't exist.
if before_comp == dict():
if state == 'absent':
# nothing to do.
if module._diff:
result['diff'] = dict(before='', after='')
result['changed'] = False
result['end_state'] = dict()
result['msg'] = 'User federation does not exist; doing nothing.'
module.exit_json(**result)
# for 'present', create a new user federation.
result['changed'] = True
if module._diff:
result['diff'] = dict(before='', after=sanitize(updated_comp))
if module.check_mode:
module.exit_json(**result)
# do it for real!
updated_comp = updated_comp.copy()
updated_mappers = updated_comp.pop('mappers', [])
after_comp = kc.create_component(updated_comp, realm)
for mapper in updated_mappers:
if mapper.get('id') is not None:
kc.update_component(mapper, realm)
else:
if mapper.get('parentId') is None:
mapper['parentId'] = after_comp['id']
mapper = kc.create_component(mapper, realm)
after_comp['mappers'] = updated_mappers
result['end_state'] = sanitize(after_comp)
result['msg'] = "User federation {id} has been created".format(id=after_comp['id'])
module.exit_json(**result)
else:
if state == 'present':
# no changes
if updated_comp == before_comp:
result['changed'] = False
result['end_state'] = sanitize(updated_comp)
result['msg'] = "No changes required to user federation {id}.".format(id=cid)
module.exit_json(**result)
# update the existing role
result['changed'] = True
if module._diff:
result['diff'] = dict(before=sanitize(before_comp), after=sanitize(updated_comp))
if module.check_mode:
module.exit_json(**result)
# do the update
updated_comp = updated_comp.copy()
updated_mappers = updated_comp.pop('mappers', [])
kc.update_component(updated_comp, realm)
after_comp = kc.get_component(cid, realm)
for mapper in updated_mappers:
if mapper.get('id') is not None:
kc.update_component(mapper, realm)
else:
if mapper.get('parentId') is None:
mapper['parentId'] = updated_comp['id']
mapper = kc.create_component(mapper, realm)
after_comp['mappers'] = updated_mappers
result['end_state'] = sanitize(after_comp)
result['msg'] = "User federation {id} has been updated".format(id=cid)
module.exit_json(**result)
elif state == 'absent':
result['changed'] = True
if module._diff:
result['diff'] = dict(before=sanitize(before_comp), after='')
if module.check_mode:
module.exit_json(**result)
# delete for real
kc.delete_component(cid, realm)
result['end_state'] = dict()
result['msg'] = "User federation {id} has been deleted".format(id=cid)
module.exit_json(**result)
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1 @@
identity/keycloak/keycloak_user_federation.py

View File

@@ -143,7 +143,8 @@ annotation:
import json
import time
import traceback
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
REQUESTS_IMP_ERR = None
try:

View File

@@ -92,13 +92,6 @@ EXAMPLES = '''
account_api_token: dummyapitoken
delegate_to: localhost
- name: Fetch my.com domain records
community.general.dnsimple:
domain: my.com
state: present
delegate_to: localhost
register: records
- name: Delete a domain
community.general.dnsimple:
domain: my.com
@@ -153,9 +146,10 @@ EXAMPLES = '''
RETURN = r"""# """
import traceback
from distutils.version import LooseVersion
import re
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
class DNSimpleV1():
"""class which uses dnsimple-python < 2"""

View File

@@ -106,11 +106,10 @@ def main():
module.fail_json(msg=missing_required_lib('python-ldap'),
exception=LDAP_IMP_ERR)
if not module.check_mode:
try:
LdapSearch(module).main()
except Exception as exception:
module.fail_json(msg="Attribute action failed.", details=to_native(exception))
try:
LdapSearch(module).main()
except Exception as exception:
module.fail_json(msg="Attribute action failed.", details=to_native(exception))
module.exit_json(changed=False)

View File

@@ -54,8 +54,9 @@ options:
- Type C(dummy) is added in community.general 3.5.0.
- Type C(generic) is added in Ansible 2.5.
- Type C(infiniband) is added in community.general 2.0.0.
- Type C(gsm) is added in community.general 3.7.0.
type: str
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, sit, team, team-slave, vlan, vxlan, wifi ]
choices: [ bond, bond-slave, bridge, bridge-slave, dummy, ethernet, generic, gre, infiniband, ipip, sit, team, team-slave, vlan, vxlan, wifi, gsm ]
mode:
description:
- This is the type of device or network connection that you wish to create for a bond or bridge.
@@ -69,7 +70,7 @@ options:
ip4:
description:
- The IPv4 address to this interface.
- Use the format C(192.0.2.24/24).
- Use the format C(192.0.2.24/24) or C(192.0.2.24).
- If defined and I(method4) is not specified, automatically set C(ipv4.method) to C(manual).
type: str
gw4:
@@ -99,7 +100,8 @@ options:
routing_rules4:
description:
- Is the same as in an C(ip route add) command, except always requires specifying a priority.
type: str
type: list
elements: str
version_added: 3.3.0
never_default4:
description:
@@ -141,7 +143,7 @@ options:
ip6:
description:
- The IPv6 address to this interface.
- Use the format C(abbe::cafe).
- Use the format C(abbe::cafe/128 or abbe::cafe).
- If defined and I(method6) is not specified, automatically set C(ipv6.method) to C(manual).
type: str
gw6:
@@ -183,7 +185,7 @@ options:
mtu:
description:
- The connection MTU, e.g. 9000. This can't be applied when creating the interface and is done once the interface has been created.
- Can be used when modifying Team, VLAN, Ethernet (Future plans to implement wifi, pppoe, infiniband)
- Can be used when modifying Team, VLAN, Ethernet (Future plans to implement wifi, gsm, pppoe, infiniband)
- This parameter defaults to C(1500) when unset.
type: int
dhcp_client_id:
@@ -643,6 +645,101 @@ options:
type: bool
default: false
version_added: 3.6.0
gsm:
description:
- The configuration of the GSM connection.
- Note the list of suboption attributes may vary depending on which version of NetworkManager/nmcli is installed on the host.
- 'An up-to-date list of supported attributes can be found here:
U(https://networkmanager.dev/docs/api/latest/settings-gsm.html).'
- 'For instance to use apn, pin, username and password:
C({apn: provider.apn, pin: 1234, username: apn.username, password: apn.password}).'
type: dict
version_added: 3.7.0
suboptions:
apn:
description:
- The GPRS Access Point Name specifying the APN used when establishing a data session with the GSM-based network.
- The APN often determines how the user will be billed for their network usage and whether the user has access to the Internet or
just a provider-specific walled-garden, so it is important to use the correct APN for the user's mobile broadband plan.
- The APN may only be composed of the characters a-z, 0-9, ., and - per GSM 03.60 Section 14.9.
type: str
auto-config:
description: When C(true), the settings such as I(gsm.apn), I(gsm.username), or I(gsm.password) will default to values that match the network
the modem will register to in the Mobile Broadband Provider database.
type: bool
default: false
device-id:
description:
- The device unique identifier (as given by the C(WWAN) management service) which this connection applies to.
- If given, the connection will only apply to the specified device.
type: str
home-only:
description:
- When C(true), only connections to the home network will be allowed.
- Connections to roaming networks will not be made.
type: bool
default: false
mtu:
description: If non-zero, only transmit packets of the specified size or smaller, breaking larger packets up into multiple Ethernet frames.
type: int
default: 0
network-id:
description:
- The Network ID (GSM LAI format, ie MCC-MNC) to force specific network registration.
- If the Network ID is specified, NetworkManager will attempt to force the device to register only on the specified network.
- This can be used to ensure that the device does not roam when direct roaming control of the device is not otherwise possible.
type: str
number:
description: Legacy setting that used to help establishing PPP data sessions for GSM-based modems.
type: str
password:
description:
- The password used to authenticate with the network, if required.
- Many providers do not require a password, or accept any password.
- But if a password is required, it is specified here.
type: str
password-flags:
description:
- NMSettingSecretFlags indicating how to handle the I(password) property.
- 'Following choices are allowed:
C(0) B(NONE): The system is responsible for providing and storing this secret (default),
C(1) B(AGENT_OWNED): A user secret agent is responsible for providing and storing this secret; when it is required agents will be
asked to retrieve it
C(2) B(NOT_SAVED): This secret should not be saved, but should be requested from the user each time it is needed
C(4) B(NOT_REQUIRED): In situations where it cannot be automatically determined that the secret is required
(some VPNs and PPP providers do not require all secrets) this flag indicates that the specific secret is not required.'
type: int
choices: [ 0, 1, 2 , 4 ]
default: 0
pin:
description:
- If the SIM is locked with a PIN it must be unlocked before any other operations are requested.
- Specify the PIN here to allow operation of the device.
type: str
pin-flags:
description:
- NMSettingSecretFlags indicating how to handle the I(gsm.pin) property.
- See I(gsm.password-flags) for NMSettingSecretFlags choices.
type: int
choices: [ 0, 1, 2 , 4 ]
default: 0
sim-id:
description:
- The SIM card unique identifier (as given by the C(WWAN) management service) which this connection applies to.
- 'If given, the connection will apply to any device also allowed by I(gsm.device-id) which contains a SIM card matching
the given identifier.'
type: str
sim-operator-id:
description:
- A MCC/MNC string like C(310260) or C(21601I) identifying the specific mobile network operator which this connection applies to.
- 'If given, the connection will apply to any device also allowed by I(gsm.device-id) and I(gsm.sim-id) which contains a SIM card
provisioned by the given operator.'
type: str
username:
description:
- The username used to authenticate with the network, if required.
- Many providers do not require a username, or accept any username.
- But if a username is required, it is specified here.
'''
EXAMPLES = r'''
@@ -979,6 +1076,19 @@ EXAMPLES = r'''
autoconnect: true
state: present
- name: Create a gsm connection
community.general.nmcli:
type: gsm
conn_name: my-gsm-provider
ifname: cdc-wdm0
gsm:
apn: my.provider.apn
username: my-provider-username
password: my-provider-password
pin: my-sim-pin
autoconnect: true
state: present
'''
RETURN = r"""#
@@ -1086,6 +1196,7 @@ class Nmcli(object):
self.ssid = module.params['ssid']
self.wifi = module.params['wifi']
self.wifi_sec = module.params['wifi_sec']
self.gsm = module.params['gsm']
if self.method4:
self.ipv4_method = self.method4
@@ -1130,7 +1241,7 @@ class Nmcli(object):
# IP address options.
if self.ip_conn_type and not self.master:
options.update({
'ipv4.addresses': self.ip4,
'ipv4.addresses': self.enforce_ipv4_cidr_notation(self.ip4),
'ipv4.dhcp-client-id': self.dhcp_client_id,
'ipv4.dns': self.dns4,
'ipv4.dns-search': self.dns4_search,
@@ -1143,7 +1254,7 @@ class Nmcli(object):
'ipv4.never-default': self.never_default4,
'ipv4.method': self.ipv4_method,
'ipv4.may-fail': self.may_fail4,
'ipv6.addresses': self.ip6,
'ipv6.addresses': self.enforce_ipv6_cidr_notation(self.ip6),
'ipv6.dns': self.dns6,
'ipv6.dns-search': self.dns6_search,
'ipv6.ignore-auto-dns': self.dns6_ignore_auto,
@@ -1221,6 +1332,9 @@ class Nmcli(object):
options.update({
'vlan.id': self.vlanid,
'vlan.parent': self.vlandev,
'vlan.flags': self.flags,
'vlan.ingress': self.ingress,
'vlan.egress': self.egress,
})
elif self.type == 'vxlan':
options.update({
@@ -1243,6 +1357,12 @@ class Nmcli(object):
options.update({
'802-11-wireless-security.%s' % name: value
})
elif self.type == 'gsm':
if self.gsm:
for name, value in self.gsm.items():
options.update({
'gsm.%s' % name: value,
})
# Convert settings values based on the situation.
for setting, value in options.items():
setting_type = self.settings_type(setting)
@@ -1280,7 +1400,8 @@ class Nmcli(object):
'sit',
'team',
'vlan',
'wifi'
'wifi',
'gsm',
)
@property
@@ -1326,6 +1447,22 @@ class Nmcli(object):
'sit',
)
@staticmethod
def enforce_ipv4_cidr_notation(ip4_address):
if ip4_address is None or '/' in ip4_address:
return ip4_address
return ip4_address + '/32'
@staticmethod
def enforce_ipv6_cidr_notation(ip6_address):
if ip6_address is None:
return None
elif '/' in ip6_address:
return ip6_address
else:
return ip6_address + '/128'
@staticmethod
def bool_to_string(boolean):
if boolean:
@@ -1353,7 +1490,7 @@ class Nmcli(object):
elif setting in ('ipv4.dns',
'ipv4.dns-search',
'ipv4.routes',
'ipv4.route-metric'
'ipv4.routing-rules',
'ipv6.dns',
'ipv6.dns-search',
'802-11-wireless-security.group',
@@ -1573,6 +1710,12 @@ class Nmcli(object):
value = value.upper()
# ensure current_value is also converted to uppercase in case nmcli changes behaviour
current_value = current_value.upper()
if key == 'gsm.apn':
# Depending on version nmcli adds double-qoutes to gsm.apn
# Need to strip them in order to compare both
current_value = current_value.strip('"')
if key == self.mtu_setting and self.mtu is None:
self.mtu = 0
else:
# parameter does not exist
current_value = None
@@ -1581,6 +1724,8 @@ class Nmcli(object):
# compare values between two lists
if sorted(current_value) != sorted(value):
changed = True
elif all([key == self.mtu_setting, self.type == 'dummy', current_value is None, value == 'auto', self.mtu is None]):
value = None
else:
if current_value != to_text(value):
changed = True
@@ -1630,13 +1775,14 @@ def main():
'vlan',
'vxlan',
'wifi',
'gsm',
]),
ip4=dict(type='str'),
gw4=dict(type='str'),
gw4_ignore_auto=dict(type='bool', default=False),
routes4=dict(type='list', elements='str'),
route_metric4=dict(type='int'),
routing_rules4=dict(type='str'),
routing_rules4=dict(type='list', elements='str'),
never_default4=dict(type='bool', default=False),
dns4=dict(type='list', elements='str'),
dns4_search=dict(type='list', elements='str'),
@@ -1700,6 +1846,7 @@ def main():
ssid=dict(type='str'),
wifi=dict(type='dict'),
wifi_sec=dict(type='dict', no_log=True),
gsm=dict(type='dict'),
),
mutually_exclusive=[['never_default4', 'gw4']],
required_if=[("type", "wifi", [("ssid")])],

View File

@@ -127,7 +127,7 @@ ansible_sysname:
type: str
sample: ubuntu-user
ansible_syslocation:
description: The physical location of this node (e.g., `telephone closet, 3rd floor').
description: The physical location of this node (e.g., C(telephone closet, 3rd floor)).
returned: success
type: str
sample: Sitting on the Dock of the Bay

View File

@@ -125,6 +125,11 @@ options:
- Sets the timeout in seconds for connection attempts.
type: int
default: 20
ehlohost:
description:
- Allows for manual specification of host for EHLO.
type: str
version_added: 3.8.0
'''
EXAMPLES = r'''
@@ -189,6 +194,16 @@ EXAMPLES = r'''
subject: Ansible-report
body: System {{ ansible_hostname }} has been successfully provisioned.
secure: starttls
- name: Sending an e-mail using StartTLS, remote server, custom EHLO
community.general.mail:
host: some.smtp.host.tld
port: 25
ehlohost: my-resolvable-hostname.tld
to: John Smith <john.smith@example.com>
subject: Ansible-report
body: System {{ ansible_hostname }} has been successfully provisioned.
secure: starttls
'''
import os
@@ -215,6 +230,7 @@ def main():
password=dict(type='str', no_log=True),
host=dict(type='str', default='localhost'),
port=dict(type='int', default=25),
ehlohost=dict(type='str', default=None),
sender=dict(type='str', default='root', aliases=['from']),
to=dict(type='list', elements='str', default=['root'], aliases=['recipients']),
cc=dict(type='list', elements='str', default=[]),
@@ -235,6 +251,7 @@ def main():
password = module.params.get('password')
host = module.params.get('host')
port = module.params.get('port')
local_hostname = module.params.get('ehlohost')
sender = module.params.get('sender')
recipients = module.params.get('to')
copies = module.params.get('cc')
@@ -259,9 +276,9 @@ def main():
if secure != 'never':
try:
if PY3:
smtp = smtplib.SMTP_SSL(host=host, port=port, timeout=timeout)
smtp = smtplib.SMTP_SSL(host=host, port=port, local_hostname=local_hostname, timeout=timeout)
else:
smtp = smtplib.SMTP_SSL(timeout=timeout)
smtp = smtplib.SMTP_SSL(local_hostname=local_hostname, timeout=timeout)
code, smtpmessage = smtp.connect(host, port)
secure_state = True
except ssl.SSLError as e:
@@ -273,9 +290,9 @@ def main():
if not secure_state:
if PY3:
smtp = smtplib.SMTP(host=host, port=port, timeout=timeout)
smtp = smtplib.SMTP(host=host, port=port, local_hostname=local_hostname, timeout=timeout)
else:
smtp = smtplib.SMTP(timeout=timeout)
smtp = smtplib.SMTP(local_hostname=local_hostname, timeout=timeout)
code, smtpmessage = smtp.connect(host, port)
except smtplib.SMTPException as e:

View File

@@ -124,7 +124,8 @@ import os
import ssl
import traceback
import platform
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
HAS_PAHOMQTT = True
PAHOMQTT_IMP_ERR = None
@@ -207,7 +208,7 @@ def main():
if tls_version:
tls_version = tls_map.get(tls_version, ssl.PROTOCOL_SSLv23)
else:
if LooseVersion(platform.python_version()) <= "3.5.2":
if LooseVersion(platform.python_version()) <= LooseVersion("3.5.2"):
# Specifying `None` on later versions of python seems sufficient to
# instruct python to autonegotiate the SSL/TLS connection. On versions
# 3.5.2 and lower though we need to specify the version.

Some files were not shown because too many files have changed in this diff Show More