mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-30 02:16:50 +00:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b3c797bf6 | ||
|
|
a81e94ddc7 | ||
|
|
e56dafde94 | ||
|
|
767a296b60 | ||
|
|
963bbaccb7 | ||
|
|
9358640ed9 | ||
|
|
2846242e95 | ||
|
|
ce934aa49b | ||
|
|
083bd49976 | ||
|
|
2cc72c2213 | ||
|
|
1f4a98c8cc | ||
|
|
53b2d69bd7 | ||
|
|
981c7849ce | ||
|
|
258471b267 | ||
|
|
3020b305bb | ||
|
|
66cbd926f2 | ||
|
|
37fb2137b3 | ||
|
|
f083a0f4e7 | ||
|
|
9dc82793c4 | ||
|
|
aab93949e1 | ||
|
|
c8d6181f64 | ||
|
|
c286758248 | ||
|
|
6e685e740e | ||
|
|
695599e7d5 | ||
|
|
29e7fae303 | ||
|
|
303000c1a1 | ||
|
|
371ffaeabe | ||
|
|
42854887eb | ||
|
|
5386f7d8cd | ||
|
|
e86fcf76fc | ||
|
|
4d2895676f | ||
|
|
97b3ad6843 | ||
|
|
d7ecd40118 | ||
|
|
fbf3b85d6b | ||
|
|
1bb1e882df | ||
|
|
a7dbefcaf1 | ||
|
|
9d5490e510 | ||
|
|
2245742255 | ||
|
|
6058a5e5b1 | ||
|
|
224567e604 | ||
|
|
1eb872ccea | ||
|
|
ba18d6232a | ||
|
|
6115f18837 | ||
|
|
3f4e3dd2b6 | ||
|
|
a573661458 | ||
|
|
0977152b39 | ||
|
|
91fe881226 | ||
|
|
b78c0cf64f | ||
|
|
c79ccbbf7e | ||
|
|
a80761a8a4 | ||
|
|
213419fb0c | ||
|
|
39a1f280e3 | ||
|
|
a13d407247 | ||
|
|
ba3903e6e0 | ||
|
|
4b6b00d249 | ||
|
|
0a0b0cb42d | ||
|
|
d0b39271b3 | ||
|
|
f07cb76b09 | ||
|
|
09031fc9e6 | ||
|
|
4481d0a4a9 | ||
|
|
5861388f11 | ||
|
|
c581daa48a | ||
|
|
75e2de3581 | ||
|
|
6c7bee1225 | ||
|
|
eafcdfbceb | ||
|
|
82a764446b | ||
|
|
a0032f3513 | ||
|
|
8444367cd0 | ||
|
|
de5fbe457f | ||
|
|
40b35acee2 | ||
|
|
9835deb17f | ||
|
|
6fe9cf11f1 | ||
|
|
d3ebdd2874 | ||
|
|
4275bfe87b | ||
|
|
2f87b8c63f | ||
|
|
100fffb4c1 | ||
|
|
1206900488 | ||
|
|
c28ae26636 | ||
|
|
e1e626cdcb | ||
|
|
f8d35eeb14 | ||
|
|
c44298c437 | ||
|
|
1b580476a8 | ||
|
|
44d2d62d38 | ||
|
|
82b2d294b7 | ||
|
|
812fbef786 | ||
|
|
9d795c334b | ||
|
|
512d412eb4 | ||
|
|
8f0ee6966f | ||
|
|
3af9e39043 | ||
|
|
7b78512c59 | ||
|
|
9f0913bf73 | ||
|
|
aea851018b | ||
|
|
69c79f618e | ||
|
|
6a51ba5169 | ||
|
|
52e8e7e928 | ||
|
|
d71b90be07 | ||
|
|
1293b0ac91 | ||
|
|
d4330a3e54 | ||
|
|
7c3bf118ca | ||
|
|
742b438b32 | ||
|
|
c59ebc82cd | ||
|
|
36a4fbfe60 | ||
|
|
ad3e7bc21d | ||
|
|
66a7fdfeba | ||
|
|
77e25a7503 | ||
|
|
05ac79cee6 | ||
|
|
22b4fc8d8b | ||
|
|
6252f3bded | ||
|
|
15c4e08295 | ||
|
|
a77bb090b3 | ||
|
|
99958cea49 | ||
|
|
b2802dc8eb | ||
|
|
8617568146 | ||
|
|
7569d6315c | ||
|
|
ab5143b15d | ||
|
|
a71ff614f0 | ||
|
|
bfd3e15074 | ||
|
|
90b815de50 | ||
|
|
056e16baa5 | ||
|
|
890cf636a0 |
@@ -48,7 +48,7 @@ variables:
|
||||
resources:
|
||||
containers:
|
||||
- container: default
|
||||
image: quay.io/ansible/azure-pipelines-test-container:1.9.0
|
||||
image: quay.io/ansible/azure-pipelines-test-container:3.0.0
|
||||
|
||||
pool: Standard
|
||||
|
||||
@@ -177,12 +177,12 @@ stages:
|
||||
test: macos/12.0
|
||||
- name: RHEL 7.9
|
||||
test: rhel/7.9
|
||||
- name: RHEL 8.5
|
||||
test: rhel/8.5
|
||||
- name: RHEL 9.0
|
||||
test: rhel/9.0
|
||||
- name: FreeBSD 12.3
|
||||
test: freebsd/12.3
|
||||
- name: FreeBSD 13.0
|
||||
test: freebsd/13.0
|
||||
- name: FreeBSD 13.1
|
||||
test: freebsd/13.1
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
@@ -251,16 +251,16 @@ stages:
|
||||
targets:
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
- name: Fedora 34
|
||||
test: fedora34
|
||||
- name: Fedora 35
|
||||
test: fedora35
|
||||
- name: Fedora 36
|
||||
test: fedora36
|
||||
- name: openSUSE 15
|
||||
test: opensuse15
|
||||
- name: Ubuntu 18.04
|
||||
test: ubuntu1804
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
@@ -297,8 +297,8 @@ stages:
|
||||
test: centos6
|
||||
- name: Fedora 34
|
||||
test: fedora34
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
- name: Ubuntu 18.04
|
||||
test: ubuntu1804
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
|
||||
39
.github/BOTMETA.yml
vendored
39
.github/BOTMETA.yml
vendored
@@ -197,6 +197,8 @@ files:
|
||||
$inventories/virtualbox.py: {}
|
||||
$lookups/:
|
||||
labels: lookups
|
||||
$lookups/bitwarden.py:
|
||||
maintainers: lungj
|
||||
$lookups/cartesian.py: {}
|
||||
$lookups/chef_databag.py: {}
|
||||
$lookups/collection_version.py:
|
||||
@@ -214,7 +216,7 @@ files:
|
||||
$lookups/dnstxt.py:
|
||||
maintainers: jpmens
|
||||
$lookups/dsv.py:
|
||||
maintainers: amigus endlesstrax
|
||||
maintainers: amigus endlesstrax delineaKrehl tylerezimmerman
|
||||
$lookups/etcd3.py:
|
||||
maintainers: eric-belhomme
|
||||
$lookups/etcd.py:
|
||||
@@ -251,9 +253,12 @@ files:
|
||||
maintainers: RevBits
|
||||
$lookups/shelvefile.py: {}
|
||||
$lookups/tss.py:
|
||||
maintainers: amigus endlesstrax
|
||||
maintainers: amigus endlesstrax delineaKrehl tylerezimmerman
|
||||
$module_utils/:
|
||||
labels: module_utils
|
||||
$module_utils/gconftool2.py:
|
||||
maintainers: russoz
|
||||
labels: gconftool2
|
||||
$module_utils/gitlab.py:
|
||||
notify: jlozadad
|
||||
maintainers: $team_gitlab
|
||||
@@ -301,9 +306,15 @@ files:
|
||||
$module_utils/utm_utils.py:
|
||||
maintainers: $team_e_spirit
|
||||
labels: utm_utils
|
||||
$module_utils/wdc_redfish_utils.py:
|
||||
maintainers: $team_wdc
|
||||
labels: wdc_redfish_utils
|
||||
$module_utils/xenserver.py:
|
||||
maintainers: bvitnik
|
||||
labels: xenserver
|
||||
$module_utils/xfconf.py:
|
||||
maintainers: russoz
|
||||
labels: xfconf
|
||||
$modules/cloud/alicloud/:
|
||||
maintainers: xiaozhu36
|
||||
$modules/cloud/atomic/atomic_container.py:
|
||||
@@ -434,6 +445,8 @@ files:
|
||||
maintainers: claco
|
||||
$modules/cloud/scaleway/:
|
||||
maintainers: $team_scaleway
|
||||
$modules/cloud/scaleway/scaleway_compute_private_network.py:
|
||||
maintainers: pastral
|
||||
$modules/cloud/scaleway/scaleway_database_backup.py:
|
||||
maintainers: guillaume_ro_fr
|
||||
$modules/cloud/scaleway/scaleway_image_info.py:
|
||||
@@ -615,7 +628,7 @@ files:
|
||||
$modules/monitoring/nagios.py:
|
||||
maintainers: tbielawa tgoetheyn
|
||||
$modules/monitoring/newrelic_deployment.py:
|
||||
maintainers: mcodd
|
||||
ignore: mcodd
|
||||
$modules/monitoring/pagerduty.py:
|
||||
maintainers: suprememoocow thaumos
|
||||
labels: pagerduty
|
||||
@@ -707,7 +720,7 @@ files:
|
||||
$modules/notification/discord.py:
|
||||
maintainers: cwollinger
|
||||
$modules/notification/flowdock.py:
|
||||
maintainers: mcodd
|
||||
ignore: mcodd
|
||||
$modules/notification/grove.py:
|
||||
maintainers: zimbatm
|
||||
$modules/notification/hipchat.py:
|
||||
@@ -915,7 +928,7 @@ files:
|
||||
$modules/packaging/os/xbps.py:
|
||||
maintainers: dinoocch the-maldridge
|
||||
$modules/packaging/os/yum_versionlock.py:
|
||||
maintainers: florianpaulhoberg aminvakil
|
||||
maintainers: gyptazy aminvakil
|
||||
$modules/packaging/os/zypper.py:
|
||||
maintainers: $team_suse
|
||||
labels: zypper
|
||||
@@ -960,6 +973,10 @@ files:
|
||||
$modules/remote_management/redfish/:
|
||||
maintainers: $team_redfish
|
||||
ignore: jose-delarosa
|
||||
$modules/remote_management/redfish/wdc_redfish_command.py:
|
||||
maintainers: $team_wdc
|
||||
$modules/remote_management/redfish/wdc_redfish_info.py:
|
||||
maintainers: $team_wdc
|
||||
$modules/remote_management/stacki/stacki_host.py:
|
||||
maintainers: bsanders bbyhuy
|
||||
labels: stacki_host
|
||||
@@ -1024,7 +1041,7 @@ files:
|
||||
$modules/system/alternatives.py:
|
||||
maintainers: mulby
|
||||
labels: alternatives
|
||||
ignore: DavidWittman
|
||||
ignore: DavidWittman jiuka
|
||||
$modules/system/aix_lvol.py:
|
||||
maintainers: adejoux
|
||||
$modules/system/awall.py:
|
||||
@@ -1052,6 +1069,9 @@ files:
|
||||
$modules/system/gconftool2.py:
|
||||
maintainers: Akasurde kevensen
|
||||
labels: gconftool2
|
||||
$modules/system/gconftool2_info.py:
|
||||
maintainers: russoz
|
||||
labels: gconftool2
|
||||
$modules/system/homectl.py:
|
||||
maintainers: jameslivulpi
|
||||
$modules/system/interfaces_file.py:
|
||||
@@ -1059,6 +1079,10 @@ files:
|
||||
labels: interfaces_file
|
||||
$modules/system/iptables_state.py:
|
||||
maintainers: quidame
|
||||
$modules/system/keyring.py:
|
||||
maintainers: ahussey-redhat
|
||||
$modules/system/keyring_info.py:
|
||||
maintainers: ahussey-redhat
|
||||
$modules/system/shutdown.py:
|
||||
maintainers: nitzmahone samdoran aminvakil
|
||||
$modules/system/java_cert.py:
|
||||
@@ -1281,5 +1305,6 @@ macros:
|
||||
team_rhn: FlossWare alikins barnabycourt vritant
|
||||
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_suse: commel evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
|
||||
team_virt: joshainglis karmab tleguern Thulium-Drake Ajpantuso
|
||||
team_wdc: mikemoerk
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -105,7 +105,7 @@ body:
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: |
|
||||
Describe exactly how to reproduce the problem, using a minimal test-case. It would *really* help us understand your problem if you could also pased any playbooks, configs and commands you used.
|
||||
Describe exactly how to reproduce the problem, using a minimal test-case. It would *really* help us understand your problem if you could also passed any playbooks, configs and commands you used.
|
||||
|
||||
**HINT:** You can paste https://gist.github.com links for larger files.
|
||||
value: |
|
||||
|
||||
217
CHANGELOG.rst
217
CHANGELOG.rst
@@ -6,6 +6,223 @@ Community General Release Notes
|
||||
|
||||
This changelog describes changes after version 4.0.0.
|
||||
|
||||
v5.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ModuleHelper module utils - added property ``verbosity`` to base class (https://github.com/ansible-collections/community.general/pull/5035).
|
||||
- apk - add ``world`` parameter for supporting a custom world file (https://github.com/ansible-collections/community.general/pull/4976).
|
||||
- consul - adds ``ttl`` parameter for session (https://github.com/ansible-collections/community.general/pull/4996).
|
||||
- dig lookup plugin - add option ``fail_on_error`` to allow stopping execution on lookup failures (https://github.com/ansible-collections/community.general/pull/4973).
|
||||
- keycloak_* modules - add ``http_agent`` parameter with default value ``Ansible`` (https://github.com/ansible-collections/community.general/issues/5023).
|
||||
- lastpass - use config manager for handling plugin options (https://github.com/ansible-collections/community.general/pull/5022).
|
||||
- listen_ports_facts - add new ``include_non_listening`` option which adds ``-a`` option to ``netstat`` and ``ss``. This shows both listening and non-listening (for TCP this means established connections) sockets, and returns ``state`` and ``foreign_address`` (https://github.com/ansible-collections/community.general/issues/4762, https://github.com/ansible-collections/community.general/pull/4953).
|
||||
- maven_artifact - add a new ``unredirected_headers`` option that can be used with ansible-core 2.12 and above. The default value is to not use ``Authorization`` and ``Cookie`` headers on redirects for security reasons. With ansible-core 2.11, all headers are still passed on for redirects (https://github.com/ansible-collections/community.general/pull/4812).
|
||||
- pacman - added parameters ``reason`` and ``reason_for`` to set/change the install reason of packages (https://github.com/ansible-collections/community.general/pull/4956).
|
||||
- xfconf - add ``stdout``, ``stderr`` and ``cmd`` to the module results (https://github.com/ansible-collections/community.general/pull/5037).
|
||||
- xfconf - use ``do_raise()`` instead of defining custom exception class (https://github.com/ansible-collections/community.general/pull/4975).
|
||||
- xfconf_info - use ``do_raise()`` instead of defining custom exception class (https://github.com/ansible-collections/community.general/pull/4975).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- keyring_info - fix the result from the keyring library never getting returned (https://github.com/ansible-collections/community.general/pull/4964).
|
||||
- pacman - fixed name resolution of URL packages (https://github.com/ansible-collections/community.general/pull/4959).
|
||||
- passwordstore lookup plugin - fix ``returnall`` for gopass (https://github.com/ansible-collections/community.general/pull/5027).
|
||||
- passwordstore lookup plugin - fix password store path detection for gopass (https://github.com/ansible-collections/community.general/pull/4955).
|
||||
- proxmox - fix error handling when getting VM by name when ``state=absent`` (https://github.com/ansible-collections/community.general/pull/4945).
|
||||
- proxmox_kvm - fix error handling when getting VM by name when ``state=absent`` (https://github.com/ansible-collections/community.general/pull/4945).
|
||||
- slack - fix incorrect channel prefix ``#`` caused by incomplete pattern detection by adding ``G0`` and ``GF`` as channel ID patterns (https://github.com/ansible-collections/community.general/pull/5019).
|
||||
- xfconf - fix setting of boolean values (https://github.com/ansible-collections/community.general/issues/4999, https://github.com/ansible-collections/community.general/pull/5007).
|
||||
|
||||
New Plugins
|
||||
-----------
|
||||
|
||||
Lookup
|
||||
~~~~~~
|
||||
|
||||
- bitwarden - Retrieve secrets from Bitwarden
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
Remote Management
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
redfish
|
||||
^^^^^^^
|
||||
|
||||
- wdc_redfish_command - Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs
|
||||
- wdc_redfish_info - Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs
|
||||
|
||||
v5.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- machinectl become plugin - can now be used with a password from another user than root, if a polkit rule is present (https://github.com/ansible-collections/community.general/pull/4849).
|
||||
- opentelemetry callback plugin - allow configuring opentelementry callback via config file (https://github.com/ansible-collections/community.general/pull/4916).
|
||||
- redfish_info - add ``GetManagerInventory`` to report list of Manager inventory information (https://github.com/ansible-collections/community.general/issues/4899).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- cmd_runner module utils - fix bug caused by using the ``command`` variable instead of ``self.command`` when looking for binary path (https://github.com/ansible-collections/community.general/pull/4903).
|
||||
- dsv lookup plugin - do not ignore the ``tld`` parameter (https://github.com/ansible-collections/community.general/pull/4911).
|
||||
- lxd connection plugin - fix incorrect ``inventory_hostname`` in ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/issues/4886).
|
||||
- proxmox inventory plugin - fix crash when ``enabled=1`` is used in agent config string (https://github.com/ansible-collections/community.general/pull/4910).
|
||||
- rax_clb_nodes - fix code to be compatible with Python 3 (https://github.com/ansible-collections/community.general/pull/4933).
|
||||
- redfish_info - fix to ``GetChassisPower`` to correctly report power information when multiple chassis exist, but not all chassis report power information (https://github.com/ansible-collections/community.general/issues/4901).
|
||||
|
||||
v5.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- cmd_runner module utils - add ``__call__`` method to invoke context (https://github.com/ansible-collections/community.general/pull/4791).
|
||||
- passwordstore lookup plugin - allow using alternative password managers by detecting wrapper scripts, allow explicit configuration of pass and gopass backends (https://github.com/ansible-collections/community.general/issues/4766).
|
||||
- sudoers - will attempt to validate the proposed sudoers rule using visudo if available, optionally skipped, or required (https://github.com/ansible-collections/community.general/pull/4794, https://github.com/ansible-collections/community.general/issues/4745).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_mount.py``.
|
||||
- redfish_command - fix the check if a virtual media is unmounted to just check for ``instered= false`` caused by Supermicro hardware that does not clear the ``ImageName`` (https://github.com/ansible-collections/community.general/pull/4839).
|
||||
- redfish_command - the Supermicro Redfish implementation only supports the ``image_url`` parameter in the underlying API calls to ``VirtualMediaInsert`` and ``VirtualMediaEject``. Any values set (or the defaults) for ``write_protected`` or ``inserted`` will be ignored (https://github.com/ansible-collections/community.general/pull/4839).
|
||||
- sudoers - fix incorrect handling of ``state: absent`` (https://github.com/ansible-collections/community.general/issues/4852).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
Cloud
|
||||
~~~~~
|
||||
|
||||
scaleway
|
||||
^^^^^^^^
|
||||
|
||||
- scaleway_compute_private_network - Scaleway compute - private network management
|
||||
|
||||
System
|
||||
~~~~~~
|
||||
|
||||
- keyring - Set or delete a passphrase using the Operating System's native keyring
|
||||
- keyring_info - Get a passphrase using the Operating System's native keyring
|
||||
|
||||
v5.1.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Bugfix release.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- alternatives - do not set the priority if the priority was not set by the user (https://github.com/ansible-collections/community.general/pull/4810).
|
||||
- alternatives - only pass subcommands when they are specified as module arguments (https://github.com/ansible-collections/community.general/issues/4803, https://github.com/ansible-collections/community.general/issues/4804, https://github.com/ansible-collections/community.general/pull/4836).
|
||||
- alternatives - when ``subcommands`` is specified, ``link`` must be given for every subcommand. This was already mentioned in the documentation, but not enforced by the code (https://github.com/ansible-collections/community.general/pull/4836).
|
||||
- nmcli - fix error caused by adding undefined module arguments for list options (https://github.com/ansible-collections/community.general/issues/4373, https://github.com/ansible-collections/community.general/pull/4813).
|
||||
- proxmox inventory plugin - fixed extended status detection for qemu (https://github.com/ansible-collections/community.general/pull/4816).
|
||||
- redhat_subscription - fix unsubscribing on RHEL 9 (https://github.com/ansible-collections/community.general/issues/4741).
|
||||
- sudoers - ensure sudoers config files are created with the permissions requested by sudoers (0440) (https://github.com/ansible-collections/community.general/pull/4814).
|
||||
|
||||
v5.1.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ModuleHelper module utils - improved ``ModuleHelperException``, using ``to_native()`` for the exception message (https://github.com/ansible-collections/community.general/pull/4755).
|
||||
- alternatives - add ``state=absent`` to be able to remove an alternative (https://github.com/ansible-collections/community.general/pull/4654).
|
||||
- alternatives - add ``subcommands`` parameter (https://github.com/ansible-collections/community.general/pull/4654).
|
||||
- ansible_galaxy_install - minor refactoring using latest ``ModuleHelper`` updates (https://github.com/ansible-collections/community.general/pull/4752).
|
||||
- cmd_runner module util - added parameters ``check_mode_skip`` and ``check_mode_return`` to ``CmdRunner.context()``, so that the command is not executed when ``check_mode=True`` (https://github.com/ansible-collections/community.general/pull/4736).
|
||||
- nmcli - adds ``vpn`` type and parameter for supporting VPN with service type L2TP and PPTP (https://github.com/ansible-collections/community.general/pull/4746).
|
||||
- proxmox inventory plugin - added new flag ``qemu_extended_statuses`` and new groups ``<group_prefix>prelaunch``, ``<group_prefix>paused``. They will be populated only when ``want_facts=true``, ``qemu_extended_statuses=true`` and only for ``QEMU`` machines (https://github.com/ansible-collections/community.general/pull/4723).
|
||||
- puppet - adds ``confdir`` parameter to configure a custom confir location (https://github.com/ansible-collections/community.general/pull/4740).
|
||||
- xfconf - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf module utils - created new module util ``xfconf`` providing a ``cmd_runner`` specific for ``xfconf`` modules (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf_info - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
- cmd_runner module utils - deprecated ``fmt`` in favour of ``cmd_runner_fmt`` as the parameter format object (https://github.com/ansible-collections/community.general/pull/4777).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
System
|
||||
~~~~~~
|
||||
|
||||
- gconftool2_info - Retrieve GConf configurations
|
||||
|
||||
v5.0.2
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Maintenance and bugfix release for Ansible 6.0.0.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Include ``simplified_bsd.txt`` license file for various module utils, the ``lxca_common`` docs fragment, and the ``utm_utils`` unit tests.
|
||||
|
||||
v5.0.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix release for inclusion in Ansible 6.0.0.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- cpanm - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- mksysb - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- pipx - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- snap - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- xfconf - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- consul - fixed bug introduced in PR 4590 (https://github.com/ansible-collections/community.general/issues/4680).
|
||||
- filesystem - handle ``fatresize --info`` output lines without ``:`` (https://github.com/ansible-collections/community.general/pull/4700).
|
||||
- filesystem - improve error messages when output cannot be parsed by including newlines in escaped form (https://github.com/ansible-collections/community.general/pull/4700).
|
||||
- keycloak_realm - fix default groups and roles (https://github.com/ansible-collections/community.general/issues/4241).
|
||||
- redis* modules - fix call to ``module.fail_json`` when failing because of missing Python libraries (https://github.com/ansible-collections/community.general/pull/4733).
|
||||
- xcc_redfish_command - for compatibility due to Redfish spec changes the virtualMedia resource location changed from Manager to System (https://github.com/ansible-collections/community.general/pull/4682).
|
||||
- zfs - fix wrong quoting of properties (https://github.com/ansible-collections/community.general/issues/4707, https://github.com/ansible-collections/community.general/pull/4726).
|
||||
|
||||
v5.0.0
|
||||
======
|
||||
|
||||
|
||||
48
PSF-license.txt
Normal file
48
PSF-license.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
@@ -755,3 +755,272 @@ releases:
|
||||
name: counter
|
||||
namespace: null
|
||||
release_date: '2022-04-29'
|
||||
5.0.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- consul - fixed bug introduced in PR 4590 (https://github.com/ansible-collections/community.general/issues/4680).
|
||||
- filesystem - handle ``fatresize --info`` output lines without ``:`` (https://github.com/ansible-collections/community.general/pull/4700).
|
||||
- filesystem - improve error messages when output cannot be parsed by including
|
||||
newlines in escaped form (https://github.com/ansible-collections/community.general/pull/4700).
|
||||
- keycloak_realm - fix default groups and roles (https://github.com/ansible-collections/community.general/issues/4241).
|
||||
- redis* modules - fix call to ``module.fail_json`` when failing because of
|
||||
missing Python libraries (https://github.com/ansible-collections/community.general/pull/4733).
|
||||
- xcc_redfish_command - for compatibility due to Redfish spec changes the virtualMedia
|
||||
resource location changed from Manager to System (https://github.com/ansible-collections/community.general/pull/4682).
|
||||
- zfs - fix wrong quoting of properties (https://github.com/ansible-collections/community.general/issues/4707,
|
||||
https://github.com/ansible-collections/community.general/pull/4726).
|
||||
minor_changes:
|
||||
- cpanm - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived
|
||||
modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- mksysb - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived
|
||||
modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- pipx - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived
|
||||
modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- snap - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived
|
||||
modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
- xfconf - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived
|
||||
modules (https://github.com/ansible-collections/community.general/pull/4674).
|
||||
release_summary: Regular bugfix release for inclusion in Ansible 6.0.0.
|
||||
fragments:
|
||||
- 4674-use-mh-raise.yaml
|
||||
- 4682-compatibility-virtualmedia-resource-location.yaml
|
||||
- 4700-code-changes.yml
|
||||
- 4712-consul-bugfix.yaml
|
||||
- 4719-fix-keycloak-realm.yaml
|
||||
- 4726-zfs.yml
|
||||
- 4733-redis-fail.yml
|
||||
- 5.0.1.yml
|
||||
release_date: '2022-05-30'
|
||||
5.0.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Include ``simplified_bsd.txt`` license file for various module utils, the
|
||||
``lxca_common`` docs fragment, and the ``utm_utils`` unit tests.
|
||||
release_summary: Maintenance and bugfix release for Ansible 6.0.0.
|
||||
fragments:
|
||||
- 5.0.2.yml
|
||||
- simplified-bsd-license.yml
|
||||
release_date: '2022-06-06'
|
||||
5.1.0:
|
||||
changes:
|
||||
deprecated_features:
|
||||
- cmd_runner module utils - deprecated ``fmt`` in favour of ``cmd_runner_fmt``
|
||||
as the parameter format object (https://github.com/ansible-collections/community.general/pull/4777).
|
||||
minor_changes:
|
||||
- ModuleHelper module utils - improved ``ModuleHelperException``, using ``to_native()``
|
||||
for the exception message (https://github.com/ansible-collections/community.general/pull/4755).
|
||||
- alternatives - add ``state=absent`` to be able to remove an alternative (https://github.com/ansible-collections/community.general/pull/4654).
|
||||
- alternatives - add ``subcommands`` parameter (https://github.com/ansible-collections/community.general/pull/4654).
|
||||
- ansible_galaxy_install - minor refactoring using latest ``ModuleHelper`` updates
|
||||
(https://github.com/ansible-collections/community.general/pull/4752).
|
||||
- cmd_runner module util - added parameters ``check_mode_skip`` and ``check_mode_return``
|
||||
to ``CmdRunner.context()``, so that the command is not executed when ``check_mode=True``
|
||||
(https://github.com/ansible-collections/community.general/pull/4736).
|
||||
- nmcli - adds ``vpn`` type and parameter for supporting VPN with service type
|
||||
L2TP and PPTP (https://github.com/ansible-collections/community.general/pull/4746).
|
||||
- proxmox inventory plugin - added new flag ``qemu_extended_statuses`` and new
|
||||
groups ``<group_prefix>prelaunch``, ``<group_prefix>paused``. They will be
|
||||
populated only when ``want_facts=true``, ``qemu_extended_statuses=true`` and
|
||||
only for ``QEMU`` machines (https://github.com/ansible-collections/community.general/pull/4723).
|
||||
- puppet - adds ``confdir`` parameter to configure a custom confir location
|
||||
(https://github.com/ansible-collections/community.general/pull/4740).
|
||||
- xfconf - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf module utils - created new module util ``xfconf`` providing a ``cmd_runner``
|
||||
specific for ``xfconf`` modules (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
- xfconf_info - changed implementation to use ``cmd_runner`` (https://github.com/ansible-collections/community.general/pull/4776).
|
||||
release_summary: Regular bugfix and feature release.
|
||||
fragments:
|
||||
- 4654-alternatives-add-subcommands.yml
|
||||
- 4724-proxmox-qemu-extend.yaml
|
||||
- 4736-cmd-runner-skip-if-check.yml
|
||||
- 4740-puppet-feature.yaml
|
||||
- 4746-add-vpn-support-nmcli.yaml
|
||||
- 4752-ansible-galaxy-install-mh-updates.yml
|
||||
- 4755-mhexception-improvement.yml
|
||||
- 4776-xfconf-cmd-runner.yaml
|
||||
- 4777-cmd-runner-deprecate-fmt.yaml
|
||||
- 5.1.0.yml
|
||||
modules:
|
||||
- description: Retrieve GConf configurations
|
||||
name: gconftool2_info
|
||||
namespace: system
|
||||
release_date: '2022-06-07'
|
||||
5.1.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- alternatives - do not set the priority if the priority was not set by the
|
||||
user (https://github.com/ansible-collections/community.general/pull/4810).
|
||||
- alternatives - only pass subcommands when they are specified as module arguments
|
||||
(https://github.com/ansible-collections/community.general/issues/4803, https://github.com/ansible-collections/community.general/issues/4804,
|
||||
https://github.com/ansible-collections/community.general/pull/4836).
|
||||
- alternatives - when ``subcommands`` is specified, ``link`` must be given for
|
||||
every subcommand. This was already mentioned in the documentation, but not
|
||||
enforced by the code (https://github.com/ansible-collections/community.general/pull/4836).
|
||||
- nmcli - fix error caused by adding undefined module arguments for list options
|
||||
(https://github.com/ansible-collections/community.general/issues/4373, https://github.com/ansible-collections/community.general/pull/4813).
|
||||
- proxmox inventory plugin - fixed extended status detection for qemu (https://github.com/ansible-collections/community.general/pull/4816).
|
||||
- redhat_subscription - fix unsubscribing on RHEL 9 (https://github.com/ansible-collections/community.general/issues/4741).
|
||||
- sudoers - ensure sudoers config files are created with the permissions requested
|
||||
by sudoers (0440) (https://github.com/ansible-collections/community.general/pull/4814).
|
||||
release_summary: Bugfix release.
|
||||
fragments:
|
||||
- 4809-redhat_subscription-unsubscribe.yaml
|
||||
- 4810-alternatives-bug.yml
|
||||
- 4813-fix-nmcli-convert-list.yaml
|
||||
- 4814-sudoers-file-permissions.yml
|
||||
- 4816-proxmox-fix-extended-status.yaml
|
||||
- 4836-alternatives.yml
|
||||
- 5.1.1.yml
|
||||
release_date: '2022-06-14'
|
||||
5.2.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_mount.py``.
|
||||
- redfish_command - fix the check if a virtual media is unmounted to just check
|
||||
for ``instered= false`` caused by Supermicro hardware that does not clear
|
||||
the ``ImageName`` (https://github.com/ansible-collections/community.general/pull/4839).
|
||||
- redfish_command - the Supermicro Redfish implementation only supports the
|
||||
``image_url`` parameter in the underlying API calls to ``VirtualMediaInsert``
|
||||
and ``VirtualMediaEject``. Any values set (or the defaults) for ``write_protected``
|
||||
or ``inserted`` will be ignored (https://github.com/ansible-collections/community.general/pull/4839).
|
||||
- 'sudoers - fix incorrect handling of ``state: absent`` (https://github.com/ansible-collections/community.general/issues/4852).'
|
||||
minor_changes:
|
||||
- cmd_runner module utils - add ``__call__`` method to invoke context (https://github.com/ansible-collections/community.general/pull/4791).
|
||||
- passwordstore lookup plugin - allow using alternative password managers by
|
||||
detecting wrapper scripts, allow explicit configuration of pass and gopass
|
||||
backends (https://github.com/ansible-collections/community.general/issues/4766).
|
||||
- sudoers - will attempt to validate the proposed sudoers rule using visudo
|
||||
if available, optionally skipped, or required (https://github.com/ansible-collections/community.general/pull/4794,
|
||||
https://github.com/ansible-collections/community.general/issues/4745).
|
||||
release_summary: Regular bugfix and feature release.
|
||||
fragments:
|
||||
- 4780-passwordstore-wrapper-compat.yml
|
||||
- 4791-cmd-runner-callable.yaml
|
||||
- 4794-sudoers-validation.yml
|
||||
- 4839-fix-VirtualMediaInsert-Supermicro.yml
|
||||
- 4852-sudoers-state-absent.yml
|
||||
- 5.2.0.yml
|
||||
- psf-license.yml
|
||||
modules:
|
||||
- description: Set or delete a passphrase using the Operating System's native
|
||||
keyring
|
||||
name: keyring
|
||||
namespace: system
|
||||
- description: Get a passphrase using the Operating System's native keyring
|
||||
name: keyring_info
|
||||
namespace: system
|
||||
- description: Scaleway compute - private network management
|
||||
name: scaleway_compute_private_network
|
||||
namespace: cloud.scaleway
|
||||
release_date: '2022-06-21'
|
||||
5.3.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- cmd_runner module utils - fix bug caused by using the ``command`` variable
|
||||
instead of ``self.command`` when looking for binary path (https://github.com/ansible-collections/community.general/pull/4903).
|
||||
- dsv lookup plugin - do not ignore the ``tld`` parameter (https://github.com/ansible-collections/community.general/pull/4911).
|
||||
- lxd connection plugin - fix incorrect ``inventory_hostname`` in ``remote_addr``.
|
||||
This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/issues/4886).
|
||||
- proxmox inventory plugin - fix crash when ``enabled=1`` is used in agent config
|
||||
string (https://github.com/ansible-collections/community.general/pull/4910).
|
||||
- rax_clb_nodes - fix code to be compatible with Python 3 (https://github.com/ansible-collections/community.general/pull/4933).
|
||||
- redfish_info - fix to ``GetChassisPower`` to correctly report power information
|
||||
when multiple chassis exist, but not all chassis report power information
|
||||
(https://github.com/ansible-collections/community.general/issues/4901).
|
||||
minor_changes:
|
||||
- machinectl become plugin - can now be used with a password from another user
|
||||
than root, if a polkit rule is present (https://github.com/ansible-collections/community.general/pull/4849).
|
||||
- opentelemetry callback plugin - allow configuring opentelementry callback
|
||||
via config file (https://github.com/ansible-collections/community.general/pull/4916).
|
||||
- redfish_info - add ``GetManagerInventory`` to report list of Manager inventory
|
||||
information (https://github.com/ansible-collections/community.general/issues/4899).
|
||||
release_summary: Regular bugfix and feature release.
|
||||
fragments:
|
||||
- 4849-add-password-prompt-support-for-machinectl.yml
|
||||
- 4886-fix-lxd-inventory-hostname.yml
|
||||
- 4899-add-GetManagerInventory-for-redfish_info.yml
|
||||
- 4901-fix-redfish-chassispower.yml
|
||||
- 4903-cmdrunner-bugfix.yaml
|
||||
- 4910-fix-for-agent-enabled.yml
|
||||
- 4911-dsv-honor-tld-option.yml
|
||||
- 4916-opentelemetry-ini-options.yaml
|
||||
- 4933-fix-rax-clb-nodes.yaml
|
||||
- 5.3.0.yml
|
||||
release_date: '2022-07-12'
|
||||
5.4.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- keyring_info - fix the result from the keyring library never getting returned
|
||||
(https://github.com/ansible-collections/community.general/pull/4964).
|
||||
- pacman - fixed name resolution of URL packages (https://github.com/ansible-collections/community.general/pull/4959).
|
||||
- passwordstore lookup plugin - fix ``returnall`` for gopass (https://github.com/ansible-collections/community.general/pull/5027).
|
||||
- passwordstore lookup plugin - fix password store path detection for gopass
|
||||
(https://github.com/ansible-collections/community.general/pull/4955).
|
||||
- proxmox - fix error handling when getting VM by name when ``state=absent``
|
||||
(https://github.com/ansible-collections/community.general/pull/4945).
|
||||
- proxmox_kvm - fix error handling when getting VM by name when ``state=absent``
|
||||
(https://github.com/ansible-collections/community.general/pull/4945).
|
||||
- slack - fix incorrect channel prefix ``#`` caused by incomplete pattern detection
|
||||
by adding ``G0`` and ``GF`` as channel ID patterns (https://github.com/ansible-collections/community.general/pull/5019).
|
||||
- xfconf - fix setting of boolean values (https://github.com/ansible-collections/community.general/issues/4999,
|
||||
https://github.com/ansible-collections/community.general/pull/5007).
|
||||
minor_changes:
|
||||
- ModuleHelper module utils - added property ``verbosity`` to base class (https://github.com/ansible-collections/community.general/pull/5035).
|
||||
- apk - add ``world`` parameter for supporting a custom world file (https://github.com/ansible-collections/community.general/pull/4976).
|
||||
- consul - adds ``ttl`` parameter for session (https://github.com/ansible-collections/community.general/pull/4996).
|
||||
- dig lookup plugin - add option ``fail_on_error`` to allow stopping execution
|
||||
on lookup failures (https://github.com/ansible-collections/community.general/pull/4973).
|
||||
- keycloak_* modules - add ``http_agent`` parameter with default value ``Ansible``
|
||||
(https://github.com/ansible-collections/community.general/issues/5023).
|
||||
- lastpass - use config manager for handling plugin options (https://github.com/ansible-collections/community.general/pull/5022).
|
||||
- listen_ports_facts - add new ``include_non_listening`` option which adds ``-a``
|
||||
option to ``netstat`` and ``ss``. This shows both listening and non-listening
|
||||
(for TCP this means established connections) sockets, and returns ``state``
|
||||
and ``foreign_address`` (https://github.com/ansible-collections/community.general/issues/4762,
|
||||
https://github.com/ansible-collections/community.general/pull/4953).
|
||||
- maven_artifact - add a new ``unredirected_headers`` option that can be used
|
||||
with ansible-core 2.12 and above. The default value is to not use ``Authorization``
|
||||
and ``Cookie`` headers on redirects for security reasons. With ansible-core
|
||||
2.11, all headers are still passed on for redirects (https://github.com/ansible-collections/community.general/pull/4812).
|
||||
- pacman - added parameters ``reason`` and ``reason_for`` to set/change the
|
||||
install reason of packages (https://github.com/ansible-collections/community.general/pull/4956).
|
||||
- xfconf - add ``stdout``, ``stderr`` and ``cmd`` to the module results (https://github.com/ansible-collections/community.general/pull/5037).
|
||||
- xfconf - use ``do_raise()`` instead of defining custom exception class (https://github.com/ansible-collections/community.general/pull/4975).
|
||||
- xfconf_info - use ``do_raise()`` instead of defining custom exception class
|
||||
(https://github.com/ansible-collections/community.general/pull/4975).
|
||||
release_summary: Regular bugfix and feature release.
|
||||
fragments:
|
||||
- 4812-expose-unredirected-headers.yml
|
||||
- 4945-fix-get_vm-int-parse-handling.yaml
|
||||
- 4953-listen-ports-facts-extend-output.yaml
|
||||
- 4955-fix-path-detection-for-gopass.yaml
|
||||
- 4956-pacman-install-reason.yaml
|
||||
- 4959-pacman-fix-url-packages-name.yaml
|
||||
- 4964-fix-keyring-info.yml
|
||||
- 4973-introduce-dig-lookup-argument.yaml
|
||||
- 4975-xfconf-use-do-raise.yaml
|
||||
- 4976-apk-add-support-for-a-custom-world-file.yaml
|
||||
- 4996-consul-session-ttl.yml
|
||||
- 4999-xfconf-bool.yml
|
||||
- 5.4.0.yml
|
||||
- 5019-slack-support-more-groups.yml
|
||||
- 5022-lastpass-lookup-cleanup.yml
|
||||
- 5023-http-agent-param-keycloak.yml
|
||||
- 5027-fix-returnall-for-gopass.yaml
|
||||
- 5035-mh-base-verbosity.yaml
|
||||
- 5037-xfconf-add-cmd-output.yaml
|
||||
modules:
|
||||
- description: Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish
|
||||
APIs
|
||||
name: wdc_redfish_command
|
||||
namespace: remote_management.redfish
|
||||
- description: Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish
|
||||
APIs
|
||||
name: wdc_redfish_info
|
||||
namespace: remote_management.redfish
|
||||
plugins:
|
||||
lookup:
|
||||
- description: Retrieve secrets from Bitwarden
|
||||
name: bitwarden
|
||||
namespace: null
|
||||
release_date: '2022-08-02'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace: community
|
||||
name: general
|
||||
version: 5.0.0
|
||||
version: 5.4.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
@@ -292,6 +292,8 @@ plugin_routing:
|
||||
redirect: community.google.gce_tag
|
||||
gconftool2:
|
||||
redirect: community.general.system.gconftool2
|
||||
gconftool2_info:
|
||||
redirect: community.general.system.gconftool2_info
|
||||
gcp_backend_service:
|
||||
tombstone:
|
||||
removal_version: 2.0.0
|
||||
@@ -606,6 +608,10 @@ plugin_routing:
|
||||
redirect: community.general.identity.keycloak.keycloak_role
|
||||
keycloak_user_federation:
|
||||
redirect: community.general.identity.keycloak.keycloak_user_federation
|
||||
keyring:
|
||||
redirect: community.general.system.keyring
|
||||
keyring_info:
|
||||
redirect: community.general.system.keyring_info
|
||||
kibana_plugin:
|
||||
redirect: community.general.database.misc.kibana_plugin
|
||||
kubevirt_cdi_upload:
|
||||
@@ -1355,6 +1361,8 @@ plugin_routing:
|
||||
redirect: community.general.notification.say
|
||||
scaleway_compute:
|
||||
redirect: community.general.cloud.scaleway.scaleway_compute
|
||||
scaleway_compute_private_network:
|
||||
redirect: community.general.cloud.scaleway.scaleway_compute_private_network
|
||||
scaleway_database_backup:
|
||||
redirect: community.general.cloud.scaleway.scaleway_database_backup
|
||||
scaleway_image_facts:
|
||||
@@ -1597,6 +1605,10 @@ plugin_routing:
|
||||
redirect: community.general.cloud.smartos.vmadm
|
||||
wakeonlan:
|
||||
redirect: community.general.remote_management.wakeonlan
|
||||
wdc_redfish_command:
|
||||
redirect: community.general.remote_management.redfish.wdc_redfish_command
|
||||
wdc_redfish_info:
|
||||
redirect: community.general.remote_management.redfish.wdc_redfish_info
|
||||
webfaction_app:
|
||||
redirect: community.general.cloud.webfaction.webfaction_app
|
||||
webfaction_db:
|
||||
|
||||
@@ -66,15 +66,46 @@ DOCUMENTATION = '''
|
||||
ini:
|
||||
- section: machinectl_become_plugin
|
||||
key: password
|
||||
notes:
|
||||
- When not using this plugin with user C(root), it only works correctly with a polkit rule which will alter
|
||||
the behaviour of machinectl. This rule must alter the prompt behaviour to ask directly for the user credentials,
|
||||
if the user is allowed to perform the action (take a look at the examples section).
|
||||
If such a rule is not present the plugin only work if it is used in context with the root user,
|
||||
because then no further prompt will be shown by machinectl.
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
# A polkit rule needed to use the module with a non-root user.
|
||||
# See the Notes section for details.
|
||||
60-machinectl-fast-user-auth.rules: |
|
||||
polkit.addRule(function(action, subject) {
|
||||
if(action.id == "org.freedesktop.machine1.host-shell" && subject.isInGroup("wheel")) {
|
||||
return polkit.Result.AUTH_SELF_KEEP;
|
||||
}
|
||||
});
|
||||
'''
|
||||
|
||||
from re import compile as re_compile
|
||||
|
||||
from ansible.plugins.become import BecomeBase
|
||||
from ansible.module_utils._text import to_bytes
|
||||
|
||||
|
||||
ansi_color_codes = re_compile(to_bytes(r'\x1B\[[0-9;]+m'))
|
||||
|
||||
|
||||
class BecomeModule(BecomeBase):
|
||||
|
||||
name = 'community.general.machinectl'
|
||||
|
||||
prompt = 'Password: '
|
||||
fail = ('==== AUTHENTICATION FAILED ====',)
|
||||
success = ('==== AUTHENTICATION COMPLETE ====',)
|
||||
|
||||
@staticmethod
|
||||
def remove_ansi_codes(line):
|
||||
return ansi_color_codes.sub(b"", line)
|
||||
|
||||
def build_become_command(self, cmd, shell):
|
||||
super(BecomeModule, self).build_become_command(cmd, shell)
|
||||
|
||||
@@ -86,3 +117,15 @@ class BecomeModule(BecomeBase):
|
||||
flags = self.get_option('become_flags')
|
||||
user = self.get_option('become_user')
|
||||
return '%s -q shell %s %s@ %s' % (become, flags, user, cmd)
|
||||
|
||||
def check_success(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
return super().check_success(b_output)
|
||||
|
||||
def check_incorrect_password(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
return super().check_incorrect_password(b_output)
|
||||
|
||||
def check_missing_password(self, b_output):
|
||||
b_output = self.remove_ansi_codes(b_output)
|
||||
return super().check_missing_password(b_output)
|
||||
|
||||
@@ -8,9 +8,9 @@ DOCUMENTATION = """
|
||||
name: sudosu
|
||||
short_description: Run tasks using sudo su -
|
||||
description:
|
||||
- This become plugins allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined.
|
||||
- This become plugin allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined.
|
||||
author:
|
||||
- Dag Wieers (@dagwieers)
|
||||
- Dag Wieers (@dagwieers)
|
||||
version_added: 2.4.0
|
||||
options:
|
||||
become_user:
|
||||
|
||||
@@ -12,7 +12,7 @@ DOCUMENTATION = '''
|
||||
type: notification
|
||||
short_description: write playbook output to log file
|
||||
description:
|
||||
- This callback writes playbook output to a file per host in the `/var/log/ansible/hosts` directory
|
||||
- This callback writes playbook output to a file per host in the C(/var/log/ansible/hosts) directory
|
||||
requirements:
|
||||
- Whitelist in configuration
|
||||
- A writeable /var/log/ansible/hosts directory by the user executing Ansible on the controller
|
||||
|
||||
@@ -24,6 +24,10 @@ DOCUMENTATION = '''
|
||||
- Hide the arguments for a task.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: hide_task_arguments
|
||||
version_added: 5.3.0
|
||||
enable_from_environment:
|
||||
type: str
|
||||
description:
|
||||
@@ -34,6 +38,10 @@ DOCUMENTATION = '''
|
||||
and if set to true this plugin will be enabled.
|
||||
env:
|
||||
- name: ANSIBLE_OPENTELEMETRY_ENABLE_FROM_ENVIRONMENT
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: enable_from_environment
|
||||
version_added: 5.3.0
|
||||
version_added: 3.8.0
|
||||
otel_service_name:
|
||||
default: ansible
|
||||
@@ -42,6 +50,10 @@ DOCUMENTATION = '''
|
||||
- The service name resource attribute.
|
||||
env:
|
||||
- name: OTEL_SERVICE_NAME
|
||||
ini:
|
||||
- section: callback_opentelemetry
|
||||
key: otel_service_name
|
||||
version_added: 5.3.0
|
||||
traceparent:
|
||||
default: None
|
||||
type: str
|
||||
@@ -61,11 +73,14 @@ examples: |
|
||||
Enable the plugin in ansible.cfg:
|
||||
[defaults]
|
||||
callbacks_enabled = community.general.opentelemetry
|
||||
[callback_opentelemetry]
|
||||
enable_from_environment = ANSIBLE_OPENTELEMETRY_ENABLED
|
||||
|
||||
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
|
||||
export ANSIBLE_OPENTELEMETRY_ENABLED=true
|
||||
'''
|
||||
|
||||
import getpass
|
||||
|
||||
@@ -14,9 +14,9 @@ DOCUMENTATION = '''
|
||||
- set as main display callback
|
||||
short_description: only print certain tasks
|
||||
description:
|
||||
- This callback only prints tasks that have been tagged with `print_action` or that have failed.
|
||||
- This callback only prints tasks that have been tagged with C(print_action) or that have failed.
|
||||
This allows operators to focus on the tasks that provide value only.
|
||||
- Tasks that are not printed are placed with a '.'.
|
||||
- Tasks that are not printed are placed with a C(.).
|
||||
- If you increase verbosity all tasks are printed.
|
||||
options:
|
||||
nocolor:
|
||||
|
||||
@@ -18,6 +18,7 @@ DOCUMENTATION = '''
|
||||
- Container identifier.
|
||||
default: inventory_hostname
|
||||
vars:
|
||||
- name: inventory_hostname
|
||||
- name: ansible_host
|
||||
- name: ansible_lxd_host
|
||||
executable:
|
||||
@@ -61,7 +62,6 @@ class Connection(ConnectionBase):
|
||||
def __init__(self, play_context, new_stdin, *args, **kwargs):
|
||||
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
|
||||
|
||||
self._host = self._play_context.remote_addr
|
||||
try:
|
||||
self._lxc_cmd = get_bin_path("lxc")
|
||||
except ValueError:
|
||||
@@ -75,14 +75,14 @@ class Connection(ConnectionBase):
|
||||
super(Connection, self)._connect()
|
||||
|
||||
if not self._connected:
|
||||
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self._host)
|
||||
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self.get_option('remote_addr'))
|
||||
self._connected = True
|
||||
|
||||
def exec_command(self, cmd, in_data=None, sudoable=True):
|
||||
""" execute a command on the lxd host """
|
||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||
|
||||
self._display.vvv(u"EXEC {0}".format(cmd), host=self._host)
|
||||
self._display.vvv(u"EXEC {0}".format(cmd), host=self.get_option('remote_addr'))
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
@@ -104,10 +104,10 @@ class Connection(ConnectionBase):
|
||||
stderr = to_text(stderr)
|
||||
|
||||
if stderr == "error: Container is not running.\n":
|
||||
raise AnsibleConnectionFailure("container not running: %s" % self._host)
|
||||
raise AnsibleConnectionFailure("container not running: %s" % self.get_option('remote_addr'))
|
||||
|
||||
if stderr == "error: not found\n":
|
||||
raise AnsibleConnectionFailure("container not found: %s" % self._host)
|
||||
raise AnsibleConnectionFailure("container not found: %s" % self.get_option('remote_addr'))
|
||||
|
||||
return process.returncode, stdout, stderr
|
||||
|
||||
@@ -115,7 +115,7 @@ class Connection(ConnectionBase):
|
||||
""" put a file from local to lxd """
|
||||
super(Connection, self).put_file(in_path, out_path)
|
||||
|
||||
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self._host)
|
||||
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr'))
|
||||
|
||||
if not os.path.isfile(to_bytes(in_path, errors='surrogate_or_strict')):
|
||||
raise AnsibleFileNotFound("input path is not a file: %s" % in_path)
|
||||
@@ -138,7 +138,7 @@ class Connection(ConnectionBase):
|
||||
""" fetch a file from lxd to local """
|
||||
super(Connection, self).fetch_file(in_path, out_path)
|
||||
|
||||
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self._host)
|
||||
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr'))
|
||||
|
||||
local_cmd = [self._lxc_cmd]
|
||||
if self.get_option("project"):
|
||||
|
||||
@@ -68,4 +68,10 @@ options:
|
||||
type: int
|
||||
default: 10
|
||||
version_added: 4.5.0
|
||||
http_agent:
|
||||
description:
|
||||
- Configures the HTTP User-Agent header.
|
||||
type: str
|
||||
default: Ansible
|
||||
version_added: 5.4.0
|
||||
'''
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2017 Lenovo, Inc.
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -21,7 +21,7 @@ DOCUMENTATION = '''
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Count occurences
|
||||
- name: Count occurrences
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ [1, 'a', 2, 2, 'a', 'b', 'a'] | community.general.counter }}
|
||||
@@ -30,7 +30,7 @@ EXAMPLES = '''
|
||||
|
||||
RETURN = '''
|
||||
_value:
|
||||
description: A dictionary with the elements of the sequence as keys, and their number of occurance in the sequence as values.
|
||||
description: A dictionary with the elements of the sequence as keys, and their number of occurrences in the sequence as values.
|
||||
type: dictionary
|
||||
'''
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ DOCUMENTATION = '''
|
||||
parser:
|
||||
description:
|
||||
- The correct parser for the input data.
|
||||
- For exmaple C(ifconfig).
|
||||
- For example C(ifconfig).
|
||||
- See U(https://github.com/kellyjonbrazil/jc#parsers) for the latest list of parsers.
|
||||
type: string
|
||||
required: true
|
||||
|
||||
@@ -213,7 +213,7 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
|
||||
self.inventory.add_child(parent_group_name, group_name)
|
||||
else:
|
||||
self.display.vvvv('Processing profile %s without parent\n' % profile['name'])
|
||||
# Create a heirarchy of profile names
|
||||
# Create a hierarchy of profile names
|
||||
profile_elements = profile['name'].split('-')
|
||||
i = 0
|
||||
while i < len(profile_elements) - 1:
|
||||
|
||||
@@ -522,7 +522,7 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
"""Helper to save data
|
||||
|
||||
Helper to save the data in self.data
|
||||
Detect if data is allready in branch and use dict_merge() to prevent that branch is overwritten.
|
||||
Detect if data is already in branch and use dict_merge() to prevent that branch is overwritten.
|
||||
|
||||
Args:
|
||||
str(instance_name): name of instance
|
||||
|
||||
@@ -92,9 +92,21 @@ DOCUMENTATION = '''
|
||||
default: proxmox_
|
||||
type: str
|
||||
want_facts:
|
||||
description: Gather LXC/QEMU configuration facts.
|
||||
description:
|
||||
- Gather LXC/QEMU configuration facts.
|
||||
- When I(want_facts) is set to C(true) more details about QEMU VM status are possible, besides the running and stopped states.
|
||||
Currently if the VM is running and it is suspended, the status will be running and the machine will be in C(running) group,
|
||||
but its actual state will be paused. See I(qemu_extended_statuses) for how to retrieve the real status.
|
||||
default: no
|
||||
type: bool
|
||||
qemu_extended_statuses:
|
||||
description:
|
||||
- Requires I(want_facts) to be set to C(true) to function. This will allow you to differentiate betweend C(paused) and C(prelaunch)
|
||||
statuses of the QEMU VMs.
|
||||
- This introduces multiple groups [prefixed with I(group_prefix)] C(prelaunch) and C(paused).
|
||||
default: no
|
||||
type: bool
|
||||
version_added: 5.1.0
|
||||
want_proxmox_nodes_ansible_host:
|
||||
version_added: 3.0.0
|
||||
description:
|
||||
@@ -400,12 +412,20 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
properties[parsed_key] = [tag.strip() for tag in stripped_value.split(",")]
|
||||
|
||||
# The first field in the agent string tells you whether the agent is enabled
|
||||
# the rest of the comma separated string is extra config for the agent
|
||||
if config == 'agent' and int(value.split(',')[0]):
|
||||
agent_iface_value = self._get_agent_network_interfaces(node, vmid, vmtype)
|
||||
if agent_iface_value:
|
||||
agent_iface_key = self.to_safe('%s%s' % (key, "_interfaces"))
|
||||
properties[agent_iface_key] = agent_iface_value
|
||||
# the rest of the comma separated string is extra config for the agent.
|
||||
# In some (newer versions of proxmox) instances it can be 'enabled=1'.
|
||||
if config == 'agent':
|
||||
agent_enabled = 0
|
||||
try:
|
||||
agent_enabled = int(value.split(',')[0])
|
||||
except ValueError:
|
||||
if value.split(',')[0] == "enabled=1":
|
||||
agent_enabled = 1
|
||||
if agent_enabled:
|
||||
agent_iface_value = self._get_agent_network_interfaces(node, vmid, vmtype)
|
||||
if agent_iface_value:
|
||||
agent_iface_key = self.to_safe('%s%s' % (key, "_interfaces"))
|
||||
properties[agent_iface_key] = agent_iface_value
|
||||
|
||||
if config == 'lxc':
|
||||
out_val = {}
|
||||
@@ -431,6 +451,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
def _get_vm_status(self, properties, node, vmid, vmtype, name):
|
||||
ret = self._get_json("%s/api2/json/nodes/%s/%s/%s/status/current" % (self.proxmox_url, node, vmtype, vmid))
|
||||
properties[self._fact('status')] = ret['status']
|
||||
if vmtype == 'qemu':
|
||||
properties[self._fact('qmpstatus')] = ret['qmpstatus']
|
||||
|
||||
def _get_vm_snapshots(self, properties, node, vmid, vmtype, name):
|
||||
ret = self._get_json("%s/api2/json/nodes/%s/%s/%s/snapshot" % (self.proxmox_url, node, vmtype, vmid))
|
||||
@@ -489,7 +511,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
name, vmid = item['name'], item['vmid']
|
||||
|
||||
# get status, config and snapshots if want_facts == True
|
||||
if self.get_option('want_facts'):
|
||||
want_facts = self.get_option('want_facts')
|
||||
if want_facts:
|
||||
self._get_vm_status(properties, node, vmid, ittype, name)
|
||||
self._get_vm_config(properties, node, vmid, ittype, name)
|
||||
self._get_vm_snapshots(properties, node, vmid, ittype, name)
|
||||
@@ -503,10 +526,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
node_type_group = self._group('%s_%s' % (node, ittype))
|
||||
self.inventory.add_child(self._group('all_' + ittype), name)
|
||||
self.inventory.add_child(node_type_group, name)
|
||||
if item['status'] == 'stopped':
|
||||
self.inventory.add_child(self._group('all_stopped'), name)
|
||||
elif item['status'] == 'running':
|
||||
self.inventory.add_child(self._group('all_running'), name)
|
||||
|
||||
item_status = item['status']
|
||||
if item_status == 'running':
|
||||
if want_facts and ittype == 'qemu' and self.get_option('qemu_extended_statuses'):
|
||||
# get more details about the status of the qemu VM
|
||||
item_status = properties.get(self._fact('qmpstatus'), item_status)
|
||||
self.inventory.add_child(self._group('all_%s' % (item_status, )), name)
|
||||
|
||||
return name
|
||||
|
||||
@@ -528,10 +554,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
def _populate(self):
|
||||
|
||||
# create common groups
|
||||
self.inventory.add_group(self._group('all_lxc'))
|
||||
self.inventory.add_group(self._group('all_qemu'))
|
||||
self.inventory.add_group(self._group('all_running'))
|
||||
self.inventory.add_group(self._group('all_stopped'))
|
||||
default_groups = ['lxc', 'qemu', 'running', 'stopped']
|
||||
|
||||
if self.get_option('qemu_extended_statuses'):
|
||||
default_groups.extend(['prelaunch', 'paused'])
|
||||
|
||||
for group in default_groups:
|
||||
self.inventory.add_group(self._group('all_%s' % (group)))
|
||||
|
||||
nodes_group = self._group('nodes')
|
||||
self.inventory.add_group(nodes_group)
|
||||
|
||||
@@ -621,6 +651,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
if proxmox_password is None and (proxmox_token_id is None or proxmox_token_secret is None):
|
||||
raise AnsibleError('You must specify either a password or both token_id and token_secret.')
|
||||
|
||||
if self.get_option('qemu_extended_statuses') and not self.get_option('want_facts'):
|
||||
raise AnsibleError('You must set want_facts to True if you want to use qemu_extended_statuses.')
|
||||
|
||||
self.cache_key = self.get_cache_key(path)
|
||||
self.use_cache = cache and self.get_option('cache')
|
||||
self.host_filters = self.get_option('filters')
|
||||
|
||||
118
plugins/lookup/bitwarden.py
Normal file
118
plugins/lookup/bitwarden.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2022, Jonathan Lung <lungj@heresjono.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 = """
|
||||
name: bitwarden
|
||||
author:
|
||||
- Jonathan Lung (@lungj) <lungj@heresjono.com>
|
||||
requirements:
|
||||
- bw (command line utility)
|
||||
- be logged into bitwarden
|
||||
short_description: Retrieve secrets from Bitwarden
|
||||
version_added: 5.4.0
|
||||
description:
|
||||
- Retrieve secrets from Bitwarden.
|
||||
options:
|
||||
_terms:
|
||||
description: Key(s) to fetch values for from login info.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
field:
|
||||
description: Field to fetch; leave unset to fetch whole response.
|
||||
type: str
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: "Get 'password' from Bitwarden record named 'a_test'"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ lookup('community.general.bitwarden', 'a_test', field='password') }}
|
||||
|
||||
- name: "Get full Bitwarden record named 'a_test'"
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ lookup('community.general.bitwarden', 'a_test') }}
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
_raw:
|
||||
description: List of requested field or JSON object of list of matches.
|
||||
type: list
|
||||
elements: raw
|
||||
"""
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
from ansible.parsing.ajson import AnsibleJSONDecoder
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
|
||||
class BitwardenException(AnsibleError):
|
||||
pass
|
||||
|
||||
|
||||
class Bitwarden(object):
|
||||
|
||||
def __init__(self, path='bw'):
|
||||
self._cli_path = path
|
||||
|
||||
@property
|
||||
def cli_path(self):
|
||||
return self._cli_path
|
||||
|
||||
@property
|
||||
def logged_in(self):
|
||||
out, err = self._run(['status'], stdin="")
|
||||
decoded = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
return decoded['status'] == 'unlocked'
|
||||
|
||||
def _run(self, args, stdin=None, expected_rc=0):
|
||||
p = Popen([self.cli_path] + args, stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
out, err = p.communicate(to_bytes(stdin))
|
||||
rc = p.wait()
|
||||
if rc != expected_rc:
|
||||
raise BitwardenException(err)
|
||||
return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict')
|
||||
|
||||
def _get_matches(self, search_value, search_field="name"):
|
||||
"""Return matching records whose search_field is equal to key.
|
||||
"""
|
||||
out, err = self._run(['list', 'items', '--search', search_value])
|
||||
|
||||
# This includes things that matched in different fields.
|
||||
initial_matches = AnsibleJSONDecoder().raw_decode(out)[0]
|
||||
|
||||
# Filter to only include results from the right field.
|
||||
return [item for item in initial_matches if item[search_field] == search_value]
|
||||
|
||||
def get_field(self, field, search_value, search_field="name"):
|
||||
"""Return a list of the specified field for records whose search_field match search_value.
|
||||
|
||||
If field is None, return the whole record for each match.
|
||||
"""
|
||||
matches = self._get_matches(search_value)
|
||||
|
||||
if field:
|
||||
return [match['login'][field] for match in matches]
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
field = self.get_option('field')
|
||||
if not _bitwarden.logged_in:
|
||||
raise AnsibleError("Not logged into Bitwarden. Run 'bw login'.")
|
||||
|
||||
return [_bitwarden.get_field(field, term) for term in terms]
|
||||
|
||||
|
||||
_bitwarden = Bitwarden()
|
||||
@@ -16,7 +16,7 @@ DOCUMENTATION = '''
|
||||
The lookup order mirrors the one from Chef, all folders in the base path are walked back looking for the following configuration
|
||||
file in order : .chef/knife.rb, ~/.chef/knife.rb, /etc/chef/client.rb"
|
||||
requirements:
|
||||
- "pychef (python library https://pychef.readthedocs.io `pip install pychef`)"
|
||||
- "pychef (L(Python library, https://pychef.readthedocs.io), C(pip install pychef))"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
|
||||
@@ -42,6 +42,15 @@ DOCUMENTATION = '''
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 3.6.0
|
||||
fail_on_error:
|
||||
description:
|
||||
- Abort execution on lookup errors.
|
||||
- The default for this option will likely change to C(true) in the future.
|
||||
The current default, C(false), is used for backwards compatibility, and will result in empty strings
|
||||
or the string C(NXDOMAIN) in the result in case of errors.
|
||||
default: false
|
||||
type: bool
|
||||
version_added: 5.4.0
|
||||
notes:
|
||||
- ALL is not a record per-se, merely the listed fields are available for any record results you retrieve in the form of a dictionary.
|
||||
- While the 'dig' lookup plugin supports anything which dnspython supports out of the box, only a subset can be converted into a dictionary.
|
||||
@@ -279,6 +288,7 @@ class LookupModule(LookupBase):
|
||||
domain = None
|
||||
qtype = 'A'
|
||||
flat = True
|
||||
fail_on_error = False
|
||||
rdclass = dns.rdataclass.from_text('IN')
|
||||
|
||||
for t in terms:
|
||||
@@ -317,6 +327,8 @@ class LookupModule(LookupBase):
|
||||
raise AnsibleError("dns lookup illegal CLASS: %s" % to_native(e))
|
||||
elif opt == 'retry_servfail':
|
||||
myres.retry_servfail = bool(arg)
|
||||
elif opt == 'fail_on_error':
|
||||
fail_on_error = bool(arg)
|
||||
|
||||
continue
|
||||
|
||||
@@ -364,16 +376,24 @@ class LookupModule(LookupBase):
|
||||
rd['class'] = dns.rdataclass.to_text(rdata.rdclass)
|
||||
|
||||
ret.append(rd)
|
||||
except Exception as e:
|
||||
ret.append(str(e))
|
||||
except Exception as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append(str(err))
|
||||
|
||||
except dns.resolver.NXDOMAIN:
|
||||
except dns.resolver.NXDOMAIN as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append('NXDOMAIN')
|
||||
except dns.resolver.NoAnswer:
|
||||
except dns.resolver.NoAnswer as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append("")
|
||||
except dns.resolver.Timeout:
|
||||
except dns.resolver.Timeout as err:
|
||||
if fail_on_error:
|
||||
raise AnsibleError("Lookup failed: %s" % str(err))
|
||||
ret.append('')
|
||||
except dns.exception.DNSException as e:
|
||||
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(e))
|
||||
except dns.exception.DNSException as err:
|
||||
raise AnsibleError("dns.resolver unhandled exception %s" % to_native(err))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -122,6 +122,7 @@ class LookupModule(LookupBase):
|
||||
"tenant": self.get_option("tenant"),
|
||||
"client_id": self.get_option("client_id"),
|
||||
"client_secret": self.get_option("client_secret"),
|
||||
"tld": self.get_option("tld"),
|
||||
"url_template": self.get_option("url_template"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ DOCUMENTATION = '''
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name : output secrets to screen (BAD IDEA)
|
||||
- name: output secrets to screen (BAD IDEA)
|
||||
ansible.builtin.debug:
|
||||
msg: "Password: {{item}}"
|
||||
with_community.general.keyring:
|
||||
|
||||
@@ -11,21 +11,24 @@ DOCUMENTATION = '''
|
||||
- Andrew Zenk (!UNKNOWN) <azenk@umn.edu>
|
||||
requirements:
|
||||
- lpass (command line utility)
|
||||
- must have already logged into lastpass
|
||||
short_description: fetch data from lastpass
|
||||
- must have already logged into LastPass
|
||||
short_description: fetch data from LastPass
|
||||
description:
|
||||
- use the lpass command line utility to fetch specific fields from lastpass
|
||||
- Use the lpass command line utility to fetch specific fields from LastPass.
|
||||
options:
|
||||
_terms:
|
||||
description: key from which you want to retrieve the field
|
||||
required: True
|
||||
description: Key from which you want to retrieve the field.
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
field:
|
||||
description: field to return from lastpass
|
||||
description: Field to return from LastPass.
|
||||
default: 'password'
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: get 'custom_field' from lastpass entry 'entry-name'
|
||||
- name: get 'custom_field' from LastPass entry 'entry-name'
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ lookup('community.general.lastpass', 'entry-name', field='custom_field') }}"
|
||||
"""
|
||||
@@ -88,12 +91,14 @@ class LPass(object):
|
||||
class LookupModule(LookupBase):
|
||||
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
field = self.get_option('field')
|
||||
|
||||
lp = LPass()
|
||||
|
||||
if not lp.logged_in:
|
||||
raise AnsibleError("Not logged into lastpass: please run 'lpass login' first")
|
||||
raise AnsibleError("Not logged into LastPass: please run 'lpass login' first")
|
||||
|
||||
field = kwargs.get('field', 'password')
|
||||
values = []
|
||||
for term in terms:
|
||||
values.append(lp.get_field(term, field))
|
||||
|
||||
@@ -21,8 +21,14 @@ DOCUMENTATION = '''
|
||||
description: query key.
|
||||
required: True
|
||||
passwordstore:
|
||||
description: location of the password store.
|
||||
default: '~/.password-store'
|
||||
description:
|
||||
- Location of the password store.
|
||||
- 'The value is decided by checking the following in order:'
|
||||
- If set, this value is used.
|
||||
- If C(directory) is set, that value will be used.
|
||||
- If I(backend=pass), then C(~/.password-store) is used.
|
||||
- If I(backend=gopass), then the C(path) field in C(~/.config/gopass/config.yml) is used,
|
||||
falling back to C(~/.local/share/gopass/stores/root) if not defined.
|
||||
directory:
|
||||
description: The directory of the password store.
|
||||
env:
|
||||
@@ -106,6 +112,22 @@ DOCUMENTATION = '''
|
||||
type: str
|
||||
default: 15m
|
||||
version_added: 4.5.0
|
||||
backend:
|
||||
description:
|
||||
- Specify which backend to use.
|
||||
- Defaults to C(pass), passwordstore.org's original pass utility.
|
||||
- C(gopass) support is incomplete.
|
||||
ini:
|
||||
- section: passwordstore_lookup
|
||||
key: backend
|
||||
vars:
|
||||
- name: passwordstore_backend
|
||||
type: str
|
||||
default: pass
|
||||
choices:
|
||||
- pass
|
||||
- gopass
|
||||
version_added: 5.2.0
|
||||
'''
|
||||
EXAMPLES = """
|
||||
ansible.cfg: |
|
||||
@@ -231,6 +253,24 @@ def check_output2(*popenargs, **kwargs):
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def __init__(self, loader=None, templar=None, **kwargs):
|
||||
|
||||
super(LookupModule, self).__init__(loader, templar, **kwargs)
|
||||
self.realpass = None
|
||||
|
||||
def is_real_pass(self):
|
||||
if self.realpass is None:
|
||||
try:
|
||||
passoutput = to_text(
|
||||
check_output2([self.pass_cmd, "--version"], env=self.env),
|
||||
errors='surrogate_or_strict'
|
||||
)
|
||||
self.realpass = 'pass: the standard unix password manager' in passoutput
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(e)
|
||||
|
||||
return self.realpass
|
||||
|
||||
def parse_params(self, term):
|
||||
# I went with the "traditional" param followed with space separated KV pairs.
|
||||
# Waiting for final implementation of lookup parameter parsing.
|
||||
@@ -270,10 +310,12 @@ class LookupModule(LookupBase):
|
||||
self.env = os.environ.copy()
|
||||
self.env['LANGUAGE'] = 'C' # make sure to get errors in English as required by check_output2
|
||||
|
||||
# Set PASSWORD_STORE_DIR
|
||||
if os.path.isdir(self.paramvals['directory']):
|
||||
if self.backend == 'gopass':
|
||||
self.env['GOPASS_NO_REMINDER'] = "YES"
|
||||
elif os.path.isdir(self.paramvals['directory']):
|
||||
# Set PASSWORD_STORE_DIR
|
||||
self.env['PASSWORD_STORE_DIR'] = self.paramvals['directory']
|
||||
else:
|
||||
elif self.is_real_pass():
|
||||
raise AnsibleError('Passwordstore directory \'{0}\' does not exist'.format(self.paramvals['directory']))
|
||||
|
||||
# Set PASSWORD_STORE_UMASK if umask is set
|
||||
@@ -288,7 +330,8 @@ class LookupModule(LookupBase):
|
||||
def check_pass(self):
|
||||
try:
|
||||
self.passoutput = to_text(
|
||||
check_output2(["pass", "show", self.passname], env=self.env),
|
||||
check_output2([self.pass_cmd, 'show'] +
|
||||
[self.passname], env=self.env),
|
||||
errors='surrogate_or_strict'
|
||||
).splitlines()
|
||||
self.password = self.passoutput[0]
|
||||
@@ -302,8 +345,10 @@ class LookupModule(LookupBase):
|
||||
if ':' in line:
|
||||
name, value = line.split(':', 1)
|
||||
self.passdict[name.strip()] = value.strip()
|
||||
if os.path.isfile(os.path.join(self.paramvals['directory'], self.passname + ".gpg")):
|
||||
# Only accept password as found, if there a .gpg file for it (might be a tree node otherwise)
|
||||
if (self.backend == 'gopass' or
|
||||
os.path.isfile(os.path.join(self.paramvals['directory'], self.passname + ".gpg"))
|
||||
or not self.is_real_pass()):
|
||||
# When using real pass, only accept password as found if there is a .gpg file for it (might be a tree node otherwise)
|
||||
return True
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
# 'not in password store' is the expected error if a password wasn't found
|
||||
@@ -339,7 +384,7 @@ class LookupModule(LookupBase):
|
||||
if self.paramvals['backup']:
|
||||
msg += "lookup_pass: old password was {0} (Updated on {1})\n".format(self.password, datetime)
|
||||
try:
|
||||
check_output2(['pass', 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(e)
|
||||
return newpass
|
||||
@@ -351,7 +396,7 @@ class LookupModule(LookupBase):
|
||||
datetime = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
msg = newpass + '\n' + "lookup_pass: First generated by ansible on {0}\n".format(datetime)
|
||||
try:
|
||||
check_output2(['pass', 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
check_output2([self.pass_cmd, 'insert', '-f', '-m', self.passname], input=msg, env=self.env)
|
||||
except (subprocess.CalledProcessError) as e:
|
||||
raise AnsibleError(e)
|
||||
return newpass
|
||||
@@ -380,17 +425,30 @@ class LookupModule(LookupBase):
|
||||
yield
|
||||
|
||||
def setup(self, variables):
|
||||
self.backend = self.get_option('backend')
|
||||
self.pass_cmd = self.backend # pass and gopass are commands as well
|
||||
self.locked = None
|
||||
timeout = self.get_option('locktimeout')
|
||||
if not re.match('^[0-9]+[smh]$', timeout):
|
||||
raise AnsibleError("{0} is not a correct value for locktimeout".format(timeout))
|
||||
unit_to_seconds = {"s": 1, "m": 60, "h": 3600}
|
||||
self.lock_timeout = int(timeout[:-1]) * unit_to_seconds[timeout[-1]]
|
||||
|
||||
directory = variables.get('passwordstore', os.environ.get('PASSWORD_STORE_DIR', None))
|
||||
|
||||
if directory is None:
|
||||
if self.backend == 'gopass':
|
||||
try:
|
||||
with open(os.path.expanduser('~/.config/gopass/config.yml')) as f:
|
||||
directory = yaml.safe_load(f)['path']
|
||||
except (FileNotFoundError, KeyError, yaml.YAMLError):
|
||||
directory = os.path.expanduser('~/.local/share/gopass/stores/root')
|
||||
else:
|
||||
directory = os.path.expanduser('~/.password-store')
|
||||
|
||||
self.paramvals = {
|
||||
'subkey': 'password',
|
||||
'directory': variables.get('passwordstore', os.environ.get(
|
||||
'PASSWORD_STORE_DIR',
|
||||
os.path.expanduser('~/.password-store'))),
|
||||
'directory': directory,
|
||||
'create': False,
|
||||
'returnall': False,
|
||||
'overwrite': False,
|
||||
@@ -402,6 +460,7 @@ class LookupModule(LookupBase):
|
||||
}
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
self.setup(variables)
|
||||
result = []
|
||||
|
||||
|
||||
@@ -3,51 +3,7 @@
|
||||
# This particular file snippet, and this file snippet only, is based on
|
||||
# Lib/posixpath.py of cpython
|
||||
# It is licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
#
|
||||
# 1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
# ("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
# otherwise using this software ("Python") in source or binary form and
|
||||
# its associated documentation.
|
||||
#
|
||||
# 2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
# grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
# analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
# distribute, and otherwise use Python alone or in any derivative version,
|
||||
# provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
# i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved"
|
||||
# are retained in Python alone or in any derivative version prepared by Licensee.
|
||||
#
|
||||
# 3. In the event Licensee prepares a derivative work that is based on
|
||||
# or incorporates Python or any part thereof, and wants to make
|
||||
# the derivative work available to others as provided herein, then
|
||||
# Licensee hereby agrees to include in any such work a brief summary of
|
||||
# the changes made to Python.
|
||||
#
|
||||
# 4. PSF is making Python available to Licensee on an "AS IS"
|
||||
# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
# INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
#
|
||||
# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
#
|
||||
# 6. This License Agreement will automatically terminate upon a material
|
||||
# breach of its terms and conditions.
|
||||
#
|
||||
# 7. Nothing in this License Agreement shall be deemed to create any
|
||||
# relationship of agency, partnership, or joint venture between PSF and
|
||||
# Licensee. This License Agreement does not grant permission to use PSF
|
||||
# trademarks or trade name in a trademark sense to endorse or promote
|
||||
# products or services of Licensee, or any third party.
|
||||
#
|
||||
# 8. By copying, installing or otherwise using Python, Licensee
|
||||
# agrees to be bound by the terms and conditions of this License
|
||||
# Agreement.
|
||||
# (See PSF-license.txt in this collection)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -191,13 +191,13 @@ class CmdRunner(object):
|
||||
environ_update = {}
|
||||
self.environ_update = environ_update
|
||||
|
||||
self.command[0] = module.get_bin_path(command[0], opt_dirs=path_prefix, required=True)
|
||||
self.command[0] = module.get_bin_path(self.command[0], opt_dirs=path_prefix, required=True)
|
||||
|
||||
for mod_param_name, spec in iteritems(module.argument_spec):
|
||||
if mod_param_name not in self.arg_formats:
|
||||
self.arg_formats[mod_param_name] = _Format.as_default_type(spec['type'], mod_param_name)
|
||||
|
||||
def context(self, args_order=None, output_process=None, ignore_value_none=True, **kwargs):
|
||||
def __call__(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs):
|
||||
if output_process is None:
|
||||
output_process = _process_as_is
|
||||
if args_order is None:
|
||||
@@ -209,18 +209,25 @@ class CmdRunner(object):
|
||||
return _CmdRunnerContext(runner=self,
|
||||
args_order=args_order,
|
||||
output_process=output_process,
|
||||
ignore_value_none=ignore_value_none, **kwargs)
|
||||
ignore_value_none=ignore_value_none,
|
||||
check_mode_skip=check_mode_skip,
|
||||
check_mode_return=check_mode_return, **kwargs)
|
||||
|
||||
def has_arg_format(self, arg):
|
||||
return arg in self.arg_formats
|
||||
|
||||
# not decided whether to keep it or not, but if deprecating it will happen in a farther future.
|
||||
context = __call__
|
||||
|
||||
|
||||
class _CmdRunnerContext(object):
|
||||
def __init__(self, runner, args_order, output_process, ignore_value_none, **kwargs):
|
||||
def __init__(self, runner, args_order, output_process, ignore_value_none, check_mode_skip, check_mode_return, **kwargs):
|
||||
self.runner = runner
|
||||
self.args_order = tuple(args_order)
|
||||
self.output_process = output_process
|
||||
self.ignore_value_none = ignore_value_none
|
||||
self.check_mode_skip = check_mode_skip
|
||||
self.check_mode_return = check_mode_return
|
||||
self.run_command_args = dict(kwargs)
|
||||
|
||||
self.environ_update = runner.environ_update
|
||||
@@ -260,6 +267,8 @@ class _CmdRunnerContext(object):
|
||||
except Exception as e:
|
||||
raise FormatError(arg_name, value, runner.arg_formats[arg_name], e)
|
||||
|
||||
if self.check_mode_skip and module.check_mode:
|
||||
return self.check_mode_return
|
||||
results = module.run_command(self.cmd, **self.run_command_args)
|
||||
self.results_rc, self.results_out, self.results_err = results
|
||||
self.results_processed = self.output_process(*results)
|
||||
@@ -288,4 +297,12 @@ class _CmdRunnerContext(object):
|
||||
return False
|
||||
|
||||
|
||||
fmt = _Format()
|
||||
cmd_runner_fmt = _Format()
|
||||
|
||||
#
|
||||
# The fmt form is deprecated and will be removed in community.general 7.0.0
|
||||
# Please use:
|
||||
# cmd_runner_fmt
|
||||
# Or, to retain the same effect, use:
|
||||
# from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt as fmt
|
||||
fmt = cmd_runner_fmt
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2019 Gregory Thiemonge <gregory.thiemonge@gmail.com>
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
26
plugins/module_utils/gconftool2.py
Normal file
26
plugins/module_utils/gconftool2.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2022, Alexei Znamensky <russoz@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 ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt
|
||||
|
||||
|
||||
def gconftool2_runner(module, **kwargs):
|
||||
return CmdRunner(
|
||||
module,
|
||||
command='gconftool-2',
|
||||
arg_formats=dict(
|
||||
key=fmt.as_list(),
|
||||
value_type=fmt.as_opt_val("--type"),
|
||||
value=fmt.as_list(),
|
||||
direct=fmt.as_bool("--direct"),
|
||||
config_source=fmt.as_opt_val("--config-source"),
|
||||
get=fmt.as_bool("--get"),
|
||||
set_arg=fmt.as_bool("--set"),
|
||||
unset=fmt.as_bool("--unset"),
|
||||
),
|
||||
**kwargs
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c), Google Inc, 2017
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or
|
||||
# Simplified BSD License (see simplified_bsd.txt or
|
||||
# https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
@@ -351,7 +351,7 @@ def wait_to_finish(target, pending, refresh, timeout, min_interval=1, delay=3):
|
||||
|
||||
if pending and status not in pending:
|
||||
raise HwcModuleException(
|
||||
"unexpect status(%s) occured" % status)
|
||||
"unexpect status(%s) occurred" % status)
|
||||
|
||||
if not is_last_time:
|
||||
wait *= 2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright (C) 2018 IBM CORPORATION
|
||||
# Author(s): Tzur Eliyahu <tzure@il.ibm.com>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -104,6 +104,7 @@ def keycloak_argument_spec():
|
||||
validate_certs=dict(type='bool', default=True),
|
||||
connection_timeout=dict(type='int', default=10),
|
||||
token=dict(type='str', no_log=True),
|
||||
http_agent=dict(type='str', default='Ansible'),
|
||||
)
|
||||
|
||||
|
||||
@@ -123,6 +124,7 @@ def get_token(module_params):
|
||||
"""
|
||||
token = module_params.get('token')
|
||||
base_url = module_params.get('auth_keycloak_url')
|
||||
http_agent = module_params.get('http_agent')
|
||||
|
||||
if not base_url.lower().startswith(('http', 'https')):
|
||||
raise KeycloakError("auth_url '%s' should either start with 'http' or 'https'." % base_url)
|
||||
@@ -149,7 +151,7 @@ def get_token(module_params):
|
||||
(k, v) for k, v in temp_payload.items() if v is not None)
|
||||
try:
|
||||
r = json.loads(to_native(open_url(auth_url, method='POST',
|
||||
validate_certs=validate_certs, timeout=connection_timeout,
|
||||
validate_certs=validate_certs, http_agent=http_agent, timeout=connection_timeout,
|
||||
data=urlencode(payload)).read()))
|
||||
except ValueError as e:
|
||||
raise KeycloakError(
|
||||
@@ -233,6 +235,7 @@ class KeycloakAPI(object):
|
||||
self.validate_certs = self.module.params.get('validate_certs')
|
||||
self.connection_timeout = self.module.params.get('connection_timeout')
|
||||
self.restheaders = connection_header
|
||||
self.http_agent = self.module.params.get('http_agent')
|
||||
|
||||
def get_realm_info_by_id(self, realm='master'):
|
||||
""" Obtain realm public info by id
|
||||
@@ -243,7 +246,8 @@ class KeycloakAPI(object):
|
||||
realm_info_url = URL_REALM_INFO.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(realm_info_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(realm_info_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -268,7 +272,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(realm_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(realm_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -293,7 +297,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(realm_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(realm_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(realmrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update realm %s: %s' % (realm, str(e)),
|
||||
@@ -307,7 +311,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALMS.format(url=self.baseurl)
|
||||
|
||||
try:
|
||||
return open_url(realm_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(realm_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(realmrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create realm %s: %s' % (realmrep['id'], str(e)),
|
||||
@@ -322,7 +326,7 @@ class KeycloakAPI(object):
|
||||
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(realm_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(realm_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete realm %s: %s' % (realm, str(e)),
|
||||
@@ -340,7 +344,8 @@ class KeycloakAPI(object):
|
||||
clientlist_url += '?clientId=%s' % filter
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientlist_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(clientlist_url, http_agent=self.http_agent, method='GET', headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
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 clients for realm %s: %s'
|
||||
@@ -371,7 +376,8 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(client_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(client_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -410,7 +416,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(client_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(client_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update client %s in realm %s: %s'
|
||||
@@ -425,7 +431,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENTS.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(client_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(client_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create client %s in realm %s: %s'
|
||||
@@ -441,7 +447,7 @@ class KeycloakAPI(object):
|
||||
client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(client_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(client_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete client %s in realm %s: %s'
|
||||
@@ -456,7 +462,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
client_roles_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(client_roles_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(client_roles_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch rolemappings for client %s in realm %s: %s"
|
||||
@@ -488,7 +495,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
rolemappings = json.loads(to_native(open_url(rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
for role in rolemappings:
|
||||
if rid == role['id']:
|
||||
@@ -508,7 +516,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS_AVAILABLE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -524,7 +533,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS_COMPOSITE.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(available_rolemappings_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -541,7 +551,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
open_url(available_rolemappings_url, method="POST", headers=self.restheaders, data=json.dumps(role_rep),
|
||||
open_url(available_rolemappings_url, method="POST", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep),
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -558,7 +568,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
available_rolemappings_url = URL_CLIENT_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid)
|
||||
try:
|
||||
open_url(available_rolemappings_url, method="DELETE", headers=self.restheaders,
|
||||
open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders,
|
||||
validate_certs=self.validate_certs, timeout=self.connection_timeout)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s"
|
||||
@@ -573,7 +583,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATES.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
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 client templates for realm %s: %s'
|
||||
@@ -592,7 +602,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATE.format(url=self.baseurl, id=id, realm=realm)
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except ValueError as e:
|
||||
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain client templates %s for realm %s: %s'
|
||||
@@ -638,7 +648,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATE.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clienttrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update client template %s in realm %s: %s'
|
||||
@@ -653,7 +663,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATES.format(url=self.baseurl, realm=realm)
|
||||
|
||||
try:
|
||||
return open_url(url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clienttrep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create client template %s in realm %s: %s'
|
||||
@@ -669,7 +679,7 @@ class KeycloakAPI(object):
|
||||
url = URL_CLIENTTEMPLATE.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete client template %s in realm %s: %s'
|
||||
@@ -686,7 +696,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(clientscopes_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of clientscopes in realm %s: %s"
|
||||
@@ -703,7 +714,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(clientscope_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(clientscope_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -748,7 +760,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
clientscopes_url = URL_CLIENTSCOPES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(clientscopes_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(clientscopes_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientscoperep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create clientscope %s in realm %s: %s"
|
||||
@@ -763,7 +775,7 @@ class KeycloakAPI(object):
|
||||
clientscope_url = URL_CLIENTSCOPE.format(url=self.baseurl, realm=realm, id=clientscoperep['id'])
|
||||
|
||||
try:
|
||||
return open_url(clientscope_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(clientscope_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(clientscoperep), validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
@@ -801,7 +813,7 @@ class KeycloakAPI(object):
|
||||
# should have a good cid by here.
|
||||
clientscope_url = URL_CLIENTSCOPE.format(realm=realm, id=cid, url=self.baseurl)
|
||||
try:
|
||||
return open_url(clientscope_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(clientscope_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
@@ -819,7 +831,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(id=cid, url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(protocolmappers_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(protocolmappers_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of protocolmappers in realm %s: %s"
|
||||
@@ -838,7 +851,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=pid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(protocolmapper_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(protocolmapper_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -885,7 +899,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
protocolmappers_url = URL_CLIENTSCOPE_PROTOCOLMAPPERS.format(url=self.baseurl, id=cid, realm=realm)
|
||||
try:
|
||||
return open_url(protocolmappers_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(protocolmappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper_rep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create protocolmapper %s in realm %s: %s"
|
||||
@@ -901,7 +915,7 @@ class KeycloakAPI(object):
|
||||
protocolmapper_url = URL_CLIENTSCOPE_PROTOCOLMAPPER.format(url=self.baseurl, realm=realm, id=cid, mapper_id=mapper_rep['id'])
|
||||
|
||||
try:
|
||||
return open_url(protocolmapper_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(protocolmapper_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper_rep), validate_certs=self.validate_certs)
|
||||
|
||||
except Exception as e:
|
||||
@@ -918,7 +932,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not fetch list of groups in realm %s: %s"
|
||||
@@ -935,7 +950,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
groups_url = URL_GROUP.format(url=self.baseurl, realm=realm, groupid=gid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(groups_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
|
||||
except HTTPError as e:
|
||||
@@ -981,7 +997,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(groups_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(groups_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Could not create group %s in realm %s: %s"
|
||||
@@ -996,7 +1012,7 @@ class KeycloakAPI(object):
|
||||
group_url = URL_GROUP.format(url=self.baseurl, realm=realm, groupid=grouprep['id'])
|
||||
|
||||
try:
|
||||
return open_url(group_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(group_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(grouprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update group %s in realm %s: %s'
|
||||
@@ -1033,7 +1049,7 @@ class KeycloakAPI(object):
|
||||
# should have a good groupid by here.
|
||||
group_url = URL_GROUP.format(realm=realm, groupid=groupid, url=self.baseurl)
|
||||
try:
|
||||
return open_url(group_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(group_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to delete group %s: %s" % (groupid, str(e)))
|
||||
@@ -1046,7 +1062,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
rolelist_url = URL_REALM_ROLES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
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 roles for realm %s: %s'
|
||||
@@ -1064,7 +1081,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
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, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(role_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1084,7 +1101,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
roles_url = URL_REALM_ROLES.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(roles_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s in realm %s: %s'
|
||||
@@ -1098,7 +1115,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
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, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update role %s in realm %s: %s'
|
||||
@@ -1112,7 +1129,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
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, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete role %s in realm %s: %s'
|
||||
@@ -1131,7 +1148,8 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
rolelist_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(rolelist_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
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 roles for client %s in realm %s: %s'
|
||||
@@ -1155,7 +1173,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
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, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(role_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1181,7 +1199,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
roles_url = URL_CLIENT_ROLES.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return open_url(roles_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(roles_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create role %s for client %s in realm %s: %s'
|
||||
@@ -1201,7 +1219,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
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, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(rolerep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update role %s for client %s in realm %s: %s'
|
||||
@@ -1220,7 +1238,7 @@ class KeycloakAPI(object):
|
||||
% (clientid, realm))
|
||||
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, timeout=self.connection_timeout,
|
||||
return open_url(role_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete role %s for client %s in realm %s: %s'
|
||||
@@ -1237,7 +1255,8 @@ class KeycloakAPI(object):
|
||||
authentication_flow = {}
|
||||
# Check if the authentication flow exists on the Keycloak serveraders
|
||||
authentications = json.load(open_url(URL_AUTHENTICATION_FLOWS.format(url=self.baseurl, realm=realm), method='GET',
|
||||
headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs))
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout, validate_certs=self.validate_certs))
|
||||
for authentication in authentications:
|
||||
if authentication["alias"] == alias:
|
||||
authentication_flow = authentication
|
||||
@@ -1256,7 +1275,7 @@ class KeycloakAPI(object):
|
||||
flow_url = URL_AUTHENTICATION_FLOW.format(url=self.baseurl, realm=realm, id=id)
|
||||
|
||||
try:
|
||||
return open_url(flow_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(flow_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not delete authentication flow %s in realm %s: %s'
|
||||
@@ -1279,7 +1298,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
copyfrom=quote(config["copyFrom"])),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
data=json.dumps(new_name),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
@@ -1288,7 +1307,7 @@ class KeycloakAPI(object):
|
||||
URL_AUTHENTICATION_FLOWS.format(url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
for flow in flow_list:
|
||||
@@ -1318,7 +1337,7 @@ class KeycloakAPI(object):
|
||||
url=self.baseurl,
|
||||
realm=realm),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
data=json.dumps(new_flow),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
@@ -1328,7 +1347,7 @@ class KeycloakAPI(object):
|
||||
url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
for flow in flow_list:
|
||||
@@ -1353,7 +1372,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='PUT',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
data=json.dumps(updatedExec),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
@@ -1374,7 +1393,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
data=json.dumps(authenticationConfig),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
@@ -1399,7 +1418,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
data=json.dumps(newSubFlow),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
@@ -1423,7 +1442,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(flowAlias)),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
data=json.dumps(newExec),
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
@@ -1447,7 +1466,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
elif diff < 0:
|
||||
@@ -1458,7 +1477,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
@@ -1480,7 +1499,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
flowalias=quote(config["alias"])),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
for execution in executions:
|
||||
@@ -1493,7 +1512,7 @@ class KeycloakAPI(object):
|
||||
realm=realm,
|
||||
id=execConfigId),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
execution["authenticationConfig"] = execConfig
|
||||
@@ -1509,7 +1528,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idps_url = URL_IDENTITY_PROVIDERS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return json.loads(to_native(open_url(idps_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(idps_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
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 identity providers for realm %s: %s'
|
||||
@@ -1526,7 +1545,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idp_url = URL_IDENTITY_PROVIDER.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return json.loads(to_native(open_url(idp_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(idp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1546,7 +1565,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idps_url = URL_IDENTITY_PROVIDERS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
return open_url(idps_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(idps_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(idprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create identity provider %s in realm %s: %s'
|
||||
@@ -1560,7 +1579,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idp_url = URL_IDENTITY_PROVIDER.format(url=self.baseurl, realm=realm, alias=idprep['alias'])
|
||||
try:
|
||||
return open_url(idp_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(idp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(idprep), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update identity provider %s in realm %s: %s'
|
||||
@@ -1573,7 +1592,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
idp_url = URL_IDENTITY_PROVIDER.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return open_url(idp_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(idp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete identity provider %s in realm %s: %s'
|
||||
@@ -1587,7 +1606,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mappers_url = URL_IDENTITY_PROVIDER_MAPPERS.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return json.loads(to_native(open_url(mappers_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(mappers_url, method='GET', http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
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 identity provider mappers for idp %s in realm %s: %s'
|
||||
@@ -1605,7 +1625,8 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mapper_url = URL_IDENTITY_PROVIDER_MAPPER.format(url=self.baseurl, realm=realm, alias=alias, id=mid)
|
||||
try:
|
||||
return json.loads(to_native(open_url(mapper_url, method="GET", headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(mapper_url, method="GET", http_agent=self.http_agent, headers=self.restheaders,
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1626,7 +1647,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mappers_url = URL_IDENTITY_PROVIDER_MAPPERS.format(url=self.baseurl, realm=realm, alias=alias)
|
||||
try:
|
||||
return open_url(mappers_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(mappers_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create identity provider mapper %s for idp %s in realm %s: %s'
|
||||
@@ -1641,7 +1662,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mapper_url = URL_IDENTITY_PROVIDER_MAPPER.format(url=self.baseurl, realm=realm, alias=alias, id=mapper['id'])
|
||||
try:
|
||||
return open_url(mapper_url, method='PUT', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(mapper_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
data=json.dumps(mapper), validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not update mapper %s for identity provider %s in realm %s: %s'
|
||||
@@ -1655,7 +1676,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
mapper_url = URL_IDENTITY_PROVIDER_MAPPER.format(url=self.baseurl, realm=realm, alias=alias, id=mid)
|
||||
try:
|
||||
return open_url(mapper_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(mapper_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete mapper %s for identity provider %s in realm %s: %s'
|
||||
@@ -1672,7 +1693,7 @@ class KeycloakAPI(object):
|
||||
comps_url += '?%s' % filter
|
||||
|
||||
try:
|
||||
return json.loads(to_native(open_url(comps_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(comps_url, method='GET', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
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'
|
||||
@@ -1689,7 +1710,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
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, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except HTTPError as e:
|
||||
if e.code == 404:
|
||||
@@ -1709,13 +1730,13 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
comps_url = URL_COMPONENTS.format(url=self.baseurl, realm=realm)
|
||||
try:
|
||||
resp = open_url(comps_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
resp = open_url(comps_url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
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, timeout=self.connection_timeout,
|
||||
return json.loads(to_native(open_url(comp_url, method="GET", http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs).read()))
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Could not create component in realm %s: %s'
|
||||
@@ -1732,7 +1753,7 @@ class KeycloakAPI(object):
|
||||
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, timeout=self.connection_timeout,
|
||||
return open_url(comp_url, method='PUT', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
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'
|
||||
@@ -1745,7 +1766,7 @@ class KeycloakAPI(object):
|
||||
"""
|
||||
comp_url = URL_COMPONENT.format(url=self.baseurl, realm=realm, id=cid)
|
||||
try:
|
||||
return open_url(comp_url, method='DELETE', headers=self.restheaders, timeout=self.connection_timeout,
|
||||
return open_url(comp_url, method='DELETE', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg='Unable to delete component %s in realm %s: %s'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 Hewlett Packard Enterprise, Inc. All rights reserved.
|
||||
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2016 Thomas Krahn (@Nosmoht)
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c), Luke Murphy @decentral1se
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c) 2018, Simon Weald <ansible@simonweald.com>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
@@ -31,6 +31,10 @@ class ModuleHelperBase(object):
|
||||
def diff_mode(self):
|
||||
return self.module._diff
|
||||
|
||||
@property
|
||||
def verbosity(self):
|
||||
return self.module._verbosity
|
||||
|
||||
def do_raise(self, *args, **kwargs):
|
||||
raise _MHE(*args, **kwargs)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
class ModuleHelperException(Exception):
|
||||
@staticmethod
|
||||
def _get_remove(key, kwargs):
|
||||
if key in kwargs:
|
||||
result = kwargs[key]
|
||||
del kwargs[key]
|
||||
return result
|
||||
return None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.msg = self._get_remove('msg', kwargs) or "Module failed with exception: {0}".format(self)
|
||||
self.update_output = self._get_remove('update_output', kwargs) or {}
|
||||
def __init__(self, msg, update_output=None, *args, **kwargs):
|
||||
self.msg = to_native(msg or "Module failed with exception: {0}".format(self))
|
||||
if update_output is None:
|
||||
update_output = {}
|
||||
self.update_output = update_output
|
||||
super(ModuleHelperException, self).__init__(*args)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2020, Alexei Znamensky <russoz@gmail.com>
|
||||
# Copyright: (c) 2020, Ansible Project
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (2016-2017) Hewlett Packard Enterprise Development LP
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Copyright 2018 www.privaz.io Valletech AB
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
@@ -83,12 +83,12 @@ class OpenNebulaModule:
|
||||
if self.module.params.get("api_username"):
|
||||
username = self.module.params.get("api_username")
|
||||
else:
|
||||
self.fail("Either api_username or the environment vairable ONE_USERNAME must be provided")
|
||||
self.fail("Either api_username or the environment variable ONE_USERNAME must be provided")
|
||||
|
||||
if self.module.params.get("api_password"):
|
||||
password = self.module.params.get("api_password")
|
||||
else:
|
||||
self.fail("Either api_password or the environment vairable ONE_PASSWORD must be provided")
|
||||
self.fail("Either api_password or the environment variable ONE_PASSWORD must be provided")
|
||||
|
||||
session = "%s:%s" % (username, password)
|
||||
|
||||
|
||||
@@ -691,7 +691,7 @@ def check_and_create_resource(
|
||||
:param model: Model used to create a resource.
|
||||
:param exclude_attributes: The attributes which should not be used to distinguish the resource. e.g. display_name,
|
||||
dns_label.
|
||||
:param dead_states: List of states which can't transition to any of the usable states of the resource. This deafults
|
||||
:param dead_states: List of states which can't transition to any of the usable states of the resource. This defaults
|
||||
to ["TERMINATING", "TERMINATED", "FAULTY", "FAILED", "DELETING", "DELETED", "UNKNOWN_ENUM_VALUE"]
|
||||
:param default_attribute_values: A dictionary containing default values for attributes.
|
||||
:return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True}
|
||||
@@ -1189,7 +1189,7 @@ def are_dicts_equal(
|
||||
|
||||
|
||||
def should_dict_attr_be_excluded(map_option_name, option_key, exclude_list):
|
||||
"""An entry for the Exclude list for excluding a map's key is specifed as a dict with the map option name as the
|
||||
"""An entry for the Exclude list for excluding a map's key is specified as a dict with the map option name as the
|
||||
key, and the value as a list of keys to be excluded within that map. For example, if the keys "k1" and "k2" of a map
|
||||
option named "m1" needs to be excluded, the exclude list must have an entry {'m1': ['k1','k2']} """
|
||||
for exclude_item in exclude_list:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# to the complete work.
|
||||
#
|
||||
# Copyright (c), Simon Dodsley <simon@purestorage.com>,2017
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c), Michael DeHaan <michael.dehaan@gmail.com>, 2012-2013
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017-2018 Dell EMC Inc.
|
||||
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# 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
|
||||
@@ -1888,14 +1888,13 @@ class RedfishUtils(object):
|
||||
for property in properties:
|
||||
if property in data:
|
||||
chassis_power_result[property] = data[property]
|
||||
else:
|
||||
return {'ret': False, 'msg': 'Key PowerControl not found.'}
|
||||
chassis_power_results.append(chassis_power_result)
|
||||
else:
|
||||
return {'ret': False, 'msg': 'Key Power not found.'}
|
||||
|
||||
result['entries'] = chassis_power_results
|
||||
return result
|
||||
if len(chassis_power_results) > 0:
|
||||
result['entries'] = chassis_power_results
|
||||
return result
|
||||
else:
|
||||
return {'ret': False, 'msg': 'Power information not found.'}
|
||||
|
||||
def get_chassis_thermals(self):
|
||||
result = {}
|
||||
@@ -2187,9 +2186,8 @@ class RedfishUtils(object):
|
||||
else:
|
||||
if media_match_strict:
|
||||
continue
|
||||
# if ejected, 'Inserted' should be False and 'ImageName' cleared
|
||||
if (not data.get('Inserted', False) and
|
||||
not data.get('ImageName')):
|
||||
# if ejected, 'Inserted' should be False
|
||||
if (not data.get('Inserted', False)):
|
||||
return uri, data
|
||||
return None, None
|
||||
|
||||
@@ -2225,7 +2223,7 @@ class RedfishUtils(object):
|
||||
return resources, headers
|
||||
|
||||
@staticmethod
|
||||
def _insert_virt_media_payload(options, param_map, data, ai):
|
||||
def _insert_virt_media_payload(options, param_map, data, ai, image_only=False):
|
||||
payload = {
|
||||
'Image': options.get('image_url')
|
||||
}
|
||||
@@ -2239,6 +2237,12 @@ class RedfishUtils(object):
|
||||
options.get(option), option,
|
||||
allowable)}
|
||||
payload[param] = options.get(option)
|
||||
|
||||
# Some hardware (such as iLO 4 or Supermicro) only supports the Image property
|
||||
# Inserted and WriteProtected are not writable
|
||||
if image_only:
|
||||
del payload['Inserted']
|
||||
del payload['WriteProtected']
|
||||
return payload
|
||||
|
||||
def virtual_media_insert_via_patch(self, options, param_map, uri, data, image_only=False):
|
||||
@@ -2247,16 +2251,10 @@ class RedfishUtils(object):
|
||||
{'AllowableValues': v}) for k, v in data.items()
|
||||
if k.endswith('@Redfish.AllowableValues'))
|
||||
# construct payload
|
||||
payload = self._insert_virt_media_payload(options, param_map, data, ai)
|
||||
if 'Inserted' not in payload:
|
||||
payload = self._insert_virt_media_payload(options, param_map, data, ai, image_only)
|
||||
if 'Inserted' not in payload and not image_only:
|
||||
payload['Inserted'] = True
|
||||
|
||||
# Some hardware (such as iLO 4) only supports the Image property on the PATCH operation
|
||||
# Inserted and WriteProtected are not writable
|
||||
if image_only:
|
||||
del payload['Inserted']
|
||||
del payload['WriteProtected']
|
||||
|
||||
# PATCH the resource
|
||||
response = self.patch_request(self.root_uri + uri, payload)
|
||||
if response['ret'] is False:
|
||||
@@ -2292,6 +2290,13 @@ class RedfishUtils(object):
|
||||
if data["FirmwareVersion"].startswith("iLO 4"):
|
||||
image_only = True
|
||||
|
||||
# Supermicro does also not support Inserted and WriteProtected
|
||||
# Supermicro uses as firmware version only a number so we can't check for it because we
|
||||
# can't be sure that this firmware version is nut used by another vendor
|
||||
# Tested with Supermicro Firmware 01.74.02
|
||||
if 'Supermicro' in data['Oem']:
|
||||
image_only = True
|
||||
|
||||
virt_media_uri = data["VirtualMedia"]["@odata.id"]
|
||||
response = self.get_request(self.root_uri + virt_media_uri)
|
||||
if response['ret'] is False:
|
||||
@@ -2346,7 +2351,7 @@ class RedfishUtils(object):
|
||||
# get ActionInfo or AllowableValues
|
||||
ai = self._get_all_action_info_values(action)
|
||||
# construct payload
|
||||
payload = self._insert_virt_media_payload(options, param_map, data, ai)
|
||||
payload = self._insert_virt_media_payload(options, param_map, data, ai, image_only)
|
||||
# POST to action
|
||||
response = self.post_request(self.root_uri + action_uri, payload)
|
||||
if response['ret'] is False:
|
||||
@@ -2392,6 +2397,9 @@ class RedfishUtils(object):
|
||||
if data["FirmwareVersion"].startswith("iLO 4"):
|
||||
image_only = True
|
||||
|
||||
if 'Supermicro' in data['Oem']:
|
||||
image_only = True
|
||||
|
||||
virt_media_uri = data["VirtualMedia"]["@odata.id"]
|
||||
response = self.get_request(self.root_uri + virt_media_uri)
|
||||
if response['ret'] is False:
|
||||
@@ -3020,3 +3028,26 @@ class RedfishUtils(object):
|
||||
if not result["entries"]:
|
||||
return {'ret': False, 'msg': "No HostInterface objects found"}
|
||||
return result
|
||||
|
||||
def get_manager_inventory(self, manager_uri):
|
||||
result = {}
|
||||
inventory = {}
|
||||
# Get these entries, but does not fail if not found
|
||||
properties = ['FirmwareVersion', 'ManagerType', 'Manufacturer', 'Model',
|
||||
'PartNumber', 'PowerState', 'SerialNumber', 'Status', 'UUID']
|
||||
|
||||
response = self.get_request(self.root_uri + manager_uri)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
|
||||
for property in properties:
|
||||
if property in data:
|
||||
inventory[property] = data[property]
|
||||
|
||||
result["entries"] = inventory
|
||||
return result
|
||||
|
||||
def get_multi_manager_inventory(self):
|
||||
return self.aggregate_managers(self.get_manager_inventory)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright (c), James Laska
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -37,7 +37,7 @@ def fail_imports(module, needs_certifi=True):
|
||||
errors.append(missing_required_lib('certifi'))
|
||||
traceback.append(CERTIFI_IMPORT_ERROR)
|
||||
if errors:
|
||||
module.fail_json(errors=errors, traceback='\n'.join(traceback))
|
||||
module.fail_json(msg='\n'.join(errors), traceback='\n'.join(traceback))
|
||||
|
||||
|
||||
def redis_auth_argument_spec(tls_default=True):
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# own license to the complete work.
|
||||
#
|
||||
# Copyright (C) 2017 Lenovo, Inc.
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
#
|
||||
# Contains LXCA common class
|
||||
# Lenovo xClarity Administrator (LXCA)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# (c) 2018 Luca 'remix_tj' Lorenzetto
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Hewlett Packard Enterprise Development LP
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# Copyright (c) 2016, Adfinis SyGroup AG
|
||||
# Tobias Rueetschi <tobias.ruetschi@adfinis-sygroup.ch>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
# Copyright: (c) 2018, Johannes Brunswicker <johannes.brunswicker@gmail.com>
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright: (c) 2019, Sandeep Kasargod <sandeep@vexata.com>
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
# Simplified BSD License (see simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
406
plugins/module_utils/wdc_redfish_utils.py
Normal file
406
plugins/module_utils/wdc_redfish_utils.py
Normal file
@@ -0,0 +1,406 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 Western Digital Corporation
|
||||
# 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 datetime
|
||||
import re
|
||||
import time
|
||||
import tarfile
|
||||
|
||||
from ansible.module_utils.urls import fetch_file
|
||||
from ansible_collections.community.general.plugins.module_utils.redfish_utils import RedfishUtils
|
||||
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlunparse
|
||||
|
||||
|
||||
class WdcRedfishUtils(RedfishUtils):
|
||||
"""Extension to RedfishUtils to support WDC enclosures."""
|
||||
# Status codes returned by WDC FW Update Status
|
||||
UPDATE_STATUS_CODE_READY_FOR_FW_UPDATE = 0
|
||||
UPDATE_STATUS_CODE_FW_UPDATE_IN_PROGRESS = 1
|
||||
UPDATE_STATUS_CODE_FW_UPDATE_COMPLETED_WAITING_FOR_ACTIVATION = 2
|
||||
UPDATE_STATUS_CODE_FW_UPDATE_FAILED = 3
|
||||
|
||||
# Status messages returned by WDC FW Update Status
|
||||
UPDATE_STATUS_MESSAGE_READY_FOR_FW_UDPATE = "Ready for FW update"
|
||||
UDPATE_STATUS_MESSAGE_FW_UPDATE_IN_PROGRESS = "FW update in progress"
|
||||
UPDATE_STATUS_MESSAGE_FW_UPDATE_COMPLETED_WAITING_FOR_ACTIVATION = "FW update completed. Waiting for activation."
|
||||
UPDATE_STATUS_MESSAGE_FW_UPDATE_FAILED = "FW update failed."
|
||||
|
||||
def __init__(self,
|
||||
creds,
|
||||
root_uris,
|
||||
timeout,
|
||||
module,
|
||||
resource_id,
|
||||
data_modification):
|
||||
super(WdcRedfishUtils, self).__init__(creds=creds,
|
||||
root_uri=root_uris[0],
|
||||
timeout=timeout,
|
||||
module=module,
|
||||
resource_id=resource_id,
|
||||
data_modification=data_modification)
|
||||
# Update the root URI if we cannot perform a Redfish GET to the first one
|
||||
self._set_root_uri(root_uris)
|
||||
|
||||
def _set_root_uri(self, root_uris):
|
||||
"""Set the root URI from a list of options.
|
||||
|
||||
If the current root URI is good, just keep it. Else cycle through our options until we find a good one.
|
||||
A URI is considered good if we can GET uri/redfish/v1.
|
||||
"""
|
||||
for root_uri in root_uris:
|
||||
uri = root_uri + "/redfish/v1"
|
||||
response = self.get_request(uri)
|
||||
if response['ret']:
|
||||
self.root_uri = root_uri
|
||||
break
|
||||
|
||||
def _find_updateservice_resource(self):
|
||||
"""Find the update service resource as well as additional WDC-specific resources."""
|
||||
response = super(WdcRedfishUtils, self)._find_updateservice_resource()
|
||||
if not response['ret']:
|
||||
return response
|
||||
return self._find_updateservice_additional_uris()
|
||||
|
||||
def _is_enclosure_multi_tenant(self):
|
||||
"""Determine if the enclosure is multi-tenant.
|
||||
|
||||
The serial number of a multi-tenant enclosure will end in "-A" or "-B".
|
||||
|
||||
:return: True/False if the enclosure is multi-tenant or not; None if unable to determine.
|
||||
"""
|
||||
response = self.get_request(self.root_uri + self.service_root + "Chassis/Enclosure")
|
||||
if response['ret'] is False:
|
||||
return None
|
||||
pattern = r".*-[A,B]"
|
||||
data = response['data']
|
||||
return re.match(pattern, data['SerialNumber']) is not None
|
||||
|
||||
def _find_updateservice_additional_uris(self):
|
||||
"""Find & set WDC-specific update service URIs"""
|
||||
response = self.get_request(self.root_uri + self._update_uri())
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
if 'Actions' not in data:
|
||||
return {'ret': False, 'msg': 'Service does not support SimpleUpdate'}
|
||||
if '#UpdateService.SimpleUpdate' not in data['Actions']:
|
||||
return {'ret': False, 'msg': 'Service does not support SimpleUpdate'}
|
||||
action = data['Actions']['#UpdateService.SimpleUpdate']
|
||||
if 'target' not in action:
|
||||
return {'ret': False, 'msg': 'Service does not support SimpleUpdate'}
|
||||
self.simple_update_uri = action['target']
|
||||
|
||||
# Simple update status URI is not provided via GET /redfish/v1/UpdateService
|
||||
# So we have to hard code it.
|
||||
self.simple_update_status_uri = "{0}/Status".format(self.simple_update_uri)
|
||||
|
||||
# FWActivate URI
|
||||
if 'Oem' not in data['Actions']:
|
||||
return {'ret': False, 'msg': 'Service does not support OEM operations'}
|
||||
if 'WDC' not in data['Actions']['Oem']:
|
||||
return {'ret': False, 'msg': 'Service does not support WDC operations'}
|
||||
if '#UpdateService.FWActivate' not in data['Actions']['Oem']['WDC']:
|
||||
return {'ret': False, 'msg': 'Service does not support FWActivate'}
|
||||
action = data['Actions']['Oem']['WDC']['#UpdateService.FWActivate']
|
||||
if 'target' not in action:
|
||||
return {'ret': False, 'msg': 'Service does not support FWActivate'}
|
||||
self.firmware_activate_uri = action['target']
|
||||
return {'ret': True}
|
||||
|
||||
def _simple_update_status_uri(self):
|
||||
return self.simple_update_status_uri
|
||||
|
||||
def _firmware_activate_uri(self):
|
||||
return self.firmware_activate_uri
|
||||
|
||||
def _update_uri(self):
|
||||
return self.update_uri
|
||||
|
||||
def get_simple_update_status(self):
|
||||
"""Issue Redfish HTTP GET to return the simple update status"""
|
||||
result = {}
|
||||
response = self.get_request(self.root_uri + self._simple_update_status_uri())
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
result['ret'] = True
|
||||
data = response['data']
|
||||
result['entries'] = data
|
||||
return result
|
||||
|
||||
def firmware_activate(self, update_opts):
|
||||
"""Perform FWActivate using Redfish HTTP API."""
|
||||
creds = update_opts.get('update_creds')
|
||||
payload = {}
|
||||
if creds:
|
||||
if creds.get('username'):
|
||||
payload["Username"] = creds.get('username')
|
||||
if creds.get('password'):
|
||||
payload["Password"] = creds.get('password')
|
||||
|
||||
# Make sure the service supports FWActivate
|
||||
response = self.get_request(self.root_uri + self._update_uri())
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
data = response['data']
|
||||
if 'Actions' not in data:
|
||||
return {'ret': False, 'msg': 'Service does not support FWActivate'}
|
||||
|
||||
response = self.post_request(self.root_uri + self._firmware_activate_uri(), payload)
|
||||
if response['ret'] is False:
|
||||
return response
|
||||
return {'ret': True, 'changed': True,
|
||||
'msg': "FWActivate requested"}
|
||||
|
||||
def _get_bundle_version(self,
|
||||
bundle_uri):
|
||||
"""Get the firmware version from a bundle file, and whether or not it is multi-tenant.
|
||||
|
||||
Only supports HTTP at this time. Assumes URI exists and is a tarfile.
|
||||
Looks for a file oobm-[version].pkg, such as 'oobm-4.0.13.pkg`. Extracts the version number
|
||||
from that filename (in the above example, the version number is "4.0.13".
|
||||
|
||||
To determine if the bundle is multi-tenant or not, it looks inside the .bin file within the tarfile,
|
||||
and checks the appropriate byte in the file.
|
||||
|
||||
:param str bundle_uri: HTTP URI of the firmware bundle.
|
||||
:return: Firmware version number contained in the bundle, and whether or not the bundle is multi-tenant.
|
||||
Either value will be None if unable to deterine.
|
||||
:rtype: str or None, bool or None
|
||||
"""
|
||||
bundle_temp_filename = fetch_file(module=self.module,
|
||||
url=bundle_uri)
|
||||
if not tarfile.is_tarfile(bundle_temp_filename):
|
||||
return None, None
|
||||
tf = tarfile.open(bundle_temp_filename)
|
||||
pattern_pkg = r"oobm-(.+)\.pkg"
|
||||
pattern_bin = r"(.*\.bin)"
|
||||
bundle_version = None
|
||||
is_multi_tenant = None
|
||||
for filename in tf.getnames():
|
||||
match_pkg = re.match(pattern_pkg, filename)
|
||||
if match_pkg is not None:
|
||||
bundle_version = match_pkg.group(1)
|
||||
match_bin = re.match(pattern_bin, filename)
|
||||
if match_bin is not None:
|
||||
bin_filename = match_bin.group(1)
|
||||
bin_file = tf.extractfile(bin_filename)
|
||||
bin_file.seek(11)
|
||||
byte_11 = bin_file.read(1)
|
||||
is_multi_tenant = byte_11 == b'\x80'
|
||||
|
||||
return bundle_version, is_multi_tenant
|
||||
|
||||
@staticmethod
|
||||
def uri_is_http(uri):
|
||||
"""Return True if the specified URI is http or https.
|
||||
|
||||
:param str uri: A URI.
|
||||
:return: True if the URI is http or https, else False
|
||||
:rtype: bool
|
||||
"""
|
||||
parsed_bundle_uri = urlparse(uri)
|
||||
return parsed_bundle_uri.scheme.lower() in ['http', 'https']
|
||||
|
||||
def update_and_activate(self, update_opts):
|
||||
"""Update and activate the firmware in a single action.
|
||||
|
||||
Orchestrates the firmware update so that everything can be done in a single command.
|
||||
Compares the update version with the already-installed version -- skips update if they are the same.
|
||||
Performs retries, handles timeouts as needed.
|
||||
|
||||
"""
|
||||
# Convert credentials to standard HTTP format
|
||||
if update_opts.get("update_creds") is not None and "username" in update_opts["update_creds"] and "password" in update_opts["update_creds"]:
|
||||
update_creds = update_opts["update_creds"]
|
||||
parsed_url = urlparse(update_opts["update_image_uri"])
|
||||
if update_creds:
|
||||
original_netloc = parsed_url.netloc
|
||||
parsed_url = parsed_url._replace(netloc="{0}:{1}@{2}".format(update_creds.get("username"),
|
||||
update_creds.get("password"),
|
||||
original_netloc))
|
||||
update_opts["update_image_uri"] = urlunparse(parsed_url)
|
||||
del update_opts["update_creds"]
|
||||
|
||||
# Make sure bundle URI is HTTP(s)
|
||||
bundle_uri = update_opts["update_image_uri"]
|
||||
|
||||
if not self.uri_is_http(bundle_uri):
|
||||
return {
|
||||
'ret': False,
|
||||
'msg': 'Bundle URI must be HTTP or HTTPS'
|
||||
}
|
||||
# Make sure IOM is ready for update
|
||||
result = self.get_simple_update_status()
|
||||
if result['ret'] is False:
|
||||
return result
|
||||
update_status = result['entries']
|
||||
status_code = update_status['StatusCode']
|
||||
status_description = update_status['Description']
|
||||
if status_code not in [
|
||||
self.UPDATE_STATUS_CODE_READY_FOR_FW_UPDATE,
|
||||
self.UPDATE_STATUS_CODE_FW_UPDATE_FAILED
|
||||
]:
|
||||
return {
|
||||
'ret': False,
|
||||
'msg': 'Target is not ready for FW update. Current status: {0} ({1})'.format(
|
||||
status_code, status_description
|
||||
)}
|
||||
|
||||
# Check the FW version in the bundle file, and compare it to what is already on the IOMs
|
||||
|
||||
# Bundle version number
|
||||
bundle_firmware_version, is_bundle_multi_tenant = self._get_bundle_version(bundle_uri)
|
||||
if bundle_firmware_version is None or is_bundle_multi_tenant is None:
|
||||
return {
|
||||
'ret': False,
|
||||
'msg': 'Unable to extract bundle version or multi-tenant status from update image tarfile'
|
||||
}
|
||||
|
||||
# Verify that the bundle is correctly multi-tenant or not
|
||||
is_enclosure_multi_tenant = self._is_enclosure_multi_tenant()
|
||||
if is_enclosure_multi_tenant != is_bundle_multi_tenant:
|
||||
return {
|
||||
'ret': False,
|
||||
'msg': 'Enclosure multi-tenant is {0} but bundle multi-tenant is {1}'.format(
|
||||
is_enclosure_multi_tenant,
|
||||
is_bundle_multi_tenant,
|
||||
)
|
||||
}
|
||||
|
||||
# Version number installed on IOMs
|
||||
firmware_inventory = self.get_firmware_inventory()
|
||||
if not firmware_inventory["ret"]:
|
||||
return firmware_inventory
|
||||
firmware_inventory_dict = {}
|
||||
for entry in firmware_inventory["entries"]:
|
||||
firmware_inventory_dict[entry["Id"]] = entry
|
||||
iom_a_firmware_version = firmware_inventory_dict.get("IOModuleA_OOBM", {}).get("Version")
|
||||
iom_b_firmware_version = firmware_inventory_dict.get("IOModuleB_OOBM", {}).get("Version")
|
||||
# If version is None, we will proceed with the update, because we cannot tell
|
||||
# for sure that we have a full version match.
|
||||
if is_enclosure_multi_tenant:
|
||||
# For multi-tenant, only one of the IOMs will be affected by the firmware update,
|
||||
# so see if that IOM already has the same firmware version as the bundle.
|
||||
firmware_already_installed = bundle_firmware_version == self._get_installed_firmware_version_of_multi_tenant_system(
|
||||
iom_a_firmware_version,
|
||||
iom_b_firmware_version)
|
||||
else:
|
||||
# For single-tenant, see if both IOMs already have the same firmware version as the bundle.
|
||||
firmware_already_installed = bundle_firmware_version == iom_a_firmware_version == iom_b_firmware_version
|
||||
# If this FW already installed, return changed: False, and do not update the firmware.
|
||||
if firmware_already_installed:
|
||||
return {
|
||||
'ret': True,
|
||||
'changed': False,
|
||||
'msg': 'Version {0} already installed'.format(bundle_firmware_version)
|
||||
}
|
||||
|
||||
# Version numbers don't match the bundle -- proceed with update (unless we are in check mode)
|
||||
if self.module.check_mode:
|
||||
return {
|
||||
'ret': True,
|
||||
'changed': True,
|
||||
'msg': 'Update not performed in check mode.'
|
||||
}
|
||||
update_successful = False
|
||||
retry_interval_seconds = 5
|
||||
max_number_of_retries = 5
|
||||
retry_number = 0
|
||||
while retry_number < max_number_of_retries and not update_successful:
|
||||
if retry_number != 0:
|
||||
time.sleep(retry_interval_seconds)
|
||||
retry_number += 1
|
||||
|
||||
result = self.simple_update(update_opts)
|
||||
if result['ret'] is not True:
|
||||
# Sometimes a timeout error is returned even though the update actually was requested.
|
||||
# Check the update status to see if the update is in progress.
|
||||
status_result = self.get_simple_update_status()
|
||||
if status_result['ret'] is False:
|
||||
continue
|
||||
update_status = status_result['entries']
|
||||
status_code = update_status['StatusCode']
|
||||
if status_code != self.UPDATE_STATUS_CODE_FW_UPDATE_IN_PROGRESS:
|
||||
# Update is not in progress -- retry until max number of retries
|
||||
continue
|
||||
else:
|
||||
update_successful = True
|
||||
else:
|
||||
update_successful = True
|
||||
if not update_successful:
|
||||
# Unable to get SimpleUpdate to work. Return the failure from the SimpleUpdate
|
||||
return result
|
||||
|
||||
# Wait for "ready to activate"
|
||||
max_wait_minutes = 30
|
||||
polling_interval_seconds = 30
|
||||
status_code = self.UPDATE_STATUS_CODE_READY_FOR_FW_UPDATE
|
||||
start_time = datetime.datetime.now()
|
||||
# For a short time, target will still say "ready for firmware update" before it transitions
|
||||
# to "update in progress"
|
||||
status_codes_for_update_incomplete = [
|
||||
self.UPDATE_STATUS_CODE_FW_UPDATE_IN_PROGRESS,
|
||||
self.UPDATE_STATUS_CODE_READY_FOR_FW_UPDATE
|
||||
]
|
||||
iteration = 0
|
||||
while status_code in status_codes_for_update_incomplete \
|
||||
and datetime.datetime.now() - start_time < datetime.timedelta(minutes=max_wait_minutes):
|
||||
if iteration != 0:
|
||||
time.sleep(polling_interval_seconds)
|
||||
iteration += 1
|
||||
result = self.get_simple_update_status()
|
||||
if result['ret'] is False:
|
||||
continue # We may get timeouts, just keep trying until we give up
|
||||
update_status = result['entries']
|
||||
status_code = update_status['StatusCode']
|
||||
status_description = update_status['Description']
|
||||
if status_code == self.UPDATE_STATUS_CODE_FW_UPDATE_IN_PROGRESS:
|
||||
# Once it says update in progress, "ready for update" is no longer a valid status code
|
||||
status_codes_for_update_incomplete = [self.UPDATE_STATUS_CODE_FW_UPDATE_IN_PROGRESS]
|
||||
|
||||
# Update no longer in progress -- verify that it finished
|
||||
if status_code != self.UPDATE_STATUS_CODE_FW_UPDATE_COMPLETED_WAITING_FOR_ACTIVATION:
|
||||
return {
|
||||
'ret': False,
|
||||
'msg': 'Target is not ready for FW activation after update. Current status: {0} ({1})'.format(
|
||||
status_code, status_description
|
||||
)}
|
||||
|
||||
self.firmware_activate(update_opts)
|
||||
return {'ret': True, 'changed': True,
|
||||
'msg': "Firmware updated and activation initiated."}
|
||||
|
||||
def _get_installed_firmware_version_of_multi_tenant_system(self,
|
||||
iom_a_firmware_version,
|
||||
iom_b_firmware_version):
|
||||
"""Return the version for the active IOM on a multi-tenant system.
|
||||
|
||||
Only call this on a multi-tenant system.
|
||||
Given the installed firmware versions for IOM A, B, this method will determine which IOM is active
|
||||
for this tenanat, and return that IOM's firmware version.
|
||||
"""
|
||||
# To determine which IOM we are on, try to GET each IOM resource
|
||||
# The one we are on will return valid data.
|
||||
# The other will return an error with message "IOM Module A/B cannot be read"
|
||||
which_iom_is_this = None
|
||||
for iom_letter in ['A', 'B']:
|
||||
iom_uri = "Chassis/IOModule{0}FRU".format(iom_letter)
|
||||
response = self.get_request(self.root_uri + self.service_root + iom_uri)
|
||||
if response['ret'] is False:
|
||||
continue
|
||||
data = response['data']
|
||||
if "Id" in data: # Assume if there is an "Id", it is valid
|
||||
which_iom_is_this = iom_letter
|
||||
break
|
||||
if which_iom_is_this == 'A':
|
||||
return iom_a_firmware_version
|
||||
elif which_iom_is_this == 'B':
|
||||
return iom_b_firmware_version
|
||||
else:
|
||||
return None
|
||||
37
plugins/module_utils/xfconf.py
Normal file
37
plugins/module_utils/xfconf.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2022, Alexei Znamensky <russoz@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 ansible.module_utils.parsing.convert_bool import boolean
|
||||
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt
|
||||
|
||||
|
||||
@fmt.unpack_args
|
||||
def _values_fmt(values, value_types):
|
||||
result = []
|
||||
for value, value_type in zip(values, value_types):
|
||||
if value_type == 'bool':
|
||||
value = 'true' if boolean(value) else 'false'
|
||||
result.extend(['--type', '{0}'.format(value_type), '--set', '{0}'.format(value)])
|
||||
return result
|
||||
|
||||
|
||||
def xfconf_runner(module, **kwargs):
|
||||
runner = CmdRunner(
|
||||
module,
|
||||
command='xfconf-query',
|
||||
arg_formats=dict(
|
||||
channel=fmt.as_opt_val("--channel"),
|
||||
property=fmt.as_opt_val("--property"),
|
||||
force_array=fmt.as_bool("--force-array"),
|
||||
reset=fmt.as_bool("--reset"),
|
||||
create=fmt.as_bool("--create"),
|
||||
list_arg=fmt.as_bool("--list"),
|
||||
values_and_types=fmt.as_func(_values_fmt),
|
||||
),
|
||||
**kwargs
|
||||
)
|
||||
return runner
|
||||
@@ -191,10 +191,10 @@ notes:
|
||||
2.1, the later requires python to be installed in the instance which can
|
||||
be done with the command module.
|
||||
- You can copy a file from the host to the instance
|
||||
with the Ansible M(ansible.builtin.copy) and M(ansible.builtin.template) module and the `lxd` connection plugin.
|
||||
with the Ansible M(ansible.builtin.copy) and M(ansible.builtin.template) module and the C(community.general.lxd) connection plugin.
|
||||
See the example below.
|
||||
- You can copy a file in the created instance to the localhost
|
||||
with `command=lxc file pull instance_name/dir/filename filename`.
|
||||
with C(command=lxc file pull instance_name/dir/filename filename).
|
||||
See the first example below.
|
||||
'''
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@ class LXDProfileManagement(object):
|
||||
)
|
||||
|
||||
def _merge_dicts(self, source, destination):
|
||||
"""Merge Dictionarys
|
||||
"""Merge Dictionaries
|
||||
|
||||
Get a list of filehandle numbers from logger to be handed to
|
||||
DaemonContext.files_preserve
|
||||
|
||||
@@ -303,7 +303,7 @@ class LXDProjectManagement(object):
|
||||
)
|
||||
|
||||
def _merge_dicts(self, source, destination):
|
||||
""" Return a new dict taht merge two dict,
|
||||
""" Return a new dict that merge two dict,
|
||||
with values in source dict overwrite destination dict
|
||||
|
||||
Args:
|
||||
|
||||
@@ -743,6 +743,8 @@ def main():
|
||||
module.fail_json(msg="restarting of VM %s failed with exception: %s" % (vmid, e))
|
||||
|
||||
elif state == 'absent':
|
||||
if not vmid:
|
||||
module.exit_json(changed=False, msg='VM with hostname = %s is already absent' % hostname)
|
||||
try:
|
||||
vm = proxmox.get_vm(vmid, ignore_missing=True)
|
||||
if not vm:
|
||||
|
||||
@@ -83,7 +83,7 @@ options:
|
||||
version_added: 1.3.0
|
||||
clone:
|
||||
description:
|
||||
- Name of VM to be cloned. If C(vmid) is setted, C(clone) can take arbitrary value but required for initiating the clone.
|
||||
- Name of VM to be cloned. If I(vmid) is set, I(clone) can take an arbitrary value but is required for initiating the clone.
|
||||
type: str
|
||||
cores:
|
||||
description:
|
||||
@@ -1204,12 +1204,12 @@ def main():
|
||||
# Ensure source VM id exists when cloning
|
||||
proxmox.get_vm(vmid)
|
||||
|
||||
# Ensure the choosen VM name doesn't already exist when cloning
|
||||
# Ensure the chosen VM name doesn't already exist when cloning
|
||||
existing_vmid = proxmox.get_vmid(name, ignore_missing=True)
|
||||
if existing_vmid:
|
||||
module.exit_json(changed=False, vmid=existing_vmid, msg="VM with name <%s> already exists" % name)
|
||||
|
||||
# Ensure the choosen VM id doesn't already exist when cloning
|
||||
# Ensure the chosen VM id doesn't already exist when cloning
|
||||
if proxmox.get_vm(newid, ignore_missing=True):
|
||||
module.exit_json(changed=False, vmid=vmid, msg="vmid %s with VM name %s already exists" % (newid, name))
|
||||
|
||||
@@ -1370,6 +1370,8 @@ def main():
|
||||
|
||||
elif state == 'absent':
|
||||
status = {}
|
||||
if not vmid:
|
||||
module.exit_json(changed=False, msg='VM with name = %s is already absent' % name)
|
||||
try:
|
||||
vm = proxmox.get_vm(vmid, ignore_missing=True)
|
||||
if not vm:
|
||||
|
||||
@@ -107,7 +107,7 @@ state:
|
||||
returned: always
|
||||
command:
|
||||
type: str
|
||||
description: Full `serverless` command run by this module, in case you want to re-run the command outside the module.
|
||||
description: Full C(serverless) command run by this module, in case you want to re-run the command outside the module.
|
||||
returned: always
|
||||
sample: serverless deploy --stage production
|
||||
'''
|
||||
|
||||
@@ -67,7 +67,7 @@ options:
|
||||
state_file:
|
||||
description:
|
||||
- The path to an existing Terraform state file to use when building plan.
|
||||
If this is not specified, the default `terraform.tfstate` will be used.
|
||||
If this is not specified, the default C(terraform.tfstate) will be used.
|
||||
- This option is ignored when plan is specified.
|
||||
type: path
|
||||
variables_files:
|
||||
@@ -103,7 +103,7 @@ options:
|
||||
force_init:
|
||||
description:
|
||||
- To avoid duplicating infra, if a state file can't be found this will
|
||||
force a `terraform init`. Generally, this should be turned off unless
|
||||
force a C(terraform init). Generally, this should be turned off unless
|
||||
you intend to provision an entirely new Terraform deployment.
|
||||
default: false
|
||||
type: bool
|
||||
@@ -149,7 +149,7 @@ options:
|
||||
type: int
|
||||
version_added: '3.8.0'
|
||||
notes:
|
||||
- To just run a `terraform plan`, use check mode.
|
||||
- To just run a C(terraform plan), use check mode.
|
||||
requirements: [ "terraform" ]
|
||||
author: "Ryan Scott Brown (@ryansb)"
|
||||
'''
|
||||
@@ -205,7 +205,7 @@ EXAMPLES = """
|
||||
RETURN = """
|
||||
outputs:
|
||||
type: complex
|
||||
description: A dictionary of all the TF outputs by their assigned name. Use `.outputs.MyOutputName.value` to access the value.
|
||||
description: A dictionary of all the TF outputs by their assigned name. Use C(.outputs.MyOutputName.value) to access the value.
|
||||
returned: on success
|
||||
sample: '{"bukkit_arn": {"sensitive": false, "type": "string", "value": "arn:aws:s3:::tf-test-bukkit"}'
|
||||
contains:
|
||||
@@ -223,12 +223,12 @@ outputs:
|
||||
description: The value of the output as interpolated by Terraform
|
||||
stdout:
|
||||
type: str
|
||||
description: Full `terraform` command stdout, in case you want to display it or examine the event log
|
||||
description: Full C(terraform) command stdout, in case you want to display it or examine the event log
|
||||
returned: always
|
||||
sample: ''
|
||||
command:
|
||||
type: str
|
||||
description: Full `terraform` command built by this module, in case you want to re-run the command outside the module or debug a problem.
|
||||
description: Full C(terraform) command built by this module, in case you want to re-run the command outside the module or debug a problem.
|
||||
returned: always
|
||||
sample: terraform apply ...
|
||||
"""
|
||||
|
||||
@@ -252,7 +252,8 @@ def main():
|
||||
'weight': weight,
|
||||
}
|
||||
|
||||
for name, value in mutable.items():
|
||||
for name in list(mutable):
|
||||
value = mutable[name]
|
||||
if value is None or value == getattr(node, name):
|
||||
mutable.pop(name)
|
||||
|
||||
|
||||
@@ -75,15 +75,15 @@ options:
|
||||
target_hostname:
|
||||
type: str
|
||||
description:
|
||||
- One of `target_hostname` and `target_alias` is required for remote.* checks,
|
||||
- One of I(target_hostname) and I(target_alias) is required for remote.* checks,
|
||||
but prohibited for agent.* checks. The hostname this check should target.
|
||||
Must be a valid IPv4, IPv6, or FQDN.
|
||||
target_alias:
|
||||
type: str
|
||||
description:
|
||||
- One of `target_alias` and `target_hostname` is required for remote.* checks,
|
||||
- One of I(target_alias) and I(target_hostname) is required for remote.* checks,
|
||||
but prohibited for agent.* checks. Use the corresponding key in the entity's
|
||||
`ip_addresses` hash to resolve an IP address to target.
|
||||
I(ip_addresses) hash to resolve an IP address to target.
|
||||
details:
|
||||
type: dict
|
||||
description:
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Scaleway VPC management module
|
||||
#
|
||||
# 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: scaleway_compute_private_network
|
||||
short_description: Scaleway compute - private network management
|
||||
version_added: 5.2.0
|
||||
author: Pascal MANGIN (@pastral)
|
||||
description:
|
||||
- This module add or remove a private network to a compute instance
|
||||
(U(https://developer.scaleway.com)).
|
||||
extends_documentation_fragment:
|
||||
- community.general.scaleway
|
||||
|
||||
|
||||
options:
|
||||
state:
|
||||
type: str
|
||||
description:
|
||||
- Indicate desired state of the VPC.
|
||||
default: present
|
||||
choices:
|
||||
- present
|
||||
- absent
|
||||
|
||||
project:
|
||||
type: str
|
||||
description:
|
||||
- Project identifier.
|
||||
required: true
|
||||
|
||||
region:
|
||||
type: str
|
||||
description:
|
||||
- Scaleway region to use (for example C(par1)).
|
||||
required: true
|
||||
choices:
|
||||
- ams1
|
||||
- EMEA-NL-EVS
|
||||
- par1
|
||||
- EMEA-FR-PAR1
|
||||
- par2
|
||||
- EMEA-FR-PAR2
|
||||
- waw1
|
||||
- EMEA-PL-WAW1
|
||||
|
||||
compute_id:
|
||||
type: str
|
||||
description:
|
||||
- ID of the compute instance (see M(community.general.scaleway_compute)).
|
||||
required: true
|
||||
|
||||
private_network_id:
|
||||
type: str
|
||||
description:
|
||||
- ID of the private network (see M(community.general.scaleway_private_network)).
|
||||
required: true
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Plug a VM to a private network
|
||||
community.general.scaleway_compute_private_network:
|
||||
project: '{{ scw_project }}'
|
||||
state: present
|
||||
region: par1
|
||||
compute_id: "12345678-f1e6-40ec-83e5-12345d67ed89"
|
||||
private_network_id: "22345678-f1e6-40ec-83e5-12345d67ed89"
|
||||
register: nicsvpc_creation_task
|
||||
|
||||
- name: Unplug a VM from a private network
|
||||
community.general.scaleway_compute_private_network:
|
||||
project: '{{ scw_project }}'
|
||||
state: absent
|
||||
region: par1
|
||||
compute_id: "12345678-f1e6-40ec-83e5-12345d67ed89"
|
||||
private_network_id: "22345678-f1e6-40ec-83e5-12345d67ed89"
|
||||
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
scaleway_compute_private_network:
|
||||
description: Information on the VPC.
|
||||
returned: success when C(state=present)
|
||||
type: dict
|
||||
sample:
|
||||
{
|
||||
"created_at": "2022-01-15T11:11:12.676445Z",
|
||||
"id": "12345678-f1e6-40ec-83e5-12345d67ed89",
|
||||
"name": "network",
|
||||
"organization_id": "a123b4cd-ef5g-678h-90i1-jk2345678l90",
|
||||
"project_id": "a123b4cd-ef5g-678h-90i1-jk2345678l90",
|
||||
"tags": [
|
||||
"tag1",
|
||||
"tag2",
|
||||
"tag3",
|
||||
"tag4",
|
||||
"tag5"
|
||||
],
|
||||
"updated_at": "2022-01-15T11:12:04.624837Z",
|
||||
"zone": "fr-par-2"
|
||||
}
|
||||
'''
|
||||
from ansible_collections.community.general.plugins.module_utils.scaleway import SCALEWAY_LOCATION, scaleway_argument_spec, Scaleway
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def get_nics_info(api, compute_id, private_network_id):
|
||||
|
||||
response = api.get('servers/' + compute_id + '/private_nics')
|
||||
if not response.ok:
|
||||
msg = "Error during get servers information: %s: '%s' (%s)" % (response.info['msg'], response.json['message'], response.json)
|
||||
api.module.fail_json(msg=msg)
|
||||
|
||||
i = 0
|
||||
list_nics = response.json['private_nics']
|
||||
|
||||
while i < len(list_nics):
|
||||
if list_nics[i]['private_network_id'] == private_network_id:
|
||||
return list_nics[i]
|
||||
i += 1
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def present_strategy(api, compute_id, private_network_id):
|
||||
|
||||
changed = False
|
||||
nic = get_nics_info(api, compute_id, private_network_id)
|
||||
if nic is not None:
|
||||
return changed, nic
|
||||
|
||||
data = {"private_network_id": private_network_id}
|
||||
changed = True
|
||||
if api.module.check_mode:
|
||||
return changed, {"status": "a private network would be add to a server"}
|
||||
|
||||
response = api.post(path='servers/' + compute_id + '/private_nics', data=data)
|
||||
|
||||
if not response.ok:
|
||||
api.module.fail_json(msg='Error when adding a private network to a server [{0}: {1}]'.format(response.status_code, response.json))
|
||||
|
||||
return changed, response.json
|
||||
|
||||
|
||||
def absent_strategy(api, compute_id, private_network_id):
|
||||
|
||||
changed = False
|
||||
nic = get_nics_info(api, compute_id, private_network_id)
|
||||
if nic is None:
|
||||
return changed, {}
|
||||
|
||||
changed = True
|
||||
if api.module.check_mode:
|
||||
return changed, {"status": "private network would be destroyed"}
|
||||
|
||||
response = api.delete('servers/' + compute_id + '/private_nics/' + nic['id'])
|
||||
|
||||
if not response.ok:
|
||||
api.module.fail_json(msg='Error deleting private network from server [{0}: {1}]'.format(
|
||||
response.status_code, response.json))
|
||||
|
||||
return changed, response.json
|
||||
|
||||
|
||||
def core(module):
|
||||
|
||||
compute_id = module.params['compute_id']
|
||||
pn_id = module.params['private_network_id']
|
||||
|
||||
region = module.params["region"]
|
||||
module.params['api_url'] = SCALEWAY_LOCATION[region]["api_endpoint"]
|
||||
|
||||
api = Scaleway(module=module)
|
||||
if module.params["state"] == "absent":
|
||||
changed, summary = absent_strategy(api=api, compute_id=compute_id, private_network_id=pn_id)
|
||||
else:
|
||||
changed, summary = present_strategy(api=api, compute_id=compute_id, private_network_id=pn_id)
|
||||
module.exit_json(changed=changed, scaleway_compute_private_network=summary)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = scaleway_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
project=dict(required=True),
|
||||
region=dict(required=True, choices=list(SCALEWAY_LOCATION.keys())),
|
||||
compute_id=dict(required=True),
|
||||
private_network_id=dict(required=True)
|
||||
))
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
core(module)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -35,7 +35,7 @@ options:
|
||||
user_data:
|
||||
type: dict
|
||||
description:
|
||||
- User defined data. Typically used with `cloud-init`.
|
||||
- User defined data. Typically used with C(cloud-init).
|
||||
- Pass your cloud-init script here as a string
|
||||
required: false
|
||||
|
||||
|
||||
@@ -1207,7 +1207,7 @@ class XenServerVM(XenServerObject):
|
||||
if (self.module.params['home_server'] and
|
||||
(not self.vm_params['affinity'] or self.module.params['home_server'] != self.vm_params['affinity']['name_label'])):
|
||||
|
||||
# Check existance only. Ignore return value.
|
||||
# Check existence only. Ignore return value.
|
||||
get_object_ref(self.module, self.module.params['home_server'], uuid=None, obj_type="home server", fail=True,
|
||||
msg_prefix="VM check home_server: ")
|
||||
|
||||
@@ -1371,7 +1371,7 @@ class XenServerVM(XenServerObject):
|
||||
disk_sr = disk_params.get('sr')
|
||||
|
||||
if disk_sr_uuid is not None or disk_sr is not None:
|
||||
# Check existance only. Ignore return value.
|
||||
# Check existence only. Ignore return value.
|
||||
get_object_ref(self.module, disk_sr, disk_sr_uuid, obj_type="SR", fail=True,
|
||||
msg_prefix="VM check disks[%s]: " % position)
|
||||
elif self.default_sr_ref == 'OpaqueRef:NULL':
|
||||
@@ -1448,7 +1448,7 @@ class XenServerVM(XenServerObject):
|
||||
|
||||
if cdrom_type == "iso":
|
||||
# Check if ISO exists.
|
||||
# Check existance only. Ignore return value.
|
||||
# Check existence only. Ignore return value.
|
||||
get_object_ref(self.module, cdrom_iso_name, uuid=None, obj_type="ISO image", fail=True,
|
||||
msg_prefix="VM check cdrom.iso_name: ")
|
||||
|
||||
@@ -1496,7 +1496,7 @@ class XenServerVM(XenServerObject):
|
||||
self.module.fail_json(msg="VM check networks[%s]: network name cannot be an empty string!" % position)
|
||||
|
||||
if network_name:
|
||||
# Check existance only. Ignore return value.
|
||||
# Check existence only. Ignore return value.
|
||||
get_object_ref(self.module, network_name, uuid=None, obj_type="network", fail=True,
|
||||
msg_prefix="VM check networks[%s]: " % position)
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ def add_service(module, service):
|
||||
service_id=result.id,
|
||||
service_name=result.name,
|
||||
service_port=result.port,
|
||||
checks=[check.to_dict() for check in service.checks],
|
||||
checks=[check.to_dict() for check in service.checks()],
|
||||
tags=result.tags)
|
||||
|
||||
|
||||
|
||||
@@ -28,14 +28,14 @@ author:
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- The action to take with the supplied key and value. If the state is 'present' and `value` is set, the key
|
||||
contents will be set to the value supplied and `changed` will be set to `true` only if the value was
|
||||
different to the current contents. If the state is 'present' and `value` is not set, the existing value
|
||||
associated to the key will be returned. The state 'absent' will remove the key/value pair,
|
||||
again 'changed' will be set to true only if the key actually existed
|
||||
- The action to take with the supplied key and value. If the state is C(present) and I(value) is set, the key
|
||||
contents will be set to the value supplied and C(changed) will be set to C(true) only if the value was
|
||||
different to the current contents. If the state is C(present) and I(value) is not set, the existing value
|
||||
associated to the key will be returned. The state C(absent) will remove the key/value pair,
|
||||
again C(changed) will be set to true only if the key actually existed
|
||||
prior to the removal. An attempt can be made to obtain or free the
|
||||
lock associated with a key/value pair with the states 'acquire' or
|
||||
'release' respectively. a valid session must be supplied to make the
|
||||
lock associated with a key/value pair with the states C(acquire) or
|
||||
C(release) respectively. a valid session must be supplied to make the
|
||||
attempt changed will be true if the attempt is successful, false
|
||||
otherwise.
|
||||
type: str
|
||||
|
||||
@@ -95,6 +95,11 @@ options:
|
||||
choices: [ delete, release ]
|
||||
type: str
|
||||
default: release
|
||||
ttl:
|
||||
description:
|
||||
- Specifies the duration of a session in seconds (between 10 and 86400).
|
||||
type: int
|
||||
version_added: 5.4.0
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -121,6 +126,11 @@ EXAMPLES = '''
|
||||
- name: Retrieve active sessions
|
||||
community.general.consul_session:
|
||||
state: list
|
||||
|
||||
- name: Register session with a ttl
|
||||
community.general.consul_session:
|
||||
name: session-with-ttl
|
||||
ttl: 600 # sec
|
||||
'''
|
||||
|
||||
try:
|
||||
@@ -185,6 +195,7 @@ def update_session(module):
|
||||
datacenter = module.params.get('datacenter')
|
||||
node = module.params.get('node')
|
||||
behavior = module.params.get('behavior')
|
||||
ttl = module.params.get('ttl')
|
||||
|
||||
consul_client = get_consul_api(module)
|
||||
|
||||
@@ -192,6 +203,7 @@ def update_session(module):
|
||||
session = consul_client.session.create(
|
||||
name=name,
|
||||
behavior=behavior,
|
||||
ttl=ttl,
|
||||
node=node,
|
||||
lock_delay=delay,
|
||||
dc=datacenter,
|
||||
@@ -201,6 +213,7 @@ def update_session(module):
|
||||
session_id=session,
|
||||
name=name,
|
||||
behavior=behavior,
|
||||
ttl=ttl,
|
||||
delay=delay,
|
||||
checks=checks,
|
||||
node=node)
|
||||
@@ -241,6 +254,7 @@ def main():
|
||||
checks=dict(type='list', elements='str'),
|
||||
delay=dict(type='int', default='15'),
|
||||
behavior=dict(type='str', default='release', choices=['release', 'delete']),
|
||||
ttl=dict(type='int'),
|
||||
host=dict(type='str', default='localhost'),
|
||||
port=dict(type='int', default=8500),
|
||||
scheme=dict(type='str', default='http'),
|
||||
|
||||
@@ -279,7 +279,7 @@ def do_ini(module, filename, section=None, option=None, values=None,
|
||||
# handling multiple instances of option=value when state is 'present' with/without exclusive is a bit complex
|
||||
#
|
||||
# 1. edit all lines where we have a option=value pair with a matching value in values[]
|
||||
# 2. edit all the remaing lines where we have a matching option
|
||||
# 2. edit all the remaining lines where we have a matching option
|
||||
# 3. delete remaining lines where we have a matching option
|
||||
# 4. insert missing option line(s) at the end of the section
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ def main():
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
out = "allready unpacked"
|
||||
out = "already unpacked"
|
||||
|
||||
if remove:
|
||||
os.remove(path)
|
||||
|
||||
@@ -45,7 +45,7 @@ EXAMPLES = r'''
|
||||
ipa_user: admin
|
||||
ipa_pass: supersecret
|
||||
|
||||
- name: Ensure the TOTP syncronization window is set to 86400 seconds
|
||||
- name: Ensure the TOTP synchronization window is set to 86400 seconds
|
||||
community.general.ipa_otpconfig:
|
||||
ipatokentotpsyncwindow: '86400'
|
||||
ipa_host: localhost
|
||||
@@ -59,7 +59,7 @@ EXAMPLES = r'''
|
||||
ipa_user: admin
|
||||
ipa_pass: supersecret
|
||||
|
||||
- name: Ensure the HOTP syncronization window is set to 100 hops
|
||||
- name: Ensure the HOTP synchronization window is set to 100 hops
|
||||
community.general.ipa_otpconfig:
|
||||
ipatokenhotpsyncwindow: '100'
|
||||
ipa_host: localhost
|
||||
|
||||
@@ -63,7 +63,7 @@ options:
|
||||
type: str
|
||||
replace:
|
||||
description:
|
||||
- Force replace the existant vault on IPA server.
|
||||
- Force replace the existent vault on IPA server.
|
||||
type: bool
|
||||
default: False
|
||||
choices: ["True", "False"]
|
||||
|
||||
@@ -163,7 +163,7 @@ options:
|
||||
aliases:
|
||||
- defaultGroups
|
||||
type: list
|
||||
elements: dict
|
||||
elements: str
|
||||
default_locale:
|
||||
description:
|
||||
- The realm default locale.
|
||||
@@ -183,7 +183,7 @@ options:
|
||||
aliases:
|
||||
- defaultRoles
|
||||
type: list
|
||||
elements: dict
|
||||
elements: str
|
||||
default_signature_algorithm:
|
||||
description:
|
||||
- The realm default signature algorithm.
|
||||
@@ -622,10 +622,10 @@ def main():
|
||||
client_authentication_flow=dict(type='str', aliases=['clientAuthenticationFlow']),
|
||||
client_scope_mappings=dict(type='dict', aliases=['clientScopeMappings']),
|
||||
default_default_client_scopes=dict(type='list', elements='str', aliases=['defaultDefaultClientScopes']),
|
||||
default_groups=dict(type='list', elements='dict', aliases=['defaultGroups']),
|
||||
default_groups=dict(type='list', elements='str', aliases=['defaultGroups']),
|
||||
default_locale=dict(type='str', aliases=['defaultLocale']),
|
||||
default_optional_client_scopes=dict(type='list', elements='str', aliases=['defaultOptionalClientScopes']),
|
||||
default_roles=dict(type='list', elements='dict', aliases=['defaultRoles']),
|
||||
default_roles=dict(type='list', elements='str', aliases=['defaultRoles']),
|
||||
default_signature_algorithm=dict(type='str', aliases=['defaultSignatureAlgorithm']),
|
||||
direct_grant_flow=dict(type='str', aliases=['directGrantFlow']),
|
||||
display_name=dict(type='str', aliases=['displayName']),
|
||||
|
||||
@@ -64,7 +64,7 @@ msg:
|
||||
|
||||
realm_info:
|
||||
description:
|
||||
- Representation of the realm public infomation.
|
||||
- Representation of the realm public information.
|
||||
returned: always
|
||||
type: dict
|
||||
contains:
|
||||
|
||||
@@ -20,7 +20,7 @@ description:
|
||||
- "Allows to post events to Datadog (www.datadoghq.com) service."
|
||||
- "Uses http://docs.datadoghq.com/api/#events API."
|
||||
author:
|
||||
- "Artūras `arturaz` Šlajus (@arturaz)"
|
||||
- "Artūras 'arturaz' Šlajus (@arturaz)"
|
||||
- "Naoya Nakazawa (@n0ts)"
|
||||
options:
|
||||
api_key:
|
||||
|
||||
@@ -99,7 +99,7 @@ options:
|
||||
weight:
|
||||
description:
|
||||
- The value passed in argument.
|
||||
- If the value ends with the `%` sign, then the new weight will be
|
||||
- If the value ends with the C(%) sign, then the new weight will be
|
||||
relative to the initially configured weight.
|
||||
- Relative weights are only permitted between 0 and 100% and absolute
|
||||
weights are permitted between 0 and 256.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user