Compare commits

..

77 Commits

Author SHA1 Message Date
Andrew Klychkov
4c24a12462 Release 9.2.0 commit (#8640) 2024-07-15 08:11:49 +02:00
patchback[bot]
990fffb563 [PR #8634/a78f7b1e backport][stable-9] #8572 - Updated docs to include pip >= 20.3b1 requirement (#8637)
#8572 - Updated docs to include pip >= 20.3b1 requirement (#8634)

* #8572 - Updated docs to include pip >= 20.3b1 requirement

* Update plugins/modules/pip_package_info.py

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

---------

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

Co-authored-by: Daniel Poggenpohl <danowar2k@googlemail.com>
2024-07-14 22:29:06 +02:00
patchback[bot]
1d9bfc206b [PR #8214/83318c36 backport][stable-9] fix: sudosu not working on some BSD machines (#8631)
fix: sudosu not working on some BSD machines (#8214)

* fix: sudosu not working on some BSD machines

* fix: sudosu: added a flag (`alt_method`) to enhance compatibility with more versions of `su`

* Update changelogs/fragments/8214-sudosu-not-working-on-some-BSD-machines.yml

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

* Update plugins/become/sudosu.py

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

* Update plugins/become/sudosu.py

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

* Update plugins/become/sudosu.py

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

* Update plugins/become/sudosu.py

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

* fix: sudosu: lint

---------

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

Co-authored-by: leko <rexx0520@gmail.com>
2024-07-14 14:22:48 +02:00
patchback[bot]
3b74e9b646 [PR #8614/9dd2b71d backport][stable-9] nsupdate: fix 'index out of range' error when changing NS records (#8630)
nsupdate: fix 'index out of range' error when changing NS records (#8614)

* nsupdate: fix 'index out of range' error when changing NS records

* add clog fragment

* Update changelogs/fragments/8614-nsupdate-index-out-of-range.yml

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

---------

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

Co-authored-by: Art Win <art@make.lv>
2024-07-14 14:11:01 +02:00
patchback[bot]
37308c929b [PR #8510/21b16c1c backport][stable-9] Update VirtualBox Group parsing to align with documentation. (#8621)
Update VirtualBox Group parsing to align with documentation. (#8510)

* Update VirtualBox Group parsing to align with documentation.

Previously, we could separate the group string on the `/` char and
consider each element to be distinct, top-level groups. This change
implements the notion of nested groups and the use of the `,` char to
split multiple groups.

* Address code review comments.

Changed the implementation from a breaking change to a minor change by
introducing a new parameter to configure the behaviour. Keep the default
values to maintain the existing behaviour, and allow consumers an option
to opt-in.

* Fix line length.

The long lines were tripping CI. Reduce the length.

* Apply suggestions from code review

Update documentation to match expected conventions and correct the final rendered formatting.
Set the initial parent_group to `None` instead of `all` and rely on the parent class' inventory reconciliation logic to ensure consistent behaviour across different inventory plugins.

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

* Reword module arg description to avoid issues with CI.

One of the lines ended with a colon character which made the CI tests
fail since it would interpret it as a YAML key. Reworded the description
altogether to avoid that issue.

* Apply suggestions from code review

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

---------

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

Co-authored-by: lyrandy <42095565+lyrandy@users.noreply.github.com>
2024-07-14 12:17:49 +02:00
patchback[bot]
2945509a55 [PR #8542/6cefde62 backport][stable-9] Improve Proxmox volume handling (#8622)
Improve Proxmox volume handling (#8542)

* proxmox: basic linting

using black via trunk.io

* proxmox: refactor mount handling (#8407)

- make mount creation idempotent: Mounts created using the special syntax "<storage>:<size>" no longer create a new volume each time
- add new keys for easier mount creation & management

* proxmox: add changelog fragment

* proxmox(fix): fix occasional syntax error

* Update changelogs/fragments/8542-fix-proxmox-volume-handling.yml

Link to pull request

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

* Update documentation

- Fix options defined as values
- Document mutual exclusivity
- Fix option hierarchy
- Add version_added tag

* Revert "proxmox: basic linting"

This reverts commit ca7214f60e.

* proxmox: Fix documentation

* Fix list identifier in documentation

* pass volume options as dict instead of list

* Update plugins/modules/proxmox.py

Update documentation wording

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

* Update plugins/modules/proxmox.py

Update documentation wording

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

* proxmox: ensure values of `disk_volume` and `mount_volumes.*` dicts are strings

* proxmox(fix): correct indentation

* Apply suggestions from code review: punctuation

Add suggested punctuation to documentation

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

* Update plugins/modules/proxmox.py: vol_string building

Accept suggested review change

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

* proxmox: Use better string check and conversion

---------

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

Co-authored-by: JL Euler <Lithimlin@users.noreply.github.com>
2024-07-14 12:17:41 +02:00
patchback[bot]
57be1e8be4 [PR #8556/9f3103e8 backport][stable-9] Add example to rpm_ostree_pkg (#8620)
Add example to rpm_ostree_pkg (#8556)

* Update rpm_ostree_pkg.py

expand examples list with 'until' example

* Apply suggestions from code review.

---------

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

Co-authored-by: Daskan <kevin81991@web.de>
2024-07-14 12:17:31 +02:00
patchback[bot]
086b4e4fb8 [PR #8595/8990f97b backport][stable-9] New Plugins #8594 (#8618)
New Plugins #8594 (#8595)

* Implement #8594

* Fix lint and BOTMETA entries.

* Fix BOTMETA

* Consolidate argument check, code simplification, and formatting. Remove test vars.

* Fix lint.

* retrigger checks

* Update plugins/plugin_utils/ansible_type.py

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

* Update plugins/test/ansible_type.py

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

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-07-13 14:19:14 +02:00
patchback[bot]
92dcf1e0b2 [PR #8613/ca8ecb1d backport][stable-9] redfish_utils: fix language check (#8617)
redfish_utils: fix language check (#8613)

* redfish_utils: fix language check

* add fragment file

* typo

* improve words

* improve words based on suggestion

---------

Co-authored-by: Mike Koreneff <mkoreneff@hudson-trading.com>
(cherry picked from commit ca8ecb1df1)

Co-authored-by: Mike Koreneff <mkoreneff@users.noreply.github.com>
2024-07-12 23:23:50 +02:00
Felix Fontein
78a02b84f3 Reformat and re-order changelogs/changelog.yaml.
(cherry picked from commit e794fa74da)
2024-07-11 22:47:16 +02:00
patchback[bot]
7846cddab8 [PR #8607/8451fc36 backport][stable-9] Remove EOL'ed FreeBSD 13.2 from CI (#8610)
Remove EOL'ed FreeBSD 13.2 from CI (#8607)

Remove EOL'ed FreeBSD 13.2 from CI.

Apparently the packages are no longer available.

(cherry picked from commit 8451fc36ca)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-07-09 11:34:02 +02:00
patchback[bot]
b0af1e9c75 [PR #8444/feb1ecbf backport][stable-9] Fix to handle Redfish Gen2 Firmware upgrade (#8601)
Fix to handle Redfish Gen2 Firmware upgrade (#8444)

* Fix to handle Redfish Gen2 Firmware upgrade

* Fixed sanity checks and unit test cases

* Added change log gragment

* Updated change log fragment

* Updated review comments

---------

Co-authored-by: Adarsh Manjunath <adarsh.manjunath@wdc.com>
(cherry picked from commit feb1ecbfcd)

Co-authored-by: cmadarsh <53748644+cmadarsh@users.noreply.github.com>
2024-07-08 22:25:30 +02:00
patchback[bot]
f6349578c6 [PR #8592/b0797d32 backport][stable-9] CmdRunner guide (#8602)
CmdRunner guide (#8592)

* initial commit

* wip

* WIP

* progressing

* remove copied part

* progressing

* document as_func()

* complete docs about arg formatting

* add PythonRunner documentation

* fix markup

* improve markup

* multiple changes

- revamp the cmd_runner_fmt doc
- add CmdRunner instance and context parameters
- changed formatting of other blocks

* fix typo + reduce tables sizes

* improve text

* fixes and adjustments

* fix sanity

* fix version added

(cherry picked from commit b0797d329c)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-07-08 22:25:23 +02:00
patchback[bot]
e4287becb1 [PR #8596/45972c23 backport][stable-9] Bump fsfe/reuse-action from 3 to 4 (#8599)
Bump fsfe/reuse-action from 3 to 4 (#8596)

Bumps [fsfe/reuse-action](https://github.com/fsfe/reuse-action) from 3 to 4.
- [Release notes](https://github.com/fsfe/reuse-action/releases)
- [Commits](https://github.com/fsfe/reuse-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: fsfe/reuse-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
(cherry picked from commit 45972c23d4)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 09:27:51 +02:00
Felix Fontein
005e8b151a Prepare 9.2.0 release. 2024-07-07 20:12:41 +02:00
patchback[bot]
cd2e55e2ab [PR #8580/a3989095 backport][stable-9] merge_variables: correct misleading short description (#8591)
merge_variables: correct misleading short description (#8580)

The short description makes it sound like the plugin would only support
matching a given suffix, while the actual description clarifies the
actual matching capabilities (suffix, prefix or regular expression).

Update the short description accordingly.

(cherry picked from commit a3989095af)

Co-authored-by: Elias Probst <mail@eliasprobst.eu>
2024-07-06 21:31:45 +02:00
patchback[bot]
1ff276ec34 [PR #8585/b2c77399 backport][stable-9] Add link to forum (#8588)
Add link to forum (#8585)

Add link to forum.

(cherry picked from commit b2c773996d)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-07-05 23:01:14 +02:00
patchback[bot]
c16c4a32d1 [PR #8517/5259caac backport][stable-9] cmd_runner - handle special value "auto" in param force_lang (#8581)
cmd_runner - handle special value "auto" in param force_lang (#8517)

* cmd_runner - handle special value "auto" in param force_lang

* add changelog frag

* update doc in puppet

* fix markup

(cherry picked from commit 5259caacae)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-07-05 08:54:04 +02:00
patchback[bot]
3a01ceb355 [PR #8482/caecb229 backport][stable-9] Feature. Add chapter 'Lists of dictionaries' to docsite (#8582)
Feature. Add chapter 'Lists of dictionaries' to docsite (#8482)

* Feature. Add chapter 'Lists of dictionaries'

* Fix copyright and licensing.

* Add maintainers for docsite chapter 'Lists of dictionaries'.

* Generate docs keep_keys and remove_keys

* Update integration tests of keep_keys and remove_keys
* Update docs helpers of keep_keys and remove_keys

* Fix copyright and licensing.

* Fix remove license from templates. Cleanup.

* Add docs helper replace_keys

* Update integration test filter_replace_keys
* Generate and update:
  filter_guide-abstract_informations-lists_of_dictionaries-replace_keys.rst

* Formatting improved.

* Fix results Jinja quotation marks.

* Update docs/docsite/helper/keep_keys/filter_guide-abstract_informations-lists_of_dictionaries-keep_keys.rst.j2

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

* Update docs/docsite/helper/keep_keys/filter_guide-abstract_informations-lists_of_dictionaries-keep_keys.rst.j2

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

* Fix references.

* Updated helpers.

* Fix licenses. Simplified templates.

* Fix licenses.

* Fix README.

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-07-05 08:53:55 +02:00
patchback[bot]
8ca91ab283 [PR #8567/01d8c7b7 backport][stable-9] Fix CI for CentOS 7 (#8570)
Fix CI for CentOS 7 (#8567)

Fix CI for CentOS 7.

(cherry picked from commit 01d8c7b769)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-07-01 14:13:22 +02:00
patchback[bot]
c88a40d3e3 [PR #8557/6e0142fe backport][stable-9] bitwarden: Fix KeyError in search_field (#8549) (#8563)
bitwarden: Fix KeyError in search_field (#8549) (#8557)

* bitwarden: Fix KeyError in search_field (#8549)

* Update changelogs/fragments/8557-fix-bug-with-bitwarden.yml

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

---------

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

Co-authored-by: Kai <github@sistason.de>
2024-06-27 10:19:35 +00:00
patchback[bot]
0dfd02c1ca [PR #8516/70c8042c backport][stable-9] proxmox_template: small refactor in get_template() (#8561)
proxmox_template: small refactor in get_template() (#8516)

* proxmox_template: small refactor in get_template()

* add changelog frag

* Update plugins/modules/proxmox_template.py

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

* rename function as per PR suggestion

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-27 12:19:24 +02:00
patchback[bot]
5d9072030e [PR #8545/10535458 backport][stable-9] keycloak_clientscope: ignore ids on diff check (#8554)
keycloak_clientscope: ignore ids on diff check (#8545)

* keycloak_clientscope: ignore ids on diff check

* keycloak_clientscope: add changelog fragment

* keycloak_clientscope: Include changelog fragment change suggestion

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

---------

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

Co-authored-by: Noah Lehmann <62204532+noahlehmann@users.noreply.github.com>
2024-06-22 11:05:25 +02:00
patchback[bot]
49765f103b [PR #8550/9e381614 backport][stable-9] CI: Replace FreeBSD 14.0 with 14.1; add 14.0 for stable-2.17 (#8552)
CI: Replace FreeBSD 14.0 with 14.1; add 14.0 for stable-2.17 (#8550)

* Replace FreeBSD 14.0 with 14.1; add 14.0 for stable-2.17.

* Skip tests that do not work.

(cherry picked from commit 9e38161400)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-22 10:48:45 +02:00
patchback[bot]
1782efb09e [PR #8541/60ba7cab backport][stable-9] add test case for cmd_runner_fmt.as_list() (#8547)
add test case for cmd_runner_fmt.as_list() (#8541)

(cherry picked from commit 60ba7cab93)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-20 21:14:01 +02:00
patchback[bot]
d8807e9b51 [PR #8533/0d50131d backport][stable-9] Enable Custom Cipher Selection for Redfish Modules (#8548)
Enable Custom Cipher Selection for Redfish Modules (#8533)

* Enable custom cipher selection for redfish modules

Signed-off-by: David Ehrman <dlehrman@liberty.edu>

* Add changelog fragment

Signed-off-by: David Ehrman <dlehrman@liberty.edu>

* Added version_added to the ciphers option in redfish modules

Signed-off-by: David Ehrman <dlehrman@liberty.edu>

---------

Signed-off-by: David Ehrman <dlehrman@liberty.edu>
(cherry picked from commit 0d50131d5e)

Co-authored-by: dlehrman <dlehrman@users.noreply.github.com>
2024-06-20 21:13:53 +02:00
patchback[bot]
d823d71442 [PR #8534/86f19cb5 backport][stable-9] Update CI for ansible-core devel (#8536)
* Update CI for ansible-core devel (#8534)

* Update CI for ansible-core devel.

* Uncomment platforms that cause problems.

(cherry picked from commit 86f19cb5d3)

* Finish updating CI (#8537)

* Uncomment TODO entries.

* Exclude some tests that fail or are known to fail.

* Also run extra VM tests on Ubuntu 24.04.

* Fix condition.

* More adjustments.

(cherry picked from commit ecb68aa5d2)

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-19 07:28:45 +02:00
Felix Fontein
46d2cddbde The next expected release will be 9.2.0. 2024-06-17 20:27:30 +02:00
Felix Fontein
069b785cb2 Release 9.1.0. 2024-06-17 19:37:55 +02:00
patchback[bot]
090d3f3709 [PR #8476/2612ceee backport][stable-9] Fix launchd check-mode to report changed correctly (#8529)
Fix launchd check-mode to report changed correctly (#8476)

* Fix launchd check-mode to report changed correctly

* Update changelog fragment.

---------

Co-authored-by: Strahinja Kustudic <strahinjak@nordeus.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2612ceee37)

Co-authored-by: Strahinja Kustudic <kustodian@gmail.com>
2024-06-17 07:55:49 +02:00
patchback[bot]
68a9b66966 [PR #8503/3314d5c8 backport][stable-9] proxmox_kvm: document that force requires archive (#8526)
proxmox_kvm: document that force requires archive (#8503)

* proxmox_kvm: document that force requires archive

As per `qm(1)`, the force option requires `archive`. Add this
information in the `proxmox_kvm` module so one will know they have to
define `archive` when using `force`.

* fix: parameter is an option O(archive)

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

---------

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

Co-authored-by: Bruno Travouillon <devel@travouillon.fr>
2024-06-17 07:24:25 +02:00
patchback[bot]
f96c6476fe [PR #8428/b11da288 backport][stable-9] Keycloak set client authentification flows by name (#8524)
Keycloak set client authentification flows by name (#8428)

* first commit

* Add change logs

* fix sanity

* Sanity 2

* Test unset flows

* Update plugins/modules/keycloak_client.py

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

* Update plugins/modules/keycloak_client.py

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

* Update changelogs/fragments/8428-assign-auth-flow-by-name-keycloak-client.yaml

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

* Remove double traitement from "alias"

* Update plugins/modules/keycloak_client.py

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

* Update plugins/modules/keycloak_client.py

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

---------

Co-authored-by: Andre Desrosiers <andre.desrosiers@ssss.gouv.qc.ca>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit b11da288d2)

Co-authored-by: desand01 <desrosiers.a@hotmail.com>
2024-06-17 07:24:17 +02:00
patchback[bot]
fc0f677535 [PR #8514/df7fe19b backport][stable-9] pacman: do not fail if there is nothing to do (#8523)
pacman: do not fail if there is nothing to do (#8514)

Do not fail if there is nothing to do.

(cherry picked from commit df7fe19bbe)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-17 07:24:08 +02:00
patchback[bot]
9a986473bd [PR #8480/69b72e4a backport][stable-9] cargo module install from source in a given directory (#8527)
`cargo` module install from source in a given directory (#8480)

* Fixes installed version for git/local.

* Support latest determination with local source.

* Adds docs.

* Improves error message.

* Setup for tests.

* Updates copyright.

* Align closer to #7895.

* Adds changelog.

* Check directory exists.

* Stop using format strings.

* Corrects directory arg type in docs.

* Setup test repo dynamically.

* Adds tests.

* Adds version matching tests.

* Update changelog fragment to match PR ID.

* Updates copyright.

* Import new directory tests.

(cherry picked from commit 69b72e4a8e)

Co-authored-by: Colin Nolan <colin-nolan@users.noreply.github.com>
2024-06-17 07:24:00 +02:00
patchback[bot]
e9f0e49283 [PR #8496/fd2cd5f2 backport][stable-9] keycloak_clientscope: add normalizations for attributes and protocol_mappers (#8521)
keycloak_clientscope: add normalizations for attributes and protocol_mappers (#8496)

Signed-off-by: Eike Waldt <waldt@b1-systems.de>
(cherry picked from commit fd2cd5f28c)

Co-authored-by: Eike Waldt <waldt@b1-systems.de>
2024-06-16 22:24:45 +02:00
patchback[bot]
5eff31e760 [PR #8489/3716187f backport][stable-9] Fix OpenNebula inventory crash when NIC does not have IP (#8519)
Fix OpenNebula inventory crash when NIC does not have IP (#8489)

* Fix OpenNebula inventory crash when NIC does not have IP

Match IPv6 behaviour.

When a NIC does not have an IP:
  File "ansible/inventory/manager.py", line 292, in parse_source
    plugin.parse(self._inventory, self._loader, source, cache=cache)
  File "ansible-cm/plugins/inventory/opennebula.py", line 263, in parse
    self._populate()
  File "ansible-cm/plugins/inventory/opennebula.py", line 226, in _populate
    servers = self._retrieve_servers(filter_by_label)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "ansible-cm/plugins/inventory/opennebula.py", line 210, in _retrieve_servers
    server['v4_first_ip'] = self._get_vm_ipv4(vm)
                            ^^^^^^^^^^^^^^^^^^^^^
  File "ansible-cm/plugins/inventory/opennebula.py", line 154, in _get_vm_ipv4
    return net['IP']

* Update to call to match IPv6 and add changelog fragment

* Update changelog fragment.

---------

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

Co-authored-by: Tom Paine <github@aioue.net>
2024-06-16 22:24:35 +02:00
Felix Fontein
39c58d5469 Prepare 9.1.0 release. 2024-06-16 21:12:20 +02:00
patchback[bot]
20d7be4f38 [PR #8512/ec4cf555 backport][stable-9] simplify cmd_runner_fmt.as_bool_not() (#8515)
simplify cmd_runner_fmt.as_bool_not() (#8512)

* simplify cmd_runner_fmt.as_bool_not()

* add changelog frag

(cherry picked from commit ec4cf55566)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-16 10:10:55 +02:00
patchback[bot]
2d26fba0b9 [PR #7989/03966624 backport][stable-9] Consul implement agent service and check (#8513)
Consul implement agent service and check (#7989)

* Implement agent service and check (#7987)

* implement update of service and check

* update tests
update documentation

* update documentation

* add consul_agent_check/service to action_groups

check if unique_identifier of name is in params to get object

add suggested improvements

* update sanity

* fix sanity issues
update documentation

* fix naming

* fix naming

check if response_data has data

* fix sanity extra-docs

* add as ignore maintainer in BOTMETA.yml
update version_added to 8.4

* fix sanity

* add to maintainers

* Update plugins/modules/consul_agent_check.py

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

* Update plugins/modules/consul_agent_check.py

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

* Update plugins/modules/consul_agent_check.py

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

* update version_added

* if create and update return no object as result we read the object again

* get_first_appearing_identifier check the params for the given identifier and return it to simplify id vs name

* add unique_identifiers as a new property and a method to decide which identifier should be used

* fix sanity

* add self to team consul
remove params with no values
add operational_attributes that inherited classes can set them
get identifier value from object

* fix sanity
fix test

* remove the possibility to add checks with consul_agent_check.
check if service has changed

* remove tests for idempotency check because for checks it is not possible

* remove unique_identifier from consul.py
change unique_identifier to unique_identifiers

* get id from params

* Revert "remove unique_identifier from consul.py"

This reverts commit a4f0d0220dd23e95871914b152c25ff352097a2c.

* update version to 8.5

* Revert "Revert "remove unique_identifier from consul.py""

This reverts commit d2c35cf04c8aaf5f0175d772f862a796e22e35d4.

* update description
update test

* fix sanity tests

* fix sanity tests

* update documentation for agent_check

* fix line length

* add documentation

* fix sanity

* simplified check for Tcp

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

* check duration with regex

* fix

* update documentation

---------

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

Co-authored-by: Ilgmi <michael.ilg@mailbox.org>
2024-06-16 10:10:45 +02:00
patchback[bot]
d6168a196b [PR #8446/1d615419 backport][stable-9] Feature filter replace_keys (#8507)
Feature filter replace_keys (#8446)

* Add filter replace_keys.

* Update examples and integration tests.

* Fix examples and copyright.

* Update documentation, examples and integration tests.

* Implement #8445. Add filter replace_keys

* Fix documentation formatting.

* Fix documentation.

* Fix type(target). Formatting improved.

* Instead of a dictionary, _keys_filter_target_dict returns a list

* No target testing in _keys_filter_params
* Interface changed _keys_filter_params(data, matching_parameter)
* If there are items with equal C(before) the B(first) one will be used.

* Update remove_keys. Interface changed _keys_filter_params(data, matching_parameter)

* The target can't be empty also in _keys_filter_target_dict

* Update plugins/filter/replace_keys.py

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

* Update plugins/filter/replace_keys.py

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

* Update plugins/filter/replace_keys.py

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

* Test attributes before and after are strings in the iteration of target.

* Update plugins/filter/replace_keys.py

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

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-06-15 12:00:09 +02:00
patchback[bot]
02de81c39e [PR #8509/d95f4d68 backport][stable-9] fix version vardict was introduced (#8511)
fix version vardict was introduced (#8509)

(cherry picked from commit d95f4d68a3)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-15 11:52:12 +02:00
patchback[bot]
4096b9fa5a [PR #8479/71f96748 backport][stable-9] cmd_runner mod util: improvements (#8504)
cmd_runner mod util: improvements (#8479)

* deprecate ignore_none in context

* add changelog frag

* raise deprecation notice when passing ignore_value_none to context

* simplify deprecation logic

(cherry picked from commit 71f9674835)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-14 07:55:02 +02:00
patchback[bot]
fe3a3a7638 [PR #8471/2574cb0d backport][stable-9] feat: proxmox_vm_info - add network information for guests (#8505)
feat: proxmox_vm_info - add network information for guests (#8471)

* feat: add network information for guests

- Uses agent information for qemu-vms
- Uses network information for lxc container

* chore: add changelog fragment

* fix: change default, add doc

* chore: clarify doc

* chore: add optional ,

* chore: fix pep8 indentation warning

* Update plugins/modules/proxmox_vm_info.py

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

* Update plugins/modules/proxmox_vm_info.py

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

---------

Co-authored-by: Jan Wenzel <jan.wenzel@gonicus.de>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 2574cb0dea)

Co-authored-by: Jan Wenzel <jan@coffeelover.de>
2024-06-14 07:54:53 +02:00
patchback[bot]
7cac741e77 [PR #8500/49e2a863 backport][stable-9] Add Python 3.13 to CI (#8502)
Add Python 3.13 to CI (#8500)

Add Python 3.13 to CI.

(cherry picked from commit 49e2a8633e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-14 07:12:20 +02:00
patchback[bot]
f84ebed63f [PR #8497/f0940d82 backport][stable-9] homectl, udm_user: guard crypt imports (#8499)
homectl, udm_user: guard crypt imports (#8497)

Guard crypt import.

(cherry picked from commit f0940d82dc)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-13 22:01:33 +02:00
patchback[bot]
f905a1bc94 [PR #8475/8f60f3ae backport][stable-9] Update docs lists_mergeby (#8495)
Update docs lists_mergeby (#8475)

* Fix #8474. Complete examples and documentation of lists_mergeby

* Fix docs syntax O(_input)

* Update docs.

* Update plugins/filter/lists_mergeby.py

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

* Update plugins/filter/lists_mergeby.py

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

* Update plugins/filter/lists_mergeby.py

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

* Update plugins/filter/lists_mergeby.py

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

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-06-13 07:48:02 +02:00
patchback[bot]
b0470f2e59 [PR #8464/800bc011 backport][stable-9] feat(redis_info): add option to fetch cluster info (#8493)
feat(redis_info): add option to fetch cluster info (#8464)

* feat(redis_info): add option to fetch cluster info

* add changelog fragment

* update description

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

* Apply suggestions from code review

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

---------

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

Co-authored-by: joris <5111464+tyxieblub@users.noreply.github.com>
2024-06-13 07:19:15 +02:00
patchback[bot]
42175e38b2 [PR #8477/ac3c0435 backport][stable-9] Update docsite chapter "Merging lists of dictionaries" (#8492)
Update docsite chapter "Merging lists of dictionaries" (#8477)

* Update docs 'Merging lists of dictionaries'

* Adding links to module and plugin options in
  docs/docsite/helper/lists_mergeby
* Add subsections and improve formatting.
* Add example-009 'Merge single list'

* Fix licenses.

* Fix variables.

* Update docs/docsite/helper/lists_mergeby/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst.j2

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

* Update docs/docsite/rst/filter_guide_abstract_informations_merging_lists_of_dictionaries.rst

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

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-06-13 07:19:02 +02:00
patchback[bot]
8e79844b75 [PR #8454/c31499a4 backport][stable-9] django_check: new module (#8490)
django_check: new module (#8454)

* django_check: new module

* sanity fix

* working version

* remove unused import

* add note about the module output

* add note on module failing when rc!=0

(cherry picked from commit c31499a411)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-13 07:18:54 +02:00
patchback[bot]
1338db358a [PR #8483/1ae6c825 backport][stable-9] CI: Bump Azure test container to 6.0.0 (#8486)
CI: Bump Azure test container to 6.0.0 (#8483)

Bump Azure test container to 6.0.0.

(cherry picked from commit 1ae6c82558)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-10 21:21:34 +02:00
patchback[bot]
06c4439a1c [PR #8441/d2d7deb4 backport][stable-9] #8440 Allow for API Port to be specified when using proxmox_kvm (#8478)
#8440 Allow for API Port to be specified when using proxmox_kvm (#8441)

* added api_port

* added changelog fragments for #8440

* api_port minor changes

- Added documentation on api_port
- Fixed multiple spaces after operator
- Switched from str to int

* Update changelogs/fragments/8440-allow-api-port-specification.yaml

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

* Update changelogs/fragments/8440-allow-api-port-specification.yaml

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

* Update plugins/doc_fragments/proxmox.py

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

* Update plugins/doc_fragments/proxmox.py

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

---------

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

Co-authored-by: Thomas Gouverneur <thomas@espix.net>
2024-06-08 14:59:27 +02:00
patchback[bot]
16d5d5fc57 [PR #8443/06f13e79 backport][stable-9] Feature filter remove_keys (#8473)
Feature filter remove_keys (#8443)

* Add filter remove_keys.

* Add filter remove_keys integration test, fragment, and maintainer.

* Update with plugins/plugin_utils/keys_filter.py

* Update according PR #8456

* Update maintainers.

* Fix typo in return doc.

* Remove local keys_filter.py. Then rebase.

* Add local keys_filter.py

* Update plugins/filter/remove_keys.py

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

* Update plugins/filter/remove_keys.py

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

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-06-07 07:14:20 +02:00
patchback[bot]
71af3226f3 [PR #8460/a0ad2d58 backport][stable-9] add docs for the vardict module utils (#8467)
add docs for the vardict module utils (#8460)

* add docs for the vardict module utils

* fix var name

* add entry to BOTMETA

* rollback adjustment in deps guide

* Update docs/docsite/rst/guide_vardict.rst

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

* adjustments

* Update docs/docsite/rst/guide_vardict.rst

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

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-06 13:19:10 +02:00
patchback[bot]
b3037a46be [PR #8452/2a3819a6 backport][stable-9] git_config: fix state=absent if value is present (#8466)
git_config: fix state=absent if value is present (#8452)

* Fix state=absent if value is present.

* Update changelog fragment.

(cherry picked from commit 2a3819a696)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-06 13:19:02 +02:00
patchback[bot]
f7df19adbd [PR #8404/1c4ab7fa backport][stable-9] Add support for SSHFP records to ipa_dnsrecord module (#8468)
Add support for SSHFP records to ipa_dnsrecord module (#8404)

* Add support for SSHFP records to ipa_dnsrecord module

* Create 8404-ipa_dnsrecord_sshfp.yml

* Apply suggestions from code review

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

* Fix a typo in the example for ipa_dnsrecord with type SSHFP

* Update plugins/modules/ipa_dnsrecord.py

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

---------

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

Co-authored-by: Daniel <mail@h3po.de>
2024-06-06 13:18:50 +02:00
patchback[bot]
3bca21aa1b [PR #8456/6f8f12f7 backport][stable-9] Feature filter keep_keys (#8462)
Feature filter keep_keys (#8456)

* Add filter keep_keys. Implement feature request #8438

* Fix comment indentation.

* Fix regex reference.

* Fix indentation.

* Fix isinstance list.

* Update plugins/plugin_utils/keys_filter.py

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

* Update plugins/plugin_utils/keys_filter.py

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

* Update plugins/plugin_utils/keys_filter.py

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

* Update plugins/plugin_utils/keys_filter.py

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

* Update plugins/filter/keep_keys.py

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

* Update documentation, examples, and integration tests.

* _keys_filter_target_str returns tuple of unique target strings if
target is list. Update documentation, function comments, and error
messages.

* Sort maintainers.

* Update plugins/filter/keep_keys.py

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

* Update examples with explicit collection.

---------

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

Co-authored-by: Vladimir Botka <vbotka@gmail.com>
2024-06-04 07:45:13 +02:00
patchback[bot]
1bb3d41e15 [PR #8453/0129346e backport][stable-9] git_config: deprecate reading values (#8463)
git_config: deprecate reading values (#8453)

Deprecate reading values.

(cherry picked from commit 0129346eda)

Co-authored-by: Felix Fontein <felix@fontein.de>
2024-06-04 06:35:46 +02:00
patchback[bot]
f214f206c3 [PR #8434/961767e2 backport][stable-9] Redfish: Add options to check the availability of the service (#8457)
Redfish: Add options to check the availability of the service (#8434)

* Redfish: Add options to check the availability of the service

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

* Updates based on review feedback

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

* Updated comment to reflect changed behavior

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

* Added changelog fragments

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

* Update changelogs/fragments/8051-Redfish-Wait-For-Service.yml

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

* Update plugins/modules/redfish_command.py

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

* Update plugins/modules/redfish_command.py

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

* Update plugins/modules/redfish_command.py

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

* Update plugins/modules/redfish_command.py

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

---------

Signed-off-by: Mike Raineri <michael.raineri@dell.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit 961767e2dd)

Co-authored-by: Mike Raineri <mraineri@gmail.com>
2024-06-03 07:15:58 +02:00
patchback[bot]
9b8011d692 [PR #8430/5041ebe5 backport][stable-9] fix(opentelemetry): remove request from the logs (#8458)
fix(opentelemetry): remove request from the logs (#8430)

* fix(opentelemetry): remove request from the logs

* add changelog

* filter by task

* add new bugfix

* rename

* support legacy and shortcat ansible tasks

* Update plugins/callback/opentelemetry.py

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

---------

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

Co-authored-by: Victor Martinez <victormartinezrubio@gmail.com>
2024-06-03 07:15:49 +02:00
patchback[bot]
f227038f38 [PR #8431/d46e12e2 backport][stable-9] ansible_galaxy_install: add upgrade feature (#8451)
ansible_galaxy_install: add upgrade feature (#8431)

* add upgrade feature

* add changelog frag

* Update plugins/modules/ansible_galaxy_install.py

* Update plugins/modules/ansible_galaxy_install.py

(cherry picked from commit d46e12e280)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-02 00:25:32 +02:00
patchback[bot]
86a2996814 [PR #8417/5a5188a4 backport][stable-9] deps module_utils: add docs (#8450)
deps module_utils: add docs  (#8417)

* add docs for the deps module utils

* wordsmithing

* fix reference and filename

* add entries to BOTMETA.yml

* Update docs/docsite/rst/moddev_guide_deps.rst

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

* adjust docs organisation

* adjust docs organisation II

* PR adjustments

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-02 00:25:24 +02:00
patchback[bot]
eb154003cf [PR #8412/54df0c9b backport][stable-9] django_createcachetable: new module (#8449)
django_createcachetable: new module (#8412)

* django_createcachetabe: new module

* add --noinput arg to testcase

* add module to BOTMETA

* rename module class name

* fix examples documentation

* remove unused config

* adjust version_added

(cherry picked from commit 54df0c9b3a)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-01 22:54:20 +02:00
patchback[bot]
212871fcaf [PR #8415/7d72300c backport][stable-9] add cmd_runner_fmt.stack decorator (#8448)
add cmd_runner_fmt.stack decorator (#8415)

* add cmd_runner_fmt.stack decorator

* fix sanity

* fix typo

* add changelog frag

(cherry picked from commit 7d72300c36)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-06-01 22:54:14 +02:00
patchback[bot]
1795a67b8e [PR #8402/e690317e backport][stable-9] Add partial diff support, not in check mode to openbsd_pkg (#8447)
Add partial diff support, not in check mode to openbsd_pkg (#8402)

* Add partial diff support, not in check mode

* Add changelog fragment

* Fix PEP8. Want to run Black against this so badly.

* Update changelogs/fragments/8402-add-diif-mode-openbsd-pkg.yml

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

* Update plugins/modules/openbsd_pkg.py

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

* Update plugins/modules/openbsd_pkg.py

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

* Remove unneeded comment

---------

Co-authored-by: Allen Smith <allsmith@allsmith.users.ipa.redhat.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit e690317e3a)

Co-authored-by: Allen Smith <lazlor@lotaris.org>
2024-06-01 22:09:42 +02:00
Felix Fontein
a71c0af9cc The next expected release is 9.1.0. 2024-05-27 20:52:56 +02:00
Felix Fontein
569cde6c3e Release 9.0.1. 2024-05-27 20:24:06 +02:00
patchback[bot]
f0db1d1f6b [PR #8406/43cb5a0d backport][stable-9] Fix the homebrew module failing because of warnings (#8425)
Fix the homebrew module failing because of warnings (#8406)

Instead of checking if there is an error message, which can also be a
warning, we now check the return code.

This commit fixes #8229 #7044

Co-authored-by: Strahinja Kustudic <strahinjak@nordeus.com>
(cherry picked from commit 43cb5a0d54)

Co-authored-by: Strahinja Kustudic <kustodian@gmail.com>
2024-05-26 21:17:46 +02:00
patchback[bot]
5a36e84b86 [PR #8403/572caeaa backport][stable-9] keycloak_client: avoid TypeError if result["attributes"] is a list (#8427)
keycloak_client: avoid TypeError if `result["attributes"]` is a list (#8403)

* fix(keycloak_client): avoid TypeError if attributes is a list

As sanitize_cr might be executed after normalise_cr, result['attributes'] can be of type list and we
run into:

TypeError: list indices must be integers or slices, not str

* Update changelog fragment.

---------

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

Co-authored-by: Thomas Bach <63091663+thomasbach-dev@users.noreply.github.com>
2024-05-26 21:17:37 +02:00
Felix Fontein
a74c6db77f Prepare 9.0.1 release. 2024-05-26 16:40:36 +02:00
patchback[bot]
9a14980ca7 [PR #8411/ec886203 backport][stable-9] multiple modules: use new vardict in module (#8419)
multiple modules: use new vardict in module (#8411)

* use new vardict in module

* add changelog frag

* Update changelogs/fragments/8411-locale-gen-vardict.yml

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

* set use_old_vardict to false in snap

* set use_old_vardict to false in cpanm

* set use_old_vardict to false in django mod helper

* set use_old_vardict to false in gconftool2_info

* set use_old_vardict to false in kernel_blacklist

* set use_old_vardict to false in mksysb

* set use_old_vardict to false in pipx_info

* set use_old_vardict to false in snap_alias

* update chglog frag

* fix typo

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-05-26 14:13:46 +02:00
patchback[bot]
8c9effce1f [PR #8413/e7ee90a9 backport][stable-9] ansible_galaxy_install: minor refactor (#8420)
ansible_galaxy_install: minor refactor (#8413)

* minor refactor

* add changelog frag

* remove commented code

* set use_old_vardict to false

(cherry picked from commit e7ee90a937)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-05-26 14:08:15 +02:00
patchback[bot]
51ec3594dd [PR #8309/d96b2642 backport][stable-9] Update lxd_container.py documentation. (#8422)
Update lxd_container.py documentation. (#8309)

remove dead link from debian example

(cherry picked from commit d96b2642bc)

Co-authored-by: Herschdorfer <herschdorfer@gmail.com>
2024-05-26 14:08:04 +02:00
patchback[bot]
802f8ea224 [PR #8387/da2c87ce backport][stable-9] fix test helper handling of rc (#8414)
fix test helper handling of rc (#8387)

* fix test helper handling of rc

* fix side_effect logic for rc != 0

* fix side_effect func + sanity tests

* fix ignore files

* fix code

* revamp the generator for run_command calls returns in testcase

* remove unused import

* Update tests/sanity/ignore-2.18.txt

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

* Update tests/sanity/ignore-2.17.txt

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

---------

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-05-24 22:11:42 +02:00
patchback[bot]
23af148021 [PR #8396/4792e214 backport][stable-9] docs: add seealso and notes for yaml callback plugin (#8397)
docs: add seealso and notes for yaml callback plugin (#8396)

* docs: add seealso and notes for yaml callback plugin

* docs: correct links to parameters

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

* docs: shorten the long lines

---------

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

Co-authored-by: kurokobo <kuro664@gmail.com>
2024-05-20 16:18:26 +02:00
Felix Fontein
1a2c2d0a64 The next expected release will be 9.0.1. 2024-05-20 13:14:06 +02:00
Felix Fontein
80243f8180 Release 9.0.0. 2024-05-20 12:41:10 +02:00
Felix Fontein
13b5c4092a Update CI cron schedules.
(cherry picked from commit 95e509753e)
2024-05-20 12:36:14 +02:00
1098 changed files with 48571 additions and 69310 deletions

View File

@@ -29,20 +29,22 @@ schedules:
always: true
branches:
include:
- stable-10
- stable-9
- stable-8
- cron: 0 11 * * 0
displayName: Weekly (old stable branches)
always: true
branches:
include:
- stable-8
- stable-7
variables:
- name: checkoutPath
value: ansible_collections/community/general
- name: coverageBranches
value: main
- name: pipelinesCoverage
value: coverage
- name: entryPoint
value: tests/utils/shippable/shippable.sh
- name: fetchDepth
@@ -71,19 +73,6 @@ stages:
- test: 3
- test: 4
- test: extra
- stage: Sanity_2_18
displayName: Sanity 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.18/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_17
displayName: Sanity 2.17
dependsOn: []
@@ -110,6 +99,19 @@ stages:
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_15
displayName: Sanity 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.15/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
### Units
- stage: Units_devel
displayName: Units devel
@@ -126,17 +128,6 @@ stages:
- test: '3.11'
- test: '3.12'
- test: '3.13'
- stage: Units_2_18
displayName: Units 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.18/units/{0}/1
targets:
- test: 3.8
- test: "3.13"
- stage: Units_2_17
displayName: Units 2.17
dependsOn: []
@@ -160,6 +151,17 @@ stages:
- test: 2.7
- test: 3.6
- test: "3.11"
- stage: Units_2_15
displayName: Units 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.15/units/{0}/1
targets:
- test: 3.5
- test: "3.10"
## Remote
- stage: Remote_devel_extra_vms
@@ -170,10 +172,10 @@ stages:
parameters:
testFormat: devel/{0}
targets:
- name: Alpine 3.21
test: alpine/3.21
# - name: Fedora 41
# test: fedora/41
- name: Alpine 3.20
test: alpine/3.20
# - name: Fedora 40
# test: fedora/40
- name: Ubuntu 22.04
test: ubuntu/22.04
- name: Ubuntu 24.04
@@ -190,24 +192,6 @@ stages:
targets:
- name: macOS 14.3
test: macos/14.3
- name: RHEL 9.5
test: rhel/9.5
- name: FreeBSD 14.2
test: freebsd/14.2
- name: FreeBSD 13.4
test: freebsd/13.4
groups:
- 1
- 2
- 3
- stage: Remote_2_18
displayName: Remote 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.18/{0}
targets:
- name: RHEL 9.4
test: rhel/9.4
- name: FreeBSD 14.1
@@ -248,14 +232,34 @@ stages:
test: rhel/9.2
- name: RHEL 8.8
test: rhel/8.8
- name: RHEL 7.9
test: rhel/7.9
# - name: FreeBSD 13.2
# test: freebsd/13.2
groups:
- 1
- 2
- 3
- stage: Remote_2_15
displayName: Remote 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.15/{0}
targets:
- name: RHEL 9.1
test: rhel/9.1
- name: RHEL 8.7
test: rhel/8.7
- name: RHEL 7.9
test: rhel/7.9
# - name: FreeBSD 13.1
# test: freebsd/13.1
# - name: FreeBSD 12.4
# test: freebsd/12.4
groups:
- 1
- 2
- 3
### Docker
- stage: Docker_devel
@@ -265,31 +269,13 @@ stages:
- template: templates/matrix.yml
parameters:
testFormat: devel/linux/{0}
targets:
- name: Fedora 41
test: fedora41
- name: Alpine 3.21
test: alpine321
- name: Ubuntu 22.04
test: ubuntu2204
- name: Ubuntu 24.04
test: ubuntu2404
groups:
- 1
- 2
- 3
- stage: Docker_2_18
displayName: Docker 2.18
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.18/linux/{0}
targets:
- name: Fedora 40
test: fedora40
- name: Alpine 3.20
test: alpine320
- name: Ubuntu 22.04
test: ubuntu2204
- name: Ubuntu 24.04
test: ubuntu2404
groups:
@@ -328,6 +314,20 @@ stages:
test: opensuse15
- name: Alpine 3
test: alpine3
groups:
- 1
- 2
- 3
- stage: Docker_2_15
displayName: Docker 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.15/linux/{0}
targets:
- name: Fedora 37
test: fedora37
- name: CentOS 7
test: centos7
groups:
@@ -349,86 +349,84 @@ stages:
- name: Debian Bookworm
test: debian-bookworm/3.11
- name: ArchLinux
test: archlinux/3.13
test: archlinux/3.12
groups:
- 1
- 2
- 3
### Generic
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - stage: Generic_devel
# displayName: Generic devel
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: devel/generic/{0}/1
# targets:
# - test: '3.8'
# - test: '3.11'
# - test: '3.13'
# - stage: Generic_2_18
# displayName: Generic 2.18
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: 2.18/generic/{0}/1
# targets:
# - test: '3.8'
# - test: '3.13'
# - stage: Generic_2_17
# displayName: Generic 2.17
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: 2.17/generic/{0}/1
# targets:
# - test: '3.7'
# - test: '3.12'
# - stage: Generic_2_16
# displayName: Generic 2.16
# dependsOn: []
# jobs:
# - template: templates/matrix.yml
# parameters:
# nameFormat: Python {0}
# testFormat: 2.16/generic/{0}/1
# targets:
# - test: '2.7'
# - test: '3.6'
# - test: '3.11'
- stage: Generic_devel
displayName: Generic devel
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: devel/generic/{0}/1
targets:
- test: '3.8'
- test: '3.11'
- test: '3.13'
- stage: Generic_2_17
displayName: Generic 2.17
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.17/generic/{0}/1
targets:
- test: '3.7'
- test: '3.12'
- stage: Generic_2_16
displayName: Generic 2.16
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.16/generic/{0}/1
targets:
- test: '2.7'
- test: '3.6'
- test: '3.11'
- stage: Generic_2_15
displayName: Generic 2.15
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.15/generic/{0}/1
targets:
- test: '3.9'
- stage: Summary
condition: succeededOrFailed()
dependsOn:
- Sanity_devel
- Sanity_2_18
- Sanity_2_17
- Sanity_2_16
- Sanity_2_15
- Units_devel
- Units_2_18
- Units_2_17
- Units_2_16
- Units_2_15
- Remote_devel_extra_vms
- Remote_devel
- Remote_2_18
- Remote_2_17
- Remote_2_16
- Remote_2_15
- Docker_devel
- Docker_2_18
- Docker_2_17
- Docker_2_16
- Docker_2_15
- Docker_community_devel
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - Generic_devel
# - Generic_2_18
# - Generic_2_17
# - Generic_2_16
# - Generic_2_15
jobs:
- template: templates/coverage.yml

View File

@@ -28,6 +28,16 @@ jobs:
- bash: .azure-pipelines/scripts/report-coverage.sh
displayName: Generate Coverage Report
condition: gt(variables.coverageFileCount, 0)
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: Cobertura
# Azure Pipelines only accepts a single coverage data file.
# That means only Python or PowerShell coverage can be uploaded, but not both.
# Set the "pipelinesCoverage" variable to determine which type is uploaded.
# Use "coverage" for Python and "coverage-powershell" for PowerShell.
summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
displayName: Publish to Azure Pipelines
condition: gt(variables.coverageFileCount, 0)
- bash: .azure-pipelines/scripts/publish-codecov.py "$(outputPath)"
displayName: Publish to codecov.io
condition: gt(variables.coverageFileCount, 0)

90
.github/BOTMETA.yml vendored
View File

@@ -61,6 +61,7 @@ files:
$callbacks/elastic.py:
keywords: apm observability
maintainers: v1v
$callbacks/hipchat.py: {}
$callbacks/jabber.py: {}
$callbacks/log_plays.py: {}
$callbacks/loganalytics.py:
@@ -111,9 +112,6 @@ files:
$connections/lxd.py:
labels: lxd
maintainers: mattclay
$connections/proxmox_pct_remote.py:
labels: proxmox
maintainers: mietzen
$connections/qubes.py:
maintainers: kushaldas
$connections/saltstack.py:
@@ -123,8 +121,6 @@ files:
maintainers: $team_ansible_core
$doc_fragments/:
labels: docs_fragments
$doc_fragments/clc.py:
maintainers: clc-runner russoz
$doc_fragments/django.py:
maintainers: russoz
$doc_fragments/hpe3par.py:
@@ -135,13 +131,9 @@ files:
maintainers: $team_huawei
$doc_fragments/nomad.py:
maintainers: chris93111 apecnascimento
$doc_fragments/pipx.py:
maintainers: russoz
$doc_fragments/xenserver.py:
labels: xenserver
maintainers: bvitnik
$filters/accumulate.py:
maintainers: VannTen
$filters/counter.py:
maintainers: keilr
$filters/crc32.py:
@@ -164,14 +156,6 @@ files:
maintainers: Ajpantuso
$filters/jc.py:
maintainers: kellyjonbrazil
$filters/json_diff.yml:
maintainers: numo68
$filters/json_patch.py:
maintainers: numo68
$filters/json_patch.yml:
maintainers: numo68
$filters/json_patch_recipe.yml:
maintainers: numo68
$filters/json_query.py: {}
$filters/keep_keys.py:
maintainers: vbotka
@@ -226,8 +210,6 @@ files:
maintainers: opoplawski
$inventories/gitlab_runners.py:
maintainers: morph027
$inventories/iocage.py:
maintainers: vbotka
$inventories/icinga2.py:
maintainers: BongoEADGC6
$inventories/linode.py:
@@ -307,8 +289,6 @@ files:
$lookups/onepassword_raw.py:
ignore: scottsb
maintainers: azenk
$lookups/onepassword_ssh_key.py:
maintainers: mohammedbabelly20
$lookups/passwordstore.py: {}
$lookups/random_pet.py:
maintainers: Akasurde
@@ -326,12 +306,8 @@ files:
maintainers: delineaKrehl tylerezimmerman
$module_utils/:
labels: module_utils
$module_utils/android_sdkmanager.py:
maintainers: shamilovstas
$module_utils/btrfs.py:
maintainers: gnfzdz
$module_utils/cmd_runner_fmt.py:
maintainers: russoz
$module_utils/cmd_runner.py:
maintainers: russoz
$module_utils/deps.py:
@@ -439,8 +415,6 @@ files:
ignore: DavidWittman jiuka
labels: alternatives
maintainers: mulby
$modules/android_sdk.py:
maintainers: shamilovstas
$modules/ansible_galaxy_install.py:
maintainers: russoz
$modules/apache2_mod_proxy.py:
@@ -471,11 +445,9 @@ files:
$modules/bearychat.py:
maintainers: tonyseek
$modules/bigpanda.py:
ignore: hkariti
maintainers: hkariti
$modules/bitbucket_:
maintainers: catcombo
$modules/bootc_manage.py:
maintainers: cooktheryan
$modules/bower.py:
maintainers: mwarkentin
$modules/btrfs_:
@@ -529,8 +501,6 @@ files:
ignore: skornehl
$modules/dconf.py:
maintainers: azaghal
$modules/decompress.py:
maintainers: shamilovstas
$modules/deploy_helper.py:
maintainers: ramondelafuente
$modules/dimensiondata_network.py:
@@ -676,11 +646,6 @@ files:
labels: homebrew_ macos
maintainers: $team_macos
notify: chris-short
$modules/homebrew_services.py:
ignore: ryansb
keywords: brew cask services darwin homebrew macosx macports osx
labels: homebrew_ macos
maintainers: $team_macos kitizz
$modules/homectl.py:
maintainers: jameslivulpi
$modules/honeybadger_deployment.py:
@@ -740,8 +705,6 @@ files:
$modules/ipa_:
maintainers: $team_ipa
ignore: fxfitz
$modules/ipa_getkeytab.py:
maintainers: abakanovskii
$modules/ipa_dnsrecord.py:
maintainers: $team_ipa jwbernin
$modules/ipbase_info.py:
@@ -787,8 +750,6 @@ files:
maintainers: sermilrod
$modules/jenkins_job_info.py:
maintainers: stpierre
$modules/jenkins_node.py:
maintainers: phyrwork
$modules/jenkins_plugin.py:
maintainers: jtyr
$modules/jenkins_script.py:
@@ -825,8 +786,6 @@ files:
maintainers: fynncfchen johncant
$modules/keycloak_clientsecret_regenerate.py:
maintainers: fynncfchen johncant
$modules/keycloak_component.py:
maintainers: fivetide
$modules/keycloak_group.py:
maintainers: adamgoossens
$modules/keycloak_identity_provider.py:
@@ -843,8 +802,6 @@ files:
maintainers: elfelip
$modules/keycloak_user_federation.py:
maintainers: laurpaum
$modules/keycloak_userprofile.py:
maintainers: yeoldegrove
$modules/keycloak_component_info.py:
maintainers: desand01
$modules/keycloak_client_rolescope.py:
@@ -859,8 +816,6 @@ files:
maintainers: ahussey-redhat
$modules/kibana_plugin.py:
maintainers: barryib
$modules/krb_ticket.py:
maintainers: abakanovskii
$modules/launchd.py:
maintainers: martinm82
$modules/layman.py:
@@ -871,8 +826,6 @@ files:
maintainers: drybjed jtyr noles
$modules/ldap_entry.py:
maintainers: jtyr
$modules/ldap_inc.py:
maintainers: pduveau
$modules/ldap_passwd.py:
maintainers: KellerFuchs jtyr
$modules/ldap_search.py:
@@ -1014,8 +967,6 @@ files:
maintainers: $team_opennebula
$modules/one_host.py:
maintainers: rvalle
$modules/one_vnet.py:
maintainers: abakanovskii
$modules/oneandone_:
maintainers: aajdinov edevenport
$modules/onepassword_info.py:
@@ -1144,10 +1095,6 @@ files:
$modules/proxmox_kvm.py:
ignore: skvidal
maintainers: helldorado krauthosting
$modules/proxmox_backup.py:
maintainers: IamLunchbox
$modules/proxmox_backup_info.py:
maintainers: raoufnezhad mmayabi
$modules/proxmox_nic.py:
maintainers: Kogelvis krauthosting
$modules/proxmox_node_info.py:
@@ -1197,6 +1144,12 @@ files:
keywords: kvm libvirt proxmox qemu
labels: rhevm virt
maintainers: $team_virt TimothyVandenbrande
$modules/rhn_channel.py:
labels: rhn_channel
maintainers: vincentvdk alikins $team_rhn
$modules/rhn_register.py:
labels: rhn_register
maintainers: jlaska $team_rhn
$modules/rhsm_release.py:
maintainers: seandst $team_rhsm
$modules/rhsm_repository.py:
@@ -1360,12 +1313,6 @@ files:
maintainers: precurse
$modules/sysrc.py:
maintainers: dlundgren
$modules/systemd_creds_decrypt.py:
maintainers: konstruktoid
$modules/systemd_creds_encrypt.py:
maintainers: konstruktoid
$modules/systemd_info.py:
maintainers: NomakCooper
$modules/sysupgrade.py:
maintainers: precurse
$modules/taiga_issue.py:
@@ -1397,19 +1344,16 @@ files:
keywords: sophos utm
maintainers: $team_e_spirit
$modules/utm_ca_host_key_cert.py:
ignore: stearz
maintainers: $team_e_spirit
maintainers: stearz
$modules/utm_ca_host_key_cert_info.py:
ignore: stearz
maintainers: $team_e_spirit
maintainers: stearz
$modules/utm_network_interface_address.py:
maintainers: steamx
$modules/utm_network_interface_address_info.py:
maintainers: steamx
$modules/utm_proxy_auth_profile.py:
keywords: sophos utm
ignore: stearz
maintainers: $team_e_spirit
maintainers: $team_e_spirit stearz
$modules/utm_proxy_exception.py:
keywords: sophos utm
maintainers: $team_e_spirit RickS-C137
@@ -1485,9 +1429,6 @@ files:
maintainers: $team_suse
$plugin_utils/ansible_type.py:
maintainers: vbotka
$modules/zypper_repository_info.py:
labels: zypper
maintainers: $team_suse TobiasZeuch181
$plugin_utils/keys_filter.py:
maintainers: vbotka
$plugin_utils/unsafe.py:
@@ -1537,16 +1478,12 @@ files:
maintainers: russoz
docs/docsite/rst/guide_deps.rst:
maintainers: russoz
docs/docsite/rst/guide_modulehelper.rst:
maintainers: russoz
docs/docsite/rst/guide_online.rst:
maintainers: remyleone
docs/docsite/rst/guide_packet.rst:
maintainers: baldwinSPC nurfet-becirevic t0mk teebes
docs/docsite/rst/guide_scaleway.rst:
maintainers: $team_scaleway
docs/docsite/rst/guide_uthelper.rst:
maintainers: russoz
docs/docsite/rst/guide_vardict.rst:
maintainers: russoz
docs/docsite/rst/test_guide.rst:
@@ -1589,7 +1526,7 @@ macros:
team_huawei: QijunPan TommyLike edisonxiang freesky-edward hwDCN niuzhenguo xuxiaowei0512 yanzhangi zengchen1024 zhongjun2
team_ipa: Akasurde Nosmoht justchris1
team_jboss: Wolfant jairojunior wbrefvem
team_keycloak: eikef ndclt mattock thomasbach-dev
team_keycloak: eikef ndclt mattock
team_linode: InTheCloudDan decentral1se displague rmcintosh Charliekenney23 LBGarber
team_macos: Akasurde kyleabenson martinm82 danieljaouen indrajitr
team_manageiq: abellotti cben gtanzillo yaacov zgalor dkorn evertmulder
@@ -1598,9 +1535,10 @@ macros:
team_oracle: manojmeda mross22 nalsaber
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt
team_rhn: FlossWare alikins barnabycourt vritant
team_rhsm: cnsnyder ptoscano
team_scaleway: remyleone abarbare
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
team_suse: commel evrardjp lrupp AnderEnder alxgu andytom sealor
team_suse: commel evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
team_virt: joshainglis karmab Thulium-Drake Ajpantuso
team_wdc: mikemoerk

View File

@@ -29,7 +29,8 @@ jobs:
strategy:
matrix:
ansible:
- '2.15'
- '2.13'
- '2.14'
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
@@ -65,12 +66,16 @@ jobs:
exclude:
- ansible: ''
include:
- ansible: '2.15'
- ansible: '2.13'
python: '2.7'
- ansible: '2.15'
python: '3.5'
- ansible: '2.15'
python: '3.10'
- ansible: '2.13'
python: '3.8'
- ansible: '2.13'
python: '2.7'
- ansible: '2.13'
python: '3.8'
- ansible: '2.14'
python: '3.9'
steps:
- name: >-
@@ -111,29 +116,54 @@ jobs:
exclude:
- ansible: ''
include:
# 2.15
- ansible: '2.15'
# 2.13
- ansible: '2.13'
docker: fedora35
python: ''
target: azp/posix/1/
- ansible: '2.13'
docker: fedora35
python: ''
target: azp/posix/2/
- ansible: '2.13'
docker: fedora35
python: ''
target: azp/posix/3/
- ansible: '2.13'
docker: opensuse15py2
python: ''
target: azp/posix/1/
- ansible: '2.13'
docker: opensuse15py2
python: ''
target: azp/posix/2/
- ansible: '2.13'
docker: opensuse15py2
python: ''
target: azp/posix/3/
- ansible: '2.13'
docker: alpine3
python: ''
target: azp/posix/1/
- ansible: '2.15'
- ansible: '2.13'
docker: alpine3
python: ''
target: azp/posix/2/
- ansible: '2.15'
- ansible: '2.13'
docker: alpine3
python: ''
target: azp/posix/3/
- ansible: '2.15'
docker: fedora37
# 2.14
- ansible: '2.14'
docker: alpine3
python: ''
target: azp/posix/1/
- ansible: '2.15'
docker: fedora37
- ansible: '2.14'
docker: alpine3
python: ''
target: azp/posix/2/
- ansible: '2.15'
docker: fedora37
- ansible: '2.14'
docker: alpine3
python: ''
target: azp/posix/3/
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
@@ -141,14 +171,11 @@ jobs:
# docker: default
# python: '3.9'
# target: azp/generic/1/
# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled.
# - ansible: '2.14'
# docker: default
# python: '3.10'
# target: azp/generic/1/
# - ansible: '2.15'
# docker: default
# python: '3.9'
# target: azp/generic/1/
steps:
- name: >-

View File

@@ -25,8 +25,6 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -7,14 +7,10 @@ name: Verify REUSE
on:
push:
branches:
- main
- stable-*
pull_request:
branches: [main]
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- main
- stable-*
branches: [main]
# Run CI once per day (at 07:30 UTC)
schedule:
- cron: '30 7 * * *'
@@ -28,8 +24,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.event.pull_request.head.sha || '' }}
- name: REUSE Compliance Check
uses: fsfe/reuse-action@v5
uses: fsfe/reuse-action@v4

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -56,8 +56,6 @@ cd ~/dev/ansible_collections/community/general
Then you can run `ansible-test` (which is a part of [ansible-core](https://pypi.org/project/ansible-core/)) inside the checkout. The following example commands expect that you have installed Docker or Podman. Note that Podman has only been supported by more recent ansible-core releases. If you are using Docker, the following will work with Ansible 2.9+.
### Sanity tests
The following commands show how to run sanity tests:
```.bash
@@ -68,8 +66,6 @@ ansible-test sanity --docker -v
ansible-test sanity --docker -v plugins/modules/system/pids.py tests/integration/targets/pids/
```
### Unit tests
The following commands show how to run unit tests:
```.bash
@@ -83,32 +79,13 @@ ansible-test units --docker -v --python 3.8
ansible-test units --docker -v --python 3.8 tests/unit/plugins/modules/net_tools/test_nmcli.py
```
### Integration tests
The following commands show how to run integration tests:
#### In Docker
Integration tests on Docker have the following parameters:
- `image_name` (required): The name of the Docker image. To get the list of supported Docker images, run
`ansible-test integration --help` and look for _target docker images_.
- `test_name` (optional): The name of the integration test.
For modules, this equals the short name of the module; for example, `pacman` in case of `community.general.pacman`.
For plugins, the plugin type is added before the plugin's short name, for example `callback_yaml` for the `community.general.yaml` callback.
```.bash
# Test all plugins/modules on fedora40
ansible-test integration -v --docker fedora40
# Run integration tests for the interfaces_files module in a Docker container using the
# fedora35 operating system image (the supported images depend on your ansible-core version):
ansible-test integration --docker fedora35 -v interfaces_file
# Template
ansible-test integration -v --docker image_name test_name
# Example community.general.ini_file module on fedora40 Docker image:
ansible-test integration -v --docker fedora40 ini_file
```
#### Without isolation
```.bash
# Run integration tests for the flattened lookup **without any isolation**:
ansible-test integration -v lookup_flattened
```

View File

@@ -6,9 +6,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
# Community General Collection
[![Documentation](https://img.shields.io/badge/docs-brightgreen.svg)](https://docs.ansible.com/ansible/latest/collections/community/general/)
[![Build Status](https://dev.azure.com/ansible/community.general/_apis/build/status/CI?branchName=stable-10)](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
[![EOL CI](https://github.com/ansible-collections/community.general/actions/workflows/ansible-test.yml/badge.svg?branch=stable-10)](https://github.com/ansible-collections/community.general/actions)
[![Build Status](https://dev.azure.com/ansible/community.general/_apis/build/status/CI?branchName=stable-9)](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
[![EOL CI](https://github.com/ansible-collections/community.general/workflows/EOL%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.general/actions)
[![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.general)](https://codecov.io/gh/ansible-collections/community.general)
[![REUSE status](https://api.reuse.software/badge/github.com/ansible-collections/community.general)](https://api.reuse.software/info/github.com/ansible-collections/community.general)
@@ -24,21 +23,9 @@ We follow [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/comm
If you encounter abusive behavior violating the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html), please refer to the [policy violations](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html#policy-violations) section of the Code of Conduct for information on how to raise a complaint.
## Communication
* Join the Ansible forum:
* [Get Help](https://forum.ansible.com/c/help/6): get help or help others. This is for questions about modules or plugins in the collection. Please add appropriate tags if you start new discussions.
* [Tag `community-general`](https://forum.ansible.com/tag/community-general): discuss the *collection itself*, instead of specific modules or plugins.
* [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts.
* [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events.
* The Ansible [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn): used to announce releases and important changes.
For more information about communication, see the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
## Tested with Ansible
Tested with the current ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, ansible-core 2.18 releases and the current development version of ansible-core. Ansible-core versions before 2.15.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
Tested with the current ansible-core 2.13, ansible-core 2.14, ansible-core 2.15, ansible-core 2.16, ansible-core 2.17 releases and the current development version of ansible-core. Ansible-core versions before 2.13.0 are not supported. This includes all ansible-base 2.10 and Ansible 2.9 releases.
## External requirements
@@ -111,13 +98,25 @@ It is necessary for maintainers of this collection to be subscribed to:
They also should be subscribed to Ansible's [The Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn).
## Communication
We announce important development changes and releases through Ansible's [The Bullhorn newsletter](https://eepurl.com/gZmiEP). If you are a collection developer, be sure you are subscribed.
Join us in the `#ansible` (general use questions and support), `#ansible-community` (community and collection development questions), and other [IRC channels](https://docs.ansible.com/ansible/devel/community/communication.html#irc-channels) on [Libera.chat](https://libera.chat).
We take part in the global quarterly [Ansible Contributor Summit](https://github.com/ansible/community/wiki/Contributor-Summit) virtually or in-person. Track [The Bullhorn newsletter](https://eepurl.com/gZmiEP) and join us.
For more information about communities, meetings and agendas see [Community Wiki](https://github.com/ansible/community/wiki/Community).
For more information about communication, refer to Ansible's the [Communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
## Publishing New Version
See the [Releasing guidelines](https://github.com/ansible/community-docs/blob/main/releasing_collections.rst) to learn how to release this collection.
## Release notes
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-10/CHANGELOG.md).
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-9/CHANGELOG.md).
## Roadmap
@@ -136,8 +135,8 @@ See [this issue](https://github.com/ansible-collections/community.general/issues
This collection is primarily licensed and distributed as a whole under the GNU General Public License v3.0 or later.
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-10/COPYING) for the full text.
See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-9/COPYING) for the full text.
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-10/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-10/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-10/LICENSES/PSF-2.0.txt).
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-9/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-9/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-9/LICENSES/PSF-2.0.txt).
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/).

File diff suppressed because it is too large Load Diff

View File

@@ -19,5 +19,3 @@ sections:
- guide_deps
- guide_vardict
- guide_cmdrunner
- guide_modulehelper
- guide_uthelper

View File

@@ -9,8 +9,6 @@ edit_on_github:
path_prefix: ''
extra_links:
- description: Ask for help
url: https://forum.ansible.com/c/help/6/none
- description: Submit a bug report
url: https://github.com/ansible-collections/community.general/issues/new?assignees=&labels=&template=bug_report.yml
- description: Request a feature
@@ -24,10 +22,10 @@ communication:
- topic: General usage and support questions
network: Libera
channel: '#ansible'
mailing_lists:
- topic: Ansible Project List
url: https://groups.google.com/g/ansible-project
forums:
- topic: "Ansible Forum: General usage and support questions"
- topic: Ansible Forum
# The following URL directly points to the "Get Help" section
url: https://forum.ansible.com/c/help/6/none
- topic: "Ansible Forum: Discussions about the collection itself, not for specific modules or plugins"
# The following URL directly points to the "community-general" tag
url: https://forum.ansible.com/tag/community-general

View File

@@ -65,7 +65,7 @@ All three statements are equivalent and give:
.. note:: Be aware that in most cases, filter calls without any argument require ``flatten=true``, otherwise the input is returned as result. The reason for this is, that the input is considered as a variable argument and is wrapped by an additional outer list. ``flatten=true`` ensures that this list is removed before the input is processed by the filter logic.
The filters :ansplugin:`community.general.lists_difference#filter` or :ansplugin:`community.general.lists_symmetric_difference#filter` can be used in the same way as the filters in the examples above. They calculate the difference or the symmetric difference between two or more lists and preserve the item order.
The filters ansplugin:`community.general.lists_difference#filter` or :ansplugin:`community.general.lists_symmetric_difference#filter` can be used in the same way as the filters in the examples above. They calculate the difference or the symmetric difference between two or more lists and preserve the item order.
For example, the symmetric difference of ``A``, ``B`` and ``C`` may be written as:

View File

@@ -124,7 +124,7 @@ To get a hash map with all ports and names of a cluster:
var: item
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
vars:
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].{name: name, port: port}"
server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}"
To extract ports from all clusters with name starting with 'server1':

View File

@@ -68,27 +68,20 @@ This is meant to be done once, then every time you need to execute the command y
with runner("version") as ctx:
dummy, stdout, dummy = ctx.run()
# passes arg 'data' to AnsibleModule.run_command()
with runner("type name", data=stdin_data) as ctx:
dummy, stdout, dummy = ctx.run()
# Another way of expressing it
dummy, stdout, dummy = runner("version").run()
Note that you can pass values for the arguments when calling ``run()``, otherwise ``CmdRunner``
uses the module options with the exact same names to provide values for the runner arguments.
If no value is passed and no module option is found for the name specified, then an exception is raised, unless
the argument is using ``cmd_runner_fmt.as_fixed`` as format function like the ``version`` in the example above.
See more about it below.
Note that you can pass values for the arguments when calling ``run()``,
otherwise ``CmdRunner`` uses the module options with the exact same names to
provide values for the runner arguments. If no value is passed and no module option
is found for the name specified, then an exception is raised, unless the
argument is using ``cmd_runner_fmt.as_fixed`` as format function like the
``version`` in the example above. See more about it below.
In the first example, values of ``type``, ``force``, ``no_deps`` and others
are taken straight from the module, whilst ``galaxy_cmd`` and ``upgrade`` are
passed explicitly.
.. note::
It is not possible to automatically retrieve values of suboptions.
That generates a resulting command line similar to (example taken from the
output of an integration test):
@@ -117,7 +110,7 @@ into something formatted for the command line.
Argument format function
""""""""""""""""""""""""
An ``arg_format`` function is defined in the form similar to:
An ``arg_format`` function should be of the form:
.. code-block:: python
@@ -162,7 +155,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_list()``
- Examples:
- Example:
+----------------------+---------------------+
| Value | Outcome |
+======================+=====================+
@@ -174,11 +167,12 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- ``cmd_runner_fmt.as_bool()``
This method receives two different parameters: ``args_true`` and ``args_false``, latter being optional.
If the boolean evaluation of ``value`` is ``True``, the format function returns ``args_true``.
If the boolean evaluation is ``False``, then the function returns ``args_false`` if it was provided, or ``[]`` otherwise.
If the boolean evaluation is ``False``, then the function returns ``args_false``
if it was provided, or ``[]`` otherwise.
- Creation (one arg):
- Creation:
``cmd_runner_fmt.as_bool("--force")``
- Examples:
- Example:
+------------+--------------------+
| Value | Outcome |
+============+====================+
@@ -186,30 +180,6 @@ In these descriptions ``value`` refers to the single parameter passed to the for
+------------+--------------------+
| ``False`` | ``[]`` |
+------------+--------------------+
- Creation (two args, ``None`` treated as ``False``):
``cmd_runner_fmt.as_bool("--relax", "--dont-do-it")``
- Examples:
+------------+----------------------+
| Value | Outcome |
+============+======================+
| ``True`` | ``["--relax"]`` |
+------------+----------------------+
| ``False`` | ``["--dont-do-it"]`` |
+------------+----------------------+
| | ``["--dont-do-it"]`` |
+------------+----------------------+
- Creation (two args, ``None`` is ignored):
``cmd_runner_fmt.as_bool("--relax", "--dont-do-it", ignore_none=True)``
- Examples:
+------------+----------------------+
| Value | Outcome |
+============+======================+
| ``True`` | ``["--relax"]`` |
+------------+----------------------+
| ``False`` | ``["--dont-do-it"]`` |
+------------+----------------------+
| | ``[]`` |
+------------+----------------------+
- ``cmd_runner_fmt.as_bool_not()``
This method receives one parameter, which is returned by the function when the boolean evaluation
@@ -217,7 +187,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_bool_not("--no-deps")``
- Examples:
- Example:
+-------------+---------------------+
| Value | Outcome |
+=============+=====================+
@@ -232,7 +202,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_optval("-i")``
- Examples:
- Example:
+---------------+---------------------+
| Value | Outcome |
+===============+=====================+
@@ -246,7 +216,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_opt_val("--name")``
- Examples:
- Example:
+--------------+--------------------------+
| Value | Outcome |
+==============+==========================+
@@ -259,7 +229,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_opt_eq_val("--num-cpus")``
- Examples:
- Example:
+------------+-------------------------+
| Value | Outcome |
+============+=========================+
@@ -273,7 +243,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_fixed("--version")``
- Examples:
- Example:
+---------+-----------------------+
| Value | Outcome |
+=========+=======================+
@@ -295,7 +265,7 @@ In these descriptions ``value`` refers to the single parameter passed to the for
- Creation:
``cmd_runner_fmt.as_map(dict(a=1, b=2, c=3), default=42)``
- Examples:
- Example:
+---------------------+---------------+
| Value | Outcome |
+=====================+===============+
@@ -389,8 +359,6 @@ Settings that can be passed to the ``CmdRunner`` constructor are:
Command to be executed. It can be a single string, the executable name, or a list
of strings containing the executable name as the first element and, optionally, fixed parameters.
Those parameters are used in all executions of the runner.
The *executable* pointed by this parameter (whether itself when ``str`` or its first element when ``list``) is
processed using ``AnsibleModule.get_bin_path()`` *unless* it is an absolute path or contains the character ``/``.
- ``arg_formats: dict``
Mapping of argument names to formatting functions.
- ``default_args_order: str``
@@ -426,10 +394,6 @@ When creating a context, the additional settings that can be passed to the call
Defaults to ``False``.
- ``check_mode_return: any``
If ``check_mode_skip=True``, then return this value instead.
- valid named arguments to ``AnsibleModule.run_command()``
Other than ``args``, any valid argument to ``run_command()`` can be passed when setting up the run context.
For example, ``data`` can be used to send information to the command's standard input.
Or ``cwd`` can be used to run the command inside a specific working directory.
Additionally, any other valid parameters for ``AnsibleModule.run_command()`` may be passed, but unexpected behavior
might occur if redefining options already present in the runner or its context creation. Use with caution.

View File

@@ -1,552 +0,0 @@
..
Copyright (c) Ansible Project
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
.. _ansible_collections.community.general.docsite.guide_modulehelper:
Module Helper guide
===================
Introduction
^^^^^^^^^^^^
Writing a module for Ansible is largely described in existing documentation.
However, a good part of that is boilerplate code that needs to be repeated every single time.
That is where ``ModuleHelper`` comes to assistance: a lot of that boilerplate code is done.
.. _ansible_collections.community.general.docsite.guide_modulehelper.quickstart:
Quickstart
""""""""""
See the `example from Ansible documentation <https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#creating-a-module>`_
written with ``ModuleHelper``.
But bear in mind that it does not showcase all of MH's features:
.. code-block:: python
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
class MyTest(ModuleHelper):
module = dict(
argument_spec=dict(
name=dict(type='str', required=True),
new=dict(type='bool', required=False, default=False),
),
supports_check_mode=True,
)
use_old_vardict = False
def __run__(self):
self.vars.original_message = ''
self.vars.message = ''
if self.check_mode:
return
self.vars.original_message = self.vars.name
self.vars.message = 'goodbye'
self.changed = self.vars['new']
if self.vars.name == "fail me":
self.do_raise("You requested this to fail")
def main():
MyTest.execute()
if __name__ == '__main__':
main()
Module Helper
^^^^^^^^^^^^^
Introduction
""""""""""""
``ModuleHelper`` is a wrapper around the standard ``AnsibleModule``, providing extra features and conveniences.
The basic structure of a module using ``ModuleHelper`` is as shown in the
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.quickstart`
section above, but there are more elements that will take part in it.
.. code-block:: python
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper
class MyTest(ModuleHelper):
output_params = ()
change_params = ()
diff_params = ()
facts_name = None
facts_params = ()
use_old_vardict = True
mute_vardict_deprecation = False
module = dict(
argument_spec=dict(...),
# ...
)
After importing the ``ModuleHelper`` class, you need to declare your own class extending it.
.. seealso::
There is a variation called ``StateModuleHelper``, which builds on top of the features provided by MH.
See :ref:`ansible_collections.community.general.docsite.guide_modulehelper.statemh` below for more details.
The easiest way of specifying the module is to create the class variable ``module`` with a dictionary
containing the exact arguments that would be passed as parameters to ``AnsibleModule``.
If you prefer to create the ``AnsibleModule`` object yourself, just assign it to the ``module`` class variable.
MH also accepts a parameter ``module`` in its constructor, if that parameter is used used,
then it will override the class variable. The parameter can either be ``dict`` or ``AnsibleModule`` as well.
Beyond the definition of the module, there are other variables that can be used to control aspects
of MH's behavior. These variables should be set at the very beginning of the class, and their semantics are
explained through this document.
The main logic of MH happens in the ``ModuleHelper.run()`` method, which looks like:
.. code-block:: python
@module_fails_on_exception
def run(self):
self.__init_module__()
self.__run__()
self.__quit_module__()
output = self.output
if 'failed' not in output:
output['failed'] = False
self.module.exit_json(changed=self.has_changed(), **output)
The method ``ModuleHelper.__run__()`` must be implemented by the module and most
modules will be able to perform their actions implementing only that MH method.
However, in some cases, you might want to execute actions before or after the main tasks, in which cases
you should implement ``ModuleHelper.__init_module__()`` and ``ModuleHelper.__quit_module__()`` respectively.
Note that the output comes from ``self.output``, which is a ``@property`` method.
By default, that property will collect all the variables that are marked for output and return them in a dictionary with their values.
Moreover, the default ``self.output`` will also handle Ansible ``facts`` and *diff mode*.
Also note the changed status comes from ``self.has_changed()``, which is usually calculated from variables that are marked
to track changes in their content.
.. seealso::
More details in sections
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.paramvaroutput` and
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.changes` below.
.. seealso::
See more about the decorator
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.modulefailsdeco` below.
Another way to write the example from the
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.quickstart`
would be:
.. code-block:: python
def __init_module__(self):
self.vars.original_message = ''
self.vars.message = ''
def __run__(self):
if self.check_mode:
return
self.vars.original_message = self.vars.name
self.vars.message = 'goodbye'
self.changed = self.vars['new']
def __quit_module__(self):
if self.vars.name == "fail me":
self.do_raise("You requested this to fail")
Notice that there are no calls to ``module.exit_json()`` nor ``module.fail_json()``: if the module fails, raise an exception.
You can use the convenience method ``self.do_raise()`` or raise the exception as usual in Python to do that.
If no exception is raised, then the module succeeds.
.. seealso::
See more about exceptions in section
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.exceptions` below.
Ansible modules must have a ``main()`` function and the usual test for ``'__main__'``. When using MH that should look like:
.. code-block:: python
def main():
MyTest.execute()
if __name__ == '__main__':
main()
The class method ``execute()`` is nothing more than a convenience shorcut for:
.. code-block:: python
m = MyTest()
m.run()
Optionally, an ``AnsibleModule`` may be passed as parameter to ``execute()``.
.. _ansible_collections.community.general.docsite.guide_modulehelper.paramvaroutput:
Parameters, variables, and output
"""""""""""""""""""""""""""""""""
All the parameters automatically become variables in the ``self.vars`` attribute, which is of the ``VarDict`` type.
By using ``self.vars``, you get a central mechanism to access the parameters but also to expose variables as return values of the module.
As described in :ref:`ansible_collections.community.general.docsite.guide_vardict`, variables in ``VarDict`` have metadata associated to them.
One of the attributes in that metadata marks the variable for output, and MH makes use of that to generate the module's return values.
.. important::
The ``VarDict`` feature described was introduced in community.general 7.1.0, but there was a first
implementation of it embedded within ``ModuleHelper``.
That older implementation is now deprecated and will be removed in community.general 11.0.0.
After community.general 7.1.0, MH modules generate a deprecation message about *using the old VarDict*.
There are two ways to prevent that from happening:
#. Set ``mute_vardict_deprecation = True`` and the deprecation will be silenced. If the module still uses the old ``VarDict``,
it will not be able to update to community.general 11.0.0 (Spring 2026) upon its release.
#. Set ``use_old_vardict = False`` to make the MH module use the new ``VarDict`` immediatelly.
The new ``VarDict`` and its use is documented and this is the recommended way to handle this.
.. code-block:: python
class MyTest(ModuleHelper):
use_old_vardict = False
mute_vardict_deprecation = True
...
These two settings are mutually exclusive, but that is not enforced and the behavior when setting both is not specified.
Contrary to new variables created in ``VarDict``, module parameters are not set for output by default.
If you want to include some module parameters in the output, list them in the ``output_params`` class variable.
.. code-block:: python
class MyTest(ModuleHelper):
output_params = ('state', 'name')
...
Another neat feature provided by MH by using ``VarDict`` is the automatic tracking of changes when setting the metadata ``change=True``.
Again, to enable this feature for module parameters, you must list them in the ``change_params`` class variable.
.. code-block:: python
class MyTest(ModuleHelper):
# example from community.general.xfconf
change_params = ('value', )
...
.. seealso::
See more about this in
:ref:`ansible_collections.community.general.docsite.guide_modulehelper.changes` below.
Similarly, if you want to use Ansible's diff mode, you can set the metadata ``diff=True`` and ``diff_params`` for module parameters.
With that, MH will automatically generate the diff output for variables that have changed.
.. code-block:: python
class MyTest(ModuleHelper):
diff_params = ('value', )
def __run__(self):
# example from community.general.gio_mime
self.vars.set_meta("handler", initial_value=gio_mime_get(self.runner, self.vars.mime_type), diff=True, change=True)
Moreover, if a module is set to return *facts* instead of return values, then again use the metadata ``fact=True`` and ``fact_params`` for module parameters.
Additionally, you must specify ``facts_name``, as in:
.. code-block:: python
class VolumeFacts(ModuleHelper):
facts_name = 'volume_facts'
def __init_module__(self):
self.vars.set("volume", 123, fact=True)
That generates an Ansible fact like:
.. code-block:: yaml+jinja
- name: Obtain volume facts
some.collection.volume_facts:
# parameters
- name: Print volume facts
debug:
msg: Volume fact is {{ ansible_facts.volume_facts.volume }}
.. important::
If ``facts_name`` is not set, the module does not generate any facts.
.. _ansible_collections.community.general.docsite.guide_modulehelper.changes:
Handling changes
""""""""""""""""
In MH there are many ways to indicate change in the module execution. Here they are:
Tracking changes in variables
-----------------------------
As explained above, you can enable change tracking in any number of variables in ``self.vars``.
By the end of the module execution, if any of those variables has a value different then the first value assigned to them,
then that will be picked up by MH and signalled as changed at the module output.
See the example below to learn how you can enabled change tracking in variables:
.. code-block:: python
# using __init_module__() as example, it works the same in __run__() and __quit_module__()
def __init_module__(self):
# example from community.general.ansible_galaxy_install
self.vars.set("new_roles", {}, change=True)
# example of "hidden" variable used only to track change in a value from community.general.gconftool2
self.vars.set('_value', self.vars.previous_value, output=False, change=True)
# enable change-tracking without assigning value
self.vars.set_meta("new_roles", change=True)
# if you must forcibly set an initial value to the variable
self.vars.set_meta("new_roles", initial_value=[])
...
If the end value of any variable marked ``change`` is different from its initial value, then MH will return ``changed=True``.
Indicating changes with ``changed``
-----------------------------------
If you want to indicate change directly in the code, then use the ``self.changed`` property in MH.
Beware that this is a ``@property`` method in MH, with both a *getter* and a *setter*.
By default, that hidden field is set to ``False``.
Effective change
----------------
The effective outcome for the module is determined in the ``self.has_changed()`` method, and it consists of the logical *OR* operation
between ``self.changed`` and the change calculated from ``self.vars``.
.. _ansible_collections.community.general.docsite.guide_modulehelper.exceptions:
Exceptions
""""""""""
In MH, instead of calling ``module.fail_json()`` you can just raise an exception.
The output variables are collected the same way they would be for a successful execution.
However, you can set output variables specifically for that exception, if you so choose.
.. code-block:: python
from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelperException
def __init_module__(self):
if not complex_validation():
self.do_raise("Validation failed!")
# Or passing output variables
awesomeness = calculate_awesomeness()
if awesomeness > 1000:
self.do_raise("Over awesome, I cannot handle it!", update_output={"awesomeness": awesomeness})
# which is just a convenience shortcut for
raise ModuleHelperException("...", update_output={...})
All exceptions derived from ``Exception`` are captured and translated into a ``fail_json()`` call.
However, if you do want to call ``self.module.fail_json()`` yourself it will work,
just keep in mind that there will be no automatic handling of output variables in that case.
Behind the curtains, all ``do_raise()`` does is to raise a ``ModuleHelperException``.
If you want to create specialized error handling for your code, the best way is to extend that clas and raise it when needed.
.. _ansible_collections.community.general.docsite.guide_modulehelper.statemh:
StateModuleHelper
^^^^^^^^^^^^^^^^^
Many modules use a parameter ``state`` that effectively controls the exact action performed by the module, such as
``state=present`` or ``state=absent`` for installing or removing packages.
By using ``StateModuleHelper`` you can make your code like the excerpt from the ``gconftool2`` below:
.. code-block:: python
from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper
class GConftool(StateModuleHelper):
...
module = dict(
...
)
use_old_vardict = False
def __init_module__(self):
self.runner = gconftool2_runner(self.module, check_rc=True)
...
self.vars.set('previous_value', self._get(), fact=True)
self.vars.set('value_type', self.vars.value_type)
self.vars.set('_value', self.vars.previous_value, output=False, change=True)
self.vars.set_meta('value', initial_value=self.vars.previous_value)
self.vars.set('playbook_value', self.vars.value, fact=True)
...
def state_absent(self):
with self.runner("state key", output_process=self._make_process(False)) as ctx:
ctx.run()
self.vars.set('run_info', ctx.run_info, verbosity=4)
self.vars.set('new_value', None, fact=True)
self.vars._value = None
def state_present(self):
with self.runner("direct config_source value_type state key value", output_process=self._make_process(True)) as ctx:
ctx.run()
self.vars.set('run_info', ctx.run_info, verbosity=4)
self.vars.set('new_value', self._get(), fact=True)
self.vars._value = self.vars.new_value
Note that the method ``__run__()`` is implemented in ``StateModuleHelper``, all you need to implement are the methods ``state_<state_value>``.
In the example above, :ansplugin:`community.general.gconftool2#module` only has two states, ``present`` and ``absent``, thus, ``state_present()`` and ``state_absent()``.
If the controlling parameter is not called ``state``, like in :ansplugin:`community.general.jira#module` module, just let SMH know about it:
.. code-block:: python
class JIRA(StateModuleHelper):
state_param = 'operation'
def operation_create(self):
...
def operation_search(self):
...
Lastly, if the module is called with ``state=somevalue`` and the method ``state_somevalue``
is not implemented, SMH will resort to call a method called ``__state_fallback__()``.
By default, this method will raise a ``ValueError`` indicating the method was not found.
Naturally, you can override that method to write a default implementation, as in :ansplugin:`community.general.locale_gen#module`:
.. code-block:: python
def __state_fallback__(self):
if self.vars.state_tracking == self.vars.state:
return
if self.vars.ubuntu_mode:
self.apply_change_ubuntu(self.vars.state, self.vars.name)
else:
self.apply_change(self.vars.state, self.vars.name)
That module has only the states ``present`` and ``absent`` and the code for both is the one in the fallback method.
.. note::
The name of the fallback method **does not change** if you set a different value of ``state_param``.
Other Conveniences
^^^^^^^^^^^^^^^^^^
Delegations to AnsibleModule
""""""""""""""""""""""""""""
The MH properties and methods below are delegated as-is to the underlying ``AnsibleModule`` instance in ``self.module``:
- ``check_mode``
- ``get_bin_path()``
- ``warn()``
- ``deprecate()``
Additionally, MH will also delegate:
- ``diff_mode`` to ``self.module._diff``
- ``verbosity`` to ``self.module._verbosity``
Starting in community.general 10.3.0, MH will also delegate the method ``debug`` to ``self.module``.
If any existing module already has a ``debug`` attribute defined, a warning message will be generated,
requesting it to be renamed. Upon the release of community.general 12.0.0, the delegation will be
preemptive and will override any existing method or property in the subclasses.
Decorators
""""""""""
The following decorators should only be used within ``ModuleHelper`` class.
@cause_changes
--------------
This decorator will control whether the outcome of the method will cause the module to signal change in its output.
If the method completes without raising an exception it is considered to have succeeded, otherwise, it will have failed.
The decorator has a parameter ``when`` that accepts three different values: ``success``, ``failure``, and ``always``.
There are also two legacy parameters, ``on_success`` and ``on_failure``, that will be deprecated, so do not use them.
The value of ``changed`` in the module output will be set to ``True``:
- ``when="success"`` and the method completes without raising an exception.
- ``when="failure"`` and the method raises an exception.
- ``when="always"``, regardless of the method raising an exception or not.
.. code-block:: python
from ansible_collections.community.general.plugins.module_utils.module_helper import cause_changes
# adapted excerpt from the community.general.jira module
class JIRA(StateModuleHelper):
@cause_changes(when="success")
def operation_create(self):
...
If ``when`` has a different value or no parameters are specificied, the decorator will have no effect whatsoever.
.. _ansible_collections.community.general.docsite.guide_modulehelper.modulefailsdeco:
@module_fails_on_exception
--------------------------
In a method using this decorator, if an exception is raised, the text message of that exception will be captured
by the decorator and used to call ``self.module.fail_json()``.
In most of the cases there will be no need to use this decorator, because ``ModuleHelper.run()`` already uses it.
@check_mode_skip
----------------
If the module is running in check mode, this decorator will prevent the method from executing.
The return value in that case is ``None``.
.. code-block:: python
from ansible_collections.community.general.plugins.module_utils.module_helper import check_mode_skip
# adapted excerpt from the community.general.locale_gen module
class LocaleGen(StateModuleHelper):
@check_mode_skip
def __state_fallback__(self):
...
@check_mode_skip_returns
------------------------
This decorator is similar to the previous one, but the developer can control the return value for the method when running in check mode.
It is used with one of two parameters. One is ``callable`` and the return value in check mode will be ``callable(self, *args, **kwargs)``,
where ``self`` is the ``ModuleHelper`` instance and the union of ``args`` and ``kwargs`` will contain all the parameters passed to the method.
The other option is to use the parameter ``value``, in which case the method will return ``value`` when in check mode.
References
^^^^^^^^^^
- `Ansible Developer Guide <https://docs.ansible.com/ansible/latest/dev_guide/index.html>`_
- `Creating a module <https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general.html#creating-a-module>`_
- `Returning ansible facts <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#ansible-facts>`_
- :ref:`ansible_collections.community.general.docsite.guide_vardict`
.. versionadded:: 3.1.0

View File

@@ -1,394 +0,0 @@
..
Copyright (c) Ansible Project
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
.. _ansible_collections.community.general.docsite.guide_uthelper:
UTHelper Guide
==============
Introduction
^^^^^^^^^^^^
``UTHelper`` was written to reduce the boilerplate code used in unit tests for modules.
It was originally written to handle tests of modules that run external commands using ``AnsibleModule.run_command()``.
At the time of writing (Feb 2025) that remains the only type of tests you can use
``UTHelper`` for, but it aims to provide support for other types of interactions.
Until now, there are many different ways to implement unit tests that validate a module based on the execution of external commands. See some examples:
* `test_apk.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_apk.py>`_ - A very simple one
* `test_bootc_manage.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_bootc_manage.py>`_ -
This one has more test cases, but do notice how the code is repeated amongst them.
* `test_modprobe.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_modprobe.py>`_ -
This one has 15 tests in it, but to achieve that it declares 8 classes repeating quite a lot of code.
As you can notice, there is no consistency in the way these tests are executed -
they all do the same thing eventually, but each one is written in a very distinct way.
``UTHelper`` aims to:
* provide a consistent idiom to define unit tests
* reduce the code to a bare minimal, and
* define tests as data instead
* allow the test cases definition to be expressed not only as a Python data structure but also as YAML content
Quickstart
""""""""""
To use UTHelper, your test module will need only a bare minimal of code:
.. code-block:: python
# tests/unit/plugin/modules/test_ansible_module.py
from ansible_collections.community.general.plugins.modules import ansible_module
from .uthelper import UTHelper, RunCommandMock
UTHelper.from_module(ansible_module, __name__, mocks=[RunCommandMock])
Then, in the test specification file, you have:
.. code-block:: yaml
# tests/unit/plugin/modules/test_ansible_module.yaml
test_cases:
- id: test_ansible_module
flags:
diff: true
input:
state: present
name: Roger the Shrubber
output:
shrubbery:
looks: nice
price: not too expensive
changed: true
diff:
before:
shrubbery: null
after:
shrubbery:
looks: nice
price: not too expensive
mocks:
run_command:
- command: [/testbin/shrubber, --version]
rc: 0
out: "2.80.0\n"
err: ''
- command: [/testbin/shrubber, --make-shrubbery]
rc: 0
out: 'Shrubbery created'
err: ''
.. note::
If you prefer to pick a different YAML file for the test cases, or if you prefer to define them in plain Python,
you can use the convenience methods ``UTHelper.from_file()`` and ``UTHelper.from_spec()``, respectively.
See more details below.
Using ``UTHelper``
^^^^^^^^^^^^^^^^^^
Test Module
"""""""""""
``UTHelper`` is **strictly for unit tests**. To use it, you import the ``.uthelper.UTHelper`` class.
As mentioned in different parts of this guide, there are three different mechanisms to load the test cases.
.. seealso::
See the UTHelper class reference below for API details on the three different mechanisms.
The easies and most recommended way of using ``UTHelper`` is literally the example shown.
See a real world example at
`test_gconftool2.py <https://github.com/ansible-collections/community.general/blob/10.3.0/tests/unit/plugins/modules/test_gconftool2.py>`_.
The ``from_module()`` method will pick the filename of the test module up (in the example above, ``tests/unit/plugins/modules/test_gconftool2.py``)
and it will search for ``tests/unit/plugins/modules/test_gconftool2.yaml`` (or ``.yml`` if that is not found).
In that file it will expect to find the test specification expressed in YAML format, conforming to the structure described below LINK LINK LINK.
If you prefer to read the test specifications a different file path, use ``from_file()`` passing the file handle for the YAML file.
And, if for any reason you prefer or need to pass the data structure rather than dealing with YAML files, use the ``from_spec()`` method.
A real world example for that can be found at
`test_snap.py <https://github.com/ansible-collections/community.general/blob/main/tests/unit/plugins/modules/test_snap.py>`_.
Test Specification
""""""""""""""""""
The structure of the test specification data is described below.
Top level
---------
At the top level there are two accepted keys:
- ``anchors: dict``
Optional. Placeholder for you to define YAML anchors that can be repeated in the test cases.
Its contents are never accessed directly by test Helper.
- ``test_cases: list``
Mandatory. List of test cases, see below for definition.
Test cases
----------
You write the test cases with five elements:
- ``id: str``
Mandatory. Used to identify the test case.
- ``flags: dict``
Optional. Flags controling the behavior of the test case. All flags are optional. Accepted flags:
* ``check: bool``: set to ``true`` if the module is to be executed in **check mode**.
* ``diff: bool``: set to ``true`` if the module is to be executed in **diff mode**.
* ``skip: str``: set the test case to be skipped, providing the message for ``pytest.skip()``.
* ``xfail: str``: set the test case to expect failure, providing the message for ``pytest.xfail()``.
- ``input: dict``
Optional. Parameters for the Ansible module, it can be empty.
- ``output: dict``
Optional. Expected return values from the Ansible module.
All RV names are used here are expected to be found in the module output, but not all RVs in the output must be here.
It can include special RVs such as ``changed`` and ``diff``.
It can be empty.
- ``mocks: dict``
Optional. Mocked interactions, ``run_command`` being the only one supported for now.
Each key in this dictionary refers to one subclass of ``TestCaseMock`` and its
structure is dictated by the ``TestCaseMock`` subclass implementation.
All keys are expected to be named using snake case, as in ``run_command``.
The ``TestCaseMock`` subclass is responsible for defining the name used in the test specification.
The structure for that specification is dependent on the implementing class.
See more details below for the implementation of ``RunCommandMock``
Example using YAML
------------------
We recommend you use ``UTHelper`` reading the test specifications from a YAML file.
See an example below of how one actually looks like (excerpt from ``test_opkg.yaml``):
.. code-block:: yaml
---
anchors:
environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
test_cases:
- id: install_zlibdev
input:
name: zlib-dev
state: present
output:
msg: installed 1 package(s)
mocks:
run_command:
- command: [/testbin/opkg, --version]
environ: *env-def
rc: 0
out: ''
err: ''
- command: [/testbin/opkg, list-installed, zlib-dev]
environ: *env-def
rc: 0
out: ''
err: ''
- command: [/testbin/opkg, install, zlib-dev]
environ: *env-def
rc: 0
out: |
Installing zlib-dev (1.2.11-6) to root...
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
Installing zlib (1.2.11-6) to root...
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk
Configuring zlib.
Configuring zlib-dev.
err: ''
- command: [/testbin/opkg, list-installed, zlib-dev]
environ: *env-def
rc: 0
out: |
zlib-dev - 1.2.11-6
err: ''
- id: install_zlibdev_present
input:
name: zlib-dev
state: present
output:
msg: package(s) already present
mocks:
run_command:
- command: [/testbin/opkg, --version]
environ: *env-def
rc: 0
out: ''
err: ''
- command: [/testbin/opkg, list-installed, zlib-dev]
environ: *env-def
rc: 0
out: |
zlib-dev - 1.2.11-6
err: ''
TestCaseMocks Specifications
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``TestCaseMock`` subclass is free to define the expected data structure.
RunCommandMock Specification
""""""""""""""""""""""""""""
``RunCommandMock`` mocks can be specified with the key ``run_command`` and it expects a ``list`` in which elements follow the structure:
- ``command: Union[list, str]``
Mandatory. The command that is expected to be executed by the module. It corresponds to the parameter ``args`` of the ``AnsibleModule.run_command()`` call.
It can be either a list or a string, though the list form is generally recommended.
- ``environ: dict``
Mandatory. All other parameters passed to the ``AnsibleModule.run_command()`` call.
Most commonly used are ``environ_update`` and ``check_rc``.
Must include all parameters the Ansible module uses in the ``AnsibleModule.run_command()`` call, otherwise the test will fail.
- ``rc: int``
Mandatory. The return code for the command execution.
As per usual in bash scripting, a value of ``0`` means success, whereas any other number is an error code.
- ``out: str``
Mandatory. The *stdout* result of the command execution, as one single string containing zero or more lines.
- ``err: str``
Mandatory. The *stderr* result of the command execution, as one single string containing zero or more lines.
``UTHelper`` Reference
^^^^^^^^^^^^^^^^^^^^^^
.. py:module:: .uthelper
.. py:class:: UTHelper
A class to encapsulate unit tests.
.. py:staticmethod:: from_spec(ansible_module, test_module, test_spec, mocks=None)
Creates an ``UTHelper`` instance from a given test specification.
:param ansible_module: The Ansible module to be tested.
:type ansible_module: module
:param test_module: The test module.
:type test_module: module
:param test_spec: The test specification.
:type test_spec: dict
:param mocks: List of ``TestCaseMocks`` to be used during testing. Currently only ``RunCommandMock`` exists.
:type mocks: list or None
:return: An ``UTHelper`` instance.
:rtype: UTHelper
Example usage of ``from_spec()``:
.. code-block:: python
import sys
from ansible_collections.community.general.plugins.modules import ansible_module
from .uthelper import UTHelper, RunCommandMock
TEST_SPEC = dict(
test_cases=[
...
]
)
helper = UTHelper.from_spec(ansible_module, sys.modules[__name__], TEST_SPEC, mocks=[RunCommandMock])
.. py:staticmethod:: from_file(ansible_module, test_module, test_spec_filehandle, mocks=None)
Creates an ``UTHelper`` instance from a test specification file.
:param ansible_module: The Ansible module to be tested.
:type ansible_module: module
:param test_module: The test module.
:type test_module: module
:param test_spec_filehandle: A file handle to an file stream handle providing the test specification in YAML format.
:type test_spec_filehandle: file
:param mocks: List of ``TestCaseMocks`` to be used during testing. Currently only ``RunCommandMock`` exists.
:type mocks: list or None
:return: An ``UTHelper`` instance.
:rtype: UTHelper
Example usage of ``from_file()``:
.. code-block:: python
import sys
from ansible_collections.community.general.plugins.modules import ansible_module
from .uthelper import UTHelper, RunCommandMock
with open("test_spec.yaml", "r") as test_spec_filehandle:
helper = UTHelper.from_file(ansible_module, sys.modules[__name__], test_spec_filehandle, mocks=[RunCommandMock])
.. py:staticmethod:: from_module(ansible_module, test_module_name, mocks=None)
Creates an ``UTHelper`` instance from a given Ansible module and test module.
:param ansible_module: The Ansible module to be tested.
:type ansible_module: module
:param test_module_name: The name of the test module. It works if passed ``__name__``.
:type test_module_name: str
:param mocks: List of ``TestCaseMocks`` to be used during testing. Currently only ``RunCommandMock`` exists.
:type mocks: list or None
:return: An ``UTHelper`` instance.
:rtype: UTHelper
Example usage of ``from_module()``:
.. code-block:: python
from ansible_collections.community.general.plugins.modules import ansible_module
from .uthelper import UTHelper, RunCommandMock
# Example usage
helper = UTHelper.from_module(ansible_module, __name__, mocks=[RunCommandMock])
Creating TestCaseMocks
^^^^^^^^^^^^^^^^^^^^^^
To create a new ``TestCaseMock`` you must extend that class and implement the relevant parts:
.. code-block:: python
class ShrubberyMock(TestCaseMock):
# this name is mandatory, it is the name used in the test specification
name = "shrubbery"
def setup(self, mocker):
# perform setup, commonly using mocker to patch some other piece of code
...
def check(self, test_case, results):
# verify the tst execution met the expectations of the test case
# for example the function was called as many times as it should
...
def fixtures(self):
# returns a dict mapping names to pytest fixtures that should be used for the test case
# for example, in RunCommandMock it creates a fixture that patches AnsibleModule.get_bin_path
...
Caveats
^^^^^^^
Known issues/opportunities for improvement:
* Only one ``UTHelper`` per test module: UTHelper injects a test function with a fixed name into the module's namespace,
so placing a second ``UTHelper`` instance is going to overwrite the function created by the first one.
* Order of elements in module's namespace is not consistent across executions in Python 3.5, so if adding more tests to the test module
might make Test Helper add its function before or after the other test functions.
In the community.general collection the CI processes uses ``pytest-xdist`` to paralellize and distribute the tests,
and it requires the order of the tests to be consistent.
.. versionadded:: 7.5.0

View File

@@ -5,7 +5,7 @@
namespace: community
name: general
version: 10.4.0
version: 9.2.0
readme: README.md
authors:
- Ansible (https://github.com/ansible)

View File

@@ -3,7 +3,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
requires_ansible: '>=2.15.0'
requires_ansible: '>=2.13.0'
action_groups:
consul:
- consul_agent_check
@@ -16,8 +16,6 @@ action_groups:
- consul_token
proxmox:
- proxmox
- proxmox_backup
- proxmox_backup_info
- proxmox_disk
- proxmox_domain_info
- proxmox_group_info
@@ -33,34 +31,6 @@ action_groups:
- proxmox_template
- proxmox_user_info
- proxmox_vm_info
keycloak:
- keycloak_authentication
- keycloak_authentication_required_actions
- keycloak_authz_authorization_scope
- keycloak_authz_custom_policy
- keycloak_authz_permission
- keycloak_authz_permission_info
- keycloak_client
- keycloak_client_rolemapping
- keycloak_client_rolescope
- keycloak_clientscope
- keycloak_clientscope_type
- keycloak_clientsecret_info
- keycloak_clientsecret_regenerate
- keycloak_clienttemplate
- keycloak_component
- keycloak_component_info
- keycloak_group
- keycloak_identity_provider
- keycloak_realm
- keycloak_realm_key
- keycloak_realm_keys_metadata_info
- keycloak_realm_rolemapping
- keycloak_role
- keycloak_user
- keycloak_user_federation
- keycloak_user_rolemapping
- keycloak_userprofile
plugin_routing:
callback:
actionable:
@@ -74,7 +44,7 @@ plugin_routing:
warning_text: Use the 'default' callback plugin with 'display_skipped_hosts
= no' option.
hipchat:
tombstone:
deprecation:
removal_version: 10.0.0
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
osx_say:
@@ -84,10 +54,6 @@ plugin_routing:
removal_version: 2.0.0
warning_text: Use the 'default' callback plugin with 'display_failed_stderr
= yes' option.
yaml:
deprecation:
removal_version: 13.0.0
warning_text: The plugin has been superseded by the the option `result_format=yaml` in callback plugin ansible.builtin.default from ansible-core 2.13 onwards.
connection:
docker:
redirect: community.docker.docker
@@ -105,64 +71,136 @@ plugin_routing:
nios_next_network:
redirect: infoblox.nios_modules.nios_next_network
modules:
consul_acl:
deprecation:
removal_version: 10.0.0
warning_text: Use community.general.consul_token and/or community.general.consul_policy instead.
rax_cbs_attachments:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cbs:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cdb_database:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cdb_user:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cdb:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_clb_nodes:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_clb_ssl:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_clb:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_dns_record:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_dns:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_facts:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_files_objects:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_files:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_identity:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_keypair:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_meta:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_alarm:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_check:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_entity:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_notification_plan:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_notification:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_network:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_queue:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_scaling_group:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_scaling_policy:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rhn_channel:
deprecation:
removal_version: 10.0.0
warning_text: RHN is EOL, please contact the community.general maintainers
if still using this; see the module documentation for more details.
rhn_register:
deprecation:
removal_version: 10.0.0
warning_text: RHN is EOL, please contact the community.general maintainers
if still using this; see the module documentation for more details.
stackdriver:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on HTTPS APIs that do not exist anymore,
and any new development in the direction of providing an alternative should
happen in the context of the google.cloud collection.
ali_instance_facts:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.ali_instance_info instead.
atomic_container:
deprecation:
removal_version: 13.0.0
warning_text: Project Atomic was sunset by the end of 2019.
atomic_host:
deprecation:
removal_version: 13.0.0
warning_text: Project Atomic was sunset by the end of 2019.
atomic_image:
deprecation:
removal_version: 13.0.0
warning_text: Project Atomic was sunset by the end of 2019.
cisco_spark:
redirect: community.general.cisco_webex
clc_alert_policy:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_blueprint_package:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_firewall_policy:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_group:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_loadbalancer:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_modify_server:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_publicip:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_server:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
clc_server_snapshot:
deprecation:
removal_version: 11.0.0
warning_text: CenturyLink Cloud services went EOL in September 2023.
consul_acl:
tombstone:
removal_version: 10.0.0
warning_text: Use community.general.consul_token and/or community.general.consul_policy instead.
docker_compose:
redirect: community.docker.docker_compose
docker_config:
@@ -217,10 +255,6 @@ plugin_routing:
redirect: community.docker.docker_volume
docker_volume_info:
redirect: community.docker.docker_volume_info
facter:
deprecation:
removal_version: 12.0.0
warning_text: Use community.general.facter_facts instead.
flowdock:
tombstone:
removal_version: 9.0.0
@@ -314,10 +348,6 @@ plugin_routing:
redirect: community.hrobot.firewall
hetzner_firewall_info:
redirect: community.hrobot.firewall_info
hipchat:
deprecation:
removal_version: 11.0.0
warning_text: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
hpilo_facts:
tombstone:
removal_version: 3.0.0
@@ -639,26 +669,6 @@ plugin_routing:
redirect: community.postgresql.postgresql_user
postgresql_user_obj_stat_info:
redirect: community.postgresql.postgresql_user_obj_stat_info
profitbricks:
deprecation:
removal_version: 11.0.0
warning_text: Supporting library is unsupported since 2021.
profitbricks_datacenter:
deprecation:
removal_version: 11.0.0
warning_text: Supporting library is unsupported since 2021.
profitbricks_nic:
deprecation:
removal_version: 11.0.0
warning_text: Supporting library is unsupported since 2021.
profitbricks_volume:
deprecation:
removal_version: 11.0.0
warning_text: Supporting library is unsupported since 2021.
profitbricks_volume_attachments:
deprecation:
removal_version: 11.0.0
warning_text: Supporting library is unsupported since 2021.
purefa_facts:
tombstone:
removal_version: 3.0.0
@@ -671,122 +681,10 @@ plugin_routing:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.python_requirements_info instead.
rax_cbs_attachments:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cbs:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cdb_database:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cdb_user:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_cdb:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_clb_nodes:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_clb_ssl:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_clb:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_dns_record:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_dns:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_facts:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_files_objects:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_files:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_identity:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_keypair:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_meta:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_alarm:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_check:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_entity:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_notification_plan:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_mon_notification:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_network:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_queue:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_scaling_group:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
rax_scaling_policy:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on the deprecated package pyrax.
redfish_facts:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.redfish_info instead.
rhn_channel:
tombstone:
removal_version: 10.0.0
warning_text: RHN is EOL.
rhn_register:
tombstone:
removal_version: 10.0.0
warning_text: RHN is EOL.
sapcar_extract:
redirect: community.sap_libs.sapcar_extract
sap_task_list_execute:
@@ -819,26 +717,6 @@ plugin_routing:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_volume_info instead.
sensu_check:
deprecation:
removal_version: 13.0.0
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
sensu_client:
deprecation:
removal_version: 13.0.0
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
sensu_handler:
deprecation:
removal_version: 13.0.0
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
sensu_silence:
deprecation:
removal_version: 13.0.0
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
sensu_subscription:
deprecation:
removal_version: 13.0.0
warning_text: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
sf_account_manager:
tombstone:
removal_version: 2.0.0
@@ -863,12 +741,6 @@ plugin_routing:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.smartos_image_info instead.
stackdriver:
tombstone:
removal_version: 9.0.0
warning_text: This module relied on HTTPS APIs that do not exist anymore,
and any new development in the direction of providing an alternative should
happen in the context of the google.cloud collection.
vertica_facts:
tombstone:
removal_version: 3.0.0
@@ -903,6 +775,11 @@ plugin_routing:
removal_version: 3.0.0
warning_text: Use community.general.xenserver_guest_info instead.
doc_fragments:
rackspace:
tombstone:
removal_version: 9.0.0
warning_text: This doc fragment was used by rax modules, that relied on the deprecated
package pyrax.
_gcp:
redirect: community.google._gcp
docker:
@@ -917,16 +794,11 @@ plugin_routing:
redirect: infoblox.nios_modules.nios
postgresql:
redirect: community.postgresql.postgresql
purestorage:
deprecation:
removal_version: 12.0.0
warning_text: The modules for purestorage were removed in community.general 3.0.0, this document fragment was left behind.
rackspace:
module_utils:
rax:
tombstone:
removal_version: 9.0.0
warning_text: This doc fragment was used by rax modules, that relied on the deprecated
package pyrax.
module_utils:
warning_text: This module util relied on the deprecated package pyrax.
docker.common:
redirect: community.docker.common
docker.swarm:
@@ -945,14 +817,6 @@ plugin_routing:
redirect: infoblox.nios_modules.api
postgresql:
redirect: community.postgresql.postgresql
pure:
deprecation:
removal_version: 12.0.0
warning_text: The modules for purestorage were removed in community.general 3.0.0, this module util was left behind.
rax:
tombstone:
removal_version: 9.0.0
warning_text: This module util relied on the deprecated package pyrax.
remote_management.dellemc.dellemc_idrac:
redirect: dellemc.openmanage.dellemc_idrac
remote_management.dellemc.ome:

View File

@@ -3,7 +3,8 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import time
@@ -21,33 +22,25 @@ class ActionModule(ActionBase):
_VALID_ARGS = frozenset(('path', 'state', 'table', 'noflush', 'counters', 'modprobe', 'ip_version', 'wait'))
DEFAULT_SUDOABLE = True
@staticmethod
def msg_error__async_and_poll_not_zero(task_poll, task_async, max_timeout):
return (
"This module doesn't support async>0 and poll>0 when its 'state' param "
"is set to 'restored'. To enable its rollback feature (that needs the "
"module to run asynchronously on the remote), please set task attribute "
f"'poll' (={task_poll}) to 0, and 'async' (={task_async}) to a value >2 and not greater than "
f"'ansible_timeout' (={max_timeout}) (recommended).")
@staticmethod
def msg_warning__no_async_is_no_rollback(task_poll, task_async, max_timeout):
return (
"Attempts to restore iptables state without rollback in case of mistake "
"may lead the ansible controller to loose access to the hosts and never "
"regain it before fixing firewall rules through a serial console, or any "
f"other way except SSH. Please set task attribute 'poll' (={task_poll}) to 0, and "
f"'async' (={task_async}) to a value >2 and not greater than 'ansible_timeout' (={max_timeout}) "
"(recommended).")
@staticmethod
def msg_warning__async_greater_than_timeout(task_poll, task_async, max_timeout):
return (
"You attempt to restore iptables state with rollback in case of mistake, "
"but with settings that will lead this rollback to happen AFTER that the "
"controller will reach its own timeout. Please set task attribute 'poll' "
f"(={task_poll}) to 0, and 'async' (={task_async}) to a value >2 and not greater than "
f"'ansible_timeout' (={max_timeout}) (recommended).")
MSG_ERROR__ASYNC_AND_POLL_NOT_ZERO = (
"This module doesn't support async>0 and poll>0 when its 'state' param "
"is set to 'restored'. To enable its rollback feature (that needs the "
"module to run asynchronously on the remote), please set task attribute "
"'poll' (=%s) to 0, and 'async' (=%s) to a value >2 and not greater than "
"'ansible_timeout' (=%s) (recommended).")
MSG_WARNING__NO_ASYNC_IS_NO_ROLLBACK = (
"Attempts to restore iptables state without rollback in case of mistake "
"may lead the ansible controller to loose access to the hosts and never "
"regain it before fixing firewall rules through a serial console, or any "
"other way except SSH. Please set task attribute 'poll' (=%s) to 0, and "
"'async' (=%s) to a value >2 and not greater than 'ansible_timeout' (=%s) "
"(recommended).")
MSG_WARNING__ASYNC_GREATER_THAN_TIMEOUT = (
"You attempt to restore iptables state with rollback in case of mistake, "
"but with settings that will lead this rollback to happen AFTER that the "
"controller will reach its own timeout. Please set task attribute 'poll' "
"(=%s) to 0, and 'async' (=%s) to a value >2 and not greater than "
"'ansible_timeout' (=%s) (recommended).")
def _async_result(self, async_status_args, task_vars, timeout):
'''
@@ -95,25 +88,21 @@ class ActionModule(ActionBase):
max_timeout = self._connection._play_context.timeout
module_args = self._task.args
async_status_args = {}
starter_cmd = None
confirm_cmd = None
if module_args.get('state', None) == 'restored':
if not wrap_async:
if not check_mode:
display.warning(self.msg_error__async_and_poll_not_zero(
display.warning(self.MSG_WARNING__NO_ASYNC_IS_NO_ROLLBACK % (
task_poll,
task_async,
max_timeout))
elif task_poll:
raise AnsibleActionFail(self.msg_warning__no_async_is_no_rollback(
raise AnsibleActionFail(self.MSG_ERROR__ASYNC_AND_POLL_NOT_ZERO % (
task_poll,
task_async,
max_timeout))
else:
if task_async > max_timeout and not check_mode:
display.warning(self.msg_warning__async_greater_than_timeout(
display.warning(self.MSG_WARNING__ASYNC_GREATER_THAN_TIMEOUT % (
task_poll,
task_async,
max_timeout))
@@ -126,10 +115,10 @@ class ActionModule(ActionBase):
# remote and local sides (if not the same, make the loop
# longer on the controller); and set a backup file path.
module_args['_timeout'] = task_async
module_args['_back'] = f'{async_dir}/iptables.state'
module_args['_back'] = '%s/iptables.state' % async_dir
async_status_args = dict(mode='status')
confirm_cmd = f"rm -f {module_args['_back']}"
starter_cmd = f"touch {module_args['_back']}.starter"
confirm_cmd = 'rm -f %s' % module_args['_back']
starter_cmd = 'touch %s.starter' % module_args['_back']
remaining_time = max(task_async, max_timeout)
# do work!

View File

@@ -5,8 +5,9 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.errors import AnsibleError, AnsibleConnectionFailure
from ansible.module_utils.common.text.converters import to_native, to_text
@@ -17,10 +18,6 @@ from ansible.utils.display import Display
display = Display()
def fmt(mapping, key):
return to_native(mapping[key]).strip()
class TimedOutException(Exception):
pass
@@ -87,26 +84,31 @@ class ActionModule(ActionBase):
def get_distribution(self, task_vars):
# FIXME: only execute the module if we don't already have the facts we need
distribution = {}
display.debug(f'{self._task.action}: running setup module to get distribution')
display.debug('{action}: running setup module to get distribution'.format(action=self._task.action))
module_output = self._execute_module(
task_vars=task_vars,
module_name='ansible.legacy.setup',
module_args={'gather_subset': 'min'})
try:
if module_output.get('failed', False):
raise AnsibleError(f"Failed to determine system distribution. {fmt(module_output, 'module_stdout')}, {fmt(module_output, 'module_stderr')}")
raise AnsibleError('Failed to determine system distribution. {0}, {1}'.format(
to_native(module_output['module_stdout']).strip(),
to_native(module_output['module_stderr']).strip()))
distribution['name'] = module_output['ansible_facts']['ansible_distribution'].lower()
distribution['version'] = to_text(
module_output['ansible_facts']['ansible_distribution_version'].split('.')[0])
distribution['family'] = to_text(module_output['ansible_facts']['ansible_os_family'].lower())
display.debug(f"{self._task.action}: distribution: {distribution}")
display.debug("{action}: distribution: {dist}".format(action=self._task.action, dist=distribution))
return distribution
except KeyError as ke:
raise AnsibleError(f'Failed to get distribution information. Missing "{ke.args[0]}" in output.')
raise AnsibleError('Failed to get distribution information. Missing "{0}" in output.'.format(ke.args[0]))
def get_shutdown_command(self, task_vars, distribution):
def find_command(command, find_search_paths):
display.debug(f'{self._task.action}: running find module looking in {find_search_paths} to get path for "{command}"')
display.debug('{action}: running find module looking in {paths} to get path for "{command}"'.format(
action=self._task.action,
command=command,
paths=find_search_paths))
find_result = self._execute_module(
task_vars=task_vars,
# prevent collection search by calling with ansible.legacy (still allows library/ override of find)
@@ -128,37 +130,42 @@ class ActionModule(ActionBase):
if is_string(search_paths):
search_paths = [search_paths]
# Error if we didn't get a list
err_msg = "'search_paths' must be a string or flat list of strings, got {0}"
try:
incorrect_type = any(not is_string(x) for x in search_paths)
if not isinstance(search_paths, list) or incorrect_type:
raise TypeError
except TypeError:
# Error if we didn't get a list
err_msg = f"'search_paths' must be a string or flat list of strings, got {search_paths}"
raise AnsibleError(err_msg)
raise AnsibleError(err_msg.format(search_paths))
full_path = find_command(shutdown_bin, search_paths) # find the path to the shutdown command
if not full_path: # if we could not find the shutdown command
# tell the user we will try with systemd
display.vvv(f'Unable to find command "{shutdown_bin}" in search paths: {search_paths}, will attempt a shutdown using systemd directly.')
display.vvv('Unable to find command "{0}" in search paths: {1}, will attempt a shutdown using systemd '
'directly.'.format(shutdown_bin, search_paths)) # tell the user we will try with systemd
systemctl_search_paths = ['/bin', '/usr/bin']
full_path = find_command('systemctl', systemctl_search_paths) # find the path to the systemctl command
if not full_path: # if we couldn't find systemctl
raise AnsibleError(
f'Could not find command "{shutdown_bin}" in search paths: {search_paths} or systemctl'
f' command in search paths: {systemctl_search_paths}, unable to shutdown.') # we give up here
'Could not find command "{0}" in search paths: {1} or systemctl command in search paths: {2}, unable to shutdown.'.
format(shutdown_bin, search_paths, systemctl_search_paths)) # we give up here
else:
return f"{full_path[0]} poweroff" # done, since we cannot use args with systemd shutdown
return "{0} poweroff".format(full_path[0]) # done, since we cannot use args with systemd shutdown
# systemd case taken care of, here we add args to the command
args = self._get_value_from_facts('SHUTDOWN_COMMAND_ARGS', distribution, 'DEFAULT_SHUTDOWN_COMMAND_ARGS')
# Convert seconds to minutes. If less that 60, set it to 0.
delay_sec = self.delay
shutdown_message = self._task.args.get('msg', self.DEFAULT_SHUTDOWN_MESSAGE)
af = args.format(delay_sec=delay_sec, delay_min=delay_sec // 60, message=shutdown_message)
return f'{full_path[0]} {af}'
return '{0} {1}'. \
format(
full_path[0],
args.format(
delay_sec=delay_sec,
delay_min=delay_sec // 60,
message=shutdown_message
)
)
def perform_shutdown(self, task_vars, distribution):
result = {}
@@ -167,8 +174,9 @@ class ActionModule(ActionBase):
self.cleanup(force=True)
try:
display.vvv(f"{self._task.action}: shutting down server...")
display.debug(f"{self._task.action}: shutting down server with command '{shutdown_command_exec}'")
display.vvv("{action}: shutting down server...".format(action=self._task.action))
display.debug("{action}: shutting down server with command '{command}'".
format(action=self._task.action, command=shutdown_command_exec))
if self._play_context.check_mode:
shutdown_result['rc'] = 0
else:
@@ -176,13 +184,16 @@ class ActionModule(ActionBase):
except AnsibleConnectionFailure as e:
# If the connection is closed too quickly due to the system being shutdown, carry on
display.debug(
f'{self._task.action}: AnsibleConnectionFailure caught and handled: {e}')
'{action}: AnsibleConnectionFailure caught and handled: {error}'.format(action=self._task.action,
error=to_text(e)))
shutdown_result['rc'] = 0
if shutdown_result['rc'] != 0:
result['failed'] = True
result['shutdown'] = False
result['msg'] = f"Shutdown command failed. Error was {fmt(shutdown_result, 'stdout')}, {fmt(shutdown_result, 'stderr')}"
result['msg'] = "Shutdown command failed. Error was {stdout}, {stderr}".format(
stdout=to_native(shutdown_result['stdout'].strip()),
stderr=to_native(shutdown_result['stderr'].strip()))
return result
result['failed'] = False
@@ -195,7 +206,7 @@ class ActionModule(ActionBase):
# If running with local connection, fail so we don't shutdown ourself
if self._connection.transport == 'local' and (not self._play_context.check_mode):
msg = f'Running {self._task.action} with local connection would shutdown the control node.'
msg = 'Running {0} with local connection would shutdown the control node.'.format(self._task.action)
return {'changed': False, 'elapsed': 0, 'shutdown': False, 'failed': True, 'msg': msg}
if task_vars is None:

View File

@@ -2,88 +2,83 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: doas
short_description: Do As user
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(doas) utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task.
type: string
ini:
- section: privilege_escalation
key: become_user
- section: doas_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_doas_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_DOAS_USER
become_exe:
description: C(doas) executable.
type: string
default: doas
ini:
- section: privilege_escalation
key: become_exe
- section: doas_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_doas_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_DOAS_EXE
become_flags:
description: Options to pass to C(doas).
type: string
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: doas_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_doas_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_DOAS_FLAGS
become_pass:
description: Password for C(doas) prompt.
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_doas_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_DOAS_PASS
ini:
- section: doas_become_plugin
key: password
prompt_l10n:
DOCUMENTATION = '''
name: doas
short_description: Do As user
description:
- List of localized strings to match for prompt detection.
- If empty we will use the built in one.
type: list
elements: string
default: []
ini:
- section: doas_become_plugin
key: localized_prompts
vars:
- name: ansible_doas_prompt_l10n
env:
- name: ANSIBLE_DOAS_PROMPT_L10N
"""
- This become plugins allows your remote/login user to execute commands as another user via the doas utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task
ini:
- section: privilege_escalation
key: become_user
- section: doas_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_doas_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_DOAS_USER
become_exe:
description: Doas executable
default: doas
ini:
- section: privilege_escalation
key: become_exe
- section: doas_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_doas_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_DOAS_EXE
become_flags:
description: Options to pass to doas
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: doas_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_doas_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_DOAS_FLAGS
become_pass:
description: password for doas prompt
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_doas_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_DOAS_PASS
ini:
- section: doas_become_plugin
key: password
prompt_l10n:
description:
- List of localized strings to match for prompt detection
- If empty we'll use the built in one
default: []
ini:
- section: doas_become_plugin
key: localized_prompts
vars:
- name: ansible_doas_prompt_l10n
env:
- name: ANSIBLE_DOAS_PROMPT_L10N
'''
import re
@@ -124,9 +119,9 @@ class BecomeModule(BecomeBase):
flags += ' -n'
become_user = self.get_option('become_user')
user = f'-u {become_user}' if become_user else ''
user = '-u %s' % (become_user) if become_user else ''
success_cmd = self._build_success_command(cmd, shell, noexe=True)
executable = getattr(shell, 'executable', shell.SHELL_FAMILY)
return f'{become_exe} {flags} {user} {executable} -c {success_cmd}'
return '%s %s %s %s -c %s' % (become_exe, flags, user, executable, success_cmd)

View File

@@ -2,74 +2,71 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: dzdo
short_description: Centrify's Direct Authorize
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(dzdo) utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task.
type: string
ini:
- section: privilege_escalation
key: become_user
- section: dzdo_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_dzdo_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_DZDO_USER
become_exe:
description: C(dzdo) executable.
type: string
default: dzdo
ini:
- section: privilege_escalation
key: become_exe
- section: dzdo_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_dzdo_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_DZDO_EXE
become_flags:
description: Options to pass to C(dzdo).
type: string
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: dzdo_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_dzdo_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_DZDO_FLAGS
become_pass:
description: Options to pass to C(dzdo).
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_dzdo_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_DZDO_PASS
ini:
- section: dzdo_become_plugin
key: password
"""
DOCUMENTATION = '''
name: dzdo
short_description: Centrify's Direct Authorize
description:
- This become plugins allows your remote/login user to execute commands as another user via the dzdo utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task
ini:
- section: privilege_escalation
key: become_user
- section: dzdo_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_dzdo_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_DZDO_USER
become_exe:
description: Dzdo executable
default: dzdo
ini:
- section: privilege_escalation
key: become_exe
- section: dzdo_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_dzdo_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_DZDO_EXE
become_flags:
description: Options to pass to dzdo
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: dzdo_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_dzdo_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_DZDO_FLAGS
become_pass:
description: Options to pass to dzdo
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_dzdo_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_DZDO_PASS
ini:
- section: dzdo_become_plugin
key: password
'''
from ansible.plugins.become import BecomeBase
@@ -91,10 +88,10 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags')
if self.get_option('become_pass'):
self.prompt = f'[dzdo via ansible, key={self._id}] password:'
flags = f"{flags.replace('-n', '')} -p \"{self.prompt}\""
self.prompt = '[dzdo via ansible, key=%s] password:' % self._id
flags = '%s -p "%s"' % (flags.replace('-n', ''), self.prompt)
become_user = self.get_option('become_user')
user = f'-u {become_user}' if become_user else ''
user = '-u %s' % (become_user) if become_user else ''
return f"{becomecmd} {flags} {user} {self._build_success_command(cmd, shell)}"
return ' '.join([becomecmd, flags, user, self._build_success_command(cmd, shell)])

View File

@@ -2,89 +2,84 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: ksu
short_description: Kerberos substitute user
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(ksu) utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task.
type: string
ini:
- section: privilege_escalation
key: become_user
- section: ksu_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_ksu_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_KSU_USER
required: true
become_exe:
description: C(ksu) executable.
type: string
default: ksu
ini:
- section: privilege_escalation
key: become_exe
- section: ksu_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_ksu_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_KSU_EXE
become_flags:
description: Options to pass to C(ksu).
type: string
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: ksu_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_ksu_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_KSU_FLAGS
become_pass:
description: C(ksu) password.
type: string
required: false
vars:
- name: ansible_ksu_pass
- name: ansible_become_pass
- name: ansible_become_password
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_KSU_PASS
ini:
- section: ksu_become_plugin
key: password
prompt_l10n:
DOCUMENTATION = '''
name: ksu
short_description: Kerberos substitute user
description:
- List of localized strings to match for prompt detection.
- If empty we will use the built in one.
type: list
elements: string
default: []
ini:
- section: ksu_become_plugin
key: localized_prompts
vars:
- name: ansible_ksu_prompt_l10n
env:
- name: ANSIBLE_KSU_PROMPT_L10N
"""
- This become plugins allows your remote/login user to execute commands as another user via the ksu utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task
ini:
- section: privilege_escalation
key: become_user
- section: ksu_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_ksu_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_KSU_USER
required: true
become_exe:
description: Su executable
default: ksu
ini:
- section: privilege_escalation
key: become_exe
- section: ksu_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_ksu_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_KSU_EXE
become_flags:
description: Options to pass to ksu
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: ksu_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_ksu_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_KSU_FLAGS
become_pass:
description: ksu password
required: false
vars:
- name: ansible_ksu_pass
- name: ansible_become_pass
- name: ansible_become_password
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_KSU_PASS
ini:
- section: ksu_become_plugin
key: password
prompt_l10n:
description:
- List of localized strings to match for prompt detection
- If empty we'll use the built in one
default: []
ini:
- section: ksu_become_plugin
key: localized_prompts
vars:
- name: ansible_ksu_prompt_l10n
env:
- name: ANSIBLE_KSU_PROMPT_L10N
'''
import re
@@ -123,4 +118,4 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags')
user = self.get_option('become_user')
return f'{exe} {user} {flags} -e {self._build_success_command(cmd, shell)} '
return '%s %s %s -e %s ' % (exe, user, flags, self._build_success_command(cmd, shell))

View File

@@ -2,92 +2,90 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: machinectl
short_description: Systemd's machinectl privilege escalation
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(machinectl) utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task.
type: string
default: ''
ini:
- section: privilege_escalation
key: become_user
- section: machinectl_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_machinectl_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_MACHINECTL_USER
become_exe:
description: C(machinectl) executable.
type: string
default: machinectl
ini:
- section: privilege_escalation
key: become_exe
- section: machinectl_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_machinectl_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_MACHINECTL_EXE
become_flags:
description: Options to pass to C(machinectl).
type: string
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: machinectl_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_machinectl_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_MACHINECTL_FLAGS
become_pass:
description: Password for C(machinectl).
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_machinectl_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_MACHINECTL_PASS
ini:
- section: machinectl_become_plugin
key: password
notes:
- When not using this plugin with user V(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.
"""
DOCUMENTATION = '''
name: machinectl
short_description: Systemd's machinectl privilege escalation
description:
- This become plugins allows your remote/login user to execute commands as another user via the machinectl utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task
default: ''
ini:
- section: privilege_escalation
key: become_user
- section: machinectl_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_machinectl_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_MACHINECTL_USER
become_exe:
description: Machinectl executable
default: machinectl
ini:
- section: privilege_escalation
key: become_exe
- section: machinectl_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_machinectl_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_MACHINECTL_EXE
become_flags:
description: Options to pass to machinectl
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: machinectl_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_machinectl_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_MACHINECTL_FLAGS
become_pass:
description: Password for machinectl
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_machinectl_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_MACHINECTL_PASS
ini:
- section: machinectl_become_plugin
key: password
notes:
- When not using this plugin with user V(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"""
EXAMPLES = r'''
# A polkit rule needed to use the module with a non-root user.
# See the Notes section for details.
/etc/polkit-1/rules.d/60-machinectl-fast-user-auth.rules: |-
/etc/polkit-1/rules.d/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
@@ -121,7 +119,7 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags')
user = self.get_option('become_user')
return f'{become} -q shell {flags} {user}@ {self._build_success_command(cmd, shell)}'
return '%s -q shell %s %s@ %s' % (become, flags, user, self._build_success_command(cmd, shell))
def check_success(self, b_output):
b_output = self.remove_ansi_codes(b_output)

View File

@@ -2,86 +2,83 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: pbrun
short_description: PowerBroker run
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(pbrun) utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task.
type: string
default: ''
ini:
- section: privilege_escalation
key: become_user
- section: pbrun_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_pbrun_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_PBRUN_USER
become_exe:
description: C(pbrun) executable.
type: string
default: pbrun
ini:
- section: privilege_escalation
key: become_exe
- section: pbrun_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_pbrun_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_PBRUN_EXE
become_flags:
description: Options to pass to C(pbrun).
type: string
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: pbrun_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_pbrun_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_PBRUN_FLAGS
become_pass:
description: Password for C(pbrun).
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_pbrun_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_PBRUN_PASS
ini:
- section: pbrun_become_plugin
key: password
wrap_exe:
description: Toggle to wrap the command C(pbrun) calls in C(shell -c) or not.
default: false
type: bool
ini:
- section: pbrun_become_plugin
key: wrap_execution
vars:
- name: ansible_pbrun_wrap_execution
env:
- name: ANSIBLE_PBRUN_WRAP_EXECUTION
"""
DOCUMENTATION = '''
name: pbrun
short_description: PowerBroker run
description:
- This become plugins allows your remote/login user to execute commands as another user via the pbrun utility.
author: Ansible Core Team
options:
become_user:
description: User you 'become' to execute the task
default: ''
ini:
- section: privilege_escalation
key: become_user
- section: pbrun_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_pbrun_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_PBRUN_USER
become_exe:
description: Sudo executable
default: pbrun
ini:
- section: privilege_escalation
key: become_exe
- section: pbrun_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_pbrun_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_PBRUN_EXE
become_flags:
description: Options to pass to pbrun
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: pbrun_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_pbrun_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_PBRUN_FLAGS
become_pass:
description: Password for pbrun
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_pbrun_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_PBRUN_PASS
ini:
- section: pbrun_become_plugin
key: password
wrap_exe:
description: Toggle to wrap the command pbrun calls in 'shell -c' or not
default: false
type: bool
ini:
- section: pbrun_become_plugin
key: wrap_execution
vars:
- name: ansible_pbrun_wrap_execution
env:
- name: ANSIBLE_PBRUN_WRAP_EXECUTION
'''
from ansible.plugins.become import BecomeBase
@@ -102,7 +99,7 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags')
become_user = self.get_option('become_user')
user = f'-u {become_user}' if become_user else ''
user = '-u %s' % (become_user) if become_user else ''
noexe = not self.get_option('wrap_exe')
return f"{become_exe} {flags} {user} {self._build_success_command(cmd, shell, noexe=noexe)}"
return ' '.join([become_exe, flags, user, self._build_success_command(cmd, shell, noexe=noexe)])

View File

@@ -2,91 +2,88 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: pfexec
short_description: profile based execution
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(pfexec) utility.
author: Ansible Core Team
options:
become_user:
DOCUMENTATION = '''
name: pfexec
short_description: profile based execution
description:
- User you 'become' to execute the task.
- This plugin ignores this setting as pfexec uses its own C(exec_attr) to figure this out, but it is supplied here for
Ansible to make decisions needed for the task execution, like file permissions.
type: string
default: root
ini:
- section: privilege_escalation
key: become_user
- section: pfexec_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_pfexec_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_PFEXEC_USER
become_exe:
description: C(pfexec) executable.
type: string
default: pfexec
ini:
- section: privilege_escalation
key: become_exe
- section: pfexec_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_pfexec_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_PFEXEC_EXE
become_flags:
description: Options to pass to C(pfexec).
type: string
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: pfexec_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_pfexec_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_PFEXEC_FLAGS
become_pass:
description: C(pfexec) password.
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_pfexec_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_PFEXEC_PASS
ini:
- section: pfexec_become_plugin
key: password
wrap_exe:
description: Toggle to wrap the command C(pfexec) calls in C(shell -c) or not.
default: false
type: bool
ini:
- section: pfexec_become_plugin
key: wrap_execution
vars:
- name: ansible_pfexec_wrap_execution
env:
- name: ANSIBLE_PFEXEC_WRAP_EXECUTION
notes:
- This plugin ignores O(become_user) as pfexec uses its own C(exec_attr) to figure this out.
"""
- This become plugins allows your remote/login user to execute commands as another user via the pfexec utility.
author: Ansible Core Team
options:
become_user:
description:
- User you 'become' to execute the task
- This plugin ignores this setting as pfexec uses it's own C(exec_attr) to figure this out,
but it is supplied here for Ansible to make decisions needed for the task execution, like file permissions.
default: root
ini:
- section: privilege_escalation
key: become_user
- section: pfexec_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_pfexec_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_PFEXEC_USER
become_exe:
description: Sudo executable
default: pfexec
ini:
- section: privilege_escalation
key: become_exe
- section: pfexec_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_pfexec_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_PFEXEC_EXE
become_flags:
description: Options to pass to pfexec
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: pfexec_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_pfexec_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_PFEXEC_FLAGS
become_pass:
description: pfexec password
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_pfexec_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_PFEXEC_PASS
ini:
- section: pfexec_become_plugin
key: password
wrap_exe:
description: Toggle to wrap the command pfexec calls in 'shell -c' or not
default: false
type: bool
ini:
- section: pfexec_become_plugin
key: wrap_execution
vars:
- name: ansible_pfexec_wrap_execution
env:
- name: ANSIBLE_PFEXEC_WRAP_EXECUTION
notes:
- This plugin ignores O(become_user) as pfexec uses it's own C(exec_attr) to figure this out.
'''
from ansible.plugins.become import BecomeBase
@@ -105,4 +102,4 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags')
noexe = not self.get_option('wrap_exe')
return f'{exe} {flags} {self._build_success_command(cmd, shell, noexe=noexe)}'
return '%s %s %s' % (exe, flags, self._build_success_command(cmd, shell, noexe=noexe))

View File

@@ -2,62 +2,60 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: pmrun
short_description: Privilege Manager run
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(pmrun) utility.
author: Ansible Core Team
options:
become_exe:
description: C(pmrun) executable.
type: string
default: pmrun
ini:
- section: privilege_escalation
key: become_exe
- section: pmrun_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_pmrun_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_PMRUN_EXE
become_flags:
description: Options to pass to C(pmrun).
type: string
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: pmrun_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_pmrun_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_PMRUN_FLAGS
become_pass:
description: C(pmrun) password.
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_pmrun_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_PMRUN_PASS
ini:
- section: pmrun_become_plugin
key: password
notes:
- This plugin ignores the C(become_user) supplied and uses C(pmrun)'s own configuration to select the user.
"""
DOCUMENTATION = '''
name: pmrun
short_description: Privilege Manager run
description:
- This become plugins allows your remote/login user to execute commands as another user via the pmrun utility.
author: Ansible Core Team
options:
become_exe:
description: Sudo executable
default: pmrun
ini:
- section: privilege_escalation
key: become_exe
- section: pmrun_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_pmrun_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_PMRUN_EXE
become_flags:
description: Options to pass to pmrun
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: pmrun_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_pmrun_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_PMRUN_FLAGS
become_pass:
description: pmrun password
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_pmrun_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_PMRUN_PASS
ini:
- section: pmrun_become_plugin
key: password
notes:
- This plugin ignores the become_user supplied and uses pmrun's own configuration to select the user.
'''
from ansible.plugins.become import BecomeBase
from ansible.module_utils.six.moves import shlex_quote
@@ -77,4 +75,4 @@ class BecomeModule(BecomeBase):
become = self.get_option('become_exe')
flags = self.get_option('become_flags')
return f'{become} {flags} {shlex_quote(self._build_success_command(cmd, shell))}'
return '%s %s %s' % (become, flags, shlex_quote(self._build_success_command(cmd, shell)))

View File

@@ -3,71 +3,72 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r"""
name: run0
short_description: Systemd's run0
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(run0) utility.
author:
- Thomas Sjögren (@konstruktoid)
version_added: '9.0.0'
options:
become_user:
description: User you 'become' to execute the task.
default: root
ini:
- section: privilege_escalation
key: become_user
- section: run0_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_run0_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_RUN0_USER
type: string
become_exe:
description: C(run0) executable.
default: run0
ini:
- section: privilege_escalation
key: become_exe
- section: run0_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_run0_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_RUN0_EXE
type: string
become_flags:
description: Options to pass to C(run0).
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: run0_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_run0_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_RUN0_FLAGS
type: string
notes:
- This plugin will only work when a C(polkit) rule is in place.
DOCUMENTATION = """
name: run0
short_description: Systemd's run0
description:
- This become plugins allows your remote/login user to execute commands as another user via the C(run0) utility.
author:
- Thomas Sjögren (@konstruktoid)
version_added: '9.0.0'
options:
become_user:
description: User you 'become' to execute the task.
default: root
ini:
- section: privilege_escalation
key: become_user
- section: run0_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_run0_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_RUN0_USER
type: string
become_exe:
description: The C(run0) executable.
default: run0
ini:
- section: privilege_escalation
key: become_exe
- section: run0_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_run0_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_RUN0_EXE
type: string
become_flags:
description: Options to pass to run0.
default: ''
ini:
- section: privilege_escalation
key: become_flags
- section: run0_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_run0_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_RUN0_FLAGS
type: string
notes:
- This plugin will only work when a polkit rule is in place.
"""
EXAMPLES = r"""
# An example polkit rule that allows the user 'ansible' in the 'wheel' group
# to execute commands using run0 without authentication.
/etc/polkit-1/rules.d/60-run0-fast-user-auth.rules: |-
/etc/polkit-1/rules.d/60-run0-fast-user-auth.rules: |
polkit.addRule(function(action, subject) {
if(action.id == "org.freedesktop.systemd1.manage-units" &&
subject.isInGroup("wheel") &&

View File

@@ -2,75 +2,72 @@
# Copyright (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: sesu
short_description: CA Privileged Access Manager
description:
- This become plugins allows your remote/login user to execute commands as another user using the C(sesu) utility.
author: ansible (@nekonyuu)
options:
become_user:
description: User you 'become' to execute the task.
type: string
default: ''
ini:
- section: privilege_escalation
key: become_user
- section: sesu_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_sesu_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_SESU_USER
become_exe:
description: C(sesu) executable.
type: string
default: sesu
ini:
- section: privilege_escalation
key: become_exe
- section: sesu_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_sesu_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_SESU_EXE
become_flags:
description: Options to pass to C(sesu).
type: string
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: sesu_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_sesu_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_SESU_FLAGS
become_pass:
description: Password to pass to C(sesu).
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_sesu_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_SESU_PASS
ini:
- section: sesu_become_plugin
key: password
"""
DOCUMENTATION = '''
name: sesu
short_description: CA Privileged Access Manager
description:
- This become plugins allows your remote/login user to execute commands as another user via the sesu utility.
author: ansible (@nekonyuu)
options:
become_user:
description: User you 'become' to execute the task
default: ''
ini:
- section: privilege_escalation
key: become_user
- section: sesu_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_sesu_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_SESU_USER
become_exe:
description: sesu executable
default: sesu
ini:
- section: privilege_escalation
key: become_exe
- section: sesu_become_plugin
key: executable
vars:
- name: ansible_become_exe
- name: ansible_sesu_exe
env:
- name: ANSIBLE_BECOME_EXE
- name: ANSIBLE_SESU_EXE
become_flags:
description: Options to pass to sesu
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: sesu_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_sesu_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_SESU_FLAGS
become_pass:
description: Password to pass to sesu
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_sesu_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_SESU_PASS
ini:
- section: sesu_become_plugin
key: password
'''
from ansible.plugins.become import BecomeBase
@@ -92,4 +89,4 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags')
user = self.get_option('become_user')
return f'{become} {flags} {user} -c {self._build_success_command(cmd, shell)}'
return '%s %s %s -c %s' % (become, flags, user, self._build_success_command(cmd, shell))

View File

@@ -2,77 +2,74 @@
# Copyright (c) 2021, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: sudosu
short_description: Run tasks using sudo su -
description:
- This become plugin allows your remote/login user to execute commands as another user using the C(sudo) and C(su) utilities
combined.
author:
- Dag Wieers (@dagwieers)
version_added: 2.4.0
options:
become_user:
description: User you 'become' to execute the task.
type: string
default: root
ini:
- section: privilege_escalation
key: become_user
- section: sudo_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_sudo_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_SUDO_USER
become_flags:
description: Options to pass to C(sudo).
type: string
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: sudo_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_sudo_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_SUDO_FLAGS
become_pass:
description: Password to pass to C(sudo).
type: string
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_sudo_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_SUDO_PASS
ini:
- section: sudo_become_plugin
key: password
alt_method:
DOCUMENTATION = """
name: sudosu
short_description: Run tasks using sudo su -
description:
- Whether to use an alternative method to call C(su). Instead of running C(su -l user /path/to/shell -c command), it
runs C(su -l user -c command).
- Use this when the default one is not working on your system.
required: false
type: boolean
ini:
- section: community.general.sudosu
key: alternative_method
vars:
- name: ansible_sudosu_alt_method
env:
- name: ANSIBLE_SUDOSU_ALT_METHOD
version_added: 9.2.0
- 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)
version_added: 2.4.0
options:
become_user:
description: User you 'become' to execute the task.
default: root
ini:
- section: privilege_escalation
key: become_user
- section: sudo_become_plugin
key: user
vars:
- name: ansible_become_user
- name: ansible_sudo_user
env:
- name: ANSIBLE_BECOME_USER
- name: ANSIBLE_SUDO_USER
become_flags:
description: Options to pass to C(sudo).
default: -H -S -n
ini:
- section: privilege_escalation
key: become_flags
- section: sudo_become_plugin
key: flags
vars:
- name: ansible_become_flags
- name: ansible_sudo_flags
env:
- name: ANSIBLE_BECOME_FLAGS
- name: ANSIBLE_SUDO_FLAGS
become_pass:
description: Password to pass to C(sudo).
required: false
vars:
- name: ansible_become_password
- name: ansible_become_pass
- name: ansible_sudo_pass
env:
- name: ANSIBLE_BECOME_PASS
- name: ANSIBLE_SUDO_PASS
ini:
- section: sudo_become_plugin
key: password
alt_method:
description:
- Whether to use an alternative method to call C(su). Instead of running C(su -l user /path/to/shell -c command),
it runs C(su -l user -c command).
- Use this when the default one is not working on your system.
required: false
type: boolean
ini:
- section: community.general.sudosu
key: alternative_method
vars:
- name: ansible_sudosu_alt_method
env:
- name: ANSIBLE_SUDOSU_ALT_METHOD
version_added: 9.2.0
"""
@@ -98,16 +95,16 @@ class BecomeModule(BecomeBase):
flags = self.get_option('become_flags') or ''
prompt = ''
if self.get_option('become_pass'):
self.prompt = f'[sudo via ansible, key={self._id}] password:'
self.prompt = '[sudo via ansible, key=%s] password:' % self._id
if flags: # this could be simplified, but kept as is for now for backwards string matching
flags = flags.replace('-n', '')
prompt = f'-p "{self.prompt}"'
prompt = '-p "%s"' % (self.prompt)
user = self.get_option('become_user') or ''
if user:
user = f'{user}'
user = '%s' % (user)
if self.get_option('alt_method'):
return f"{becomecmd} {flags} {prompt} su -l {user} -c {self._build_success_command(cmd, shell, True)}"
return ' '.join([becomecmd, flags, prompt, "su -l", user, "-c", self._build_success_command(cmd, shell, True)])
else:
return f"{becomecmd} {flags} {prompt} su -l {user} {self._build_success_command(cmd, shell)}"
return ' '.join([becomecmd, flags, prompt, 'su -l', user, self._build_success_command(cmd, shell)])

View File

@@ -4,48 +4,47 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: memcached
short_description: Use memcached DB for cache
description:
- This cache uses JSON formatted, per host records saved in memcached.
requirements:
- memcache (python lib)
options:
_uri:
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: memcached
short_description: Use memcached DB for cache
description:
- List of connection information for the memcached DBs.
default: ['127.0.0.1:11211']
type: list
elements: string
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the DB entries.
type: string
default: ansible_facts
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_timeout:
default: 86400
type: integer
# TODO: determine whether it is OK to change to: type: float
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
"""
- This cache uses JSON formatted, per host records saved in memcached.
requirements:
- memcache (python lib)
options:
_uri:
description:
- List of connection information for the memcached DBs
default: ['127.0.0.1:11211']
type: list
elements: string
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the DB entries
default: ansible_facts
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
type: integer
'''
import collections
import os
@@ -190,7 +189,7 @@ class CacheModule(BaseCacheModule):
self._keys = CacheModuleKeys(self._db, self._db.get(CacheModuleKeys.PREFIX) or [])
def _make_key(self, key):
return f"{self._prefix}{key}"
return "{0}{1}".format(self._prefix, key)
def _expire_keys(self):
if self._timeout > 0:

View File

@@ -5,43 +5,41 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: pickle
short_description: Pickle formatted files
description:
- This cache uses Python's pickle serialization format, in per host files, saved to the filesystem.
author: Brian Coca (@bcoca)
options:
_uri:
required: true
DOCUMENTATION = '''
name: pickle
short_description: Pickle formatted files.
description:
- Path in which the cache plugin will save the files.
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
type: path
_prefix:
description: User defined prefix to use when creating the files.
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
type: string
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
type: float
"""
- This cache uses Python's pickle serialization format, in per host files, saved to the filesystem.
author: Brian Coca (@bcoca)
options:
_uri:
required: true
description:
- Path in which the cache plugin will save the files
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the files
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
'''
try:
import cPickle as pickle

127
plugins/cache/redis.py vendored
View File

@@ -3,75 +3,72 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: redis
short_description: Use Redis DB for cache
description:
- This cache uses JSON formatted, per host records saved in Redis.
requirements:
- redis>=2.4.5 (python lib)
options:
_uri:
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: redis
short_description: Use Redis DB for cache
description:
- A colon separated string of connection information for Redis.
- The format is V(host:port:db:password), for example V(localhost:6379:0:changeme).
- To use encryption in transit, prefix the connection with V(tls://), as in V(tls://localhost:6379:0:changeme).
- To use redis sentinel, use separator V(;), for example V(localhost:26379;localhost:26379;0:changeme). Requires redis>=2.9.0.
type: string
required: true
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the DB entries.
type: string
default: ansible_facts
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_keyset_name:
description: User defined name for cache keyset name.
type: string
default: ansible_cache_keys
env:
- name: ANSIBLE_CACHE_REDIS_KEYSET_NAME
ini:
- key: fact_caching_redis_keyset_name
section: defaults
version_added: 1.3.0
_sentinel_service_name:
description: The redis sentinel service name (or referenced as cluster name).
type: string
env:
- name: ANSIBLE_CACHE_REDIS_SENTINEL
ini:
- key: fact_caching_redis_sentinel
section: defaults
version_added: 1.3.0
_timeout:
default: 86400
type: integer
# TODO: determine whether it is OK to change to: type: float
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
"""
- This cache uses JSON formatted, per host records saved in Redis.
requirements:
- redis>=2.4.5 (python lib)
options:
_uri:
description:
- A colon separated string of connection information for Redis.
- The format is V(host:port:db:password), for example V(localhost:6379:0:changeme).
- To use encryption in transit, prefix the connection with V(tls://), as in V(tls://localhost:6379:0:changeme).
- To use redis sentinel, use separator V(;), for example V(localhost:26379;localhost:26379;0:changeme). Requires redis>=2.9.0.
required: true
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the DB entries
default: ansible_facts
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_keyset_name:
description: User defined name for cache keyset name.
default: ansible_cache_keys
env:
- name: ANSIBLE_CACHE_REDIS_KEYSET_NAME
ini:
- key: fact_caching_redis_keyset_name
section: defaults
version_added: 1.3.0
_sentinel_service_name:
description: The redis sentinel service name (or referenced as cluster name).
env:
- name: ANSIBLE_CACHE_REDIS_SENTINEL
ini:
- key: fact_caching_redis_sentinel
section: defaults
version_added: 1.3.0
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
type: integer
'''
import re
import time
import json
from ansible.errors import AnsibleError
from ansible.module_utils.common.text.converters import to_native
from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder
from ansible.plugins.cache import BaseCacheModule
from ansible.utils.display import Display
@@ -129,7 +126,7 @@ class CacheModule(BaseCacheModule):
connection = self._parse_connection(self.re_url_conn, uri)
self._db = StrictRedis(*connection, **kw)
display.vv(f'Redis connection: {self._db}')
display.vv('Redis connection: %s' % self._db)
@staticmethod
def _parse_connection(re_patt, uri):
@@ -162,12 +159,12 @@ class CacheModule(BaseCacheModule):
pass # password is optional
sentinels = [self._parse_connection(self.re_sent_conn, shost) for shost in connections]
display.vv(f'\nUsing redis sentinels: {sentinels}')
display.vv('\nUsing redis sentinels: %s' % sentinels)
scon = Sentinel(sentinels, **kw)
try:
return scon.master_for(self._sentinel_service_name, socket_timeout=0.2)
except Exception as exc:
raise AnsibleError(f'Could not connect to redis sentinel: {exc}')
raise AnsibleError('Could not connect to redis sentinel: %s' % to_native(exc))
def _make_key(self, key):
return self._prefix + key
@@ -225,7 +222,7 @@ class CacheModule(BaseCacheModule):
def copy(self):
# TODO: there is probably a better way to do this in redis
ret = {k: self.get(k) for k in self.keys()}
ret = dict([(k, self.get(k)) for k in self.keys()])
return ret
def __getstate__(self):

70
plugins/cache/yaml.py vendored
View File

@@ -5,44 +5,42 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: yaml
short_description: YAML formatted files
description:
- This cache uses YAML formatted, per host, files saved to the filesystem.
author: Brian Coca (@bcoca)
options:
_uri:
required: true
DOCUMENTATION = '''
name: yaml
short_description: YAML formatted files.
description:
- Path in which the cache plugin will save the files.
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
type: string
_prefix:
description: User defined prefix to use when creating the files.
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
type: string
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire.
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
type: integer
# TODO: determine whether it is OK to change to: type: float
"""
- This cache uses YAML formatted, per host, files saved to the filesystem.
author: Brian Coca (@bcoca)
options:
_uri:
required: true
description:
- Path in which the cache plugin will save the files
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the files
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
type: integer
'''
import codecs

View File

@@ -4,43 +4,41 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: cgroup_memory_recap
type: aggregate
requirements:
- whitelist in configuration
- cgroups
short_description: Profiles maximum memory usage of tasks and full execution using cgroups
description:
- This is an Ansible callback plugin that profiles maximum memory usage of Ansible and individual tasks, and displays a
recap at the end using cgroups.
notes:
- Requires ansible to be run from within a C(cgroup), such as with C(cgexec -g memory:ansible_profile ansible-playbook ...).
- This C(cgroup) should only be used by Ansible to get accurate results.
- To create the C(cgroup), first use a command such as C(sudo cgcreate -a ec2-user:ec2-user -t ec2-user:ec2-user -g memory:ansible_profile).
options:
max_mem_file:
required: true
description: Path to cgroups C(memory.max_usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.max_usage_in_bytes).
type: str
env:
- name: CGROUP_MAX_MEM_FILE
ini:
- section: callback_cgroupmemrecap
key: max_mem_file
cur_mem_file:
required: true
description: Path to C(memory.usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.usage_in_bytes).
type: str
env:
- name: CGROUP_CUR_MEM_FILE
ini:
- section: callback_cgroupmemrecap
key: cur_mem_file
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: cgroup_memory_recap
type: aggregate
requirements:
- whitelist in configuration
- cgroups
short_description: Profiles maximum memory usage of tasks and full execution using cgroups
description:
- This is an ansible callback plugin that profiles maximum memory usage of ansible and individual tasks, and displays a recap at the end using cgroups.
notes:
- Requires ansible to be run from within a cgroup, such as with C(cgexec -g memory:ansible_profile ansible-playbook ...).
- This cgroup should only be used by ansible to get accurate results.
- To create the cgroup, first use a command such as C(sudo cgcreate -a ec2-user:ec2-user -t ec2-user:ec2-user -g memory:ansible_profile).
options:
max_mem_file:
required: true
description: Path to cgroups C(memory.max_usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.max_usage_in_bytes).
env:
- name: CGROUP_MAX_MEM_FILE
ini:
- section: callback_cgroupmemrecap
key: max_mem_file
cur_mem_file:
required: true
description: Path to C(memory.usage_in_bytes) file. Example V(/sys/fs/cgroup/memory/ansible_profile/memory.usage_in_bytes).
env:
- name: CGROUP_CUR_MEM_FILE
ini:
- section: callback_cgroupmemrecap
key: cur_mem_file
'''
import time
import threading
@@ -114,7 +112,7 @@ class CallbackModule(CallbackBase):
max_results = int(f.read().strip()) / 1024 / 1024
self._display.banner('CGROUP MEMORY RECAP')
self._display.display(f'Execution Maximum: {max_results:0.2f}MB\n\n')
self._display.display('Execution Maximum: %0.2fMB\n\n' % max_results)
for task, memory in self.task_results:
self._display.display(f'{task.get_name()} ({task._uuid}): {memory:0.2f}MB')
self._display.display('%s (%s): %0.2fMB' % (task.get_name(), task._uuid, memory))

View File

@@ -4,19 +4,20 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: context_demo
type: aggregate
short_description: demo callback that adds play/task context
description:
- Displays some play and task context along with normal output.
- This is mostly for demo purposes.
requirements:
- whitelist in configuration
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: context_demo
type: aggregate
short_description: demo callback that adds play/task context
description:
- Displays some play and task context along with normal output.
- This is mostly for demo purposes.
requirements:
- whitelist in configuration
'''
from ansible.plugins.callback import CallbackBase
@@ -37,15 +38,15 @@ class CallbackModule(CallbackBase):
self.play = None
def v2_on_any(self, *args, **kwargs):
self._display.display(f"--- play: {getattr(self.play, 'name', None)} task: {self.task} ---")
self._display.display("--- play: {0} task: {1} ---".format(getattr(self.play, 'name', None), self.task))
self._display.display(" --- ARGS ")
for i, a in enumerate(args):
self._display.display(f' {i}: {a}')
self._display.display(' %s: %s' % (i, a))
self._display.display(" --- KWARGS ")
for k in kwargs:
self._display.display(f' {k}: {kwargs[k]}')
self._display.display(' %s: %s' % (k, kwargs[k]))
def v2_playbook_on_play_start(self, play):
self.play = play

View File

@@ -6,22 +6,23 @@
Counter enabled Ansible callback plugin (See DOCUMENTATION for more information)
'''
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: counter_enabled
type: stdout
short_description: adds counters to the output items (tasks and hosts/task)
description:
- Use this callback when you need a kind of progress bar on a large environments.
- You will know how many tasks has the playbook to run, and which one is actually running.
- You will know how many hosts may run a task, and which of them is actually running.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout callback in C(ansible.cfg) (C(stdout_callback = counter_enabled))
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: counter_enabled
type: stdout
short_description: adds counters to the output items (tasks and hosts/task)
description:
- Use this callback when you need a kind of progress bar on a large environments.
- You will know how many tasks has the playbook to run, and which one is actually running.
- You will know how many hosts may run a task, and which of them is actually running.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout callback in C(ansible.cfg) (C(stdout_callback = counter_enabled))
'''
from ansible import constants as C
from ansible.plugins.callback import CallbackBase
@@ -70,7 +71,7 @@ class CallbackModule(CallbackBase):
if not name:
msg = u"play"
else:
msg = f"PLAY [{name}]"
msg = u"PLAY [%s]" % name
self._play = play
@@ -90,17 +91,25 @@ class CallbackModule(CallbackBase):
for host in hosts:
stat = stats.summarize(host)
self._display.display(
f"{hostcolor(host, stat)} : {colorize('ok', stat['ok'], C.COLOR_OK)} {colorize('changed', stat['changed'], C.COLOR_CHANGED)} "
f"{colorize('unreachable', stat['unreachable'], C.COLOR_UNREACHABLE)} {colorize('failed', stat['failures'], C.COLOR_ERROR)} "
f"{colorize('rescued', stat['rescued'], C.COLOR_OK)} {colorize('ignored', stat['ignored'], C.COLOR_WARN)}",
self._display.display(u"%s : %s %s %s %s %s %s" % (
hostcolor(host, stat),
colorize(u'ok', stat['ok'], C.COLOR_OK),
colorize(u'changed', stat['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', stat['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', stat['failures'], C.COLOR_ERROR),
colorize(u'rescued', stat['rescued'], C.COLOR_OK),
colorize(u'ignored', stat['ignored'], C.COLOR_WARN)),
screen_only=True
)
self._display.display(
f"{hostcolor(host, stat, False)} : {colorize('ok', stat['ok'], None)} {colorize('changed', stat['changed'], None)} "
f"{colorize('unreachable', stat['unreachable'], None)} {colorize('failed', stat['failures'], None)} "
f"{colorize('rescued', stat['rescued'], None)} {colorize('ignored', stat['ignored'], None)}",
self._display.display(u"%s : %s %s %s %s %s %s" % (
hostcolor(host, stat, False),
colorize(u'ok', stat['ok'], None),
colorize(u'changed', stat['changed'], None),
colorize(u'unreachable', stat['unreachable'], None),
colorize(u'failed', stat['failures'], None),
colorize(u'rescued', stat['rescued'], None),
colorize(u'ignored', stat['ignored'], None)),
log_only=True
)
@@ -115,14 +124,12 @@ class CallbackModule(CallbackBase):
for k in sorted(stats.custom.keys()):
if k == '_run':
continue
_custom_stats = self._dump_results(stats.custom[k], indent=1).replace('\n', '')
self._display.display(f'\t{k}: {_custom_stats}')
self._display.display('\t%s: %s' % (k, self._dump_results(stats.custom[k], indent=1).replace('\n', '')))
# print per run custom stats
if '_run' in stats.custom:
self._display.display("", screen_only=True)
_custom_stats_run = self._dump_results(stats.custom['_run'], indent=1).replace('\n', '')
self._display.display(f'\tRUN: {_custom_stats_run}')
self._display.display('\tRUN: %s' % self._dump_results(stats.custom['_run'], indent=1).replace('\n', ''))
self._display.display("", screen_only=True)
def v2_playbook_on_task_start(self, task, is_conditional):
@@ -136,13 +143,13 @@ class CallbackModule(CallbackBase):
# that they can secure this if they feel that their stdout is insecure
# (shoulder surfing, logging stdout straight to a file, etc).
if not task.no_log and C.DISPLAY_ARGS_TO_STDOUT:
args = ', '.join(('{k}={v}' for k, v in task.args.items()))
args = f' {args}'
self._display.banner(f"TASK {self._task_counter}/{self._task_total} [{task.get_name().strip()}{args}]")
args = ', '.join(('%s=%s' % a for a in task.args.items()))
args = ' %s' % args
self._display.banner("TASK %d/%d [%s%s]" % (self._task_counter, self._task_total, task.get_name().strip(), args))
if self._display.verbosity >= 2:
path = task.get_path()
if path:
self._display.display(f"task path: {path}", color=C.COLOR_DEBUG)
self._display.display("task path: %s" % path, color=C.COLOR_DEBUG)
self._host_counter = self._previous_batch_total
self._task_counter += 1
@@ -159,15 +166,15 @@ class CallbackModule(CallbackBase):
return
elif result._result.get('changed', False):
if delegated_vars:
msg = f"changed: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> {delegated_vars['ansible_host']}]"
msg = "changed: %d/%d [%s -> %s]" % (self._host_counter, self._host_total, result._host.get_name(), delegated_vars['ansible_host'])
else:
msg = f"changed: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
msg = "changed: %d/%d [%s]" % (self._host_counter, self._host_total, result._host.get_name())
color = C.COLOR_CHANGED
else:
if delegated_vars:
msg = f"ok: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> {delegated_vars['ansible_host']}]"
msg = "ok: %d/%d [%s -> %s]" % (self._host_counter, self._host_total, result._host.get_name(), delegated_vars['ansible_host'])
else:
msg = f"ok: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
msg = "ok: %d/%d [%s]" % (self._host_counter, self._host_total, result._host.get_name())
color = C.COLOR_OK
self._handle_warnings(result._result)
@@ -178,7 +185,7 @@ class CallbackModule(CallbackBase):
self._clean_results(result._result, result._task.action)
if self._run_is_verbose(result):
msg += f" => {self._dump_results(result._result)}"
msg += " => %s" % (self._dump_results(result._result),)
self._display.display(msg, color=color)
def v2_runner_on_failed(self, result, ignore_errors=False):
@@ -199,16 +206,14 @@ class CallbackModule(CallbackBase):
else:
if delegated_vars:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> "
f"{delegated_vars['ansible_host']}]: FAILED! => {self._dump_results(result._result)}",
color=C.COLOR_ERROR
)
self._display.display("fatal: %d/%d [%s -> %s]: FAILED! => %s" % (self._host_counter, self._host_total,
result._host.get_name(), delegated_vars['ansible_host'],
self._dump_results(result._result)),
color=C.COLOR_ERROR)
else:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()}]: FAILED! => {self._dump_results(result._result)}",
color=C.COLOR_ERROR
)
self._display.display("fatal: %d/%d [%s]: FAILED! => %s" % (self._host_counter, self._host_total,
result._host.get_name(), self._dump_results(result._result)),
color=C.COLOR_ERROR)
if ignore_errors:
self._display.display("...ignoring", color=C.COLOR_SKIP)
@@ -226,9 +231,9 @@ class CallbackModule(CallbackBase):
if result._task.loop and 'results' in result._result:
self._process_items(result)
else:
msg = f"skipping: {self._host_counter}/{self._host_total} [{result._host.get_name()}]"
msg = "skipping: %d/%d [%s]" % (self._host_counter, self._host_total, result._host.get_name())
if self._run_is_verbose(result):
msg += f" => {self._dump_results(result._result)}"
msg += " => %s" % self._dump_results(result._result)
self._display.display(msg, color=C.COLOR_SKIP)
def v2_runner_on_unreachable(self, result):
@@ -239,13 +244,11 @@ class CallbackModule(CallbackBase):
delegated_vars = result._result.get('_ansible_delegated_vars', None)
if delegated_vars:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()} -> "
f"{delegated_vars['ansible_host']}]: UNREACHABLE! => {self._dump_results(result._result)}",
color=C.COLOR_UNREACHABLE
)
self._display.display("fatal: %d/%d [%s -> %s]: UNREACHABLE! => %s" % (self._host_counter, self._host_total,
result._host.get_name(), delegated_vars['ansible_host'],
self._dump_results(result._result)),
color=C.COLOR_UNREACHABLE)
else:
self._display.display(
f"fatal: {self._host_counter}/{self._host_total} [{result._host.get_name()}]: UNREACHABLE! => {self._dump_results(result._result)}",
color=C.COLOR_UNREACHABLE
)
self._display.display("fatal: %d/%d [%s]: UNREACHABLE! => %s" % (self._host_counter, self._host_total,
result._host.get_name(), self._dump_results(result._result)),
color=C.COLOR_UNREACHABLE)

View File

@@ -4,33 +4,35 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: default_without_diff
type: stdout
short_description: The default ansible callback without diff output
version_added: 8.4.0
description:
- This is basically the default ansible callback plugin (P(ansible.builtin.default#callback)) without showing diff output.
This can be useful when using another callback which sends more detailed information to another service, like the L(ARA,
https://ara.recordsansible.org/) callback, and you want diff output sent to that plugin but not shown on the console output.
author: Felix Fontein (@felixfontein)
extends_documentation_fragment:
- ansible.builtin.default_callback
- ansible.builtin.result_format_callback
"""
DOCUMENTATION = r'''
name: default_without_diff
type: stdout
short_description: The default ansible callback without diff output
version_added: 8.4.0
description:
- This is basically the default ansible callback plugin (P(ansible.builtin.default#callback)) without
showing diff output. This can be useful when using another callback which sends more detailed information
to another service, like the L(ARA, https://ara.recordsansible.org/) callback, and you want diff output
sent to that plugin but not shown on the console output.
author: Felix Fontein (@felixfontein)
extends_documentation_fragment:
- ansible.builtin.default_callback
- ansible.builtin.result_format_callback
'''
EXAMPLES = r"""
EXAMPLES = r'''
# Enable callback in ansible.cfg:
ansible_config: |
[defaults]
stdout_callback = community.general.default_without_diff
# Enable callback with environment variables:
environment_variable: |-
environment_variable: |
ANSIBLE_STDOUT_CALLBACK=community.general.default_without_diff
"""
'''
from ansible.plugins.callback.default import CallbackModule as Default

View File

@@ -4,21 +4,22 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
DOCUMENTATION = '''
name: dense
type: stdout
short_description: minimal stdout output
extends_documentation_fragment:
- default_callback
- default_callback
description:
- When in verbose mode it will act the same as the default callback.
- When in verbose mode it will act the same as the default callback.
author:
- Dag Wieers (@dagwieers)
- Dag Wieers (@dagwieers)
requirements:
- set as stdout in configuration
"""
- set as stdout in configuration
'''
HAS_OD = False
try:
@@ -194,7 +195,7 @@ class CallbackModule(CallbackModule_default):
self.disabled = True
def __del__(self):
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
def _add_host(self, result, status):
name = result._host.get_name()
@@ -242,7 +243,7 @@ class CallbackModule(CallbackModule_default):
def _handle_exceptions(self, result):
if 'exception' in result:
# Remove the exception from the result so it is not shown every time
# Remove the exception from the result so it's not shown every time
del result['exception']
if self._display.verbosity == 1:
@@ -251,7 +252,7 @@ class CallbackModule(CallbackModule_default):
def _display_progress(self, result=None):
# Always rewrite the complete line
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.nolinewrap + vt100.underline)
sys.stdout.write(f'{self.type} {self.count[self.type]}:')
sys.stdout.write('%s %d:' % (self.type, self.count[self.type]))
sys.stdout.write(vt100.reset)
sys.stdout.flush()
@@ -259,7 +260,7 @@ class CallbackModule(CallbackModule_default):
for name in self.hosts:
sys.stdout.write(' ')
if self.hosts[name].get('delegate', None):
sys.stdout.write(f"{self.hosts[name]['delegate']}>")
sys.stdout.write(self.hosts[name]['delegate'] + '>')
sys.stdout.write(colors[self.hosts[name]['state']] + name + vt100.reset)
sys.stdout.flush()
@@ -273,8 +274,8 @@ class CallbackModule(CallbackModule_default):
if not self.shown_title:
self.shown_title = True
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.underline)
sys.stdout.write(f'{self.type} {self.count[self.type]}: {self.task.get_name().strip()}')
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write('%s %d: %s' % (self.type, self.count[self.type], self.task.get_name().strip()))
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
sys.stdout.flush()
else:
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
@@ -283,7 +284,7 @@ class CallbackModule(CallbackModule_default):
def _display_results(self, result, status):
# Leave the previous task on screen (as it has changes/errors)
if self._display.verbosity == 0 and self.keep:
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
else:
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
self.keep = False
@@ -308,15 +309,15 @@ class CallbackModule(CallbackModule_default):
if result._task.loop and 'results' in result._result:
self._process_items(result)
else:
sys.stdout.write(f"{colors[status] + status}: ")
sys.stdout.write(colors[status] + status + ': ')
delegated_vars = result._result.get('_ansible_delegated_vars', None)
if delegated_vars:
sys.stdout.write(f"{vt100.reset + result._host.get_name()}>{colors[status]}{delegated_vars['ansible_host']}")
sys.stdout.write(vt100.reset + result._host.get_name() + '>' + colors[status] + delegated_vars['ansible_host'])
else:
sys.stdout.write(result._host.get_name())
sys.stdout.write(f": {dump}\n")
sys.stdout.write(': ' + dump + '\n')
sys.stdout.write(vt100.reset + vt100.save + vt100.clearline)
sys.stdout.flush()
@@ -326,7 +327,7 @@ class CallbackModule(CallbackModule_default):
def v2_playbook_on_play_start(self, play):
# Leave the previous task on screen (as it has changes/errors)
if self._display.verbosity == 0 and self.keep:
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}{vt100.bold}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.bold)
else:
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.bold)
@@ -340,14 +341,14 @@ class CallbackModule(CallbackModule_default):
name = play.get_name().strip()
if not name:
name = 'unnamed'
sys.stdout.write(f"PLAY {self.count['play']}: {name.upper()}")
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write('PLAY %d: %s' % (self.count['play'], name.upper()))
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
sys.stdout.flush()
def v2_playbook_on_task_start(self, task, is_conditional):
# Leave the previous task on screen (as it has changes/errors)
if self._display.verbosity == 0 and self.keep:
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}{vt100.underline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.underline)
else:
# Do not clear line, since we want to retain the previous output
sys.stdout.write(vt100.restore + vt100.reset + vt100.underline)
@@ -364,14 +365,14 @@ class CallbackModule(CallbackModule_default):
self.count['task'] += 1
# Write the next task on screen (behind the prompt is the previous output)
sys.stdout.write(f'{self.type} {self.count[self.type]}.')
sys.stdout.write('%s %d.' % (self.type, self.count[self.type]))
sys.stdout.write(vt100.reset)
sys.stdout.flush()
def v2_playbook_on_handler_task_start(self, task):
# Leave the previous task on screen (as it has changes/errors)
if self._display.verbosity == 0 and self.keep:
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}{vt100.underline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.underline)
else:
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.underline)
@@ -387,7 +388,7 @@ class CallbackModule(CallbackModule_default):
self.count[self.type] += 1
# Write the next task on screen (behind the prompt is the previous output)
sys.stdout.write(f'{self.type} {self.count[self.type]}.')
sys.stdout.write('%s %d.' % (self.type, self.count[self.type]))
sys.stdout.write(vt100.reset)
sys.stdout.flush()
@@ -450,13 +451,13 @@ class CallbackModule(CallbackModule_default):
def v2_playbook_on_no_hosts_remaining(self):
if self._display.verbosity == 0 and self.keep:
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
else:
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
self.keep = False
sys.stdout.write(f"{vt100.white + vt100.redbg}NO MORE HOSTS LEFT")
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write(vt100.white + vt100.redbg + 'NO MORE HOSTS LEFT')
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
sys.stdout.flush()
def v2_playbook_on_include(self, included_file):
@@ -464,7 +465,7 @@ class CallbackModule(CallbackModule_default):
def v2_playbook_on_stats(self, stats):
if self._display.verbosity == 0 and self.keep:
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
else:
sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
@@ -475,16 +476,22 @@ class CallbackModule(CallbackModule_default):
sys.stdout.write(vt100.bold + vt100.underline)
sys.stdout.write('SUMMARY')
sys.stdout.write(f"{vt100.restore}{vt100.reset}\n{vt100.save}{vt100.clearline}")
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
sys.stdout.flush()
hosts = sorted(stats.processed.keys())
for h in hosts:
t = stats.summarize(h)
self._display.display(
f"{hostcolor(h, t)} : {colorize('ok', t['ok'], C.COLOR_OK)} {colorize('changed', t['changed'], C.COLOR_CHANGED)} "
f"{colorize('unreachable', t['unreachable'], C.COLOR_UNREACHABLE)} {colorize('failed', t['failures'], C.COLOR_ERROR)} "
f"{colorize('rescued', t['rescued'], C.COLOR_OK)} {colorize('ignored', t['ignored'], C.COLOR_WARN)}",
u"%s : %s %s %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR),
colorize(u'rescued', t['rescued'], C.COLOR_OK),
colorize(u'ignored', t['ignored'], C.COLOR_WARN),
),
screen_only=True
)

File diff suppressed because it is too large Load Diff

View File

@@ -2,71 +2,72 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
name: elastic
type: notification
short_description: Create distributed traces for each Ansible task in Elastic APM
version_added: 3.8.0
description:
- This callback creates distributed traces for each Ansible task in Elastic APM.
- You can configure the plugin with environment variables.
- See U(https://www.elastic.co/guide/en/apm/agent/python/current/configuration.html).
options:
hide_task_arguments:
default: false
type: bool
DOCUMENTATION = '''
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
name: elastic
type: notification
short_description: Create distributed traces for each Ansible task in Elastic APM
version_added: 3.8.0
description:
- Hide the arguments for a task.
env:
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
apm_service_name:
default: ansible
type: str
description:
- The service name resource attribute.
env:
- name: ELASTIC_APM_SERVICE_NAME
apm_server_url:
type: str
description:
- Use the APM server and its environment variables.
env:
- name: ELASTIC_APM_SERVER_URL
apm_secret_token:
type: str
description:
- Use the APM server token.
env:
- name: ELASTIC_APM_SECRET_TOKEN
apm_api_key:
type: str
description:
- Use the APM API key.
env:
- name: ELASTIC_APM_API_KEY
apm_verify_server_cert:
default: true
type: bool
description:
- Verifies the SSL certificate if an HTTPS connection.
env:
- name: ELASTIC_APM_VERIFY_SERVER_CERT
traceparent:
type: str
description:
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
env:
- name: TRACEPARENT
requirements:
- elastic-apm (Python library)
"""
- This callback creates distributed traces for each Ansible task in Elastic APM.
- You can configure the plugin with environment variables.
- See U(https://www.elastic.co/guide/en/apm/agent/python/current/configuration.html).
options:
hide_task_arguments:
default: false
type: bool
description:
- Hide the arguments for a task.
env:
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
apm_service_name:
default: ansible
type: str
description:
- The service name resource attribute.
env:
- name: ELASTIC_APM_SERVICE_NAME
apm_server_url:
type: str
description:
- Use the APM server and its environment variables.
env:
- name: ELASTIC_APM_SERVER_URL
apm_secret_token:
type: str
description:
- Use the APM server token
env:
- name: ELASTIC_APM_SECRET_TOKEN
apm_api_key:
type: str
description:
- Use the APM API key
env:
- name: ELASTIC_APM_API_KEY
apm_verify_server_cert:
default: true
type: bool
description:
- Verifies the SSL certificate if an HTTPS connection.
env:
- name: ELASTIC_APM_VERIFY_SERVER_CERT
traceparent:
type: str
description:
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
env:
- name: TRACEPARENT
requirements:
- elastic-apm (Python library)
'''
EXAMPLES = r"""
examples: |-
EXAMPLES = '''
examples: |
Enable the plugin in ansible.cfg:
[defaults]
callbacks_enabled = community.general.elastic
@@ -75,7 +76,7 @@ examples: |-
export ELASTIC_APM_SERVER_URL=<your APM server URL)>
export ELASTIC_APM_SERVICE_NAME=your_service_name
export ELASTIC_APM_API_KEY=your_APM_API_KEY
"""
'''
import getpass
import socket
@@ -117,7 +118,7 @@ class TaskData:
if host.uuid in self.host_data:
if host.status == 'included':
# concatenate task include output from multiple items
host.result = f'{self.host_data[host.uuid].result}\n{host.result}'
host.result = '%s\n%s' % (self.host_data[host.uuid].result, host.result)
else:
return
@@ -165,7 +166,7 @@ class ElasticSource(object):
args = None
if not task.no_log and not hide_task_arguments:
args = ', '.join((f'{k}={v}' for k, v in task.args.items()))
args = ', '.join(('%s=%s' % a for a in task.args.items()))
tasks_data[uuid] = TaskData(uuid, name, path, play_name, action, args)
@@ -224,7 +225,7 @@ class ElasticSource(object):
def create_span_data(self, apm_cli, task_data, host_data):
""" create the span with the given TaskData and HostData """
name = f'[{host_data.name}] {task_data.play}: {task_data.name}'
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
message = "success"
status = "success"
@@ -258,7 +259,7 @@ class ElasticSource(object):
"ansible.task.host.status": host_data.status}) as span:
span.outcome = status
if 'failure' in status:
exception = AnsibleRuntimeError(message=f"{task_data.action}: {name} failed with error message {enriched_error_message}")
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, enriched_error_message))
apm_cli.capture_exception(exc_info=(type(exception), exception, exception.__traceback__), handled=True)
def init_apm_client(self, apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key):
@@ -287,7 +288,7 @@ class ElasticSource(object):
message = result.get('msg', 'failed')
exception = result.get('exception')
stderr = result.get('stderr')
return f"message: \"{message}\"\nexception: \"{exception}\"\nstderr: \"{stderr}\""
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
class CallbackModule(CallbackBase):

233
plugins/callback/hipchat.py Normal file
View File

@@ -0,0 +1,233 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2014, Matt Martz <matt@sivel.net>
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: hipchat
type: notification
requirements:
- whitelist in configuration.
- prettytable (python lib)
short_description: post task events to hipchat
description:
- This callback plugin sends status updates to a HipChat channel during playbook execution.
- Before 2.4 only environment variables were available for configuring this plugin.
deprecated:
removed_in: 10.0.0
why: The hipchat service has been discontinued and the self-hosted variant has been End of Life since 2020.
alternative: There is none.
options:
token:
description: HipChat API token for v1 or v2 API.
required: true
env:
- name: HIPCHAT_TOKEN
ini:
- section: callback_hipchat
key: token
api_version:
description: HipChat API version, v1 or v2.
required: false
default: v1
env:
- name: HIPCHAT_API_VERSION
ini:
- section: callback_hipchat
key: api_version
room:
description: HipChat room to post in.
default: ansible
env:
- name: HIPCHAT_ROOM
ini:
- section: callback_hipchat
key: room
from:
description: Name to post as
default: ansible
env:
- name: HIPCHAT_FROM
ini:
- section: callback_hipchat
key: from
notify:
description: Add notify flag to important messages
type: bool
default: true
env:
- name: HIPCHAT_NOTIFY
ini:
- section: callback_hipchat
key: notify
'''
import os
import json
try:
import prettytable
HAS_PRETTYTABLE = True
except ImportError:
HAS_PRETTYTABLE = False
from ansible.plugins.callback import CallbackBase
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.urls import open_url
class CallbackModule(CallbackBase):
"""This is an example ansible callback plugin that sends status
updates to a HipChat channel during playbook execution.
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'notification'
CALLBACK_NAME = 'community.general.hipchat'
CALLBACK_NEEDS_WHITELIST = True
API_V1_URL = 'https://api.hipchat.com/v1/rooms/message'
API_V2_URL = 'https://api.hipchat.com/v2/'
def __init__(self):
super(CallbackModule, self).__init__()
if not HAS_PRETTYTABLE:
self.disabled = True
self._display.warning('The `prettytable` python module is not installed. '
'Disabling the HipChat callback plugin.')
self.printed_playbook = False
self.playbook_name = None
self.play = None
def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
self.token = self.get_option('token')
self.api_version = self.get_option('api_version')
self.from_name = self.get_option('from')
self.allow_notify = self.get_option('notify')
self.room = self.get_option('room')
if self.token is None:
self.disabled = True
self._display.warning('HipChat token could not be loaded. The HipChat '
'token can be provided using the `HIPCHAT_TOKEN` '
'environment variable.')
# Pick the request handler.
if self.api_version == 'v2':
self.send_msg = self.send_msg_v2
else:
self.send_msg = self.send_msg_v1
def send_msg_v2(self, msg, msg_format='text', color='yellow', notify=False):
"""Method for sending a message to HipChat"""
headers = {'Authorization': 'Bearer %s' % self.token, 'Content-Type': 'application/json'}
body = {}
body['room_id'] = self.room
body['from'] = self.from_name[:15] # max length is 15
body['message'] = msg
body['message_format'] = msg_format
body['color'] = color
body['notify'] = self.allow_notify and notify
data = json.dumps(body)
url = self.API_V2_URL + "room/{room_id}/notification".format(room_id=self.room)
try:
response = open_url(url, data=data, headers=headers, method='POST')
return response.read()
except Exception as ex:
self._display.warning('Could not submit message to hipchat: {0}'.format(ex))
def send_msg_v1(self, msg, msg_format='text', color='yellow', notify=False):
"""Method for sending a message to HipChat"""
params = {}
params['room_id'] = self.room
params['from'] = self.from_name[:15] # max length is 15
params['message'] = msg
params['message_format'] = msg_format
params['color'] = color
params['notify'] = int(self.allow_notify and notify)
url = ('%s?auth_token=%s' % (self.API_V1_URL, self.token))
try:
response = open_url(url, data=urlencode(params))
return response.read()
except Exception as ex:
self._display.warning('Could not submit message to hipchat: {0}'.format(ex))
def v2_playbook_on_play_start(self, play):
"""Display Playbook and play start messages"""
self.play = play
name = play.name
# This block sends information about a playbook when it starts
# The playbook object is not immediately available at
# playbook_on_start so we grab it via the play
#
# Displays info about playbook being started by a person on an
# inventory, as well as Tags, Skip Tags and Limits
if not self.printed_playbook:
self.playbook_name, dummy = os.path.splitext(os.path.basename(self.play.playbook.filename))
host_list = self.play.playbook.inventory.host_list
inventory = os.path.basename(os.path.realpath(host_list))
self.send_msg("%s: Playbook initiated by %s against %s" %
(self.playbook_name,
self.play.playbook.remote_user,
inventory), notify=True)
self.printed_playbook = True
subset = self.play.playbook.inventory._subset
skip_tags = self.play.playbook.skip_tags
self.send_msg("%s:\nTags: %s\nSkip Tags: %s\nLimit: %s" %
(self.playbook_name,
', '.join(self.play.playbook.only_tags),
', '.join(skip_tags) if skip_tags else None,
', '.join(subset) if subset else subset))
# This is where we actually say we are starting a play
self.send_msg("%s: Starting play: %s" %
(self.playbook_name, name))
def playbook_on_stats(self, stats):
"""Display info about playbook statistics"""
hosts = sorted(stats.processed.keys())
t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',
'Failures'])
failures = False
unreachable = False
for h in hosts:
s = stats.summarize(h)
if s['failures'] > 0:
failures = True
if s['unreachable'] > 0:
unreachable = True
t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',
'failures']])
self.send_msg("%s: Playbook complete" % self.playbook_name,
notify=True)
if failures or unreachable:
color = 'red'
self.send_msg("%s: Failures detected" % self.playbook_name,
color=color, notify=True)
else:
color = 'green'
self.send_msg("/code %s:\n%s" % (self.playbook_name, t), color=color)

View File

@@ -4,44 +4,41 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: jabber
type: notification
short_description: post task events to a Jabber server
description:
- The chatty part of ChatOps with a Hipchat server as a target.
- This callback plugin sends status updates to a HipChat channel during playbook execution.
requirements:
- xmpp (Python library U(https://github.com/ArchipelProject/xmpppy))
options:
server:
description: Connection info to Jabber server.
type: str
required: true
env:
- name: JABBER_SERV
user:
description: Jabber user to authenticate as.
type: str
required: true
env:
- name: JABBER_USER
password:
description: Password for the user to the Jabber server.
type: str
required: true
env:
- name: JABBER_PASS
to:
description: Chat identifier that will receive the message.
type: str
required: true
env:
- name: JABBER_TO
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: jabber
type: notification
short_description: post task events to a jabber server
description:
- The chatty part of ChatOps with a Hipchat server as a target.
- This callback plugin sends status updates to a HipChat channel during playbook execution.
requirements:
- xmpp (Python library U(https://github.com/ArchipelProject/xmpppy))
options:
server:
description: connection info to jabber server
required: true
env:
- name: JABBER_SERV
user:
description: Jabber user to authenticate as
required: true
env:
- name: JABBER_USER
password:
description: Password for the user to the jabber server
required: true
env:
- name: JABBER_PASS
to:
description: chat identifier that will receive the message
required: true
env:
- name: JABBER_TO
'''
import os
@@ -101,7 +98,7 @@ class CallbackModule(CallbackBase):
"""Display Playbook and play start messages"""
self.play = play
name = play.name
self.send_msg(f"Ansible starting play: {name}")
self.send_msg("Ansible starting play: %s" % (name))
def playbook_on_stats(self, stats):
name = self.play
@@ -117,7 +114,7 @@ class CallbackModule(CallbackBase):
if failures or unreachable:
out = self.debug
self.send_msg(f"{name}: Failures detected \n{self.task} \nHost: {h}\n Failed at:\n{out}")
self.send_msg("%s: Failures detected \n%s \nHost: %s\n Failed at:\n%s" % (name, self.task, h, out))
else:
out = self.debug
self.send_msg(f"Great! \n Playbook {name} completed:\n{s} \n Last task debug:\n {out}")
self.send_msg("Great! \n Playbook %s completed:\n%s \n Last task debug:\n %s" % (name, s, out))

View File

@@ -4,29 +4,29 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: log_plays
type: notification
short_description: write playbook output to log file
description:
- This callback writes playbook output to a file per host in the C(/var/log/ansible/hosts) directory.
requirements:
- Whitelist in configuration
- A writeable C(/var/log/ansible/hosts) directory by the user executing Ansible on the controller
options:
log_folder:
default: /var/log/ansible/hosts
description: The folder where log files will be created.
type: str
env:
- name: ANSIBLE_LOG_FOLDER
ini:
- section: callback_log_plays
key: log_folder
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: log_plays
type: notification
short_description: write playbook output to log file
description:
- This callback writes playbook output to a file per host in the C(/var/log/ansible/hosts) directory.
requirements:
- Whitelist in configuration
- A writeable C(/var/log/ansible/hosts) directory by the user executing Ansible on the controller
options:
log_folder:
default: /var/log/ansible/hosts
description: The folder where log files will be created.
env:
- name: ANSIBLE_LOG_FOLDER
ini:
- section: callback_log_plays
key: log_folder
'''
import os
import time
@@ -56,10 +56,7 @@ class CallbackModule(CallbackBase):
CALLBACK_NEEDS_WHITELIST = True
TIME_FORMAT = "%b %d %Y %H:%M:%S"
@staticmethod
def _make_msg(now, playbook, task_name, task_action, category, data):
return f"{now} - {playbook} - {task_name} - {task_action} - {category} - {data}\n\n"
MSG_FORMAT = "%(now)s - %(playbook)s - %(task_name)s - %(task_action)s - %(category)s - %(data)s\n\n"
def __init__(self):
@@ -84,12 +81,22 @@ class CallbackModule(CallbackBase):
invocation = data.pop('invocation', None)
data = json.dumps(data, cls=AnsibleJSONEncoder)
if invocation is not None:
data = f"{json.dumps(invocation)} => {data} "
data = json.dumps(invocation) + " => %s " % data
path = os.path.join(self.log_folder, result._host.get_name())
now = time.strftime(self.TIME_FORMAT, time.localtime())
msg = to_bytes(self._make_msg(now, self.playbook, result._task.name, result._task.action, category, data))
msg = to_bytes(
self.MSG_FORMAT
% dict(
now=now,
playbook=self.playbook,
task_name=result._task.name,
task_action=result._task.action,
category=category,
data=data,
)
)
with open(path, "ab") as fd:
fd.write(msg)

View File

@@ -3,43 +3,42 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: loganalytics
type: notification
short_description: Posts task results to Azure Log Analytics
author: "Cyrus Li (@zhcli) <cyrus1006@gmail.com>"
description:
- This callback plugin will post task results in JSON formatted to an Azure Log Analytics workspace.
- Credits to authors of splunk callback plugin.
version_added: "2.4.0"
requirements:
- Whitelisting this callback plugin.
- An Azure log analytics work space has been established.
options:
workspace_id:
description: Workspace ID of the Azure log analytics workspace.
type: str
required: true
env:
- name: WORKSPACE_ID
ini:
- section: callback_loganalytics
key: workspace_id
shared_key:
description: Shared key to connect to Azure log analytics workspace.
type: str
required: true
env:
- name: WORKSPACE_SHARED_KEY
ini:
- section: callback_loganalytics
key: shared_key
"""
DOCUMENTATION = '''
name: loganalytics
type: notification
short_description: Posts task results to Azure Log Analytics
author: "Cyrus Li (@zhcli) <cyrus1006@gmail.com>"
description:
- This callback plugin will post task results in JSON formatted to an Azure Log Analytics workspace.
- Credits to authors of splunk callback plugin.
version_added: "2.4.0"
requirements:
- Whitelisting this callback plugin.
- An Azure log analytics work space has been established.
options:
workspace_id:
description: Workspace ID of the Azure log analytics workspace.
required: true
env:
- name: WORKSPACE_ID
ini:
- section: callback_loganalytics
key: workspace_id
shared_key:
description: Shared key to connect to Azure log analytics workspace.
required: true
env:
- name: WORKSPACE_SHARED_KEY
ini:
- section: callback_loganalytics
key: shared_key
'''
EXAMPLES = r"""
examples: |-
EXAMPLES = '''
examples: |
Whitelist the plugin in ansible.cfg:
[defaults]
callback_whitelist = community.general.loganalytics
@@ -50,7 +49,7 @@ examples: |-
[callback_loganalytics]
workspace_id = 01234567-0123-0123-0123-01234567890a
shared_key = dZD0kCbKl3ehZG6LHFMuhtE0yHiFCmetzFMc2u+roXIUQuatqU924SsAAAAPemhjbGlAemhjbGktTUJQAQIDBA==
"""
'''
import hashlib
import hmac
@@ -83,17 +82,18 @@ class AzureLogAnalyticsSource(object):
def __build_signature(self, date, workspace_id, shared_key, content_length):
# Build authorisation signature for Azure log analytics API call
sigs = f"POST\n{content_length}\napplication/json\nx-ms-date:{date}\n/api/logs"
sigs = "POST\n{0}\napplication/json\nx-ms-date:{1}\n/api/logs".format(
str(content_length), date)
utf8_sigs = sigs.encode('utf-8')
decoded_shared_key = base64.b64decode(shared_key)
hmac_sha256_sigs = hmac.new(
decoded_shared_key, utf8_sigs, digestmod=hashlib.sha256).digest()
encoded_hash = base64.b64encode(hmac_sha256_sigs).decode('utf-8')
signature = f"SharedKey {workspace_id}:{encoded_hash}"
signature = "SharedKey {0}:{1}".format(workspace_id, encoded_hash)
return signature
def __build_workspace_url(self, workspace_id):
return f"https://{workspace_id}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01"
return "https://{0}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01".format(workspace_id)
def __rfc1123date(self):
return now().strftime('%a, %d %b %Y %H:%M:%S GMT')

View File

@@ -3,58 +3,59 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: logdna
type: notification
short_description: Sends playbook logs to LogDNA
description:
- This callback will report logs from playbook actions, tasks, and events to LogDNA (U(https://app.logdna.com)).
requirements:
- LogDNA Python Library (U(https://github.com/logdna/python))
- whitelisting in configuration
options:
conf_key:
required: true
description: LogDNA Ingestion Key.
type: string
env:
- name: LOGDNA_INGESTION_KEY
ini:
- section: callback_logdna
key: conf_key
plugin_ignore_errors:
required: false
description: Whether to ignore errors on failing or not.
type: boolean
env:
- name: ANSIBLE_IGNORE_ERRORS
ini:
- section: callback_logdna
key: plugin_ignore_errors
default: false
conf_hostname:
required: false
description: Alternative Host Name; the current host name by default.
type: string
env:
- name: LOGDNA_HOSTNAME
ini:
- section: callback_logdna
key: conf_hostname
conf_tags:
required: false
description: Tags.
type: string
env:
- name: LOGDNA_TAGS
ini:
- section: callback_logdna
key: conf_tags
default: ansible
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: logdna
type: notification
short_description: Sends playbook logs to LogDNA
description:
- This callback will report logs from playbook actions, tasks, and events to LogDNA (U(https://app.logdna.com)).
requirements:
- LogDNA Python Library (U(https://github.com/logdna/python))
- whitelisting in configuration
options:
conf_key:
required: true
description: LogDNA Ingestion Key.
type: string
env:
- name: LOGDNA_INGESTION_KEY
ini:
- section: callback_logdna
key: conf_key
plugin_ignore_errors:
required: false
description: Whether to ignore errors on failing or not.
type: boolean
env:
- name: ANSIBLE_IGNORE_ERRORS
ini:
- section: callback_logdna
key: plugin_ignore_errors
default: false
conf_hostname:
required: false
description: Alternative Host Name; the current host name by default.
type: string
env:
- name: LOGDNA_HOSTNAME
ini:
- section: callback_logdna
key: conf_hostname
conf_tags:
required: false
description: Tags.
type: string
env:
- name: LOGDNA_TAGS
ini:
- section: callback_logdna
key: conf_tags
default: ansible
'''
import logging
import json
@@ -72,7 +73,7 @@ except ImportError:
# Getting MAC Address of system:
def get_mac():
mac = f"{getnode():012x}"
mac = "%012x" % getnode()
return ":".join(map(lambda index: mac[index:index + 2], range(int(len(mac) / 2))))
@@ -160,7 +161,7 @@ class CallbackModule(CallbackBase):
if ninvalidKeys > 0:
for key in invalidKeys:
del meta[key]
meta['__errors'] = f"These keys have been sanitized: {', '.join(invalidKeys)}"
meta['__errors'] = 'These keys have been sanitized: ' + ', '.join(invalidKeys)
return meta
def sanitizeJSON(self, data):

View File

@@ -3,79 +3,78 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: logentries
type: notification
short_description: Sends events to Logentries
description:
- This callback plugin will generate JSON objects and send them to Logentries using TCP for auditing/debugging purposes.
requirements:
- whitelisting in configuration
- certifi (Python library)
- flatdict (Python library), if you want to use the O(flatten) option
options:
api:
description: URI to the Logentries API.
type: str
env:
- name: LOGENTRIES_API
default: data.logentries.com
ini:
- section: callback_logentries
key: api
port:
description: HTTP port to use when connecting to the API.
type: int
env:
- name: LOGENTRIES_PORT
default: 80
ini:
- section: callback_logentries
key: port
tls_port:
description: Port to use when connecting to the API when TLS is enabled.
type: int
env:
- name: LOGENTRIES_TLS_PORT
default: 443
ini:
- section: callback_logentries
key: tls_port
token:
description: The logentries C(TCP token).
type: str
env:
- name: LOGENTRIES_ANSIBLE_TOKEN
required: true
ini:
- section: callback_logentries
key: token
use_tls:
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: logentries
type: notification
short_description: Sends events to Logentries
description:
- Toggle to decide whether to use TLS to encrypt the communications with the API server.
env:
- name: LOGENTRIES_USE_TLS
default: false
type: boolean
ini:
- section: callback_logentries
key: use_tls
flatten:
description: Flatten complex data structures into a single dictionary with complex keys.
type: boolean
default: false
env:
- name: LOGENTRIES_FLATTEN
ini:
- section: callback_logentries
key: flatten
"""
- This callback plugin will generate JSON objects and send them to Logentries via TCP for auditing/debugging purposes.
- Before 2.4, if you wanted to use an ini configuration, the file must be placed in the same directory as this plugin and named C(logentries.ini).
- In 2.4 and above you can just put it in the main Ansible configuration file.
requirements:
- whitelisting in configuration
- certifi (Python library)
- flatdict (Python library), if you want to use the O(flatten) option
options:
api:
description: URI to the Logentries API.
env:
- name: LOGENTRIES_API
default: data.logentries.com
ini:
- section: callback_logentries
key: api
port:
description: HTTP port to use when connecting to the API.
env:
- name: LOGENTRIES_PORT
default: 80
ini:
- section: callback_logentries
key: port
tls_port:
description: Port to use when connecting to the API when TLS is enabled.
env:
- name: LOGENTRIES_TLS_PORT
default: 443
ini:
- section: callback_logentries
key: tls_port
token:
description: The logentries C(TCP token).
env:
- name: LOGENTRIES_ANSIBLE_TOKEN
required: true
ini:
- section: callback_logentries
key: token
use_tls:
description:
- Toggle to decide whether to use TLS to encrypt the communications with the API server.
env:
- name: LOGENTRIES_USE_TLS
default: false
type: boolean
ini:
- section: callback_logentries
key: use_tls
flatten:
description: Flatten complex data structures into a single dictionary with complex keys.
type: boolean
default: false
env:
- name: LOGENTRIES_FLATTEN
ini:
- section: callback_logentries
key: flatten
'''
EXAMPLES = r"""
examples: >-
EXAMPLES = '''
examples: >
To enable, add this to your ansible.cfg file in the defaults block
[defaults]
@@ -94,7 +93,7 @@ examples: >-
use_tls = true
token = dd21fc88-f00a-43ff-b977-e3a4233c53af
flatten = false
"""
'''
import os
import socket
@@ -132,7 +131,7 @@ class PlainTextSocketAppender(object):
# Error message displayed when an incorrect Token has been detected
self.INVALID_TOKEN = "\n\nIt appears the LOGENTRIES_TOKEN parameter you entered is incorrect!\n\n"
# Unicode Line separator character \u2028
self.LINE_SEP = '\u2028'
self.LINE_SEP = u'\u2028'
self._display = display
self._conn = None
@@ -150,7 +149,7 @@ class PlainTextSocketAppender(object):
self.open_connection()
return
except Exception as e:
self._display.vvvv(f"Unable to connect to Logentries: {e}")
self._display.vvvv(u"Unable to connect to Logentries: %s" % to_text(e))
root_delay *= 2
if root_delay > self.MAX_DELAY:
@@ -159,7 +158,7 @@ class PlainTextSocketAppender(object):
wait_for = root_delay + random.uniform(0, root_delay)
try:
self._display.vvvv(f"sleeping {wait_for} before retry")
self._display.vvvv("sleeping %s before retry" % wait_for)
time.sleep(wait_for)
except KeyboardInterrupt:
raise
@@ -172,8 +171,8 @@ class PlainTextSocketAppender(object):
# Replace newlines with Unicode line separator
# for multi-line events
data = to_text(data, errors='surrogate_or_strict')
multiline = data.replace('\n', self.LINE_SEP)
multiline += "\n"
multiline = data.replace(u'\n', self.LINE_SEP)
multiline += u"\n"
# Send data, reconnect if needed
while True:
try:
@@ -246,7 +245,7 @@ class CallbackModule(CallbackBase):
self.use_tls = self.get_option('use_tls')
self.flatten = self.get_option('flatten')
except KeyError as e:
self._display.warning(f"Missing option for Logentries callback plugin: {e}")
self._display.warning(u"Missing option for Logentries callback plugin: %s" % to_text(e))
self.disabled = True
try:
@@ -265,10 +264,10 @@ class CallbackModule(CallbackBase):
if not self.disabled:
if self.use_tls:
self._display.vvvv(f"Connecting to {self.api_url}:{self.api_tls_port} with TLS")
self._display.vvvv("Connecting to %s:%s with TLS" % (self.api_url, self.api_tls_port))
self._appender = TLSSocketAppender(display=self._display, LE_API=self.api_url, LE_TLS_PORT=self.api_tls_port)
else:
self._display.vvvv(f"Connecting to {self.api_url}:{self.api_port}")
self._display.vvvv("Connecting to %s:%s" % (self.api_url, self.api_port))
self._appender = PlainTextSocketAppender(display=self._display, LE_API=self.api_url, LE_PORT=self.api_port)
self._appender.reopen_connection()
@@ -281,7 +280,7 @@ class CallbackModule(CallbackBase):
def emit(self, record):
msg = record.rstrip('\n')
msg = f"{self.token} {msg}"
msg = "{0} {1}".format(self.token, msg)
self._appender.put(msg)
self._display.vvvv("Sent event to logentries")

View File

@@ -4,96 +4,94 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Yevhen Khmelenko (@ujenmr)
name: logstash
type: notification
short_description: Sends events to Logstash
description:
- This callback will report facts and task events to Logstash U(https://www.elastic.co/products/logstash).
requirements:
- whitelisting in configuration
- logstash (Python library)
options:
server:
description: Address of the Logstash server.
type: str
env:
- name: LOGSTASH_SERVER
ini:
- section: callback_logstash
key: server
version_added: 1.0.0
default: localhost
port:
description: Port on which logstash is listening.
type: int
env:
- name: LOGSTASH_PORT
ini:
- section: callback_logstash
key: port
version_added: 1.0.0
default: 5000
type:
description: Message type.
type: str
env:
- name: LOGSTASH_TYPE
ini:
- section: callback_logstash
key: type
version_added: 1.0.0
default: ansible
pre_command:
description: Executes command before run and its result is added to the C(ansible_pre_command_output) logstash field.
type: str
version_added: 2.0.0
ini:
- section: callback_logstash
key: pre_command
env:
- name: LOGSTASH_PRE_COMMAND
format_version:
description: Logging format.
type: str
version_added: 2.0.0
ini:
- section: callback_logstash
key: format_version
env:
- name: LOGSTASH_FORMAT_VERSION
default: v1
choices:
- v1
- v2
"""
DOCUMENTATION = r'''
author: Yevhen Khmelenko (@ujenmr)
name: logstash
type: notification
short_description: Sends events to Logstash
description:
- This callback will report facts and task events to Logstash U(https://www.elastic.co/products/logstash).
requirements:
- whitelisting in configuration
- logstash (Python library)
options:
server:
description: Address of the Logstash server.
env:
- name: LOGSTASH_SERVER
ini:
- section: callback_logstash
key: server
version_added: 1.0.0
default: localhost
port:
description: Port on which logstash is listening.
env:
- name: LOGSTASH_PORT
ini:
- section: callback_logstash
key: port
version_added: 1.0.0
default: 5000
type:
description: Message type.
env:
- name: LOGSTASH_TYPE
ini:
- section: callback_logstash
key: type
version_added: 1.0.0
default: ansible
pre_command:
description: Executes command before run and its result is added to the C(ansible_pre_command_output) logstash field.
version_added: 2.0.0
ini:
- section: callback_logstash
key: pre_command
env:
- name: LOGSTASH_PRE_COMMAND
format_version:
description: Logging format.
type: str
version_added: 2.0.0
ini:
- section: callback_logstash
key: format_version
env:
- name: LOGSTASH_FORMAT_VERSION
default: v1
choices:
- v1
- v2
EXAMPLES = r"""
'''
EXAMPLES = r'''
ansible.cfg: |
# Enable Callback plugin
[defaults]
callback_whitelist = community.general.logstash
# Enable Callback plugin
[defaults]
callback_whitelist = community.general.logstash
[callback_logstash]
server = logstash.example.com
port = 5000
pre_command = git rev-parse HEAD
type = ansible
[callback_logstash]
server = logstash.example.com
port = 5000
pre_command = git rev-parse HEAD
type = ansible
11-input-tcp.conf: |-
# Enable Logstash TCP Input
input {
tcp {
port => 5000
codec => json
add_field => { "[@metadata][beat]" => "notify" }
add_field => { "[@metadata][type]" => "ansible" }
}
}
"""
11-input-tcp.conf: |
# Enable Logstash TCP Input
input {
tcp {
port => 5000
codec => json
add_field => { "[@metadata][beat]" => "notify" }
add_field => { "[@metadata][type]" => "ansible" }
}
}
'''
import os
import json

View File

@@ -4,82 +4,84 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
DOCUMENTATION = '''
name: mail
type: notification
short_description: Sends failure events through email
short_description: Sends failure events via email
description:
- This callback will report failures through email.
- This callback will report failures via email.
author:
- Dag Wieers (@dagwieers)
- Dag Wieers (@dagwieers)
requirements:
- whitelisting in configuration
- whitelisting in configuration
options:
mta:
description:
- Mail Transfer Agent, server that accepts SMTP.
- Mail Transfer Agent, server that accepts SMTP.
type: str
env:
- name: SMTPHOST
- name: SMTPHOST
ini:
- section: callback_mail
key: smtphost
- section: callback_mail
key: smtphost
default: localhost
mtaport:
description:
- Mail Transfer Agent Port.
- Port at which server SMTP.
- Mail Transfer Agent Port.
- Port at which server SMTP.
type: int
ini:
- section: callback_mail
key: smtpport
- section: callback_mail
key: smtpport
default: 25
to:
description:
- Mail recipient.
- Mail recipient.
type: list
elements: str
ini:
- section: callback_mail
key: to
- section: callback_mail
key: to
default: [root]
sender:
description:
- Mail sender.
- This is required since community.general 6.0.0.
- Mail sender.
- This is required since community.general 6.0.0.
type: str
required: true
ini:
- section: callback_mail
key: sender
- section: callback_mail
key: sender
cc:
description:
- CC'd recipients.
- CC'd recipients.
type: list
elements: str
ini:
- section: callback_mail
key: cc
- section: callback_mail
key: cc
bcc:
description:
- BCC'd recipients.
- BCC'd recipients.
type: list
elements: str
ini:
- section: callback_mail
key: bcc
- section: callback_mail
key: bcc
message_id_domain:
description:
- The domain name to use for the L(Message-ID header, https://en.wikipedia.org/wiki/Message-ID).
- The default is the hostname of the control node.
- The domain name to use for the L(Message-ID header, https://en.wikipedia.org/wiki/Message-ID).
- The default is the hostname of the control node.
type: str
ini:
- section: callback_mail
key: message_id_domain
- section: callback_mail
key: message_id_domain
version_added: 8.2.0
"""
'''
import json
import os
@@ -133,14 +135,14 @@ class CallbackModule(CallbackBase):
if self.bcc:
bcc_addresses = email.utils.getaddresses(self.bcc)
content = f'Date: {email.utils.formatdate()}\n'
content += f'From: {email.utils.formataddr(sender_address)}\n'
content = 'Date: %s\n' % email.utils.formatdate()
content += 'From: %s\n' % email.utils.formataddr(sender_address)
if self.to:
content += f"To: {', '.join([email.utils.formataddr(pair) for pair in to_addresses])}\n"
content += 'To: %s\n' % ', '.join([email.utils.formataddr(pair) for pair in to_addresses])
if self.cc:
content += f"Cc: {', '.join([email.utils.formataddr(pair) for pair in cc_addresses])}\n"
content += f"Message-ID: {email.utils.make_msgid(domain=self.get_option('message_id_domain'))}\n"
content += f'Subject: {subject.strip()}\n\n'
content += 'Cc: %s\n' % ', '.join([email.utils.formataddr(pair) for pair in cc_addresses])
content += 'Message-ID: %s\n' % email.utils.make_msgid(domain=self.get_option('message_id_domain'))
content += 'Subject: %s\n\n' % subject.strip()
content += body
addresses = to_addresses
@@ -157,22 +159,23 @@ class CallbackModule(CallbackBase):
smtp.quit()
def subject_msg(self, multiline, failtype, linenr):
msg = multiline.strip('\r\n').splitlines()[linenr]
return f'{failtype}: {msg}'
return '%s: %s' % (failtype, multiline.strip('\r\n').splitlines()[linenr])
def indent(self, multiline, indent=8):
return re.sub('^', ' ' * indent, multiline, flags=re.MULTILINE)
def body_blob(self, multiline, texttype):
''' Turn some text output in a well-indented block for sending in a mail body '''
intro = f'with the following {texttype}:\n\n'
blob = "\n".join(multiline.strip('\r\n').splitlines())
return f"{intro}{self.indent(blob)}\n"
intro = 'with the following %s:\n\n' % texttype
blob = ''
for line in multiline.strip('\r\n').splitlines():
blob += '%s\n' % line
return intro + self.indent(blob) + '\n'
def mail_result(self, result, failtype):
host = result._host.get_name()
if not self.sender:
self.sender = f'"Ansible: {host}" <root>'
self.sender = '"Ansible: %s" <root>' % host
# Add subject
if self.itembody:
@@ -188,32 +191,31 @@ class CallbackModule(CallbackBase):
elif result._result.get('exception'): # Unrelated exceptions are added to output :-/
subject = self.subject_msg(result._result['exception'], failtype, -1)
else:
subject = f'{failtype}: {result._task.name or result._task.action}'
subject = '%s: %s' % (failtype, result._task.name or result._task.action)
# Make playbook name visible (e.g. in Outlook/Gmail condensed view)
body = f'Playbook: {os.path.basename(self.playbook._file_name)}\n'
body = 'Playbook: %s\n' % os.path.basename(self.playbook._file_name)
if result._task.name:
body += f'Task: {result._task.name}\n'
body += f'Module: {result._task.action}\n'
body += f'Host: {host}\n'
body += 'Task: %s\n' % result._task.name
body += 'Module: %s\n' % result._task.action
body += 'Host: %s\n' % host
body += '\n'
# Add task information (as much as possible)
body += 'The following task failed:\n\n'
if 'invocation' in result._result:
body += self.indent(f"{result._task.action}: {json.dumps(result._result['invocation']['module_args'], indent=4)}\n")
body += self.indent('%s: %s\n' % (result._task.action, json.dumps(result._result['invocation']['module_args'], indent=4)))
elif result._task.name:
body += self.indent(f'{result._task.name} ({result._task.action})\n')
body += self.indent('%s (%s)\n' % (result._task.name, result._task.action))
else:
body += self.indent(f'{result._task.action}\n')
body += self.indent('%s\n' % result._task.action)
body += '\n'
# Add item / message
if self.itembody:
body += self.itembody
elif result._result.get('failed_when_result') is True:
fail_cond = self.indent('failed_when:\n- ' + '\n- '.join(result._task.failed_when))
body += f"due to the following condition:\n\n{fail_cond}\n\n"
body += "due to the following condition:\n\n" + self.indent('failed_when:\n- ' + '\n- '.join(result._task.failed_when)) + '\n\n'
elif result._result.get('msg'):
body += self.body_blob(result._result['msg'], 'message')
@@ -226,13 +228,13 @@ class CallbackModule(CallbackBase):
body += self.body_blob(result._result['exception'], 'exception')
if result._result.get('warnings'):
for i in range(len(result._result.get('warnings'))):
body += self.body_blob(result._result['warnings'][i], f'exception {i + 1}')
body += self.body_blob(result._result['warnings'][i], 'exception %d' % (i + 1))
if result._result.get('deprecations'):
for i in range(len(result._result.get('deprecations'))):
body += self.body_blob(result._result['deprecations'][i], f'exception {i + 1}')
body += self.body_blob(result._result['deprecations'][i], 'exception %d' % (i + 1))
body += 'and a complete dump of the error:\n\n'
body += self.indent(f'{failtype}: {json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4)}')
body += self.indent('%s: %s' % (failtype, json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4)))
self.mail(subject=subject, body=body)
@@ -255,4 +257,4 @@ class CallbackModule(CallbackBase):
def v2_runner_item_on_failed(self, result):
# Pass item information to task failure
self.itemsubject = result._result['msg']
self.itembody += self.body_blob(json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4), f"failed item dump '{result._result['item']}'")
self.itembody += self.body_blob(json.dumps(result._result, cls=AnsibleJSONEncoder, indent=4), "failed item dump '%(item)s'" % result._result)

View File

@@ -4,67 +4,68 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: nrdp
type: notification
author: "Remi VERCHERE (@rverchere)"
short_description: Post task results to a Nagios server through nrdp
description:
- This callback send playbook result to Nagios.
- Nagios shall use NRDP to receive passive events.
- The passive check is sent to a dedicated host/service for Ansible.
options:
url:
description: URL of the nrdp server.
required: true
env:
- name: NRDP_URL
ini:
- section: callback_nrdp
key: url
type: string
validate_certs:
description: Validate the SSL certificate of the nrdp server. (Used for HTTPS URLs).
env:
- name: NRDP_VALIDATE_CERTS
ini:
- section: callback_nrdp
key: validate_nrdp_certs
- section: callback_nrdp
key: validate_certs
type: boolean
default: false
aliases: [validate_nrdp_certs]
token:
description: Token to be allowed to push nrdp events.
required: true
env:
- name: NRDP_TOKEN
ini:
- section: callback_nrdp
key: token
type: string
hostname:
description: Hostname where the passive check is linked to.
required: true
env:
- name: NRDP_HOSTNAME
ini:
- section: callback_nrdp
key: hostname
type: string
servicename:
description: Service where the passive check is linked to.
required: true
env:
- name: NRDP_SERVICENAME
ini:
- section: callback_nrdp
key: servicename
type: string
"""
DOCUMENTATION = '''
name: nrdp
type: notification
author: "Remi VERCHERE (@rverchere)"
short_description: Post task results to a Nagios server through nrdp
description:
- This callback send playbook result to Nagios.
- Nagios shall use NRDP to receive passive events.
- The passive check is sent to a dedicated host/service for Ansible.
options:
url:
description: URL of the nrdp server.
required: true
env:
- name : NRDP_URL
ini:
- section: callback_nrdp
key: url
type: string
validate_certs:
description: Validate the SSL certificate of the nrdp server. (Used for HTTPS URLs.)
env:
- name: NRDP_VALIDATE_CERTS
ini:
- section: callback_nrdp
key: validate_nrdp_certs
- section: callback_nrdp
key: validate_certs
type: boolean
default: false
aliases: [ validate_nrdp_certs ]
token:
description: Token to be allowed to push nrdp events.
required: true
env:
- name: NRDP_TOKEN
ini:
- section: callback_nrdp
key: token
type: string
hostname:
description: Hostname where the passive check is linked to.
required: true
env:
- name : NRDP_HOSTNAME
ini:
- section: callback_nrdp
key: hostname
type: string
servicename:
description: Service where the passive check is linked to.
required: true
env:
- name : NRDP_SERVICENAME
ini:
- section: callback_nrdp
key: servicename
type: string
'''
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.common.text.converters import to_bytes
@@ -131,10 +132,10 @@ class CallbackModule(CallbackBase):
xmldata = "<?xml version='1.0'?>\n"
xmldata += "<checkresults>\n"
xmldata += "<checkresult type='service'>\n"
xmldata += f"<hostname>{self.hostname}</hostname>\n"
xmldata += f"<servicename>{self.servicename}</servicename>\n"
xmldata += f"<state>{state}</state>\n"
xmldata += f"<output>{msg}</output>\n"
xmldata += "<hostname>%s</hostname>\n" % self.hostname
xmldata += "<servicename>%s</servicename>\n" % self.servicename
xmldata += "<state>%d</state>\n" % state
xmldata += "<output>%s</output>\n" % msg
xmldata += "</checkresult>\n"
xmldata += "</checkresults>\n"
@@ -151,7 +152,7 @@ class CallbackModule(CallbackBase):
validate_certs=self.validate_nrdp_certs)
return response.read()
except Exception as ex:
self._display.warning(f"NRDP callback cannot send result {ex}")
self._display.warning("NRDP callback cannot send result {0}".format(ex))
def v2_playbook_on_play_start(self, play):
'''
@@ -169,16 +170,17 @@ class CallbackModule(CallbackBase):
critical = warning = 0
for host in hosts:
stat = stats.summarize(host)
gstats += (
f"'{host}_ok'={stat['ok']} '{host}_changed'={stat['changed']} '{host}_unreachable'={stat['unreachable']} '{host}_failed'={stat['failures']} "
)
gstats += "'%s_ok'=%d '%s_changed'=%d \
'%s_unreachable'=%d '%s_failed'=%d " % \
(host, stat['ok'], host, stat['changed'],
host, stat['unreachable'], host, stat['failures'])
# Critical when failed tasks or unreachable host
critical += stat['failures']
critical += stat['unreachable']
# Warning when changed tasks
warning += stat['changed']
msg = f"{name} | {gstats}"
msg = "%s | %s" % (name, gstats)
if critical:
# Send Critical
self._send_nrdp(self.CRITICAL, msg)

View File

@@ -4,18 +4,19 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: 'null'
type: stdout
requirements:
- set as main display callback
short_description: do not display stuff to screen
description:
- This callback prevents outputting events to screen.
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: 'null'
type: stdout
requirements:
- set as main display callback
short_description: Don't display stuff to screen
description:
- This callback prevents outputting events to screen.
'''
from ansible.plugins.callback import CallbackBase

View File

@@ -3,122 +3,123 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
name: opentelemetry
type: notification
short_description: Create distributed traces with OpenTelemetry
version_added: 3.7.0
description:
- This callback creates distributed traces for each Ansible task with OpenTelemetry.
- You can configure the OpenTelemetry exporter and SDK with environment variables.
- See U(https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html).
- See
U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#opentelemetry-sdk-environment-variables).
options:
hide_task_arguments:
default: false
type: bool
DOCUMENTATION = '''
author: Victor Martinez (@v1v) <VictorMartinezRubio@gmail.com>
name: opentelemetry
type: notification
short_description: Create distributed traces with OpenTelemetry
version_added: 3.7.0
description:
- 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:
- Whether to enable this callback only if the given environment variable exists and it is set to V(true).
- This is handy when you use Configuration as Code and want to send distributed traces if running in the CI rather when
running Ansible locally.
- For such, it evaluates the given O(enable_from_environment) value as environment variable 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
type: str
description:
- 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
description:
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
env:
- name: TRACEPARENT
disable_logs:
default: false
type: bool
description:
- Disable sending logs.
env:
- name: ANSIBLE_OPENTELEMETRY_DISABLE_LOGS
ini:
- section: callback_opentelemetry
key: disable_logs
version_added: 5.8.0
disable_attributes_in_logs:
default: false
type: bool
description:
- Disable populating span attributes to the logs.
env:
- name: ANSIBLE_OPENTELEMETRY_DISABLE_ATTRIBUTES_IN_LOGS
ini:
- section: callback_opentelemetry
key: disable_attributes_in_logs
version_added: 7.1.0
store_spans_in_file:
type: str
description:
- It stores the exported spans in the given file.
env:
- name: ANSIBLE_OPENTELEMETRY_STORE_SPANS_IN_FILE
ini:
- section: callback_opentelemetry
key: store_spans_in_file
version_added: 9.0.0
otel_exporter_otlp_traces_protocol:
type: str
description:
- E(OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) represents the the transport protocol for spans.
- See
U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#envvar-OTEL_EXPORTER_OTLP_TRACES_PROTOCOL).
default: grpc
choices:
- grpc
- http/protobuf
env:
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
ini:
- section: callback_opentelemetry
key: otel_exporter_otlp_traces_protocol
version_added: 9.0.0
requirements:
- opentelemetry-api (Python library)
- opentelemetry-exporter-otlp (Python library)
- opentelemetry-sdk (Python library)
"""
- This callback creates distributed traces for each Ansible task with OpenTelemetry.
- You can configure the OpenTelemetry exporter and SDK with environment variables.
- See U(https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html).
- See U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#opentelemetry-sdk-environment-variables).
options:
hide_task_arguments:
default: false
type: bool
description:
- Hide the arguments for a task.
env:
- name: ANSIBLE_OPENTELEMETRY_HIDE_TASK_ARGUMENTS
ini:
- section: callback_opentelemetry
key: hide_task_arguments
version_added: 5.3.0
enable_from_environment:
type: str
description:
- Whether to enable this callback only if the given environment variable exists and it is set to V(true).
- This is handy when you use Configuration as Code and want to send distributed traces
if running in the CI rather when running Ansible locally.
- For such, it evaluates the given O(enable_from_environment) value as environment variable
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
type: str
description:
- 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
description:
- The L(W3C Trace Context header traceparent,https://www.w3.org/TR/trace-context-1/#traceparent-header).
env:
- name: TRACEPARENT
disable_logs:
default: false
type: bool
description:
- Disable sending logs.
env:
- name: ANSIBLE_OPENTELEMETRY_DISABLE_LOGS
ini:
- section: callback_opentelemetry
key: disable_logs
version_added: 5.8.0
disable_attributes_in_logs:
default: false
type: bool
description:
- Disable populating span attributes to the logs.
env:
- name: ANSIBLE_OPENTELEMETRY_DISABLE_ATTRIBUTES_IN_LOGS
ini:
- section: callback_opentelemetry
key: disable_attributes_in_logs
version_added: 7.1.0
store_spans_in_file:
default: None
type: str
description:
- It stores the exported spans in the given file
env:
- name: ANSIBLE_OPENTELEMETRY_STORE_SPANS_IN_FILE
ini:
- section: callback_opentelemetry
key: store_spans_in_file
version_added: 9.0.0
otel_exporter_otlp_traces_protocol:
type: str
description:
- E(OTEL_EXPORTER_OTLP_TRACES_PROTOCOL) represents the the transport protocol for spans.
- See
U(https://opentelemetry-python.readthedocs.io/en/latest/sdk/environment_variables.html#envvar-OTEL_EXPORTER_OTLP_TRACES_PROTOCOL).
default: grpc
choices:
- grpc
- http/protobuf
env:
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
ini:
- section: callback_opentelemetry
key: otel_exporter_otlp_traces_protocol
version_added: 9.0.0
requirements:
- opentelemetry-api (Python library)
- opentelemetry-exporter-otlp (Python library)
- opentelemetry-sdk (Python library)
'''
EXAMPLES = r"""
examples: |-
EXAMPLES = '''
examples: |
Enable the plugin in ansible.cfg:
[defaults]
callbacks_enabled = community.general.opentelemetry
@@ -130,14 +131,15 @@ examples: |-
export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer your_otel_token"
export OTEL_SERVICE_NAME=your_service_name
export ANSIBLE_OPENTELEMETRY_ENABLED=true
"""
'''
import getpass
import json
import os
import socket
import sys
import time
import uuid
from time import time_ns
from collections import OrderedDict
from os.path import basename
@@ -163,12 +165,31 @@ try:
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
InMemorySpanExporter
)
# Support for opentelemetry-api <= 1.12
try:
from opentelemetry.util._time import _time_ns
except ImportError as imp_exc:
OTEL_LIBRARY_TIME_NS_ERROR = imp_exc
else:
OTEL_LIBRARY_TIME_NS_ERROR = None
except ImportError as imp_exc:
OTEL_LIBRARY_IMPORT_ERROR = imp_exc
OTEL_LIBRARY_TIME_NS_ERROR = imp_exc
else:
OTEL_LIBRARY_IMPORT_ERROR = None
if sys.version_info >= (3, 7):
time_ns = time.time_ns
elif not OTEL_LIBRARY_TIME_NS_ERROR:
time_ns = _time_ns
else:
def time_ns():
# Support versions older than 3.7 with opentelemetry-api > 1.12
return int(time.time() * 1e9)
class TaskData:
"""
Data about an individual task.
@@ -189,7 +210,7 @@ class TaskData:
if host.uuid in self.host_data:
if host.status == 'included':
# concatenate task include output from multiple items
host.result = f'{self.host_data[host.uuid].result}\n{host.result}'
host.result = '%s\n%s' % (self.host_data[host.uuid].result, host.result)
else:
return
@@ -327,7 +348,7 @@ class OpenTelemetrySource(object):
def update_span_data(self, task_data, host_data, span, disable_logs, disable_attributes_in_logs):
""" update the span with the given TaskData and HostData """
name = f'[{host_data.name}] {task_data.play}: {task_data.name}'
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
message = 'success'
res = {}
@@ -335,7 +356,6 @@ class OpenTelemetrySource(object):
status = Status(status_code=StatusCode.OK)
if host_data.status != 'included':
# Support loops
enriched_error_message = None
if 'results' in host_data.result._result:
if host_data.status == 'failed':
message = self.get_error_message_from_results(host_data.result._result['results'], task_data.action)
@@ -450,7 +470,7 @@ class OpenTelemetrySource(object):
def get_error_message_from_results(results, action):
for result in results:
if result.get('failed', False):
return f"{action}({result.get('item', 'none')}) - {OpenTelemetrySource.get_error_message(result)}"
return ('{0}({1}) - {2}').format(action, result.get('item', 'none'), OpenTelemetrySource.get_error_message(result))
@staticmethod
def _last_line(text):
@@ -462,14 +482,14 @@ class OpenTelemetrySource(object):
message = result.get('msg', 'failed')
exception = result.get('exception')
stderr = result.get('stderr')
return f"message: \"{message}\"\nexception: \"{exception}\"\nstderr: \"{stderr}\""
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
@staticmethod
def enrich_error_message_from_results(results, action):
message = ""
for result in results:
if result.get('failed', False):
message = f"{action}({result.get('item', 'none')}) - {OpenTelemetrySource.enrich_error_message(result)}\n{message}"
message = ('{0}({1}) - {2}\n{3}').format(action, result.get('item', 'none'), OpenTelemetrySource.enrich_error_message(result), message)
return message
@@ -515,9 +535,8 @@ class CallbackModule(CallbackBase):
environment_variable = self.get_option('enable_from_environment')
if environment_variable is not None and os.environ.get(environment_variable, 'false').lower() != 'true':
self.disabled = True
self._display.warning(
f"The `enable_from_environment` option has been set and {environment_variable} is not enabled. Disabling the `opentelemetry` callback plugin."
)
self._display.warning("The `enable_from_environment` option has been set and {0} is not enabled. "
"Disabling the `opentelemetry` callback plugin.".format(environment_variable))
self.hide_task_arguments = self.get_option('hide_task_arguments')

View File

@@ -5,19 +5,20 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: say
type: notification
requirements:
- whitelisting in configuration
- the C(/usr/bin/say) command line program (standard on macOS) or C(espeak) command line program
short_description: notify using software speech synthesizer
description:
- This plugin will use the C(say) or C(espeak) program to "speak" about play events.
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: say
type: notification
requirements:
- whitelisting in configuration
- the C(/usr/bin/say) command line program (standard on macOS) or C(espeak) command line program
short_description: notify using software speech synthesizer
description:
- This plugin will use the C(say) or C(espeak) program to "speak" about play events.
'''
import platform
import subprocess
@@ -49,7 +50,7 @@ class CallbackModule(CallbackBase):
self.synthesizer = get_bin_path('say')
if platform.system() != 'Darwin':
# 'say' binary available, it might be GNUstep tool which doesn't support 'voice' parameter
self._display.warning(f"'say' executable found but system is '{platform.system()}': ignoring voice parameter")
self._display.warning("'say' executable found but system is '%s': ignoring voice parameter" % platform.system())
else:
self.FAILED_VOICE = 'Zarvox'
self.REGULAR_VOICE = 'Trinoids'
@@ -68,7 +69,7 @@ class CallbackModule(CallbackBase):
# ansible will not call any callback if disabled is set to True
if not self.synthesizer:
self.disabled = True
self._display.warning(f"Unable to find either 'say' or 'espeak' executable, plugin {os.path.basename(__file__)} disabled")
self._display.warning("Unable to find either 'say' or 'espeak' executable, plugin %s disabled" % os.path.basename(__file__))
def say(self, msg, voice):
cmd = [self.synthesizer, msg]
@@ -77,7 +78,7 @@ class CallbackModule(CallbackBase):
subprocess.call(cmd)
def runner_on_failed(self, host, res, ignore_errors=False):
self.say(f"Failure on host {host}", self.FAILED_VOICE)
self.say("Failure on host %s" % host, self.FAILED_VOICE)
def runner_on_ok(self, host, res):
self.say("pew", self.LASER_VOICE)
@@ -86,13 +87,13 @@ class CallbackModule(CallbackBase):
self.say("pew", self.LASER_VOICE)
def runner_on_unreachable(self, host, res):
self.say(f"Failure on host {host}", self.FAILED_VOICE)
self.say("Failure on host %s" % host, self.FAILED_VOICE)
def runner_on_async_ok(self, host, res, jid):
self.say("pew", self.LASER_VOICE)
def runner_on_async_failed(self, host, res, jid):
self.say(f"Failure on host {host}", self.FAILED_VOICE)
self.say("Failure on host %s" % host, self.FAILED_VOICE)
def playbook_on_start(self):
self.say("Running Playbook", self.REGULAR_VOICE)
@@ -102,15 +103,15 @@ class CallbackModule(CallbackBase):
def playbook_on_task_start(self, name, is_conditional):
if not is_conditional:
self.say(f"Starting task: {name}", self.REGULAR_VOICE)
self.say("Starting task: %s" % name, self.REGULAR_VOICE)
else:
self.say(f"Notifying task: {name}", self.REGULAR_VOICE)
self.say("Notifying task: %s" % name, self.REGULAR_VOICE)
def playbook_on_setup(self):
self.say("Gathering facts", self.REGULAR_VOICE)
def playbook_on_play_start(self, name):
self.say(f"Starting play: {name}", self.HAPPY_VOICE)
self.say("Starting play: %s" % name, self.HAPPY_VOICE)
def playbook_on_stats(self, stats):
self.say("Play complete", self.HAPPY_VOICE)

View File

@@ -4,37 +4,38 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: selective
type: stdout
requirements:
- set as main display callback
short_description: only print certain tasks
description:
- 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 C(.).
- If you increase verbosity all tasks are printed.
options:
nocolor:
default: false
description: This setting allows suppressing colorizing output.
env:
- name: ANSIBLE_NOCOLOR
- name: ANSIBLE_SELECTIVE_DONT_COLORIZE
ini:
- section: defaults
key: nocolor
type: boolean
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: selective
type: stdout
requirements:
- set as main display callback
short_description: only print certain tasks
description:
- 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 C(.).
- If you increase verbosity all tasks are printed.
options:
nocolor:
default: false
description: This setting allows suppressing colorizing output.
env:
- name: ANSIBLE_NOCOLOR
- name: ANSIBLE_SELECTIVE_DONT_COLORIZE
ini:
- section: defaults
key: nocolor
type: boolean
'''
EXAMPLES = r"""
- ansible.builtin.debug: msg="This will not be printed"
- ansible.builtin.debug: msg="But this will"
tags: [print_action]
EXAMPLES = """
- ansible.builtin.debug: msg="This will not be printed"
- ansible.builtin.debug: msg="But this will"
tags: [print_action]
"""
import difflib
@@ -47,13 +48,13 @@ from ansible.module_utils.common.text.converters import to_text
DONT_COLORIZE = False
COLORS = {
'normal': '\033[0m',
'ok': f'\x1b[{C.COLOR_CODES[C.COLOR_OK]}m',
'ok': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_OK]),
'bold': '\033[1m',
'not_so_bold': '\033[1m\033[34m',
'changed': f'\x1b[{C.COLOR_CODES[C.COLOR_CHANGED]}m',
'failed': f'\x1b[{C.COLOR_CODES[C.COLOR_ERROR]}m',
'changed': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_CHANGED]),
'failed': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_ERROR]),
'endc': '\033[0m',
'skipped': f'\x1b[{C.COLOR_CODES[C.COLOR_SKIP]}m',
'skipped': '\033[{0}m'.format(C.COLOR_CODES[C.COLOR_SKIP]),
}
@@ -72,7 +73,7 @@ def colorize(msg, color):
if DONT_COLORIZE:
return msg
else:
return f"{COLORS[color]}{msg}{COLORS['endc']}"
return '{0}{1}{2}'.format(COLORS[color], msg, COLORS['endc'])
class CallbackModule(CallbackBase):
@@ -105,15 +106,15 @@ class CallbackModule(CallbackBase):
line_length = 120
if self.last_skipped:
print()
line = f"# {task_name} "
msg = colorize(f"{line}{'*' * (line_length - len(line))}", 'bold')
line = "# {0} ".format(task_name)
msg = colorize("{0}{1}".format(line, '*' * (line_length - len(line))), 'bold')
print(msg)
def _indent_text(self, text, indent_level):
lines = text.splitlines()
result_lines = []
for l in lines:
result_lines.append(f"{' ' * indent_level}{l}")
result_lines.append("{0}{1}".format(' ' * indent_level, l))
return '\n'.join(result_lines)
def _print_diff(self, diff, indent_level):
@@ -146,19 +147,19 @@ class CallbackModule(CallbackBase):
change_string = colorize('FAILED!!!', color)
else:
color = 'changed' if changed else 'ok'
change_string = colorize(f"changed={changed}", color)
change_string = colorize("changed={0}".format(changed), color)
msg = colorize(msg, color)
line_length = 120
spaces = ' ' * (40 - len(name) - indent_level)
line = f"{' ' * indent_level} * {name}{spaces}- {change_string}"
line = "{0} * {1}{2}- {3}".format(' ' * indent_level, name, spaces, change_string)
if len(msg) < 50:
line += f' -- {msg}'
print(f"{line} {'-' * (line_length - len(line))}---------")
line += ' -- {0}'.format(msg)
print("{0} {1}---------".format(line, '-' * (line_length - len(line))))
else:
print(f"{line} {'-' * (line_length - len(line))}")
print("{0} {1}".format(line, '-' * (line_length - len(line))))
print(self._indent_text(msg, indent_level + 4))
if diff:
@@ -238,10 +239,8 @@ class CallbackModule(CallbackBase):
else:
color = 'ok'
msg = (
f"{host} : ok={s['ok']}\tchanged={s['changed']}\tfailed={s['failures']}\tunreachable="
f"{s['unreachable']}\trescued={s['rescued']}\tignored={s['ignored']}"
)
msg = '{0} : ok={1}\tchanged={2}\tfailed={3}\tunreachable={4}\trescued={5}\tignored={6}'.format(
host, s['ok'], s['changed'], s['failures'], s['unreachable'], s['rescued'], s['ignored'])
print(colorize(msg, color))
def v2_runner_on_skipped(self, result, **kwargs):
@@ -253,15 +252,17 @@ class CallbackModule(CallbackBase):
line_length = 120
spaces = ' ' * (31 - len(result._host.name) - 4)
line = f" * {colorize(result._host.name, 'not_so_bold')}{spaces}- {colorize('skipped', 'skipped')}"
line = " * {0}{1}- {2}".format(colorize(result._host.name, 'not_so_bold'),
spaces,
colorize("skipped", 'skipped'),)
reason = result._result.get('skipped_reason', '') or \
result._result.get('skip_reason', '')
if len(reason) < 50:
line += f' -- {reason}'
print(f"{line} {'-' * (line_length - len(line))}---------")
line += ' -- {0}'.format(reason)
print("{0} {1}---------".format(line, '-' * (line_length - len(line))))
else:
print(f"{line} {'-' * (line_length - len(line))}")
print("{0} {1}".format(line, '-' * (line_length - len(line))))
print(self._indent_text(reason, 8))
print(reason)

View File

@@ -5,62 +5,61 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: slack
type: notification
requirements:
- whitelist in configuration
- prettytable (python library)
short_description: Sends play events to a Slack channel
description:
- This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.
options:
webhook_url:
required: true
description: Slack Webhook URL.
type: str
env:
- name: SLACK_WEBHOOK_URL
ini:
- section: callback_slack
key: webhook_url
channel:
default: "#ansible"
description: Slack room to post in.
type: str
env:
- name: SLACK_CHANNEL
ini:
- section: callback_slack
key: channel
username:
description: Username to post as.
type: str
env:
- name: SLACK_USERNAME
default: ansible
ini:
- section: callback_slack
key: username
validate_certs:
description: Validate the SSL certificate of the Slack server for HTTPS URLs.
env:
- name: SLACK_VALIDATE_CERTS
ini:
- section: callback_slack
key: validate_certs
default: true
type: bool
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: slack
type: notification
requirements:
- whitelist in configuration
- prettytable (python library)
short_description: Sends play events to a Slack channel
description:
- This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution.
options:
webhook_url:
required: true
description: Slack Webhook URL.
env:
- name: SLACK_WEBHOOK_URL
ini:
- section: callback_slack
key: webhook_url
channel:
default: "#ansible"
description: Slack room to post in.
env:
- name: SLACK_CHANNEL
ini:
- section: callback_slack
key: channel
username:
description: Username to post as.
env:
- name: SLACK_USERNAME
default: ansible
ini:
- section: callback_slack
key: username
validate_certs:
description: Validate the SSL certificate of the Slack server for HTTPS URLs.
env:
- name: SLACK_VALIDATE_CERTS
ini:
- section: callback_slack
key: validate_certs
default: true
type: bool
'''
import json
import os
import uuid
from ansible import context
from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.urls import open_url
from ansible.plugins.callback import CallbackBase
@@ -136,13 +135,14 @@ class CallbackModule(CallbackBase):
headers=headers)
return response.read()
except Exception as e:
self._display.warning(f'Could not submit message to Slack: {e}')
self._display.warning(u'Could not submit message to Slack: %s' %
to_text(e))
def v2_playbook_on_start(self, playbook):
self.playbook_name = os.path.basename(playbook._file_name)
title = [
f'*Playbook initiated* (_{self.guid}_)'
'*Playbook initiated* (_%s_)' % self.guid
]
invocation_items = []
@@ -153,23 +153,23 @@ class CallbackModule(CallbackBase):
subset = context.CLIARGS['subset']
inventory = [os.path.abspath(i) for i in context.CLIARGS['inventory']]
invocation_items.append(f"Inventory: {', '.join(inventory)}")
invocation_items.append('Inventory: %s' % ', '.join(inventory))
if tags and tags != ['all']:
invocation_items.append(f"Tags: {', '.join(tags)}")
invocation_items.append('Tags: %s' % ', '.join(tags))
if skip_tags:
invocation_items.append(f"Skip Tags: {', '.join(skip_tags)}")
invocation_items.append('Skip Tags: %s' % ', '.join(skip_tags))
if subset:
invocation_items.append(f'Limit: {subset}')
invocation_items.append('Limit: %s' % subset)
if extra_vars:
invocation_items.append(f"Extra Vars: {' '.join(extra_vars)}")
invocation_items.append('Extra Vars: %s' %
' '.join(extra_vars))
title.append(f"by *{context.CLIARGS['remote_user']}*")
title.append('by *%s*' % context.CLIARGS['remote_user'])
title.append(f'\n\n*{self.playbook_name}*')
title.append('\n\n*%s*' % self.playbook_name)
msg_items = [' '.join(title)]
if invocation_items:
_inv_item = '\n'.join(invocation_items)
msg_items.append(f'```\n{_inv_item}\n```')
msg_items.append('```\n%s\n```' % '\n'.join(invocation_items))
msg = '\n'.join(msg_items)
@@ -189,8 +189,8 @@ class CallbackModule(CallbackBase):
def v2_playbook_on_play_start(self, play):
"""Display Play start messages"""
name = play.name or f'Play name not specified ({play._uuid})'
msg = f'*Starting play* (_{self.guid}_)\n\n*{name}*'
name = play.name or 'Play name not specified (%s)' % play._uuid
msg = '*Starting play* (_%s_)\n\n*%s*' % (self.guid, name)
attachments = [
{
'fallback': msg,
@@ -225,7 +225,7 @@ class CallbackModule(CallbackBase):
attachments = []
msg_items = [
f'*Playbook Complete* (_{self.guid}_)'
'*Playbook Complete* (_%s_)' % self.guid
]
if failures or unreachable:
color = 'danger'
@@ -234,7 +234,7 @@ class CallbackModule(CallbackBase):
color = 'good'
msg_items.append('\n*Success!*')
msg_items.append(f'```\n{t}\n```')
msg_items.append('```\n%s\n```' % t)
msg = '\n'.join(msg_items)

View File

@@ -3,75 +3,74 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: splunk
type: notification
short_description: Sends task result events to Splunk HTTP Event Collector
author: "Stuart Hirst (!UNKNOWN) <support@convergingdata.com>"
description:
- This callback plugin will send task results as JSON formatted events to a Splunk HTTP collector.
- The companion Splunk Monitoring & Diagnostics App is available here U(https://splunkbase.splunk.com/app/4023/).
- Credit to "Ryan Currah (@ryancurrah)" for original source upon which this is based.
requirements:
- Whitelisting this callback plugin
- 'Create a HTTP Event Collector in Splunk'
- 'Define the URL and token in C(ansible.cfg)'
options:
url:
description: URL to the Splunk HTTP collector source.
type: str
env:
- name: SPLUNK_URL
ini:
- section: callback_splunk
key: url
authtoken:
description: Token to authenticate the connection to the Splunk HTTP collector.
type: str
env:
- name: SPLUNK_AUTHTOKEN
ini:
- section: callback_splunk
key: authtoken
validate_certs:
description: Whether to validate certificates for connections to HEC. It is not recommended to set to V(false) except
when you are sure that nobody can intercept the connection between this plugin and HEC, as setting it to V(false) allows
man-in-the-middle attacks!
env:
- name: SPLUNK_VALIDATE_CERTS
ini:
- section: callback_splunk
key: validate_certs
type: bool
default: true
version_added: '1.0.0'
include_milliseconds:
description: Whether to include milliseconds as part of the generated timestamp field in the event sent to the Splunk
HTTP collector.
env:
- name: SPLUNK_INCLUDE_MILLISECONDS
ini:
- section: callback_splunk
key: include_milliseconds
type: bool
default: false
version_added: 2.0.0
batch:
DOCUMENTATION = '''
name: splunk
type: notification
short_description: Sends task result events to Splunk HTTP Event Collector
author: "Stuart Hirst (!UNKNOWN) <support@convergingdata.com>"
description:
- Correlation ID which can be set across multiple playbook executions.
env:
- name: SPLUNK_BATCH
ini:
- section: callback_splunk
key: batch
type: str
version_added: 3.3.0
"""
- This callback plugin will send task results as JSON formatted events to a Splunk HTTP collector.
- The companion Splunk Monitoring & Diagnostics App is available here U(https://splunkbase.splunk.com/app/4023/).
- Credit to "Ryan Currah (@ryancurrah)" for original source upon which this is based.
requirements:
- Whitelisting this callback plugin
- 'Create a HTTP Event Collector in Splunk'
- 'Define the URL and token in C(ansible.cfg)'
options:
url:
description: URL to the Splunk HTTP collector source.
env:
- name: SPLUNK_URL
ini:
- section: callback_splunk
key: url
authtoken:
description: Token to authenticate the connection to the Splunk HTTP collector.
env:
- name: SPLUNK_AUTHTOKEN
ini:
- section: callback_splunk
key: authtoken
validate_certs:
description: Whether to validate certificates for connections to HEC. It is not recommended to set to
V(false) except when you are sure that nobody can intercept the connection
between this plugin and HEC, as setting it to V(false) allows man-in-the-middle attacks!
env:
- name: SPLUNK_VALIDATE_CERTS
ini:
- section: callback_splunk
key: validate_certs
type: bool
default: true
version_added: '1.0.0'
include_milliseconds:
description: Whether to include milliseconds as part of the generated timestamp field in the event
sent to the Splunk HTTP collector.
env:
- name: SPLUNK_INCLUDE_MILLISECONDS
ini:
- section: callback_splunk
key: include_milliseconds
type: bool
default: false
version_added: 2.0.0
batch:
description:
- Correlation ID which can be set across multiple playbook executions.
env:
- name: SPLUNK_BATCH
ini:
- section: callback_splunk
key: batch
type: str
version_added: 3.3.0
'''
EXAMPLES = r"""
examples: >-
EXAMPLES = '''
examples: >
To enable, add this to your ansible.cfg file in the defaults block
[defaults]
callback_whitelist = community.general.splunk
@@ -82,7 +81,7 @@ examples: >-
[callback_splunk]
url = http://mysplunkinstance.datapaas.io:8088/services/collector/event
authtoken = f23blad6-5965-4537-bf69-5b5a545blabla88
"""
'''
import json
import uuid
@@ -152,14 +151,15 @@ class SplunkHTTPCollectorSource(object):
data['ansible_result'] = result._result
# This wraps the json payload in and outer json event needed by Splunk
jsondata = json.dumps({"event": data}, cls=AnsibleJSONEncoder, sort_keys=True)
jsondata = json.dumps(data, cls=AnsibleJSONEncoder, sort_keys=True)
jsondata = '{"event":' + jsondata + "}"
open_url(
url,
jsondata,
headers={
'Content-type': 'application/json',
'Authorization': f"Splunk {authtoken}"
'Authorization': 'Splunk ' + authtoken
},
method='POST',
validate_certs=validate_certs

View File

@@ -3,9 +3,10 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
DOCUMENTATION = r'''
name: sumologic
type: notification
short_description: Sends task result events to Sumologic
@@ -14,21 +15,20 @@ description:
- This callback plugin will send task results as JSON formatted events to a Sumologic HTTP collector source.
requirements:
- Whitelisting this callback plugin
- 'Create a HTTP collector source in Sumologic and specify a custom timestamp format of V(yyyy-MM-dd HH:mm:ss ZZZZ) and
a custom timestamp locator of V("timestamp": "(.*\)")'
- 'Create a HTTP collector source in Sumologic and specify a custom timestamp format of V(yyyy-MM-dd HH:mm:ss ZZZZ) and a custom timestamp locator
of V("timestamp": "(.*\)")'
options:
url:
description: URL to the Sumologic HTTP collector source.
type: str
env:
- name: SUMOLOGIC_URL
ini:
- section: callback_sumologic
key: url
"""
'''
EXAMPLES = r"""
examples: |-
EXAMPLES = '''
examples: |
To enable, add this to your ansible.cfg file in the defaults block
[defaults]
callback_whitelist = community.general.sumologic
@@ -39,7 +39,7 @@ examples: |-
Set the ansible.cfg variable in the callback_sumologic block
[callback_sumologic]
url = https://endpoint1.collection.us2.sumologic.com/receiver/v1/http/R8moSv1d8EW9LAUFZJ6dbxCFxwLH6kfCdcBfddlfxCbLuL-BN5twcTpMk__pYy_cDmp==
"""
'''
import json
import uuid

View File

@@ -4,56 +4,54 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: syslog_json
type: notification
requirements:
- whitelist in configuration
short_description: sends JSON events to syslog
description:
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format.
options:
server:
description: Syslog server that will receive the event.
type: str
env:
- name: SYSLOG_SERVER
default: localhost
ini:
- section: callback_syslog_json
key: syslog_server
port:
description: Port on which the syslog server is listening.
type: int
env:
- name: SYSLOG_PORT
default: 514
ini:
- section: callback_syslog_json
key: syslog_port
facility:
description: Syslog facility to log as.
type: str
env:
- name: SYSLOG_FACILITY
default: user
ini:
- section: callback_syslog_json
key: syslog_facility
setup:
description: Log setup tasks.
env:
- name: ANSIBLE_SYSLOG_SETUP
type: bool
default: true
ini:
- section: callback_syslog_json
key: syslog_setup
version_added: 4.5.0
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: syslog_json
type: notification
requirements:
- whitelist in configuration
short_description: sends JSON events to syslog
description:
- This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format.
options:
server:
description: Syslog server that will receive the event.
env:
- name: SYSLOG_SERVER
default: localhost
ini:
- section: callback_syslog_json
key: syslog_server
port:
description: Port on which the syslog server is listening.
env:
- name: SYSLOG_PORT
default: 514
ini:
- section: callback_syslog_json
key: syslog_port
facility:
description: Syslog facility to log as.
env:
- name: SYSLOG_FACILITY
default: user
ini:
- section: callback_syslog_json
key: syslog_facility
setup:
description: Log setup tasks.
env:
- name: ANSIBLE_SYSLOG_SETUP
type: bool
default: true
ini:
- section: callback_syslog_json
key: syslog_setup
version_added: 4.5.0
'''
import logging
import logging.handlers

View File

@@ -5,49 +5,51 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r"""
name: timestamp
type: stdout
short_description: Adds simple timestamp for each header
version_added: 9.0.0
description:
- This callback adds simple timestamp for each header.
author: kurokobo (@kurokobo)
options:
timezone:
description:
- Timezone to use for the timestamp in IANA time zone format.
- For example V(America/New_York), V(Asia/Tokyo)). Ignored on Python < 3.9.
ini:
- section: callback_timestamp
key: timezone
env:
- name: ANSIBLE_CALLBACK_TIMESTAMP_TIMEZONE
type: string
format_string:
description:
- Format of the timestamp shown to user in 1989 C standard format.
- Refer to L(the Python documentation,https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)
for the available format codes.
ini:
- section: callback_timestamp
key: format_string
env:
- name: ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING
default: "%H:%M:%S"
type: string
seealso:
- plugin: ansible.posix.profile_tasks
plugin_type: callback
description: >-
You can use P(ansible.posix.profile_tasks#callback) callback plugin to time individual tasks and overall execution time
with detailed timestamps.
extends_documentation_fragment:
- ansible.builtin.default_callback
- ansible.builtin.result_format_callback
name: timestamp
type: stdout
short_description: Adds simple timestamp for each header
version_added: 9.0.0
description:
- This callback adds simple timestamp for each header.
author: kurokobo (@kurokobo)
options:
timezone:
description:
- Timezone to use for the timestamp in IANA time zone format.
- For example C(America/New_York), C(Asia/Tokyo)). Ignored on Python < 3.9.
ini:
- section: callback_timestamp
key: timezone
env:
- name: ANSIBLE_CALLBACK_TIMESTAMP_TIMEZONE
type: string
format_string:
description:
- Format of the timestamp shown to user in 1989 C standard format.
- >
Refer to L(the Python documentation,https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)
for the available format codes.
ini:
- section: callback_timestamp
key: format_string
env:
- name: ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING
default: "%H:%M:%S"
type: string
seealso:
- plugin: ansible.posix.profile_tasks
plugin_type: callback
description: >
You can use P(ansible.posix.profile_tasks#callback) callback plugin to time individual tasks and overall execution time
with detailed timestamps.
extends_documentation_fragment:
- ansible.builtin.default_callback
- ansible.builtin.result_format_callback
"""
@@ -83,7 +85,7 @@ def banner(self, msg, color=None, cows=True):
msg = to_text(msg)
if self.b_cowsay and cows:
try:
self.banner_cowsay(f"{msg} @ {timestamp}")
self.banner_cowsay("%s @ %s" % (msg, timestamp))
return
except OSError:
self.warning("somebody cleverly deleted cowsay or something during the PB run. heh.")
@@ -96,7 +98,7 @@ def banner(self, msg, color=None, cows=True):
if star_len <= 3:
star_len = 3
stars = "*" * star_len
self.display(f"\n{msg} {stars} {timestamp}", color=color)
self.display("\n%s %s %s" % (msg, stars, timestamp), color=color)
class CallbackModule(Default):

View File

@@ -5,20 +5,21 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: unixy
type: stdout
author: Al Bowles (@akatch)
short_description: condensed Ansible output
description:
- Consolidated Ansible output in the style of LINUX/UNIX startup logs.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
"""
DOCUMENTATION = '''
name: unixy
type: stdout
author: Al Bowles (@akatch)
short_description: condensed Ansible output
description:
- Consolidated Ansible output in the style of LINUX/UNIX startup logs.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
'''
from os.path import basename
from ansible import constants as C
@@ -66,24 +67,24 @@ class CallbackModule(CallbackModule_default):
def _process_result_output(self, result, msg):
task_host = result._host.get_name()
task_result = f"{task_host} {msg}"
task_result = "%s %s" % (task_host, msg)
if self._run_is_verbose(result):
task_result = f"{task_host} {msg}: {self._dump_results(result._result, indent=4)}"
task_result = "%s %s: %s" % (task_host, msg, self._dump_results(result._result, indent=4))
return task_result
if self.delegated_vars:
task_delegate_host = self.delegated_vars['ansible_host']
task_result = f"{task_host} -> {task_delegate_host} {msg}"
task_result = "%s -> %s %s" % (task_host, task_delegate_host, msg)
if result._result.get('msg') and result._result.get('msg') != "All items completed":
task_result += f" | msg: {to_text(result._result.get('msg'))}"
task_result += " | msg: " + to_text(result._result.get('msg'))
if result._result.get('stdout'):
task_result += f" | stdout: {result._result.get('stdout')}"
task_result += " | stdout: " + result._result.get('stdout')
if result._result.get('stderr'):
task_result += f" | stderr: {result._result.get('stderr')}"
task_result += " | stderr: " + result._result.get('stderr')
return task_result
@@ -91,30 +92,30 @@ class CallbackModule(CallbackModule_default):
self._get_task_display_name(task)
if self.task_display_name is not None:
if task.check_mode and self.get_option('check_mode_markers'):
self._display.display(f"{self.task_display_name} (check mode)...")
self._display.display("%s (check mode)..." % self.task_display_name)
else:
self._display.display(f"{self.task_display_name}...")
self._display.display("%s..." % self.task_display_name)
def v2_playbook_on_handler_task_start(self, task):
self._get_task_display_name(task)
if self.task_display_name is not None:
if task.check_mode and self.get_option('check_mode_markers'):
self._display.display(f"{self.task_display_name} (via handler in check mode)... ")
self._display.display("%s (via handler in check mode)... " % self.task_display_name)
else:
self._display.display(f"{self.task_display_name} (via handler)... ")
self._display.display("%s (via handler)... " % self.task_display_name)
def v2_playbook_on_play_start(self, play):
name = play.get_name().strip()
if play.check_mode and self.get_option('check_mode_markers'):
if name and play.hosts:
msg = f"\n- {name} (in check mode) on hosts: {','.join(play.hosts)} -"
msg = u"\n- %s (in check mode) on hosts: %s -" % (name, ",".join(play.hosts))
else:
msg = "- check mode -"
msg = u"- check mode -"
else:
if name and play.hosts:
msg = f"\n- {name} on hosts: {','.join(play.hosts)} -"
msg = u"\n- %s on hosts: %s -" % (name, ",".join(play.hosts))
else:
msg = "---"
msg = u"---"
self._display.display(msg)
@@ -125,7 +126,7 @@ class CallbackModule(CallbackModule_default):
msg = "skipped"
task_result = self._process_result_output(result, msg)
self._display.display(f" {task_result}", display_color)
self._display.display(" " + task_result, display_color)
else:
return
@@ -135,10 +136,10 @@ class CallbackModule(CallbackModule_default):
msg = "failed"
item_value = self._get_item_label(result._result)
if item_value:
msg += f" | item: {item_value}"
msg += " | item: %s" % (item_value,)
task_result = self._process_result_output(result, msg)
self._display.display(f" {task_result}", display_color, stderr=self.get_option('display_failed_stderr'))
self._display.display(" " + task_result, display_color, stderr=self.get_option('display_failed_stderr'))
def v2_runner_on_ok(self, result, msg="ok", display_color=C.COLOR_OK):
self._preprocess_result(result)
@@ -148,13 +149,13 @@ class CallbackModule(CallbackModule_default):
msg = "done"
item_value = self._get_item_label(result._result)
if item_value:
msg += f" | item: {item_value}"
msg += " | item: %s" % (item_value,)
display_color = C.COLOR_CHANGED
task_result = self._process_result_output(result, msg)
self._display.display(f" {task_result}", display_color)
self._display.display(" " + task_result, display_color)
elif self.get_option('display_ok_hosts'):
task_result = self._process_result_output(result, msg)
self._display.display(f" {task_result}", display_color)
self._display.display(" " + task_result, display_color)
def v2_runner_item_on_skipped(self, result):
self.v2_runner_on_skipped(result)
@@ -172,7 +173,7 @@ class CallbackModule(CallbackModule_default):
display_color = C.COLOR_UNREACHABLE
task_result = self._process_result_output(result, msg)
self._display.display(f" {task_result}", display_color, stderr=self.get_option('display_failed_stderr'))
self._display.display(" " + task_result, display_color, stderr=self.get_option('display_failed_stderr'))
def v2_on_file_diff(self, result):
if result._task.loop and 'results' in result._result:
@@ -194,17 +195,25 @@ class CallbackModule(CallbackModule_default):
# TODO how else can we display these?
t = stats.summarize(h)
self._display.display(
f" {hostcolor(h, t)} : {colorize('ok', t['ok'], C.COLOR_OK)} {colorize('changed', t['changed'], C.COLOR_CHANGED)} "
f"{colorize('unreachable', t['unreachable'], C.COLOR_UNREACHABLE)} {colorize('failed', t['failures'], C.COLOR_ERROR)} "
f"{colorize('rescued', t['rescued'], C.COLOR_OK)} {colorize('ignored', t['ignored'], C.COLOR_WARN)}",
self._display.display(u" %s : %s %s %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR),
colorize(u'rescued', t['rescued'], C.COLOR_OK),
colorize(u'ignored', t['ignored'], C.COLOR_WARN)),
screen_only=True
)
self._display.display(
f" {hostcolor(h, t, False)} : {colorize('ok', t['ok'], None)} {colorize('changed', t['changed'], None)} "
f"{colorize('unreachable', t['unreachable'], None)} {colorize('failed', t['failures'], None)} {colorize('rescued', t['rescued'], None)} "
f"{colorize('ignored', t['ignored'], None)}",
self._display.display(u" %s : %s %s %s %s %s %s" % (
hostcolor(h, t, False),
colorize(u'ok', t['ok'], None),
colorize(u'changed', t['changed'], None),
colorize(u'unreachable', t['unreachable'], None),
colorize(u'failed', t['failures'], None),
colorize(u'rescued', t['rescued'], None),
colorize(u'ignored', t['ignored'], None)),
log_only=True
)
if stats.custom and self.get_option('show_custom_stats'):
@@ -214,14 +223,12 @@ class CallbackModule(CallbackModule_default):
for k in sorted(stats.custom.keys()):
if k == '_run':
continue
stat_val = self._dump_results(stats.custom[k], indent=1).replace('\n', '')
self._display.display(f'\t{k}: {stat_val}')
self._display.display('\t%s: %s' % (k, self._dump_results(stats.custom[k], indent=1).replace('\n', '')))
# print per run custom stats
if '_run' in stats.custom:
self._display.display("", screen_only=True)
stat_val_run = self._dump_results(stats.custom['_run'], indent=1).replace('\n', '')
self._display.display(f'\tRUN: {stat_val_run}')
self._display.display('\tRUN: %s' % self._dump_results(stats.custom['_run'], indent=1).replace('\n', ''))
self._display.display("", screen_only=True)
def v2_playbook_on_no_hosts_matched(self):
@@ -232,23 +239,23 @@ class CallbackModule(CallbackModule_default):
def v2_playbook_on_start(self, playbook):
if context.CLIARGS['check'] and self.get_option('check_mode_markers'):
self._display.display(f"Executing playbook {basename(playbook._file_name)} in check mode")
self._display.display("Executing playbook %s in check mode" % basename(playbook._file_name))
else:
self._display.display(f"Executing playbook {basename(playbook._file_name)}")
self._display.display("Executing playbook %s" % basename(playbook._file_name))
# show CLI arguments
if self._display.verbosity > 3:
if context.CLIARGS.get('args'):
self._display.display(f"Positional arguments: {' '.join(context.CLIARGS['args'])}",
self._display.display('Positional arguments: %s' % ' '.join(context.CLIARGS['args']),
color=C.COLOR_VERBOSE, screen_only=True)
for argument in (a for a in context.CLIARGS if a != 'args'):
val = context.CLIARGS[argument]
if val:
self._display.vvvv(f'{argument}: {val}')
self._display.vvvv('%s: %s' % (argument, val))
def v2_runner_retry(self, result):
msg = f" Retrying... ({result._result['attempts']} of {result._result['retries']})"
msg = " Retrying... (%d of %d)" % (result._result['attempts'], result._result['retries'])
if self._run_is_verbose(result):
msg += f"Result was: {self._dump_results(result._result)}"
msg += "Result was: %s" % self._dump_results(result._result)
self._display.display(msg, color=C.COLOR_DEBUG)

View File

@@ -4,34 +4,32 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: yaml
type: stdout
short_description: YAML-ized Ansible screen output
deprecated:
removed_in: 13.0.0
why: Starting in ansible-core 2.13, the P(ansible.builtin.default#callback) callback has support for printing output in
YAML format.
alternative: Use O(ansible.builtin.default#callback:result_format=yaml).
description:
- Ansible output that can be quite a bit easier to read than the default JSON formatting.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
seealso:
- plugin: ansible.builtin.default
plugin_type: callback
description: >-
There is a parameter O(ansible.builtin.default#callback:result_format) in P(ansible.builtin.default#callback) that allows
you to change the output format to YAML.
notes:
- With ansible-core 2.13 or newer, you can instead specify V(yaml) for the parameter O(ansible.builtin.default#callback:result_format)
in P(ansible.builtin.default#callback).
"""
DOCUMENTATION = '''
author: Unknown (!UNKNOWN)
name: yaml
type: stdout
short_description: YAML-ized Ansible screen output
description:
- Ansible output that can be quite a bit easier to read than the
default JSON formatting.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
seealso:
- plugin: ansible.builtin.default
plugin_type: callback
description: >
There is a parameter O(ansible.builtin.default#callback:result_format) in P(ansible.builtin.default#callback)
that allows you to change the output format to YAML.
notes:
- >
With ansible-core 2.13 or newer, you can instead specify V(yaml) for the parameter O(ansible.builtin.default#callback:result_format)
in P(ansible.builtin.default#callback).
'''
import yaml
import json
@@ -47,7 +45,7 @@ from ansible.plugins.callback.default import CallbackModule as Default
# from http://stackoverflow.com/a/15423007/115478
def should_use_block(value):
"""Returns true if string should be in block format"""
for c in "\u000a\u000d\u001c\u001d\u001e\u0085\u2028\u2029":
for c in u"\u000a\u000d\u001c\u001d\u001e\u0085\u2028\u2029":
if c in value:
return True
return False
@@ -115,11 +113,11 @@ class CallbackModule(Default):
# put changed and skipped into a header line
if 'changed' in abridged_result:
dumped += f"changed={str(abridged_result['changed']).lower()} "
dumped += 'changed=' + str(abridged_result['changed']).lower() + ' '
del abridged_result['changed']
if 'skipped' in abridged_result:
dumped += f"skipped={str(abridged_result['skipped']).lower()} "
dumped += 'skipped=' + str(abridged_result['skipped']).lower() + ' '
del abridged_result['skipped']
# if we already have stdout, we don't need stdout_lines

View File

@@ -7,68 +7,79 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Maykel Moya (!UNKNOWN) <mmoya@speedyrails.com>
name: chroot
short_description: Interact with local chroot
description:
- Run commands or put/fetch files to an existing chroot on the Ansible controller.
options:
remote_addr:
DOCUMENTATION = '''
author: Maykel Moya (!UNKNOWN) <mmoya@speedyrails.com>
name: chroot
short_description: Interact with local chroot
description:
- The path of the chroot you want to access.
type: string
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
executable:
description:
- User specified executable shell.
type: string
ini:
- section: defaults
key: executable
env:
- name: ANSIBLE_EXECUTABLE
vars:
- name: ansible_executable
default: /bin/sh
chroot_exe:
description:
- User specified chroot binary.
type: string
ini:
- section: chroot_connection
key: exe
env:
- name: ANSIBLE_CHROOT_EXE
vars:
- name: ansible_chroot_exe
default: chroot
disable_root_check:
description:
- Do not check that the user is not root.
ini:
- section: chroot_connection
key: disable_root_check
env:
- name: ANSIBLE_CHROOT_DISABLE_ROOT_CHECK
vars:
- name: ansible_chroot_disable_root_check
default: false
type: bool
version_added: 7.3.0
"""
- Run commands or put/fetch files to an existing chroot on the Ansible controller.
options:
remote_addr:
description:
- The path of the chroot you want to access.
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
executable:
description:
- User specified executable shell
ini:
- section: defaults
key: executable
env:
- name: ANSIBLE_EXECUTABLE
vars:
- name: ansible_executable
default: /bin/sh
chroot_exe:
description:
- User specified chroot binary
ini:
- section: chroot_connection
key: exe
env:
- name: ANSIBLE_CHROOT_EXE
vars:
- name: ansible_chroot_exe
default: chroot
disable_root_check:
description:
- Do not check that the user is not root.
ini:
- section: chroot_connection
key: disable_root_check
env:
- name: ANSIBLE_CHROOT_DISABLE_ROOT_CHECK
vars:
- name: ansible_chroot_disable_root_check
default: false
type: bool
version_added: 7.3.0
'''
EXAMPLES = r"""
# Plugin requires root privileges for chroot, -E preserves your env (and location of ~/.ansible):
# sudo -E ansible-playbook ...
#
# Static inventory file
# [chroots]
# /path/to/debootstrap
# /path/to/feboostrap
# /path/to/lxc-image
# /path/to/chroot
# playbook
---
- hosts: chroots
connection: community.general.chroot
tasks:
- debug:
msg: "This is coming from chroot environment"
"""
import os
@@ -80,7 +91,7 @@ from ansible.errors import AnsibleError
from ansible.module_utils.basic import is_executable
from ansible.module_utils.common.process import get_bin_path
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.common.text.converters import to_bytes
from ansible.module_utils.common.text.converters import to_bytes, to_native
from ansible.plugins.connection import ConnectionBase, BUFSIZE
from ansible.utils.display import Display
@@ -106,15 +117,15 @@ class Connection(ConnectionBase):
# do some trivial checks for ensuring 'host' is actually a chroot'able dir
if not os.path.isdir(self.chroot):
raise AnsibleError(f"{self.chroot} is not a directory")
raise AnsibleError("%s is not a directory" % self.chroot)
chrootsh = os.path.join(self.chroot, 'bin/sh')
# Want to check for a usable bourne shell inside the chroot.
# is_executable() == True is sufficient. For symlinks it
# gets really complicated really fast. So we punt on finding that
# out. As long as it is a symlink we assume that it will work
# out. As long as it's a symlink we assume that it will work
if not (is_executable(chrootsh) or (os.path.lexists(chrootsh) and os.path.islink(chrootsh))):
raise AnsibleError(f"{self.chroot} does not look like a chrootable dir (/bin/sh missing)")
raise AnsibleError("%s does not look like a chrootable dir (/bin/sh missing)" % self.chroot)
def _connect(self):
""" connect to the chroot """
@@ -129,7 +140,7 @@ class Connection(ConnectionBase):
try:
self.chroot_cmd = get_bin_path(self.get_option('chroot_exe'))
except ValueError as e:
raise AnsibleError(str(e))
raise AnsibleError(to_native(e))
super(Connection, self)._connect()
if not self._connected:
@@ -147,7 +158,7 @@ class Connection(ConnectionBase):
executable = self.get_option('executable')
local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
display.vvv(f"EXEC {local_cmd}", host=self.chroot)
display.vvv("EXEC %s" % local_cmd, host=self.chroot)
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -172,7 +183,7 @@ class Connection(ConnectionBase):
exist in any given chroot. So for now we're choosing "/" instead.
This also happens to be the former default.
Can revisit using $HOME instead if it is a problem
Can revisit using $HOME instead if it's a problem
"""
if not remote_path.startswith(os.path.sep):
remote_path = os.path.join(os.path.sep, remote_path)
@@ -181,7 +192,7 @@ class Connection(ConnectionBase):
def put_file(self, in_path, out_path):
""" transfer a file from local to chroot """
super(Connection, self).put_file(in_path, out_path)
display.vvv(f"PUT {in_path} TO {out_path}", host=self.chroot)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.chroot)
out_path = shlex_quote(self._prefix_login_path(out_path))
try:
@@ -191,27 +202,27 @@ class Connection(ConnectionBase):
else:
count = ''
try:
p = self._buffered_exec_command(f'dd of={out_path} bs={BUFSIZE}{count}', stdin=in_file)
p = self._buffered_exec_command('dd of=%s bs=%s%s' % (out_path, BUFSIZE, count), stdin=in_file)
except OSError:
raise AnsibleError("chroot connection requires dd command in the chroot")
try:
stdout, stderr = p.communicate()
except Exception:
traceback.print_exc()
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}")
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
if p.returncode != 0:
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}:\n{stdout}\n{stderr}")
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
except IOError:
raise AnsibleError(f"file or module does not exist at: {in_path}")
raise AnsibleError("file or module does not exist at: %s" % in_path)
def fetch_file(self, in_path, out_path):
""" fetch a file from chroot to local """
super(Connection, self).fetch_file(in_path, out_path)
display.vvv(f"FETCH {in_path} TO {out_path}", host=self.chroot)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.chroot)
in_path = shlex_quote(self._prefix_login_path(in_path))
try:
p = self._buffered_exec_command(f'dd if={in_path} bs={BUFSIZE}')
p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
except OSError:
raise AnsibleError("chroot connection requires dd command in the chroot")
@@ -223,10 +234,10 @@ class Connection(ConnectionBase):
chunk = p.stdout.read(BUFSIZE)
except Exception:
traceback.print_exc()
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}")
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}:\n{stdout}\n{stderr}")
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
def close(self):
""" terminate the connection; nothing to do here """

View File

@@ -6,26 +6,26 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Michael Scherer (@mscherer) <misc@zarb.org>
name: funcd
short_description: Use funcd to connect to target
description:
- This transport permits you to use Ansible over Func.
- For people who have already setup func and that wish to play with ansible, this permit to move gradually to ansible without
having to redo completely the setup of the network.
options:
remote_addr:
DOCUMENTATION = '''
author: Michael Scherer (@mscherer) <misc@zarb.org>
name: funcd
short_description: Use funcd to connect to target
description:
- The path of the chroot you want to access.
type: string
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_func_host
"""
- This transport permits you to use Ansible over Func.
- For people who have already setup func and that wish to play with ansible,
this permit to move gradually to ansible without having to redo completely the setup of the network.
options:
remote_addr:
description:
- The path of the chroot you want to access.
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_func_host
'''
HAVE_FUNC = False
try:
@@ -71,7 +71,7 @@ class Connection(ConnectionBase):
raise AnsibleError("Internal Error: this module does not support optimized module pipelining")
# totally ignores privilege escalation
display.vvv(f"EXEC {cmd}", host=self.host)
display.vvv("EXEC %s" % cmd, host=self.host)
p = self.client.command.run(cmd)[self.host]
return p[0], p[1], p[2]
@@ -86,14 +86,14 @@ class Connection(ConnectionBase):
""" transfer a file from local to remote """
out_path = self._normalize_path(out_path, '/')
display.vvv(f"PUT {in_path} TO {out_path}", host=self.host)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
self.client.local.copyfile.send(in_path, out_path)
def fetch_file(self, in_path, out_path):
""" fetch a file from remote to local """
in_path = self._normalize_path(in_path, '/')
display.vvv(f"FETCH {in_path} TO {out_path}", host=self.host)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
# need to use a tmp dir due to difference of semantic for getfile
# ( who take a # directory as destination) and fetch_file, who
# take a file directly

View File

@@ -5,74 +5,46 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Stéphane Graber (@stgraber)
name: incus
short_description: Run tasks in Incus instances using the Incus CLI
description:
- Run commands or put/fetch files to an existing Incus instance using Incus CLI.
version_added: "8.2.0"
options:
remote_addr:
DOCUMENTATION = """
author: Stéphane Graber (@stgraber)
name: incus
short_description: Run tasks in Incus instances via the Incus CLI.
description:
- The instance identifier.
type: string
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_incus_host
executable:
description:
- The shell to use for execution inside the instance.
type: string
default: /bin/sh
vars:
- name: ansible_executable
- name: ansible_incus_executable
incus_become_method:
description:
- Become command used to switch to a non-root user.
- Is only used when O(remote_user) is not V(root).
type: str
default: /bin/su
vars:
- name: incus_become_method
version_added: 10.4.0
remote:
description:
- The name of the Incus remote to use (per C(incus remote list)).
- Remotes are used to access multiple servers from a single client.
type: string
default: local
vars:
- name: ansible_incus_remote
remote_user:
description:
- User to login/authenticate as.
- Can be set from the CLI via the C(--user) or C(-u) options.
type: string
default: root
vars:
- name: ansible_user
env:
- name: ANSIBLE_REMOTE_USER
ini:
- section: defaults
key: remote_user
keyword:
- name: remote_user
version_added: 10.4.0
project:
description:
- The name of the Incus project to use (per C(incus project list)).
- Projects are used to divide the instances running on a server.
type: string
default: default
vars:
- name: ansible_incus_project
- Run commands or put/fetch files to an existing Incus instance using Incus CLI.
version_added: "8.2.0"
options:
remote_addr:
description:
- The instance identifier.
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_incus_host
executable:
description:
- The shell to use for execution inside the instance.
default: /bin/sh
vars:
- name: ansible_executable
- name: ansible_incus_executable
remote:
description:
- The name of the Incus remote to use (per C(incus remote list)).
- Remotes are used to access multiple servers from a single client.
default: local
vars:
- name: ansible_incus_remote
project:
description:
- The name of the Incus project to use (per C(incus project list)).
- Projects are used to divide the instances running on a server.
default: default
vars:
- name: ansible_incus_project
"""
import os
@@ -89,6 +61,7 @@ class Connection(ConnectionBase):
transport = "incus"
has_pipelining = True
default_user = 'root'
def __init__(self, play_context, new_stdin, *args, **kwargs):
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
@@ -103,34 +76,10 @@ class Connection(ConnectionBase):
super(Connection, self)._connect()
if not self._connected:
self._display.vvv(f"ESTABLISH Incus CONNECTION FOR USER: {self.get_option('remote_user')}",
self._display.vvv(u"ESTABLISH Incus CONNECTION FOR USER: root",
host=self._instance())
self._connected = True
def _build_command(self, cmd) -> str:
"""build the command to execute on the incus host"""
exec_cmd = [
self._incus_cmd,
"--project", self.get_option("project"),
"exec",
f"{self.get_option('remote')}:{self._instance()}",
"--"]
if self.get_option("remote_user") != "root":
self._display.vvv(
f"INFO: Running as non-root user: {self.get_option('remote_user')}, \
trying to run 'incus exec' with become method: {self.get_option('incus_become_method')}",
host=self._instance(),
)
exec_cmd.extend(
[self.get_option("incus_become_method"), self.get_option("remote_user"), "-c"]
)
exec_cmd.extend([self.get_option("executable"), "-c", cmd])
return exec_cmd
def _instance(self):
# Return only the leading part of the FQDN as the instance name
# as Incus instance names cannot be a FQDN.
@@ -140,11 +89,16 @@ class Connection(ConnectionBase):
""" execute a command on the Incus host """
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
self._display.vvv(f"EXEC {cmd}",
self._display.vvv(u"EXEC {0}".format(cmd),
host=self._instance())
local_cmd = self._build_command(cmd)
self._display.vvvvv(f"EXEC {local_cmd}", host=self._instance())
local_cmd = [
self._incus_cmd,
"--project", self.get_option("project"),
"exec",
"%s:%s" % (self.get_option("remote"), self._instance()),
"--",
self._play_context.executable, "-c", cmd]
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
in_data = to_bytes(in_data, errors='surrogate_or_strict', nonstring='passthru')
@@ -156,71 +110,33 @@ class Connection(ConnectionBase):
stderr = to_text(stderr)
if stderr == "Error: Instance is not running.\n":
raise AnsibleConnectionFailure(f"instance not running: {self._instance()}")
raise AnsibleConnectionFailure("instance not running: %s" %
self._instance())
if stderr == "Error: Instance not found\n":
raise AnsibleConnectionFailure(f"instance not found: {self._instance()}")
raise AnsibleConnectionFailure("instance not found: %s" %
self._instance())
return process.returncode, stdout, stderr
def _get_remote_uid_gid(self) -> tuple[int, int]:
"""Get the user and group ID of 'remote_user' from the instance."""
rc, uid_out, err = self.exec_command("/bin/id -u")
if rc != 0:
raise AnsibleError(
f"Failed to get remote uid for user {self.get_option('remote_user')}: {err}"
)
uid = uid_out.strip()
rc, gid_out, err = self.exec_command("/bin/id -g")
if rc != 0:
raise AnsibleError(
f"Failed to get remote gid for user {self.get_option('remote_user')}: {err}"
)
gid = gid_out.strip()
return int(uid), int(gid)
def put_file(self, in_path, out_path):
""" put a file from local to Incus """
super(Connection, self).put_file(in_path, out_path)
self._display.vvv(f"PUT {in_path} TO {out_path}",
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path),
host=self._instance())
if not os.path.isfile(to_bytes(in_path, errors='surrogate_or_strict')):
raise AnsibleFileNotFound(f"input path is not a file: {in_path}")
raise AnsibleFileNotFound("input path is not a file: %s" % in_path)
if self.get_option("remote_user") != "root":
uid, gid = self._get_remote_uid_gid()
local_cmd = [
self._incus_cmd,
"--project",
self.get_option("project"),
"file",
"push",
"--uid",
str(uid),
"--gid",
str(gid),
"--quiet",
in_path,
f"{self.get_option('remote')}:{self._instance()}/{out_path}",
]
else:
local_cmd = [
self._incus_cmd,
"--project",
self.get_option("project"),
"file",
"push",
"--quiet",
in_path,
f"{self.get_option('remote')}:{self._instance()}/{out_path}",
]
self._display.vvvvv(f"PUT {local_cmd}", host=self._instance())
local_cmd = [
self._incus_cmd,
"--project", self.get_option("project"),
"file", "push", "--quiet",
in_path,
"%s:%s/%s" % (self.get_option("remote"),
self._instance(),
out_path)]
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
@@ -230,14 +146,16 @@ class Connection(ConnectionBase):
""" fetch a file from Incus to local """
super(Connection, self).fetch_file(in_path, out_path)
self._display.vvv(f"FETCH {in_path} TO {out_path}",
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path),
host=self._instance())
local_cmd = [
self._incus_cmd,
"--project", self.get_option("project"),
"file", "pull", "--quiet",
f"{self.get_option('remote')}:{self._instance()}/{in_path}",
"%s:%s/%s" % (self.get_option("remote"),
self._instance(),
in_path),
out_path]
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]

View File

@@ -7,30 +7,29 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Stephan Lohse (!UNKNOWN) <dev-github@ploek.org>
name: iocage
short_description: Run tasks in iocage jails
description:
- Run commands or put/fetch files to an existing iocage jail.
options:
remote_addr:
DOCUMENTATION = '''
author: Stephan Lohse (!UNKNOWN) <dev-github@ploek.org>
name: iocage
short_description: Run tasks in iocage jails
description:
- Path to the jail.
type: string
vars:
- name: ansible_host
- name: ansible_iocage_host
remote_user:
description:
- User to execute as inside the jail.
type: string
vars:
- name: ansible_user
- name: ansible_iocage_user
"""
- Run commands or put/fetch files to an existing iocage jail
options:
remote_addr:
description:
- Path to the jail
vars:
- name: ansible_host
- name: ansible_iocage_host
remote_user:
description:
- User to execute as inside the jail
vars:
- name: ansible_user
- name: ansible_iocage_user
'''
import subprocess
@@ -54,12 +53,11 @@ class Connection(Jail):
jail_uuid = self.get_jail_uuid()
kwargs[Jail.modified_jailname_key] = f'ioc-{jail_uuid}'
kwargs[Jail.modified_jailname_key] = 'ioc-{0}'.format(jail_uuid)
display.vvv(
f"Jail {self.ioc_jail} has been translated to {kwargs[Jail.modified_jailname_key]}",
host=kwargs[Jail.modified_jailname_key]
)
display.vvv(u"Jail {iocjail} has been translated to {rawjail}".format(
iocjail=self.ioc_jail, rawjail=kwargs[Jail.modified_jailname_key]),
host=kwargs[Jail.modified_jailname_key])
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
@@ -81,6 +79,6 @@ class Connection(Jail):
p.wait()
if p.returncode != 0:
raise AnsibleError(f"iocage returned an error: {stdout}")
raise AnsibleError(u"iocage returned an error: {0}".format(stdout))
return stdout.strip('\n')

View File

@@ -7,32 +7,31 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Ansible Core Team
name: jail
short_description: Run tasks in jails
description:
- Run commands or put/fetch files to an existing jail.
options:
remote_addr:
DOCUMENTATION = '''
author: Ansible Core Team
name: jail
short_description: Run tasks in jails
description:
- Path to the jail.
type: string
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_jail_host
remote_user:
description:
- User to execute as inside the jail.
type: string
vars:
- name: ansible_user
- name: ansible_jail_user
"""
- Run commands or put/fetch files to an existing jail
options:
remote_addr:
description:
- Path to the jail
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_jail_host
remote_user:
description:
- User to execute as inside the jail
vars:
- name: ansible_user
- name: ansible_jail_user
'''
import os
import os.path
@@ -74,14 +73,14 @@ class Connection(ConnectionBase):
self.jexec_cmd = self._search_executable('jexec')
if self.jail not in self.list_jails():
raise AnsibleError(f"incorrect jail name {self.jail}")
raise AnsibleError("incorrect jail name %s" % self.jail)
@staticmethod
def _search_executable(executable):
try:
return get_bin_path(executable)
except ValueError:
raise AnsibleError(f"{executable} command not found in PATH")
raise AnsibleError("%s command not found in PATH" % executable)
def list_jails(self):
p = subprocess.Popen([self.jls_cmd, '-q', 'name'],
@@ -96,7 +95,7 @@ class Connection(ConnectionBase):
""" connect to the jail; nothing to do here """
super(Connection, self)._connect()
if not self._connected:
display.vvv(f"ESTABLISH JAIL CONNECTION FOR USER: {self._play_context.remote_user}", host=self.jail)
display.vvv(u"ESTABLISH JAIL CONNECTION FOR USER: {0}".format(self._play_context.remote_user), host=self.jail)
self._connected = True
def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE):
@@ -114,11 +113,11 @@ class Connection(ConnectionBase):
if self._play_context.remote_user is not None:
local_cmd += ['-U', self._play_context.remote_user]
# update HOME since -U does not update the jail environment
set_env = f"HOME=~{self._play_context.remote_user} "
set_env = 'HOME=~' + self._play_context.remote_user + ' '
local_cmd += [self.jail, self._play_context.executable, '-c', set_env + cmd]
display.vvv(f"EXEC {local_cmd}", host=self.jail)
display.vvv("EXEC %s" % (local_cmd,), host=self.jail)
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -143,7 +142,7 @@ class Connection(ConnectionBase):
exist in any given chroot. So for now we're choosing "/" instead.
This also happens to be the former default.
Can revisit using $HOME instead if it is a problem
Can revisit using $HOME instead if it's a problem
"""
if not remote_path.startswith(os.path.sep):
remote_path = os.path.join(os.path.sep, remote_path)
@@ -152,7 +151,7 @@ class Connection(ConnectionBase):
def put_file(self, in_path, out_path):
""" transfer a file from local to jail """
super(Connection, self).put_file(in_path, out_path)
display.vvv(f"PUT {in_path} TO {out_path}", host=self.jail)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.jail)
out_path = shlex_quote(self._prefix_login_path(out_path))
try:
@@ -162,27 +161,27 @@ class Connection(ConnectionBase):
else:
count = ''
try:
p = self._buffered_exec_command(f'dd of={out_path} bs={BUFSIZE}{count}', stdin=in_file)
p = self._buffered_exec_command('dd of=%s bs=%s%s' % (out_path, BUFSIZE, count), stdin=in_file)
except OSError:
raise AnsibleError("jail connection requires dd command in the jail")
try:
stdout, stderr = p.communicate()
except Exception:
traceback.print_exc()
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}")
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
if p.returncode != 0:
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}:\n{to_native(stdout)}\n{to_native(stderr)}")
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, to_native(stdout), to_native(stderr)))
except IOError:
raise AnsibleError(f"file or module does not exist at: {in_path}")
raise AnsibleError("file or module does not exist at: %s" % in_path)
def fetch_file(self, in_path, out_path):
""" fetch a file from jail to local """
super(Connection, self).fetch_file(in_path, out_path)
display.vvv(f"FETCH {in_path} TO {out_path}", host=self.jail)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.jail)
in_path = shlex_quote(self._prefix_login_path(in_path))
try:
p = self._buffered_exec_command(f'dd if={in_path} bs={BUFSIZE}')
p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
except OSError:
raise AnsibleError("jail connection requires dd command in the jail")
@@ -194,10 +193,10 @@ class Connection(ConnectionBase):
chunk = p.stdout.read(BUFSIZE)
except Exception:
traceback.print_exc()
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}")
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}:\n{to_native(stdout)}\n{to_native(stderr)}")
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, to_native(stdout), to_native(stderr)))
def close(self):
""" terminate the connection; nothing to do here """

View File

@@ -4,33 +4,32 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Joerg Thalheim (!UNKNOWN) <joerg@higgsboson.tk>
name: lxc
short_description: Run tasks in LXC containers using lxc python library
description:
- Run commands or put/fetch files to an existing LXC container using lxc python library.
options:
remote_addr:
DOCUMENTATION = '''
author: Joerg Thalheim (!UNKNOWN) <joerg@higgsboson.tk>
name: lxc
short_description: Run tasks in lxc containers via lxc python library
description:
- Container identifier.
type: string
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_lxc_host
executable:
default: /bin/sh
description:
- Shell executable.
type: string
vars:
- name: ansible_executable
- name: ansible_lxc_executable
"""
- Run commands or put/fetch files to an existing lxc container using lxc python library
options:
remote_addr:
description:
- Container identifier
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_lxc_host
executable:
default: /bin/sh
description:
- Shell executable
vars:
- name: ansible_executable
- name: ansible_lxc_executable
'''
import os
import shutil
@@ -81,7 +80,7 @@ class Connection(ConnectionBase):
self._display.vvv("THIS IS A LOCAL LXC DIR", host=self.container_name)
self.container = _lxc.Container(self.container_name)
if self.container.state == "STOPPED":
raise errors.AnsibleError(f"{self.container_name} is not running")
raise errors.AnsibleError("%s is not running" % self.container_name)
@staticmethod
def _communicate(pid, in_data, stdin, stdout, stderr):
@@ -143,10 +142,10 @@ class Connection(ConnectionBase):
read_stdin, write_stdin = os.pipe()
kwargs['stdin'] = self._set_nonblocking(read_stdin)
self._display.vvv(f"EXEC {local_cmd}", host=self.container_name)
self._display.vvv("EXEC %s" % (local_cmd), host=self.container_name)
pid = self.container.attach(_lxc.attach_run_command, local_cmd, **kwargs)
if pid == -1:
msg = f"failed to attach to container {self.container_name}"
msg = "failed to attach to container %s" % self.container_name
raise errors.AnsibleError(msg)
write_stdout = os.close(write_stdout)
@@ -173,18 +172,18 @@ class Connection(ConnectionBase):
def put_file(self, in_path, out_path):
''' transfer a file from local to lxc '''
super(Connection, self).put_file(in_path, out_path)
self._display.vvv(f"PUT {in_path} TO {out_path}", host=self.container_name)
self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.container_name)
in_path = to_bytes(in_path, errors='surrogate_or_strict')
out_path = to_bytes(out_path, errors='surrogate_or_strict')
if not os.path.exists(in_path):
msg = f"file or module does not exist: {in_path}"
msg = "file or module does not exist: %s" % in_path
raise errors.AnsibleFileNotFound(msg)
try:
src_file = open(in_path, "rb")
except IOError:
traceback.print_exc()
raise errors.AnsibleError(f"failed to open input file to {in_path}")
raise errors.AnsibleError("failed to open input file to %s" % in_path)
try:
def write_file(args):
with open(out_path, 'wb+') as dst_file:
@@ -193,7 +192,7 @@ class Connection(ConnectionBase):
self.container.attach_wait(write_file, None)
except IOError:
traceback.print_exc()
msg = f"failed to transfer file to {out_path}"
msg = "failed to transfer file to %s" % out_path
raise errors.AnsibleError(msg)
finally:
src_file.close()
@@ -201,7 +200,7 @@ class Connection(ConnectionBase):
def fetch_file(self, in_path, out_path):
''' fetch a file from lxc to local '''
super(Connection, self).fetch_file(in_path, out_path)
self._display.vvv(f"FETCH {in_path} TO {out_path}", host=self.container_name)
self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.container_name)
in_path = to_bytes(in_path, errors='surrogate_or_strict')
out_path = to_bytes(out_path, errors='surrogate_or_strict')
@@ -209,7 +208,7 @@ class Connection(ConnectionBase):
dst_file = open(out_path, "wb")
except IOError:
traceback.print_exc()
msg = f"failed to open output file {out_path}"
msg = "failed to open output file %s" % out_path
raise errors.AnsibleError(msg)
try:
def write_file(args):
@@ -224,7 +223,7 @@ class Connection(ConnectionBase):
self.container.attach_wait(write_file, None)
except IOError:
traceback.print_exc()
msg = f"failed to transfer file from {in_path} to {out_path}"
msg = "failed to transfer file from %s to %s" % (in_path, out_path)
raise errors.AnsibleError(msg)
finally:
dst_file.close()

View File

@@ -4,75 +4,47 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Matt Clay (@mattclay) <matt@mystile.com>
name: lxd
short_description: Run tasks in LXD instances using C(lxc) CLI
description:
- Run commands or put/fetch files to an existing instance using C(lxc) CLI.
options:
remote_addr:
DOCUMENTATION = '''
author: Matt Clay (@mattclay) <matt@mystile.com>
name: lxd
short_description: Run tasks in LXD instances via C(lxc) CLI
description:
- Instance (container/VM) identifier.
- Since community.general 8.0.0, a FQDN can be provided; in that case, the first component (the part before C(.)) is
used as the instance identifier.
type: string
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_lxd_host
executable:
description:
- Shell to use for execution inside instance.
type: string
default: /bin/sh
vars:
- name: ansible_executable
- name: ansible_lxd_executable
lxd_become_method:
description:
- Become command used to switch to a non-root user.
- Is only used when O(remote_user) is not V(root).
type: str
default: /bin/su
vars:
- name: lxd_become_method
version_added: 10.4.0
remote:
description:
- Name of the LXD remote to use.
type: string
default: local
vars:
- name: ansible_lxd_remote
version_added: 2.0.0
remote_user:
description:
- User to login/authenticate as.
- Can be set from the CLI via the C(--user) or C(-u) options.
type: string
default: root
vars:
- name: ansible_user
env:
- name: ANSIBLE_REMOTE_USER
ini:
- section: defaults
key: remote_user
keyword:
- name: remote_user
version_added: 10.4.0
project:
description:
- Name of the LXD project to use.
type: string
vars:
- name: ansible_lxd_project
version_added: 2.0.0
"""
- Run commands or put/fetch files to an existing instance using C(lxc) CLI.
options:
remote_addr:
description:
- Instance (container/VM) identifier.
- Since community.general 8.0.0, a FQDN can be provided; in that case, the first component (the part before C(.))
is used as the instance identifier.
default: inventory_hostname
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_lxd_host
executable:
description:
- Shell to use for execution inside instance.
default: /bin/sh
vars:
- name: ansible_executable
- name: ansible_lxd_executable
remote:
description:
- Name of the LXD remote to use.
default: local
vars:
- name: ansible_lxd_remote
version_added: 2.0.0
project:
description:
- Name of the LXD project to use.
vars:
- name: ansible_lxd_project
version_added: 2.0.0
'''
import os
from subprocess import Popen, PIPE
@@ -88,6 +60,7 @@ class Connection(ConnectionBase):
transport = 'community.general.lxd'
has_pipelining = True
default_user = 'root'
def __init__(self, play_context, new_stdin, *args, **kwargs):
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
@@ -97,6 +70,9 @@ class Connection(ConnectionBase):
except ValueError:
raise AnsibleError("lxc command not found in PATH")
if self._play_context.remote_user is not None and self._play_context.remote_user != 'root':
self._display.warning('lxd does not support remote_user, using default: root')
def _host(self):
""" translate remote_addr to lxd (short) hostname """
return self.get_option("remote_addr").split(".", 1)[0]
@@ -106,41 +82,26 @@ class Connection(ConnectionBase):
super(Connection, self)._connect()
if not self._connected:
self._display.vvv(f"ESTABLISH LXD CONNECTION FOR USER: {self.get_option('remote_user')}", host=self._host())
self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self._host())
self._connected = True
def _build_command(self, cmd) -> str:
"""build the command to execute on the lxd host"""
exec_cmd = [self._lxc_cmd]
if self.get_option("project"):
exec_cmd.extend(["--project", self.get_option("project")])
exec_cmd.extend(["exec", f"{self.get_option('remote')}:{self._host()}", "--"])
if self.get_option("remote_user") != "root":
self._display.vvv(
f"INFO: Running as non-root user: {self.get_option('remote_user')}, \
trying to run 'lxc exec' with become method: {self.get_option('lxd_become_method')}",
host=self._host(),
)
exec_cmd.extend(
[self.get_option("lxd_become_method"), self.get_option("remote_user"), "-c"]
)
exec_cmd.extend([self.get_option("executable"), "-c", cmd])
return exec_cmd
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(f"EXEC {cmd}", host=self._host())
self._display.vvv(u"EXEC {0}".format(cmd), host=self._host())
local_cmd = self._build_command(cmd)
self._display.vvvvv(f"EXEC {local_cmd}", host=self._host())
local_cmd = [self._lxc_cmd]
if self.get_option("project"):
local_cmd.extend(["--project", self.get_option("project")])
local_cmd.extend([
"exec",
"%s:%s" % (self.get_option("remote"), self._host()),
"--",
self.get_option("executable"), "-c", cmd
])
self._display.vvvvv(u"EXEC {0}".format(local_cmd), host=self._host())
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
in_data = to_bytes(in_data, errors='surrogate_or_strict', nonstring='passthru')
@@ -151,73 +112,33 @@ class Connection(ConnectionBase):
stdout = to_text(stdout)
stderr = to_text(stderr)
self._display.vvvvv(f"EXEC lxc output: {stdout} {stderr}", host=self._host())
self._display.vvvvv(u"EXEC lxc output: {0} {1}".format(stdout, stderr), host=self._host())
if "is not running" in stderr:
raise AnsibleConnectionFailure(f"instance not running: {self._host()}")
raise AnsibleConnectionFailure("instance not running: %s" % self._host())
if stderr.strip() == "Error: Instance not found" or stderr.strip() == "error: not found":
raise AnsibleConnectionFailure(f"instance not found: {self._host()}")
raise AnsibleConnectionFailure("instance not found: %s" % self._host())
return process.returncode, stdout, stderr
def _get_remote_uid_gid(self) -> tuple[int, int]:
"""Get the user and group ID of 'remote_user' from the instance."""
rc, uid_out, err = self.exec_command("/bin/id -u")
if rc != 0:
raise AnsibleError(
f"Failed to get remote uid for user {self.get_option('remote_user')}: {err}"
)
uid = uid_out.strip()
rc, gid_out, err = self.exec_command("/bin/id -g")
if rc != 0:
raise AnsibleError(
f"Failed to get remote gid for user {self.get_option('remote_user')}: {err}"
)
gid = gid_out.strip()
return int(uid), int(gid)
def put_file(self, in_path, out_path):
""" put a file from local to lxd """
super(Connection, self).put_file(in_path, out_path)
self._display.vvv(f"PUT {in_path} TO {out_path}", host=self._host())
self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self._host())
if not os.path.isfile(to_bytes(in_path, errors='surrogate_or_strict')):
raise AnsibleFileNotFound(f"input path is not a file: {in_path}")
raise AnsibleFileNotFound("input path is not a file: %s" % in_path)
local_cmd = [self._lxc_cmd]
if self.get_option("project"):
local_cmd.extend(["--project", self.get_option("project")])
if self.get_option("remote_user") != "root":
uid, gid = self._get_remote_uid_gid()
local_cmd.extend(
[
"file",
"push",
"--uid",
str(uid),
"--gid",
str(gid),
in_path,
f"{self.get_option('remote')}:{self._host()}/{out_path}",
]
)
else:
local_cmd.extend(
[
"file",
"push",
in_path,
f"{self.get_option('remote')}:{self._host()}/{out_path}",
]
)
self._display.vvvvv(f"PUT {local_cmd}", host=self._host())
local_cmd.extend([
"file", "push",
in_path,
"%s:%s/%s" % (self.get_option("remote"), self._host(), out_path)
])
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
@@ -228,14 +149,14 @@ class Connection(ConnectionBase):
""" fetch a file from lxd to local """
super(Connection, self).fetch_file(in_path, out_path)
self._display.vvv(f"FETCH {in_path} TO {out_path}", host=self._host())
self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self._host())
local_cmd = [self._lxc_cmd]
if self.get_option("project"):
local_cmd.extend(["--project", self.get_option("project")])
local_cmd.extend([
"file", "pull",
f"{self.get_option('remote')}:{self._host()}/{in_path}",
"%s:%s/%s" % (self.get_option("remote"), self._host(), in_path),
out_path
])

View File

@@ -1,857 +0,0 @@
# -*- coding: utf-8 -*-
# Derived from ansible/plugins/connection/paramiko_ssh.py (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
# Copyright (c) 2024 Nils Stein (@mietzen) <github.nstein@mailbox.org>
# Copyright (c) 2024 Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
author: Nils Stein (@mietzen) <github.nstein@mailbox.org>
name: proxmox_pct_remote
short_description: Run tasks in Proxmox LXC container instances using pct CLI via SSH
requirements:
- paramiko
description:
- Run commands or put/fetch files to an existing Proxmox LXC container using pct CLI via SSH.
- Uses the Python SSH implementation (Paramiko) to connect to the Proxmox host.
version_added: "10.3.0"
options:
remote_addr:
description:
- Address of the remote target.
default: inventory_hostname
type: string
vars:
- name: inventory_hostname
- name: ansible_host
- name: ansible_ssh_host
- name: ansible_paramiko_host
port:
description: Remote port to connect to.
type: int
default: 22
ini:
- section: defaults
key: remote_port
- section: paramiko_connection
key: remote_port
env:
- name: ANSIBLE_REMOTE_PORT
- name: ANSIBLE_REMOTE_PARAMIKO_PORT
vars:
- name: ansible_port
- name: ansible_ssh_port
- name: ansible_paramiko_port
keyword:
- name: port
remote_user:
description:
- User to login/authenticate as.
- Can be set from the CLI via the C(--user) or C(-u) options.
type: string
vars:
- name: ansible_user
- name: ansible_ssh_user
- name: ansible_paramiko_user
env:
- name: ANSIBLE_REMOTE_USER
- name: ANSIBLE_PARAMIKO_REMOTE_USER
ini:
- section: defaults
key: remote_user
- section: paramiko_connection
key: remote_user
keyword:
- name: remote_user
password:
description:
- Secret used to either login the SSH server or as a passphrase for SSH keys that require it.
- Can be set from the CLI via the C(--ask-pass) option.
type: string
vars:
- name: ansible_password
- name: ansible_ssh_pass
- name: ansible_ssh_password
- name: ansible_paramiko_pass
- name: ansible_paramiko_password
use_rsa_sha2_algorithms:
description:
- Whether or not to enable RSA SHA2 algorithms for pubkeys and hostkeys.
- On paramiko versions older than 2.9, this only affects hostkeys.
- For behavior matching paramiko<2.9 set this to V(false).
vars:
- name: ansible_paramiko_use_rsa_sha2_algorithms
ini:
- {key: use_rsa_sha2_algorithms, section: paramiko_connection}
env:
- {name: ANSIBLE_PARAMIKO_USE_RSA_SHA2_ALGORITHMS}
default: true
type: boolean
host_key_auto_add:
description: "Automatically add host keys to C(~/.ssh/known_hosts)."
env:
- name: ANSIBLE_PARAMIKO_HOST_KEY_AUTO_ADD
ini:
- key: host_key_auto_add
section: paramiko_connection
type: boolean
look_for_keys:
default: True
description: "Set to V(false) to disable searching for private key files in C(~/.ssh/)."
env:
- name: ANSIBLE_PARAMIKO_LOOK_FOR_KEYS
ini:
- {key: look_for_keys, section: paramiko_connection}
type: boolean
proxy_command:
default: ""
description:
- Proxy information for running the connection via a jumphost.
type: string
env:
- name: ANSIBLE_PARAMIKO_PROXY_COMMAND
ini:
- {key: proxy_command, section: paramiko_connection}
vars:
- name: ansible_paramiko_proxy_command
pty:
default: True
description: "C(sudo) usually requires a PTY, V(true) to give a PTY and V(false) to not give a PTY."
env:
- name: ANSIBLE_PARAMIKO_PTY
ini:
- section: paramiko_connection
key: pty
type: boolean
record_host_keys:
default: True
description: "Save the host keys to a file."
env:
- name: ANSIBLE_PARAMIKO_RECORD_HOST_KEYS
ini:
- section: paramiko_connection
key: record_host_keys
type: boolean
host_key_checking:
description: "Set this to V(false) if you want to avoid host key checking by the underlying tools Ansible uses to connect to the host."
type: boolean
default: true
env:
- name: ANSIBLE_HOST_KEY_CHECKING
- name: ANSIBLE_SSH_HOST_KEY_CHECKING
- name: ANSIBLE_PARAMIKO_HOST_KEY_CHECKING
ini:
- section: defaults
key: host_key_checking
- section: paramiko_connection
key: host_key_checking
vars:
- name: ansible_host_key_checking
- name: ansible_ssh_host_key_checking
- name: ansible_paramiko_host_key_checking
use_persistent_connections:
description: "Toggles the use of persistence for connections."
type: boolean
default: False
env:
- name: ANSIBLE_USE_PERSISTENT_CONNECTIONS
ini:
- section: defaults
key: use_persistent_connections
banner_timeout:
type: float
default: 30
description:
- Configures, in seconds, the amount of time to wait for the SSH
banner to be presented. This option is supported by paramiko
version 1.15.0 or newer.
ini:
- section: paramiko_connection
key: banner_timeout
env:
- name: ANSIBLE_PARAMIKO_BANNER_TIMEOUT
timeout:
type: int
default: 10
description: Number of seconds until the plugin gives up on failing to establish a TCP connection.
ini:
- section: defaults
key: timeout
- section: ssh_connection
key: timeout
- section: paramiko_connection
key: timeout
env:
- name: ANSIBLE_TIMEOUT
- name: ANSIBLE_SSH_TIMEOUT
- name: ANSIBLE_PARAMIKO_TIMEOUT
vars:
- name: ansible_ssh_timeout
- name: ansible_paramiko_timeout
cli:
- name: timeout
lock_file_timeout:
type: int
default: 60
description: Number of seconds until the plugin gives up on trying to write a lock file when writing SSH known host keys.
vars:
- name: ansible_lock_file_timeout
env:
- name: ANSIBLE_LOCK_FILE_TIMEOUT
private_key_file:
description:
- Path to private key file to use for authentication.
type: string
ini:
- section: defaults
key: private_key_file
- section: paramiko_connection
key: private_key_file
env:
- name: ANSIBLE_PRIVATE_KEY_FILE
- name: ANSIBLE_PARAMIKO_PRIVATE_KEY_FILE
vars:
- name: ansible_private_key_file
- name: ansible_ssh_private_key_file
- name: ansible_paramiko_private_key_file
cli:
- name: private_key_file
option: "--private-key"
vmid:
description:
- LXC Container ID
type: int
vars:
- name: proxmox_vmid
proxmox_become_method:
description:
- Become command used in proxmox
type: str
default: sudo
vars:
- name: proxmox_become_method
notes:
- >
When NOT using this plugin as root, you need to have a become mechanism,
e.g. C(sudo), installed on Proxmox and setup so we can run it without prompting for the password.
Inside the container, we need a shell, for example C(sh) and the C(cat) command to be available in the C(PATH) for this plugin to work.
"""
EXAMPLES = r"""
# --------------------------------------------------------------
# Setup sudo with password less access to pct for user 'ansible':
# --------------------------------------------------------------
#
# Open a Proxmox root shell and execute:
# $ useradd -d /opt/ansible-pct -r -m -s /bin/sh ansible
# $ mkdir -p /opt/ansible-pct/.ssh
# $ ssh-keygen -t ed25519 -C 'ansible' -N "" -f /opt/ansible-pct/.ssh/ansible <<< y > /dev/null
# $ cat /opt/ansible-pct/.ssh/ansible
# $ mv /opt/ansible-pct/.ssh/ansible.pub /opt/ansible-pct/.ssh/authorized-keys
# $ rm /opt/ansible-pct/.ssh/ansible*
# $ chown -R ansible:ansible /opt/ansible-pct/.ssh
# $ chmod 700 /opt/ansible-pct/.ssh
# $ chmod 600 /opt/ansible-pct/.ssh/authorized-keys
# $ echo 'ansible ALL = (root) NOPASSWD: /usr/sbin/pct' > /etc/sudoers.d/ansible_pct
#
# Save the displayed private key and add it to your ssh-agent
#
# Or use ansible:
# ---
# - name: Setup ansible-pct user and configure environment on Proxmox host
# hosts: proxmox
# become: true
# gather_facts: false
#
# tasks:
# - name: Create ansible user
# ansible.builtin.user:
# name: ansible
# comment: Ansible User
# home: /opt/ansible-pct
# shell: /bin/sh
# create_home: true
# system: true
#
# - name: Create .ssh directory
# ansible.builtin.file:
# path: /opt/ansible-pct/.ssh
# state: directory
# owner: ansible
# group: ansible
# mode: '0700'
#
# - name: Generate SSH key for ansible user
# community.crypto.openssh_keypair:
# path: /opt/ansible-pct/.ssh/ansible
# type: ed25519
# comment: 'ansible'
# force: true
# mode: '0600'
# owner: ansible
# group: ansible
#
# - name: Set public key as authorized key
# ansible.builtin.copy:
# src: /opt/ansible-pct/.ssh/ansible.pub
# dest: /opt/ansible-pct/.ssh/authorized-keys
# remote_src: yes
# owner: ansible
# group: ansible
# mode: '0600'
#
# - name: Add sudoers entry for ansible user
# ansible.builtin.copy:
# content: 'ansible ALL = (root) NOPASSWD: /usr/sbin/pct'
# dest: /etc/sudoers.d/ansible_pct
# owner: root
# group: root
# mode: '0440'
#
# - name: Fetch private SSH key to localhost
# ansible.builtin.fetch:
# src: /opt/ansible-pct/.ssh/ansible
# dest: ~/.ssh/proxmox_ansible_private_key
# flat: yes
# fail_on_missing: true
#
# - name: Clean up generated SSH keys
# ansible.builtin.file:
# path: /opt/ansible-pct/.ssh/ansible*
# state: absent
#
# - name: Configure private key permissions on localhost
# hosts: localhost
# tasks:
# - name: Set permissions for fetched private key
# ansible.builtin.file:
# path: ~/.ssh/proxmox_ansible_private_key
# mode: '0600'
#
# --------------------------------
# Static inventory file: hosts.yml
# --------------------------------
# all:
# children:
# lxc:
# hosts:
# container-1:
# ansible_host: 10.0.0.10
# proxmox_vmid: 100
# ansible_connection: community.general.proxmox_pct_remote
# ansible_user: ansible
# container-2:
# ansible_host: 10.0.0.10
# proxmox_vmid: 200
# ansible_connection: community.general.proxmox_pct_remote
# ansible_user: ansible
# proxmox:
# hosts:
# proxmox-1:
# ansible_host: 10.0.0.10
#
#
# ---------------------------------------------
# Dynamic inventory file: inventory.proxmox.yml
# ---------------------------------------------
# plugin: community.general.proxmox
# url: https://10.0.0.10:8006
# validate_certs: false
# user: ansible@pam
# token_id: ansible
# token_secret: !vault |
# $ANSIBLE_VAULT;1.1;AES256
# ...
# want_facts: true
# exclude_nodes: true
# filters:
# - proxmox_vmtype == "lxc"
# want_proxmox_nodes_ansible_host: false
# compose:
# ansible_host: "'10.0.0.10'"
# ansible_connection: "'community.general.proxmox_pct_remote'"
# ansible_user: "'ansible'"
#
#
# ----------------------
# Playbook: playbook.yml
# ----------------------
---
- hosts: lxc
# On nodes with many containers you might want to deactivate the devices facts
# or set `gather_facts: false` if you don't need them.
# More info on gathering fact subsets:
# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html
#
# gather_facts: true
# gather_subset:
# - "!devices"
tasks:
- name: Ping LXC container
ansible.builtin.ping:
"""
import os
import pathlib
import socket
import tempfile
import typing as t
from ansible.errors import (
AnsibleAuthenticationFailure,
AnsibleConnectionFailure,
AnsibleError,
)
from ansible_collections.community.general.plugins.module_utils._filelock import FileLock, LockTimeout
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.compat.paramiko import PARAMIKO_IMPORT_ERR, paramiko
from ansible.module_utils.compat.version import LooseVersion
from ansible.plugins.connection import ConnectionBase
from ansible.utils.display import Display
from ansible.utils.path import makedirs_safe
from binascii import hexlify
display = Display()
def authenticity_msg(hostname: str, ktype: str, fingerprint: str) -> str:
msg = f"""
paramiko: The authenticity of host '{hostname}' can't be established.
The {ktype} key fingerprint is {fingerprint}.
Are you sure you want to continue connecting (yes/no)?
"""
return msg
MissingHostKeyPolicy: type = object
if paramiko:
MissingHostKeyPolicy = paramiko.MissingHostKeyPolicy
class MyAddPolicy(MissingHostKeyPolicy):
"""
Based on AutoAddPolicy in paramiko so we can determine when keys are added
and also prompt for input.
Policy for automatically adding the hostname and new host key to the
local L{HostKeys} object, and saving it. This is used by L{SSHClient}.
"""
def __init__(self, connection: Connection) -> None:
self.connection = connection
self._options = connection._options
def missing_host_key(self, client, hostname, key) -> None:
if all((self.connection.get_option('host_key_checking'), not self.connection.get_option('host_key_auto_add'))):
fingerprint = hexlify(key.get_fingerprint())
ktype = key.get_name()
if self.connection.get_option('use_persistent_connections') or self.connection.force_persistence:
# don't print the prompt string since the user cannot respond
# to the question anyway
raise AnsibleError(authenticity_msg(hostname, ktype, fingerprint)[1:92])
inp = to_text(
display.prompt_until(authenticity_msg(hostname, ktype, fingerprint), private=False),
errors='surrogate_or_strict'
)
if inp.lower() not in ['yes', 'y', '']:
raise AnsibleError('host connection rejected by user')
key._added_by_ansible_this_time = True
# existing implementation below:
client._host_keys.add(hostname, key.get_name(), key)
# host keys are actually saved in close() function below
# in order to control ordering.
class Connection(ConnectionBase):
""" SSH based connections (paramiko) to Proxmox pct """
transport = 'community.general.proxmox_pct_remote'
_log_channel: str | None = None
def __init__(self, play_context, new_stdin, *args, **kwargs):
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
def _set_log_channel(self, name: str) -> None:
""" Mimic paramiko.SSHClient.set_log_channel """
self._log_channel = name
def _parse_proxy_command(self, port: int = 22) -> dict[str, t.Any]:
proxy_command = self.get_option('proxy_command') or None
sock_kwarg = {}
if proxy_command:
replacers = {
'%h': self.get_option('remote_addr'),
'%p': port,
'%r': self.get_option('remote_user')
}
for find, replace in replacers.items():
proxy_command = proxy_command.replace(find, str(replace))
try:
sock_kwarg = {'sock': paramiko.ProxyCommand(proxy_command)}
display.vvv(f'CONFIGURE PROXY COMMAND FOR CONNECTION: {proxy_command}', host=self.get_option('remote_addr'))
except AttributeError:
display.warning('Paramiko ProxyCommand support unavailable. '
'Please upgrade to Paramiko 1.9.0 or newer. '
'Not using configured ProxyCommand')
return sock_kwarg
def _connect(self) -> Connection:
""" activates the connection object """
if paramiko is None:
raise AnsibleError(f'paramiko is not installed: {to_native(PARAMIKO_IMPORT_ERR)}')
port = self.get_option('port')
display.vvv(f'ESTABLISH PARAMIKO SSH CONNECTION FOR USER: {self.get_option("remote_user")} on PORT {to_text(port)} TO {self.get_option("remote_addr")}',
host=self.get_option('remote_addr'))
ssh = paramiko.SSHClient()
# Set pubkey and hostkey algorithms to disable, the only manipulation allowed currently
# is keeping or omitting rsa-sha2 algorithms
# default_keys: t.Tuple[str] = ()
paramiko_preferred_pubkeys = getattr(paramiko.Transport, '_preferred_pubkeys', ())
paramiko_preferred_hostkeys = getattr(paramiko.Transport, '_preferred_keys', ())
use_rsa_sha2_algorithms = self.get_option('use_rsa_sha2_algorithms')
disabled_algorithms: t.Dict[str, t.Iterable[str]] = {}
if not use_rsa_sha2_algorithms:
if paramiko_preferred_pubkeys:
disabled_algorithms['pubkeys'] = tuple(a for a in paramiko_preferred_pubkeys if 'rsa-sha2' in a)
if paramiko_preferred_hostkeys:
disabled_algorithms['keys'] = tuple(a for a in paramiko_preferred_hostkeys if 'rsa-sha2' in a)
# override paramiko's default logger name
if self._log_channel is not None:
ssh.set_log_channel(self._log_channel)
self.keyfile = os.path.expanduser('~/.ssh/known_hosts')
if self.get_option('host_key_checking'):
for ssh_known_hosts in ('/etc/ssh/ssh_known_hosts', '/etc/openssh/ssh_known_hosts'):
try:
ssh.load_system_host_keys(ssh_known_hosts)
break
except IOError:
pass # file was not found, but not required to function
except paramiko.hostkeys.InvalidHostKey as e:
raise AnsibleConnectionFailure(f'Invalid host key: {to_text(e.line)}')
try:
ssh.load_system_host_keys()
except paramiko.hostkeys.InvalidHostKey as e:
raise AnsibleConnectionFailure(f'Invalid host key: {to_text(e.line)}')
ssh_connect_kwargs = self._parse_proxy_command(port)
ssh.set_missing_host_key_policy(MyAddPolicy(self))
conn_password = self.get_option('password')
allow_agent = True
if conn_password is not None:
allow_agent = False
try:
key_filename = None
if self.get_option('private_key_file'):
key_filename = os.path.expanduser(self.get_option('private_key_file'))
# paramiko 2.2 introduced auth_timeout parameter
if LooseVersion(paramiko.__version__) >= LooseVersion('2.2.0'):
ssh_connect_kwargs['auth_timeout'] = self.get_option('timeout')
# paramiko 1.15 introduced banner timeout parameter
if LooseVersion(paramiko.__version__) >= LooseVersion('1.15.0'):
ssh_connect_kwargs['banner_timeout'] = self.get_option('banner_timeout')
ssh.connect(
self.get_option('remote_addr').lower(),
username=self.get_option('remote_user'),
allow_agent=allow_agent,
look_for_keys=self.get_option('look_for_keys'),
key_filename=key_filename,
password=conn_password,
timeout=self.get_option('timeout'),
port=port,
disabled_algorithms=disabled_algorithms,
**ssh_connect_kwargs,
)
except paramiko.ssh_exception.BadHostKeyException as e:
raise AnsibleConnectionFailure(f'host key mismatch for {to_text(e.hostname)}')
except paramiko.ssh_exception.AuthenticationException as e:
msg = f'Failed to authenticate: {e}'
raise AnsibleAuthenticationFailure(msg)
except Exception as e:
msg = to_text(e)
if u'PID check failed' in msg:
raise AnsibleError('paramiko version issue, please upgrade paramiko on the machine running ansible')
elif u'Private key file is encrypted' in msg:
msg = f'ssh {self.get_option("remote_user")}@{self.get_options("remote_addr")}:{port} : ' + \
f'{msg}\nTo connect as a different user, use -u <username>.'
raise AnsibleConnectionFailure(msg)
else:
raise AnsibleConnectionFailure(msg)
self.ssh = ssh
self._connected = True
return self
def _any_keys_added(self) -> bool:
for hostname, keys in self.ssh._host_keys.items():
for keytype, key in keys.items():
added_this_time = getattr(key, '_added_by_ansible_this_time', False)
if added_this_time:
return True
return False
def _save_ssh_host_keys(self, filename: str) -> None:
"""
not using the paramiko save_ssh_host_keys function as we want to add new SSH keys at the bottom so folks
don't complain about it :)
"""
if not self._any_keys_added():
return
path = os.path.expanduser('~/.ssh')
makedirs_safe(path)
with open(filename, 'w') as f:
for hostname, keys in self.ssh._host_keys.items():
for keytype, key in keys.items():
# was f.write
added_this_time = getattr(key, '_added_by_ansible_this_time', False)
if not added_this_time:
f.write(f'{hostname} {keytype} {key.get_base64()}\n')
for hostname, keys in self.ssh._host_keys.items():
for keytype, key in keys.items():
added_this_time = getattr(key, '_added_by_ansible_this_time', False)
if added_this_time:
f.write(f'{hostname} {keytype} {key.get_base64()}\n')
def _build_pct_command(self, cmd: str) -> str:
cmd = ['/usr/sbin/pct', 'exec', str(self.get_option('vmid')), '--', cmd]
if self.get_option('remote_user') != 'root':
cmd = [self.get_option('proxmox_become_method')] + cmd
display.vvv(f'INFO Running as non root user: {self.get_option("remote_user")}, trying to run pct with become method: ' +
f'{self.get_option("proxmox_become_method")}',
host=self.get_option('remote_addr'))
return ' '.join(cmd)
def exec_command(self, cmd: str, in_data: bytes | None = None, sudoable: bool = True) -> tuple[int, bytes, bytes]:
""" run a command on inside the LXC container """
cmd = self._build_pct_command(cmd)
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
bufsize = 4096
try:
self.ssh.get_transport().set_keepalive(5)
chan = self.ssh.get_transport().open_session()
except Exception as e:
text_e = to_text(e)
msg = 'Failed to open session'
if text_e:
msg += f': {text_e}'
raise AnsibleConnectionFailure(to_native(msg))
# sudo usually requires a PTY (cf. requiretty option), therefore
# we give it one by default (pty=True in ansible.cfg), and we try
# to initialise from the calling environment when sudoable is enabled
if self.get_option('pty') and sudoable:
chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0)))
display.vvv(f'EXEC {cmd}', host=self.get_option('remote_addr'))
cmd = to_bytes(cmd, errors='surrogate_or_strict')
no_prompt_out = b''
no_prompt_err = b''
become_output = b''
try:
chan.exec_command(cmd)
if self.become and self.become.expect_prompt():
password_prompt = False
become_success = False
while not (become_success or password_prompt):
display.debug('Waiting for Privilege Escalation input')
chunk = chan.recv(bufsize)
display.debug(f'chunk is: {to_text(chunk)}')
if not chunk:
if b'unknown user' in become_output:
n_become_user = to_native(self.become.get_option('become_user'))
raise AnsibleError(f'user {n_become_user} does not exist')
else:
break
# raise AnsibleError('ssh connection closed waiting for password prompt')
become_output += chunk
# need to check every line because we might get lectured
# and we might get the middle of a line in a chunk
for line in become_output.splitlines(True):
if self.become.check_success(line):
become_success = True
break
elif self.become.check_password_prompt(line):
password_prompt = True
break
if password_prompt:
if self.become:
become_pass = self.become.get_option('become_pass')
chan.sendall(to_bytes(become_pass, errors='surrogate_or_strict') + b'\n')
else:
raise AnsibleError('A password is required but none was supplied')
else:
no_prompt_out += become_output
no_prompt_err += become_output
if in_data:
for i in range(0, len(in_data), bufsize):
chan.send(in_data[i:i + bufsize])
chan.shutdown_write()
elif in_data == b'':
chan.shutdown_write()
except socket.timeout:
raise AnsibleError('ssh timed out waiting for privilege escalation.\n' + to_text(become_output))
stdout = b''.join(chan.makefile('rb', bufsize))
stderr = b''.join(chan.makefile_stderr('rb', bufsize))
returncode = chan.recv_exit_status()
if 'pct: not found' in stderr.decode('utf-8'):
raise AnsibleError(
f'pct not found in path of host: {to_text(self.get_option("remote_addr"))}')
return (returncode, no_prompt_out + stdout, no_prompt_out + stderr)
def put_file(self, in_path: str, out_path: str) -> None:
""" transfer a file from local to remote """
display.vvv(f'PUT {in_path} TO {out_path}', host=self.get_option('remote_addr'))
try:
with open(in_path, 'rb') as f:
data = f.read()
returncode, stdout, stderr = self.exec_command(
' '.join([
self._shell.executable, '-c',
self._shell.quote(f'cat > {out_path}')]),
in_data=data,
sudoable=False)
if returncode != 0:
if 'cat: not found' in stderr.decode('utf-8'):
raise AnsibleError(
f'cat not found in path of container: {to_text(self.get_option("vmid"))}')
raise AnsibleError(
f'{to_text(stdout)}\n{to_text(stderr)}')
except Exception as e:
raise AnsibleError(
f'error occurred while putting file from {in_path} to {out_path}!\n{to_text(e)}')
def fetch_file(self, in_path: str, out_path: str) -> None:
""" save a remote file to the specified path """
display.vvv(f'FETCH {in_path} TO {out_path}', host=self.get_option('remote_addr'))
try:
returncode, stdout, stderr = self.exec_command(
' '.join([
self._shell.executable, '-c',
self._shell.quote(f'cat {in_path}')]),
sudoable=False)
if returncode != 0:
if 'cat: not found' in stderr.decode('utf-8'):
raise AnsibleError(
f'cat not found in path of container: {to_text(self.get_option("vmid"))}')
raise AnsibleError(
f'{to_text(stdout)}\n{to_text(stderr)}')
with open(out_path, 'wb') as f:
f.write(stdout)
except Exception as e:
raise AnsibleError(
f'error occurred while fetching file from {in_path} to {out_path}!\n{to_text(e)}')
def reset(self) -> None:
""" reset the connection """
if not self._connected:
return
self.close()
self._connect()
def close(self) -> None:
""" terminate the connection """
if self.get_option('host_key_checking') and self.get_option('record_host_keys') and self._any_keys_added():
# add any new SSH host keys -- warning -- this could be slow
# (This doesn't acquire the connection lock because it needs
# to exclude only other known_hosts writers, not connections
# that are starting up.)
lockfile = os.path.basename(self.keyfile)
dirname = os.path.dirname(self.keyfile)
makedirs_safe(dirname)
tmp_keyfile_name = None
try:
with FileLock().lock_file(lockfile, dirname, self.get_option('lock_file_timeout')):
# just in case any were added recently
self.ssh.load_system_host_keys()
self.ssh._host_keys.update(self.ssh._system_host_keys)
# gather information about the current key file, so
# we can ensure the new file has the correct mode/owner
key_dir = os.path.dirname(self.keyfile)
if os.path.exists(self.keyfile):
key_stat = os.stat(self.keyfile)
mode = key_stat.st_mode & 0o777
uid = key_stat.st_uid
gid = key_stat.st_gid
else:
mode = 0o644
uid = os.getuid()
gid = os.getgid()
# Save the new keys to a temporary file and move it into place
# rather than rewriting the file. We set delete=False because
# the file will be moved into place rather than cleaned up.
with tempfile.NamedTemporaryFile(dir=key_dir, delete=False) as tmp_keyfile:
tmp_keyfile_name = tmp_keyfile.name
os.chmod(tmp_keyfile_name, mode)
os.chown(tmp_keyfile_name, uid, gid)
self._save_ssh_host_keys(tmp_keyfile_name)
os.rename(tmp_keyfile_name, self.keyfile)
except LockTimeout:
raise AnsibleError(
f'writing lock file for {self.keyfile} ran in to the timeout of {self.get_option("lock_file_timeout")}s')
except paramiko.hostkeys.InvalidHostKey as e:
raise AnsibleConnectionFailure(f'Invalid host key: {e.line}')
except Exception as e:
# unable to save keys, including scenario when key was invalid
# and caught earlier
raise AnsibleError(
f'error occurred while writing SSH host keys!\n{to_text(e)}')
finally:
if tmp_keyfile_name is not None:
pathlib.Path(tmp_keyfile_name).unlink(missing_ok=True)
self.ssh.close()
self._connected = False

View File

@@ -8,36 +8,36 @@
#
# Written by: Kushal Das (https://github.com/kushaldas)
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
name: qubes
short_description: Interact with an existing QubesOS AppVM
DOCUMENTATION = '''
name: qubes
short_description: Interact with an existing QubesOS AppVM
description:
- Run commands or put/fetch files to an existing Qubes AppVM using qubes tools.
author: Kushal Das (@kushaldas)
options:
remote_addr:
description:
- VM name.
type: string
default: inventory_hostname
vars:
- name: ansible_host
remote_user:
description:
- The user to execute as inside the VM.
type: string
default: The I(user) account as default in Qubes OS.
vars:
- name: ansible_user
- Run commands or put/fetch files to an existing Qubes AppVM using qubes tools.
author: Kushal Das (@kushaldas)
options:
remote_addr:
description:
- vm name
default: inventory_hostname
vars:
- name: ansible_host
remote_user:
description:
- The user to execute as inside the vm.
default: The *user* account as default in Qubes OS.
vars:
- name: ansible_user
# keyword:
# - name: hosts
"""
'''
import subprocess
@@ -76,7 +76,7 @@ class Connection(ConnectionBase):
"""
display.vvvv("CMD: ", cmd)
if not cmd.endswith("\n"):
cmd = f"{cmd}\n"
cmd = cmd + "\n"
local_cmd = []
# For dom0
@@ -93,7 +93,7 @@ class Connection(ConnectionBase):
display.vvvv("Local cmd: ", local_cmd)
display.vvv(f"RUN {local_cmd}", host=self._remote_vmname)
display.vvv("RUN %s" % (local_cmd,), host=self._remote_vmname)
p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -112,42 +112,42 @@ class Connection(ConnectionBase):
"""Run specified command in a running QubesVM """
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
display.vvvv(f"CMD IS: {cmd}")
display.vvvv("CMD IS: %s" % cmd)
rc, stdout, stderr = self._qubes(cmd)
display.vvvvv(f"STDOUT {stdout!r} STDERR {stderr!r}")
display.vvvvv("STDOUT %r STDERR %r" % (stderr, stderr))
return rc, stdout, stderr
def put_file(self, in_path, out_path):
""" Place a local file located in 'in_path' inside VM at 'out_path' """
super(Connection, self).put_file(in_path, out_path)
display.vvv(f"PUT {in_path} TO {out_path}", host=self._remote_vmname)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._remote_vmname)
with open(in_path, "rb") as fobj:
source_data = fobj.read()
retcode, dummy, dummy = self._qubes(f'cat > "{out_path}\"\n', source_data, "qubes.VMRootShell")
retcode, dummy, dummy = self._qubes('cat > "{0}"\n'.format(out_path), source_data, "qubes.VMRootShell")
# if qubes.VMRootShell service not supported, fallback to qubes.VMShell and
# hope it will have appropriate permissions
if retcode == 127:
retcode, dummy, dummy = self._qubes(f'cat > "{out_path}\"\n', source_data)
retcode, dummy, dummy = self._qubes('cat > "{0}"\n'.format(out_path), source_data)
if retcode != 0:
raise AnsibleConnectionFailure(f'Failed to put_file to {out_path}')
raise AnsibleConnectionFailure('Failed to put_file to {0}'.format(out_path))
def fetch_file(self, in_path, out_path):
"""Obtain file specified via 'in_path' from the container and place it at 'out_path' """
super(Connection, self).fetch_file(in_path, out_path)
display.vvv(f"FETCH {in_path} TO {out_path}", host=self._remote_vmname)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._remote_vmname)
# We are running in dom0
cmd_args_list = ["qvm-run", "--pass-io", self._remote_vmname, f"cat {in_path}"]
cmd_args_list = ["qvm-run", "--pass-io", self._remote_vmname, "cat {0}".format(in_path)]
with open(out_path, "wb") as fobj:
p = subprocess.Popen(cmd_args_list, shell=False, stdout=fobj)
p.communicate()
if p.returncode != 0:
raise AnsibleConnectionFailure(f'Failed to fetch file to {out_path}')
raise AnsibleConnectionFailure('Failed to fetch file to {0}'.format(out_path))
def close(self):
""" Closing the connection """

View File

@@ -7,15 +7,16 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Michael Scherer (@mscherer) <misc@zarb.org>
name: saltstack
short_description: Allow ansible to piggyback on salt minions
description:
- This allows you to use existing Saltstack infrastructure to connect to targets.
"""
DOCUMENTATION = '''
author: Michael Scherer (@mscherer) <misc@zarb.org>
name: saltstack
short_description: Allow ansible to piggyback on salt minions
description:
- This allows you to use existing Saltstack infrastructure to connect to targets.
'''
import os
import base64
@@ -58,11 +59,11 @@ class Connection(ConnectionBase):
if in_data:
raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining")
self._display.vvv(f"EXEC {cmd}", host=self.host)
self._display.vvv("EXEC %s" % cmd, host=self.host)
# need to add 'true;' to work around https://github.com/saltstack/salt/issues/28077
res = self.client.cmd(self.host, 'cmd.exec_code_all', ['bash', f"true;{cmd}"])
res = self.client.cmd(self.host, 'cmd.exec_code_all', ['bash', 'true;' + cmd])
if self.host not in res:
raise errors.AnsibleError(f"Minion {self.host} didn't answer, check if salt-minion is running and the name is correct")
raise errors.AnsibleError("Minion %s didn't answer, check if salt-minion is running and the name is correct" % self.host)
p = res[self.host]
return p['retcode'], p['stdout'], p['stderr']
@@ -80,7 +81,7 @@ class Connection(ConnectionBase):
super(Connection, self).put_file(in_path, out_path)
out_path = self._normalize_path(out_path, '/')
self._display.vvv(f"PUT {in_path} TO {out_path}", host=self.host)
self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.host)
with open(in_path, 'rb') as in_fh:
content = in_fh.read()
self.client.cmd(self.host, 'hashutil.base64_decodefile', [base64.b64encode(content), out_path])
@@ -92,7 +93,7 @@ class Connection(ConnectionBase):
super(Connection, self).fetch_file(in_path, out_path)
in_path = self._normalize_path(in_path, '/')
self._display.vvv(f"FETCH {in_path} TO {out_path}", host=self.host)
self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host)
content = self.client.cmd(self.host, 'cp.get_file_str', [in_path])[self.host]
open(out_path, 'wb').write(content)

View File

@@ -8,24 +8,24 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r"""
author: Ansible Core Team
name: zone
short_description: Run tasks in a zone instance
description:
- Run commands or put/fetch files to an existing zone.
options:
remote_addr:
DOCUMENTATION = '''
author: Ansible Core Team
name: zone
short_description: Run tasks in a zone instance
description:
- Zone identifier.
type: string
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_zone_host
"""
- Run commands or put/fetch files to an existing zone
options:
remote_addr:
description:
- Zone identifier
default: inventory_hostname
vars:
- name: ansible_host
- name: ansible_zone_host
'''
import os
import os.path
@@ -61,14 +61,14 @@ class Connection(ConnectionBase):
self.zlogin_cmd = to_bytes(self._search_executable('zlogin'))
if self.zone not in self.list_zones():
raise AnsibleError(f"incorrect zone name {self.zone}")
raise AnsibleError("incorrect zone name %s" % self.zone)
@staticmethod
def _search_executable(executable):
try:
return get_bin_path(executable)
except ValueError:
raise AnsibleError(f"{executable} command not found in PATH")
raise AnsibleError("%s command not found in PATH" % executable)
def list_zones(self):
process = subprocess.Popen([self.zoneadm_cmd, 'list', '-ip'],
@@ -93,7 +93,7 @@ class Connection(ConnectionBase):
# stdout, stderr = p.communicate()
path = process.stdout.readlines()[0].split(':')[3]
return f"{path}/root"
return path + '/root'
def _connect(self):
""" connect to the zone; nothing to do here """
@@ -116,7 +116,7 @@ class Connection(ConnectionBase):
local_cmd = [self.zlogin_cmd, self.zone, cmd]
local_cmd = map(to_bytes, local_cmd)
display.vvv(f"EXEC {local_cmd}", host=self.zone)
display.vvv("EXEC %s" % (local_cmd), host=self.zone)
p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -139,7 +139,7 @@ class Connection(ConnectionBase):
exist in any given chroot. So for now we're choosing "/" instead.
This also happens to be the former default.
Can revisit using $HOME instead if it is a problem
Can revisit using $HOME instead if it's a problem
"""
if not remote_path.startswith(os.path.sep):
remote_path = os.path.join(os.path.sep, remote_path)
@@ -148,7 +148,7 @@ class Connection(ConnectionBase):
def put_file(self, in_path, out_path):
""" transfer a file from local to zone """
super(Connection, self).put_file(in_path, out_path)
display.vvv(f"PUT {in_path} TO {out_path}", host=self.zone)
display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.zone)
out_path = shlex_quote(self._prefix_login_path(out_path))
try:
@@ -158,27 +158,27 @@ class Connection(ConnectionBase):
else:
count = ''
try:
p = self._buffered_exec_command(f'dd of={out_path} bs={BUFSIZE}{count}', stdin=in_file)
p = self._buffered_exec_command('dd of=%s bs=%s%s' % (out_path, BUFSIZE, count), stdin=in_file)
except OSError:
raise AnsibleError("jail connection requires dd command in the jail")
try:
stdout, stderr = p.communicate()
except Exception:
traceback.print_exc()
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}")
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
if p.returncode != 0:
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}:\n{stdout}\n{stderr}")
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
except IOError:
raise AnsibleError(f"file or module does not exist at: {in_path}")
raise AnsibleError("file or module does not exist at: %s" % in_path)
def fetch_file(self, in_path, out_path):
""" fetch a file from zone to local """
super(Connection, self).fetch_file(in_path, out_path)
display.vvv(f"FETCH {in_path} TO {out_path}", host=self.zone)
display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.zone)
in_path = shlex_quote(self._prefix_login_path(in_path))
try:
p = self._buffered_exec_command(f'dd if={in_path} bs={BUFSIZE}')
p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
except OSError:
raise AnsibleError("zone connection requires dd command in the zone")
@@ -190,10 +190,10 @@ class Connection(ConnectionBase):
chunk = p.stdout.read(BUFSIZE)
except Exception:
traceback.print_exc()
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}")
raise AnsibleError("failed to transfer file %s to %s" % (in_path, out_path))
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleError(f"failed to transfer file {in_path} to {out_path}:\n{stdout}\n{stderr}")
raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
def close(self):
""" terminate the connection; nothing to do here """

View File

@@ -11,73 +11,75 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Alicloud only documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
alicloud_access_key:
description:
- Alibaba Cloud access key. If not set then the value of environment variable E(ALICLOUD_ACCESS_KEY), E(ALICLOUD_ACCESS_KEY_ID)
will be used instead.
- Alibaba Cloud access key. If not set then the value of environment variable E(ALICLOUD_ACCESS_KEY),
E(ALICLOUD_ACCESS_KEY_ID) will be used instead.
aliases: ['access_key_id', 'access_key']
type: str
alicloud_secret_key:
description:
- Alibaba Cloud secret key. If not set then the value of environment variable E(ALICLOUD_SECRET_KEY), E(ALICLOUD_SECRET_ACCESS_KEY)
will be used instead.
- Alibaba Cloud secret key. If not set then the value of environment variable E(ALICLOUD_SECRET_KEY),
E(ALICLOUD_SECRET_ACCESS_KEY) will be used instead.
aliases: ['secret_access_key', 'secret_key']
type: str
alicloud_region:
description:
- The Alibaba Cloud region to use. If not specified then the value of environment variable E(ALICLOUD_REGION), E(ALICLOUD_REGION_ID)
will be used instead.
- The Alibaba Cloud region to use. If not specified then the value of environment variable
E(ALICLOUD_REGION), E(ALICLOUD_REGION_ID) will be used instead.
aliases: ['region', 'region_id']
required: true
type: str
alicloud_security_token:
description:
- The Alibaba Cloud security token. If not specified then the value of environment variable E(ALICLOUD_SECURITY_TOKEN)
will be used instead.
- The Alibaba Cloud security token. If not specified then the value of environment variable
E(ALICLOUD_SECURITY_TOKEN) will be used instead.
aliases: ['security_token']
type: str
alicloud_assume_role:
description:
- If provided with a role ARN, Ansible will attempt to assume this role using the supplied credentials.
- The nested assume_role block supports C(alicloud_assume_role_arn), C(alicloud_assume_role_session_name), C(alicloud_assume_role_session_expiration)
and C(alicloud_assume_role_policy).
- The nested assume_role block supports C(alicloud_assume_role_arn), C(alicloud_assume_role_session_name),
C(alicloud_assume_role_session_expiration) and C(alicloud_assume_role_policy).
type: dict
aliases: ['assume_role']
alicloud_assume_role_arn:
description:
- The Alibaba Cloud C(role_arn). The ARN of the role to assume. If ARN is set to an empty string, it does not perform
role switching. It supports environment variable E(ALICLOUD_ASSUME_ROLE_ARN). ansible will execute with provided credentials.
- The Alibaba Cloud C(role_arn). The ARN of the role to assume. If ARN is set to an empty string,
it does not perform role switching. It supports environment variable E(ALICLOUD_ASSUME_ROLE_ARN).
ansible will execute with provided credentials.
aliases: ['assume_role_arn']
type: str
alicloud_assume_role_session_name:
description:
- The Alibaba Cloud session_name. The session name to use when assuming the role. If omitted, 'ansible' is passed to
the AssumeRole call as session name. It supports environment variable E(ALICLOUD_ASSUME_ROLE_SESSION_NAME).
- The Alibaba Cloud session_name. The session name to use when assuming the role. If omitted,
'ansible' is passed to the AssumeRole call as session name. It supports environment variable
E(ALICLOUD_ASSUME_ROLE_SESSION_NAME).
aliases: ['assume_role_session_name']
type: str
alicloud_assume_role_session_expiration:
description:
- The Alibaba Cloud C(session_expiration). The time after which the established session for assuming role expires. Valid
value range 900-3600 seconds. Default to 3600 (in this case Alicloud use own default value). It supports environment
variable E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION).
- The Alibaba Cloud C(session_expiration). The time after which the established session for assuming
role expires. Valid value range 900-3600 seconds. Default to 3600 (in this case Alicloud use own default
value). It supports environment variable E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION).
aliases: ['assume_role_session_expiration']
type: int
ecs_role_name:
description:
- The RAM Role Name attached on a ECS instance for API operations. You can retrieve this from the 'Access Control' section
of the Alibaba Cloud console.
- If you are running Ansible from an ECS instance with RAM Instance using RAM Role, Ansible will just access the metadata
U(http://100.100.100.200/latest/meta-data/ram/security-credentials/<ecs_role_name>) to obtain the STS credential.
This is a preferred approach over any other when running in ECS as you can avoid hard coding credentials. Instead
these are leased on-the-fly by Ansible which reduces the chance of leakage.
- The RAM Role Name attached on a ECS instance for API operations. You can retrieve this from the 'Access Control'
section of the Alibaba Cloud console.
- If you're running Ansible from an ECS instance with RAM Instance using RAM Role, Ansible will just access the
metadata U(http://100.100.100.200/latest/meta-data/ram/security-credentials/<ecs_role_name>) to obtain the STS
credential. This is a preferred approach over any other when running in ECS as you can avoid hard coding
credentials. Instead these are leased on-the-fly by Ansible which reduces the chance of leakage.
aliases: ['role_name']
type: str
profile:
description:
- This is the Alicloud profile name as set in the shared credentials file. It can also be sourced from the E(ALICLOUD_PROFILE)
environment variable.
- This is the Alicloud profile name as set in the shared credentials file. It can also be sourced from the
E(ALICLOUD_PROFILE) environment variable.
type: str
shared_credentials_file:
description:
@@ -86,14 +88,22 @@ options:
- If this is not set and a profile is specified, C(~/.aliyun/config.json) will be used.
type: str
author:
- "He Guimin (@xiaozhu36)"
- "He Guimin (@xiaozhu36)"
requirements:
- "Python >= 3.6"
- "Python >= 3.6"
notes:
- If parameters are not set within the module, the following environment variables can be used in decreasing order of precedence
E(ALICLOUD_ACCESS_KEY) or E(ALICLOUD_ACCESS_KEY_ID), E(ALICLOUD_SECRET_KEY) or E(ALICLOUD_SECRET_ACCESS_KEY), E(ALICLOUD_REGION)
or E(ALICLOUD_REGION_ID), E(ALICLOUD_SECURITY_TOKEN), E(ALICLOUD_ECS_ROLE_NAME), E(ALICLOUD_SHARED_CREDENTIALS_FILE),
E(ALICLOUD_PROFILE), E(ALICLOUD_ASSUME_ROLE_ARN), E(ALICLOUD_ASSUME_ROLE_SESSION_NAME), E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION).
- E(ALICLOUD_REGION) or E(ALICLOUD_REGION_ID) can be typically be used to specify the Alicloud region, when required, but
this can also be configured in the footmark config file.
"""
- If parameters are not set within the module, the following
environment variables can be used in decreasing order of precedence
E(ALICLOUD_ACCESS_KEY) or E(ALICLOUD_ACCESS_KEY_ID),
E(ALICLOUD_SECRET_KEY) or E(ALICLOUD_SECRET_ACCESS_KEY),
E(ALICLOUD_REGION) or E(ALICLOUD_REGION_ID),
E(ALICLOUD_SECURITY_TOKEN),
E(ALICLOUD_ECS_ROLE_NAME),
E(ALICLOUD_SHARED_CREDENTIALS_FILE),
E(ALICLOUD_PROFILE),
E(ALICLOUD_ASSUME_ROLE_ARN),
E(ALICLOUD_ASSUME_ROLE_SESSION_NAME),
E(ALICLOUD_ASSUME_ROLE_SESSION_EXPIRATION).
- E(ALICLOUD_REGION) or E(ALICLOUD_REGION_ID) can be typically be used to specify the
Alicloud region, when required, but this can also be configured in the footmark config file
'''

View File

@@ -11,22 +11,22 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options: {}
attributes:
check_mode:
description: Can run in C(check_mode) and return changed status prediction without modifying target.
diff_mode:
description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode.
"""
check_mode:
description: Can run in C(check_mode) and return changed status prediction without modifying target.
diff_mode:
description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode.
'''
PLATFORM = r"""
PLATFORM = r'''
options: {}
attributes:
platform:
description: Target OS/families that can be operated against.
support: N/A
"""
platform:
description: Target OS/families that can be operated against.
support: N/A
'''
# Should be used together with the standard fragment
INFO_MODULE = r'''
@@ -42,23 +42,23 @@ attributes:
- This action does not modify state.
'''
CONN = r"""
CONN = r'''
options: {}
attributes:
become:
description: Is usable alongside C(become) keywords.
connection:
description: Uses the target's configured connection information to execute code on it.
delegation:
description: Can be used in conjunction with C(delegate_to) and related keywords.
"""
become:
description: Is usable alongside C(become) keywords.
connection:
description: Uses the target's configured connection information to execute code on it.
delegation:
description: Can be used in conjunction with C(delegate_to) and related keywords.
'''
FACTS = r"""
FACTS = r'''
options: {}
attributes:
facts:
description: Action returns an C(ansible_facts) dictionary that will update existing host facts.
"""
facts:
description: Action returns an C(ansible_facts) dictionary that will update existing host facts.
'''
# Should be used together with the standard fragment and the FACTS fragment
FACTS_MODULE = r'''
@@ -76,18 +76,18 @@ attributes:
support: full
'''
FILES = r"""
FILES = r'''
options: {}
attributes:
safe_file_operations:
description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption.
"""
safe_file_operations:
description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption.
'''
FLOW = r"""
FLOW = r'''
options: {}
attributes:
action:
description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller.
async:
description: Supports being used with the C(async) keyword.
"""
action:
description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller.
async:
description: Supports being used with the C(async) keyword.
'''

View File

@@ -10,7 +10,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
api_url:
description:
@@ -29,4 +29,4 @@ options:
- Whether or not to validate SSL certs when supplying a HTTPS endpoint.
type: bool
default: true
"""
'''

View File

@@ -11,7 +11,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
client_id:
description:
@@ -30,7 +30,7 @@ options:
- O(ignore:username) is an alias of O(user) since community.general 6.0.0. It was an alias of O(workspace) before.
type: str
version_added: 4.0.0
aliases: [username]
aliases: [ username ]
password:
description:
- The App password.
@@ -41,4 +41,4 @@ notes:
- Bitbucket OAuth consumer key and secret can be obtained from Bitbucket profile -> Settings -> Access Management -> OAuth.
- Bitbucket App password can be created from Bitbucket profile -> Personal Settings -> App passwords.
- If both OAuth and Basic Auth credentials are passed, OAuth credentials take precedence.
"""
'''

View File

@@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2024, Alexei Znamensky <russoz@gmail.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r"""
options: {}
requirements:
- requests >= 2.5.0
- clc-sdk
notes:
- To use this module, it is required to set the below environment variables which enables access to the Centurylink Cloud.
- E(CLC_V2_API_USERNAME), the account login ID for the Centurylink Cloud.
- E(CLC_V2_API_PASSWORD), the account password for the Centurylink Cloud.
- Alternatively, the module accepts the API token and account alias. The API token can be generated using the CLC account
login and password using the HTTP API call @ U(https://api.ctl.io/v2/authentication/login).
- E(CLC_V2_API_TOKEN), the API token generated from U(https://api.ctl.io/v2/authentication/login).
- E(CLC_ACCT_ALIAS), the account alias associated with the Centurylink Cloud.
- Users can set E(CLC_V2_API_URL) to specify an endpoint for pointing to a different CLC environment.
"""

View File

@@ -15,7 +15,7 @@ class ModuleDocFragment:
options:
host:
description:
- Host of the Consul agent.
- Host of the consul agent, defaults to V(localhost).
default: localhost
type: str
port:
@@ -25,18 +25,18 @@ options:
default: 8500
scheme:
description:
- The protocol scheme on which the Consul agent is running. Defaults to V(http) and can be set to V(https) for secure
connections.
- The protocol scheme on which the consul agent is running.
Defaults to V(http) and can be set to V(https) for secure connections.
default: http
type: str
validate_certs:
type: bool
description:
- Whether to verify the TLS certificate of the Consul agent.
- Whether to verify the TLS certificate of the consul agent.
default: true
ca_path:
description:
- The CA bundle to use for https connections.
- The CA bundle to use for https connections
type: str
"""

View File

@@ -14,7 +14,8 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Dimension Data doc fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
region:
description:
@@ -47,4 +48,4 @@ options:
- This should only be used on private instances of the CloudControl API that use self-signed certificates.
type: bool
default: true
"""
'''

View File

@@ -14,7 +14,8 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Dimension Data ("wait-for-completion" parameters) doc fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
wait:
description:
@@ -33,4 +34,4 @@ options:
- Only applicable if O(wait=true).
type: int
default: 2
"""
'''

View File

@@ -8,7 +8,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
venv:
description:
@@ -43,19 +43,20 @@ options:
notes:
- The C(django-admin) command is always executed using the C(C) locale, and the option C(--no-color) is always passed.
seealso:
- name: django-admin and manage.py in official Django documentation
description: >-
Refer to this documentation for the builtin commands and options of C(django-admin). Please make sure that you select
the right version of Django in the version selector on that page.
Refer to this documentation for the builtin commands and options of C(django-admin).
Please make sure that you select the right version of Django in the version selector on that page.
link: https://docs.djangoproject.com/en/5.0/ref/django-admin/
"""
'''
DATABASE = r"""
DATABASE = r'''
options:
database:
description:
- Specify the database to be used.
type: str
default: default
"""
'''

View File

@@ -10,6 +10,15 @@ __metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
options:
- See respective platform section for more details
requirements:
- See respective platform section for more details
notes:
- Ansible modules are available for EMC VNX.
'''
# Documentation fragment for VNX (emc_vnx)
EMC_VNX = r'''
options:

View File

@@ -10,7 +10,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
requirements:
- requests (Python library U(https://pypi.org/project/requests/))
@@ -34,4 +34,4 @@ options:
- The CA certificates bundle to use to verify GitLab server certificate.
type: str
version_added: 8.1.0
"""
'''

View File

@@ -10,26 +10,26 @@ __metaclass__ = type
class ModuleDocFragment(object):
# HPE 3PAR doc fragment
DOCUMENTATION = r"""
DOCUMENTATION = '''
options:
storage_system_ip:
description:
- The storage system IP address.
type: str
required: true
storage_system_password:
description:
- The storage system password.
type: str
required: true
storage_system_username:
description:
- The storage system user name.
type: str
required: true
storage_system_ip:
description:
- The storage system IP address.
type: str
required: true
storage_system_password:
description:
- The storage system password.
type: str
required: true
storage_system_username:
description:
- The storage system user name.
type: str
required: true
requirements:
- hpe3par_sdk >= 1.0.2. Install using C(pip install hpe3par_sdk).
- WSAPI service should be enabled on the 3PAR storage array.
notes:
"""
'''

View File

@@ -10,50 +10,56 @@ __metaclass__ = type
class ModuleDocFragment(object):
# HWC doc fragment.
DOCUMENTATION = r"""
DOCUMENTATION = '''
options:
identity_endpoint:
description:
- The Identity authentication URL.
type: str
required: true
user:
description:
- The user name to login with.
- Currently only user names are supported, and not user IDs.
type: str
required: true
password:
description:
- The password to login with.
type: str
required: true
domain:
description:
- The name of the Domain to scope to (Identity v3).
- Currently only domain names are supported, and not domain IDs.
type: str
required: true
project:
description:
- The name of the Tenant (Identity v2) or Project (Identity v3).
- Currently only project names are supported, and not project IDs.
type: str
required: true
region:
description:
- The region to which the project belongs.
type: str
id:
description:
- The ID of resource to be managed.
type: str
identity_endpoint:
description:
- The Identity authentication URL.
type: str
required: true
user:
description:
- The user name to login with.
- Currently only user names are supported, and not user IDs.
type: str
required: true
password:
description:
- The password to login with.
type: str
required: true
domain:
description:
- The name of the Domain to scope to (Identity v3).
- Currently only domain names are supported, and not domain IDs.
type: str
required: true
project:
description:
- The name of the Tenant (Identity v2) or Project (Identity v3).
- Currently only project names are supported, and not project IDs.
type: str
required: true
region:
description:
- The region to which the project belongs.
type: str
id:
description:
- The ID of resource to be managed.
type: str
notes:
- For authentication, you can set identity_endpoint using the E(ANSIBLE_HWC_IDENTITY_ENDPOINT) environment variable.
- For authentication, you can set user using the E(ANSIBLE_HWC_USER) environment variable.
- For authentication, you can set password using the E(ANSIBLE_HWC_PASSWORD) environment variable.
- For authentication, you can set domain using the E(ANSIBLE_HWC_DOMAIN) environment variable.
- For authentication, you can set project using the E(ANSIBLE_HWC_PROJECT) environment variable.
- For authentication, you can set identity_endpoint using the
E(ANSIBLE_HWC_IDENTITY_ENDPOINT) environment variable.
- For authentication, you can set user using the
E(ANSIBLE_HWC_USER) environment variable.
- For authentication, you can set password using the E(ANSIBLE_HWC_PASSWORD) environment
variable.
- For authentication, you can set domain using the E(ANSIBLE_HWC_DOMAIN) environment
variable.
- For authentication, you can set project using the E(ANSIBLE_HWC_PROJECT) environment
variable.
- For authentication, you can set region using the E(ANSIBLE_HWC_REGION) environment variable.
- Environment variables values will only be used if the playbook values are not set.
"""
- Environment variables values will only be used if the playbook values are
not set.
'''

View File

@@ -12,25 +12,26 @@ __metaclass__ = type
class ModuleDocFragment(object):
# ibm_storage documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
username:
description:
- Management user on the Spectrum Accelerate storage system.
type: str
required: true
password:
description:
- Password for username on the Spectrum Accelerate storage system.
type: str
required: true
endpoints:
description:
- The hostname or management IP of Spectrum Accelerate storage system.
type: str
required: true
username:
description:
- Management user on the spectrum accelerate storage system.
type: str
required: true
password:
description:
- Password for username on the spectrum accelerate storage system.
type: str
required: true
endpoints:
description:
- The hostname or management IP of Spectrum Accelerate storage system.
type: str
required: true
notes:
- This module requires pyxcli python library. Use C(pip install pyxcli) in order to get pyxcli.
- This module requires pyxcli python library.
Use C(pip install pyxcli) in order to get pyxcli.
requirements:
- pyxcli
"""
'''

View File

@@ -11,72 +11,72 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Parameters for influxdb modules
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
hostname:
description:
- The hostname or IP address on which InfluxDB server is listening.
- The hostname or IP address on which InfluxDB server is listening.
type: str
default: localhost
username:
description:
- Username that will be used to authenticate against InfluxDB server.
- Username that will be used to authenticate against InfluxDB server.
type: str
default: root
aliases: [login_username]
aliases: [ login_username ]
password:
description:
- Password that will be used to authenticate against InfluxDB server.
- Password that will be used to authenticate against InfluxDB server.
type: str
default: root
aliases: [login_password]
aliases: [ login_password ]
port:
description:
- The port on which InfluxDB server is listening.
- The port on which InfluxDB server is listening.
type: int
default: 8086
path:
description:
- The path on which InfluxDB server is accessible.
- Only available when using python-influxdb >= 5.1.0.
- The path on which InfluxDB server is accessible.
- Only available when using python-influxdb >= 5.1.0.
type: str
default: ''
version_added: '0.2.0'
validate_certs:
description:
- If set to V(false), the SSL certificates will not be validated.
- This should only set to V(false) used on personally controlled sites using self-signed certificates.
- If set to V(false), the SSL certificates will not be validated.
- This should only set to V(false) used on personally controlled sites using self-signed certificates.
type: bool
default: true
ssl:
description:
- Use https instead of http to connect to InfluxDB server.
- Use https instead of http to connect to InfluxDB server.
type: bool
default: false
timeout:
description:
- Number of seconds Requests will wait for client to establish a connection.
- Number of seconds Requests will wait for client to establish a connection.
type: int
retries:
description:
- Number of retries client will try before aborting.
- V(0) indicates try until success.
- Only available when using C(python-influxdb) >= 4.1.0.
- Number of retries client will try before aborting.
- V(0) indicates try until success.
- Only available when using python-influxdb >= 4.1.0.
type: int
default: 3
use_udp:
description:
- Use UDP to connect to InfluxDB server.
- Use UDP to connect to InfluxDB server.
type: bool
default: false
udp_port:
description:
- UDP port to connect to InfluxDB server.
- UDP port to connect to InfluxDB server.
type: int
default: 4444
proxies:
description:
- HTTP(S) proxy to use for Requests to connect to InfluxDB server.
- HTTP(S) proxy to use for Requests to connect to InfluxDB server.
type: dict
default: {}
"""
'''

View File

@@ -11,66 +11,61 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Parameters for FreeIPA/IPA modules
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
ipa_port:
description:
- Port of FreeIPA / IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_PORT) will be used instead.
- If both the environment variable E(IPA_PORT) and the value are not specified in the task, then default value is set.
- Port of FreeIPA / IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_PORT) will be used instead.
- If both the environment variable E(IPA_PORT) and the value are not specified in the task, then default value is set.
type: int
default: 443
ipa_host:
description:
- IP or hostname of IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_HOST) will be used instead.
- If both the environment variable E(IPA_HOST) and the value are not specified in the task, then DNS will be used to
try to discover the FreeIPA server.
- The relevant entry needed in FreeIPA is the C(ipa-ca) entry.
- If neither the DNS entry, nor the environment E(IPA_HOST), nor the value are available in the task, then the default
value will be used.
- IP or hostname of IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_HOST) will be used instead.
- If both the environment variable E(IPA_HOST) and the value are not specified in the task, then DNS will be used to try to discover the FreeIPA server.
- The relevant entry needed in FreeIPA is the C(ipa-ca) entry.
- If neither the DNS entry, nor the environment E(IPA_HOST), nor the value are available in the task, then the default value will be used.
type: str
default: ipa.example.com
ipa_user:
description:
- Administrative account used on IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_USER) will be used instead.
- If both the environment variable E(IPA_USER) and the value are not specified in the task, then default value is set.
- Administrative account used on IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_USER) will be used instead.
- If both the environment variable E(IPA_USER) and the value are not specified in the task, then default value is set.
type: str
default: admin
ipa_pass:
description:
- Password of administrative user.
- If the value is not specified in the task, the value of environment variable E(IPA_PASS) will be used instead.
- Note that if the C(urllib_gssapi) library is available, it is possible to use GSSAPI to authenticate to FreeIPA.
- If the environment variable E(KRB5CCNAME) is available, the module will use this kerberos credentials cache to authenticate
to the FreeIPA server.
- If the environment variable E(KRB5_CLIENT_KTNAME) is available, and E(KRB5CCNAME) is not; the module will use this
kerberos keytab to authenticate.
- If GSSAPI is not available, the usage of O(ipa_pass) is required.
- Password of administrative user.
- If the value is not specified in the task, the value of environment variable E(IPA_PASS) will be used instead.
- Note that if the C(urllib_gssapi) library is available, it is possible to use GSSAPI to authenticate to FreeIPA.
- If the environment variable E(KRB5CCNAME) is available, the module will use this kerberos credentials cache to authenticate to the FreeIPA server.
- If the environment variable E(KRB5_CLIENT_KTNAME) is available, and E(KRB5CCNAME) is not; the module will use this kerberos keytab to authenticate.
- If GSSAPI is not available, the usage of O(ipa_pass) is required.
type: str
ipa_prot:
description:
- Protocol used by IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_PROT) will be used instead.
- If both the environment variable E(IPA_PROT) and the value are not specified in the task, then default value is set.
- Protocol used by IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_PROT) will be used instead.
- If both the environment variable E(IPA_PROT) and the value are not specified in the task, then default value is set.
type: str
choices: [http, https]
choices: [ http, https ]
default: https
validate_certs:
description:
- This only applies if O(ipa_prot) is V(https).
- If set to V(false), the SSL certificates will not be validated.
- This should only set to V(false) used on personally controlled sites using self-signed certificates.
- This only applies if O(ipa_prot) is V(https).
- If set to V(false), the SSL certificates will not be validated.
- This should only set to V(false) used on personally controlled sites using self-signed certificates.
type: bool
default: true
ipa_timeout:
description:
- Specifies idle timeout (in seconds) for the connection.
- For bulk operations, you may want to increase this in order to avoid timeout from IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_TIMEOUT) will be used instead.
- If both the environment variable E(IPA_TIMEOUT) and the value are not specified in the task, then default value is
set.
- Specifies idle timeout (in seconds) for the connection.
- For bulk operations, you may want to increase this in order to avoid timeout from IPA server.
- If the value is not specified in the task, the value of environment variable E(IPA_TIMEOUT) will be used instead.
- If both the environment variable E(IPA_TIMEOUT) and the value are not specified in the task, then default value is set.
type: int
default: 10
"""
'''

View File

@@ -11,85 +11,69 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
auth_keycloak_url:
description:
- URL to the Keycloak instance.
type: str
required: true
aliases:
- url
auth_keycloak_url:
description:
- URL to the Keycloak instance.
type: str
required: true
aliases:
- url
auth_client_id:
description:
- OpenID Connect C(client_id) to authenticate to the API with.
type: str
default: admin-cli
auth_client_id:
description:
- OpenID Connect C(client_id) to authenticate to the API with.
type: str
default: admin-cli
auth_realm:
description:
- Keycloak realm name to authenticate to for API access.
type: str
auth_realm:
description:
- Keycloak realm name to authenticate to for API access.
type: str
auth_client_secret:
description:
- Client Secret to use in conjunction with O(auth_client_id) (if required).
type: str
auth_client_secret:
description:
- Client Secret to use in conjunction with O(auth_client_id) (if required).
type: str
auth_username:
description:
- Username to authenticate for API access with.
type: str
aliases:
- username
auth_username:
description:
- Username to authenticate for API access with.
type: str
aliases:
- username
auth_password:
description:
- Password to authenticate for API access with.
type: str
aliases:
- password
auth_password:
description:
- Password to authenticate for API access with.
type: str
aliases:
- password
token:
description:
- Authentication token for Keycloak API.
type: str
version_added: 3.0.0
token:
description:
- Authentication token for Keycloak API.
type: str
version_added: 3.0.0
refresh_token:
description:
- Authentication refresh token for Keycloak API.
type: str
version_added: 10.3.0
validate_certs:
description:
- Verify TLS certificates (do not disable this in production).
type: bool
default: true
validate_certs:
description:
- Verify TLS certificates (do not disable this in production).
type: bool
default: true
connection_timeout:
description:
- Controls the HTTP connections timeout period (in seconds) to Keycloak API.
type: int
default: 10
version_added: 4.5.0
connection_timeout:
description:
- Controls the HTTP connections timeout period (in seconds) to Keycloak API.
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
"""
ACTIONGROUP_KEYCLOAK = r"""
options: {}
attributes:
action_group:
description: Use C(group/community.general.keycloak) in C(module_defaults) to set defaults for this module.
support: full
membership:
- community.general.keycloak
"""
http_agent:
description:
- Configures the HTTP User-Agent header.
type: str
default: Ansible
version_added: 5.4.0
'''

View File

@@ -12,17 +12,12 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard LDAP documentation fragment
DOCUMENTATION = r"""
notes:
- The default authentication settings will attempt to use a SASL EXTERNAL bind over a UNIX domain socket. This works well
with the default Ubuntu install for example, which includes a C(cn=peercred,cn=external,cn=auth) ACL rule allowing root
to modify the server configuration. If you need to use a simple bind to access your server, pass the credentials in O(bind_dn)
and O(bind_pw).
DOCUMENTATION = r'''
options:
bind_dn:
description:
- A DN to bind with. Try to use a SASL bind with the EXTERNAL mechanism as default when this parameter is omitted.
- Use an anonymous bind if the parameter is blank.
- A DN to bind with. If this is omitted, we'll try a SASL bind with the EXTERNAL mechanism as default.
- If this is blank, we'll use an anonymous bind.
type: str
bind_pw:
description:
@@ -62,8 +57,7 @@ options:
version_added: 2.0.0
server_uri:
description:
- The O(server_uri) parameter may be a comma- or whitespace-separated list of URIs containing only the schema, the host,
and the port fields.
- The O(server_uri) parameter may be a comma- or whitespace-separated list of URIs containing only the schema, the host, and the port fields.
- The default value lets the underlying LDAP client library look for a UNIX domain socket in its default location.
- Note that when using multiple URIs you cannot determine to which URI your client gets connected.
- For URIs containing additional fields, particularly when using commas, behavior is undefined.
@@ -71,7 +65,7 @@ options:
default: ldapi:///
start_tls:
description:
- Use the START_TLS LDAP extension if set to V(true).
- If true, we'll use the START_TLS LDAP extension.
type: bool
default: false
validate_certs:
@@ -97,4 +91,4 @@ options:
choices: ['enable', 'auto', 'disable']
default: auto
version_added: "6.4.0"
"""
'''

View File

@@ -10,7 +10,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard Pylxca documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
author:
- Naval Patel (@navalkp)
- Prashant Bhosale (@prabhosa)
@@ -18,19 +18,19 @@ author:
options:
login_user:
description:
- The username for use in HTTP basic authentication.
- The username for use in HTTP basic authentication.
type: str
required: true
login_password:
description:
- The password for use in HTTP basic authentication.
- The password for use in HTTP basic authentication.
type: str
required: true
auth_url:
description:
- Lxca HTTPS full web address.
- lxca HTTPS full web address.
type: str
required: true
@@ -40,4 +40,4 @@ requirements:
notes:
- Additional detail about pylxca can be found at U(https://github.com/lenovo/pylxca).
- Playbooks using these modules can be found at U(https://github.com/lenovo/ansible.lenovo-lxca).
"""
'''

View File

@@ -11,7 +11,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard ManageIQ documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
manageiq_connection:
description:
@@ -34,21 +34,20 @@ options:
type: str
token:
description:
- ManageIQ token. E(MIQ_TOKEN) environment variable if set. Otherwise, required if no username or password is passed
in.
- ManageIQ token. E(MIQ_TOKEN) environment variable if set. Otherwise, required if no username or password is passed in.
type: str
validate_certs:
description:
- Whether SSL certificates should be verified for HTTPS requests.
type: bool
default: true
aliases: [verify_ssl]
aliases: [ verify_ssl ]
ca_cert:
description:
- The path to a CA bundle file or directory with certificates.
type: str
aliases: [ca_bundle_path]
aliases: [ ca_bundle_path ]
requirements:
- 'manageiq-client U(https://github.com/ManageIQ/manageiq-api-client-python/)'
"""
'''

View File

@@ -11,48 +11,48 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
host:
description:
- FQDN of Nomad server.
required: true
type: str
port:
description:
- Port of Nomad server.
type: int
default: 4646
version_added: 8.0.0
use_ssl:
description:
- Use TLS/SSL connection.
type: bool
default: true
timeout:
description:
- Timeout (in seconds) for the request to Nomad.
type: int
default: 5
validate_certs:
description:
- Enable TLS/SSL certificate validation.
type: bool
default: true
client_cert:
description:
- Path of certificate for TLS/SSL.
type: path
client_key:
description:
- Path of certificate's private key for TLS/SSL.
type: path
namespace:
description:
- Namespace for Nomad.
type: str
token:
description:
- ACL token for authentication.
type: str
"""
host:
description:
- FQDN of Nomad server.
required: true
type: str
port:
description:
- Port of Nomad server.
type: int
default: 4646
version_added: 8.0.0
use_ssl:
description:
- Use TLS/SSL connection.
type: bool
default: true
timeout:
description:
- Timeout (in seconds) for the request to Nomad.
type: int
default: 5
validate_certs:
description:
- Enable TLS/SSL certificate validation.
type: bool
default: true
client_cert:
description:
- Path of certificate for TLS/SSL.
type: path
client_key:
description:
- Path of certificate's private key for TLS/SSL.
type: path
namespace:
description:
- Namespace for Nomad.
type: str
token:
description:
- ACL token for authentication.
type: str
'''

View File

@@ -9,7 +9,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r"""
DOCUMENTATION = r'''
requirements:
- See U(https://support.1password.com/command-line/)
options:
@@ -18,8 +18,7 @@ options:
aliases: ['vault_password']
type: str
section:
description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from
any section.
description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from any section.
domain:
description: Domain of 1Password.
default: '1password.com'
@@ -56,25 +55,25 @@ options:
env:
- name: OP_CONNECT_TOKEN
version_added: 8.1.0
"""
'''
LOOKUP = r"""
LOOKUP = r'''
options:
service_account_token:
env:
- name: OP_SERVICE_ACCOUNT_TOKEN
version_added: 8.2.0
notes:
- This lookup will use an existing 1Password session if one exists. If not, and you have already performed an initial sign
in (meaning C(~/.op/config), C(~/.config/op/config) or C(~/.config/.op/config) exists), then only the O(master_password)
is required. You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used
by C(op).
- This lookup will use an existing 1Password session if one exists. If not, and you have already
performed an initial sign in (meaning C(~/.op/config), C(~/.config/op/config) or C(~/.config/.op/config) exists), then only the
O(master_password) is required. You may optionally specify O(subdomain) in this scenario, otherwise the last used subdomain will be used by C(op).
- This lookup can perform an initial login by providing O(subdomain), O(username), O(secret_key), and O(master_password).
- Can target a specific account by providing the O(account_id).
- Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal
credentials needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or
greater in strength to the 1Password master password.
- This lookup stores potentially sensitive data from 1Password as Ansible facts. Facts are subject to caching if enabled,
which means this data could be stored in clear text on disk or in a database.
- Due to the B(very) sensitive nature of these credentials, it is B(highly) recommended that you only pass in the minimal credentials
needed at any given time. Also, store these credentials in an Ansible Vault using a key that is equal to or greater in strength
to the 1Password master password.
- This lookup stores potentially sensitive data from 1Password as Ansible facts.
Facts are subject to caching if enabled, which means this data could be stored in clear text
on disk or in a database.
- Tested with C(op) version 2.7.2.
"""
'''

View File

@@ -11,67 +11,70 @@ __metaclass__ = type
class ModuleDocFragment(object):
# OneView doc fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
config:
description:
- Path to a JSON configuration file containing the OneView client configuration. The configuration file is optional
and when used should be present in the host running the ansible commands. If the file path is not provided, the configuration
will be loaded from environment variables. For links to example configuration files or how to use the environment
variables verify the notes section.
type: path
api_version:
description:
- OneView API Version.
type: int
image_streamer_hostname:
description:
- IP address or hostname for the HPE Image Streamer REST API.
type: str
hostname:
description:
- IP address or hostname for the appliance.
type: str
username:
description:
- Username for API authentication.
type: str
password:
description:
- Password for API authentication.
type: str
config:
description:
- Path to a JSON configuration file containing the OneView client configuration.
The configuration file is optional and when used should be present in the host running the ansible commands.
If the file path is not provided, the configuration will be loaded from environment variables.
For links to example configuration files or how to use the environment variables verify the notes section.
type: path
api_version:
description:
- OneView API Version.
type: int
image_streamer_hostname:
description:
- IP address or hostname for the HPE Image Streamer REST API.
type: str
hostname:
description:
- IP address or hostname for the appliance.
type: str
username:
description:
- Username for API authentication.
type: str
password:
description:
- Password for API authentication.
type: str
requirements:
- Python >= 2.7.9
notes:
- 'A sample configuration file for the config parameter can be found at:
U(https://github.com/HewlettPackard/oneview-ansible/blob/master/examples/oneview_config-rename.json).'
- 'Check how to use environment variables for configuration at: U(https://github.com/HewlettPackard/oneview-ansible#environment-variables).'
- 'Additional Playbooks for the HPE OneView Ansible modules can be found at: U(https://github.com/HewlettPackard/oneview-ansible/tree/master/examples).'
- 'The OneView API version used will directly affect returned and expected fields in resources. Information on setting the
desired API version and can be found at: U(https://github.com/HewlettPackard/oneview-ansible#setting-your-oneview-version).'
"""
- "A sample configuration file for the config parameter can be found at:
U(https://github.com/HewlettPackard/oneview-ansible/blob/master/examples/oneview_config-rename.json)"
- "Check how to use environment variables for configuration at:
U(https://github.com/HewlettPackard/oneview-ansible#environment-variables)"
- "Additional Playbooks for the HPE OneView Ansible modules can be found at:
U(https://github.com/HewlettPackard/oneview-ansible/tree/master/examples)"
- "The OneView API version used will directly affect returned and expected fields in resources.
Information on setting the desired API version and can be found at:
U(https://github.com/HewlettPackard/oneview-ansible#setting-your-oneview-version)"
'''
VALIDATEETAG = r"""
VALIDATEETAG = r'''
options:
validate_etag:
description:
- When the ETag Validation is enabled, the request will be conditionally processed only if the current ETag for the
resource matches the ETag provided in the data.
type: bool
default: true
"""
validate_etag:
description:
- When the ETag Validation is enabled, the request will be conditionally processed only if the current ETag
for the resource matches the ETag provided in the data.
type: bool
default: true
'''
FACTSPARAMS = r"""
FACTSPARAMS = r'''
options:
params:
description:
- List of parameters to delimit, filter and sort the list of resources.
- 'Parameter keys allowed are:'
- 'V(start): The first item to return, using 0-based indexing.'
- 'V(count): The number of resources to return.'
- 'V(filter): A general filter/query string to narrow the list of items returned.'
- 'V(sort): The sort order of the returned data set.'
type: dict
"""
params:
description:
- List of parameters to delimit, filter and sort the list of resources.
- "Parameter keys allowed are:"
- "C(start): The first item to return, using 0-based indexing."
- "C(count): The number of resources to return."
- "C(filter): A general filter/query string to narrow the list of items returned."
- "C(sort): The sort order of the returned data set."
type: dict
'''

View File

@@ -10,26 +10,26 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
api_token:
description:
- Online OAuth token.
type: str
required: true
aliases: [oauth_token]
aliases: [ oauth_token ]
api_url:
description:
- Online API URL.
type: str
default: 'https://api.online.net'
aliases: [base_url]
aliases: [ base_url ]
api_timeout:
description:
- HTTP timeout to Online API in seconds.
type: int
default: 30
aliases: [timeout]
aliases: [ timeout ]
validate_certs:
description:
- Validate SSL certs of the Online API.
@@ -37,7 +37,9 @@ options:
default: true
notes:
- Also see the API documentation on U(https://console.online.net/en/api/).
- If O(api_token) is not set within the module, the following environment variables can be used in decreasing order of precedence
- If O(api_token) is not set within the module, the following
environment variables can be used in decreasing order of precedence
E(ONLINE_TOKEN), E(ONLINE_API_KEY), E(ONLINE_OAUTH_TOKEN), E(ONLINE_API_TOKEN).
- If one wants to use a different O(api_url) one can also set the E(ONLINE_API_URL) environment variable.
"""
- If one wants to use a different O(api_url) one can also set the E(ONLINE_API_URL)
environment variable.
'''

View File

@@ -10,36 +10,36 @@ __metaclass__ = type
class ModuleDocFragment(object):
# OpenNebula common documentation
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
api_url:
description:
- The ENDPOINT URL of the XMLRPC server.
- If not specified then the value of the E(ONE_URL) environment variable, if any, is used.
type: str
aliases:
- api_endpoint
api_username:
description:
- The name of the user for XMLRPC authentication.
- If not specified then the value of the E(ONE_USERNAME) environment variable, if any, is used.
type: str
api_password:
description:
- The password or token for XMLRPC authentication.
- If not specified then the value of the E(ONE_PASSWORD) environment variable, if any, is used.
type: str
aliases:
- api_token
validate_certs:
description:
- Whether to validate the TLS/SSL certificates or not.
- This parameter is ignored if E(PYTHONHTTPSVERIFY) environment variable is used.
type: bool
default: true
wait_timeout:
description:
- Time to wait for the desired state to be reached before timeout, in seconds.
type: int
default: 300
"""
api_url:
description:
- The ENDPOINT URL of the XMLRPC server.
- If not specified then the value of the E(ONE_URL) environment variable, if any, is used.
type: str
aliases:
- api_endpoint
api_username:
description:
- The name of the user for XMLRPC authentication.
- If not specified then the value of the E(ONE_USERNAME) environment variable, if any, is used.
type: str
api_password:
description:
- The password or token for XMLRPC authentication.
- If not specified then the value of the E(ONE_PASSWORD) environment variable, if any, is used.
type: str
aliases:
- api_token
validate_certs:
description:
- Whether to validate the TLS/SSL certificates or not.
- This parameter is ignored if E(PYTHONHTTPSVERIFY) environment variable is used.
type: bool
default: true
wait_timeout:
description:
- Time to wait for the desired state to be reached before timeout, in seconds.
type: int
default: 300
'''

View File

@@ -11,62 +11,75 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = r"""
DOCUMENTATION = r'''
options:
host:
description:
- Specifies the DNS host name or address for connecting to the remote device over the specified transport. The value
of host is used as the destination address for the transport. Note this argument does not affect the SSH argument.
- Specifies the DNS host name or address for connecting to the remote
device over the specified transport. The value of host is used as
the destination address for the transport. Note this argument
does not affect the SSH argument.
type: str
port:
description:
- Specifies the port to use when building the connection to the remote device. This value applies to either O(transport=cli)
or O(transport=rest). The port value will default to the appropriate transport common port if none is provided in
the task. (cli=22, http=80, https=443). Note this argument does not affect the SSH transport.
- Specifies the port to use when building the connection to the remote
device. This value applies to either O(transport=cli) or O(transport=rest). The port
value will default to the appropriate transport common port if
none is provided in the task. (cli=22, http=80, https=443). Note
this argument does not affect the SSH transport.
type: int
default: 0 (use common port)
username:
description:
- Configures the username to use to authenticate the connection to the remote device. This value is used to authenticate
either the CLI login or the eAPI authentication depending on which transport is used. Note this argument does not
affect the SSH transport. If the value is not specified in the task, the value of environment variable E(ANSIBLE_NET_USERNAME)
will be used instead.
- Configures the username to use to authenticate the connection to
the remote device. This value is used to authenticate
either the CLI login or the eAPI authentication depending on which
transport is used. Note this argument does not affect the SSH
transport. If the value is not specified in the task, the value of
environment variable E(ANSIBLE_NET_USERNAME) will be used instead.
type: str
password:
description:
- Specifies the password to use to authenticate the connection to the remote device. This is a common argument used
for either O(transport=cli) or O(transport=rest). Note this argument does not affect the SSH transport. If the value
is not specified in the task, the value of environment variable E(ANSIBLE_NET_PASSWORD) will be used instead.
- Specifies the password to use to authenticate the connection to
the remote device. This is a common argument used for either O(transport=cli)
or O(transport=rest). Note this argument does not affect the SSH
transport. If the value is not specified in the task, the value of
environment variable E(ANSIBLE_NET_PASSWORD) will be used instead.
type: str
timeout:
description:
- Specifies the timeout in seconds for communicating with the network device for either connecting or sending commands.
If the timeout is exceeded before the operation is completed, the module will error.
- Specifies the timeout in seconds for communicating with the network device
for either connecting or sending commands. If the timeout is
exceeded before the operation is completed, the module will error.
type: int
default: 10
ssh_keyfile:
description:
- Specifies the SSH key to use to authenticate the connection to the remote device. This argument is only used for O(transport=cli).
If the value is not specified in the task, the value of environment variable E(ANSIBLE_NET_SSH_KEYFILE) will be used
instead.
- Specifies the SSH key to use to authenticate the connection to
the remote device. This argument is only used for O(transport=cli).
If the value is not specified in the task, the value of
environment variable E(ANSIBLE_NET_SSH_KEYFILE) will be used instead.
type: path
transport:
description:
- Configures the transport connection to use when connecting to the remote device. The transport argument supports connectivity
to the device over SSH (V(ssh)), CLI (V(cli)), or REST (V(rest)).
- Configures the transport connection to use when connecting to the
remote device. The transport argument supports connectivity to the
device over SSH (V(ssh)), CLI (V(cli)), or REST (V(rest)).
required: true
type: str
choices: [cli, rest, ssh]
choices: [ cli, rest, ssh ]
default: ssh
use_ssl:
description:
- Configures the O(transport) to use SSL if set to V(true) only when the O(transport) argument is configured as rest.
If the transport argument is not V(rest), this value is ignored.
- Configures the O(transport) to use SSL if set to V(true) only when the
O(transport) argument is configured as rest. If the transport
argument is not V(rest), this value is ignored.
type: bool
default: true
provider:
description:
- Convenience method that allows all C(openswitch) arguments to be passed as a dict object. All constraints (required,
choices, and so on) must be met either by individual arguments or values in this dict.
- Convenience method that allows all C(openswitch) arguments to be passed as
a dict object. All constraints (required, choices, etc) must be
met either by individual arguments or values in this dict.
type: dict
"""
'''

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