Compare commits

..

269 Commits
2.2.0 ... 3.0.1

Author SHA1 Message Date
Felix Fontein
b0b783f8ff Release 3.0.1. 2021-05-04 12:45:19 +02:00
patchback[bot]
e670ca666a OpenNebula one_vm.py: Fix missing keys (#2435) (#2447)
* OpenNebula one_vm.py: Fix missing keys

* fixup OpenNebula one_vm.py: Fix missing keys

(cherry picked from commit aaa561163b)

Co-authored-by: Jan Orel <jorel@opennebula.io>
2021-05-04 12:43:25 +02:00
patchback[bot]
49b991527e Remove shippable config. (#2440) (#2444)
(cherry picked from commit 1f41e66f09)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-05-03 22:56:40 +02:00
patchback[bot]
e6cc671a0d lvol - bug fix - Convert units to lowercase when using LVS or VGS command (#2369) (#2439)
* Added lower call for units when checking lvs/vgs size

* Changelog

* Size roudning correction

* Rounding

* Changelog

* Remove whitespace

(cherry picked from commit 06bdabcad9)

Co-authored-by: zigaSRC <65527456+zigaSRC@users.noreply.github.com>
2021-05-03 22:07:52 +02:00
patchback[bot]
797ea23e50 Clean up test entries from sysrc tests (#2330) (#2438)
* Clean up test entries from sysrc tests

* sysrc: enable tests

* sysrc: cache the files to be changed and restore them

* Update the ezjail archive host and remove obsolete file

* sysrc: set ezjail to use archives for 12.0 or less

* sysrc: Detect the version to use ftp vs ftp-archive using http

* sysrc: Skip ezjail test on FreeBSD 12.0

(cherry picked from commit 7007c68ab7)

Co-authored-by: David Lundgren <dlundgren@syberisle.net>
2021-05-03 21:34:19 +02:00
patchback[bot]
4d23b7a48b linode_v4 - fixed error message (#2430) (#2433)
* fixed error message

* added changelog fragment

(cherry picked from commit 5064aa8ec6)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-05-03 13:51:38 +02:00
patchback[bot]
020b47a1a9 Make plugins pass validation. (#2414) (#2431)
(cherry picked from commit 6a72c3b338)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-05-03 13:51:10 +02:00
patchback[bot]
0da9d956a0 nmcli: Compare MAC addresses case insensitively (#2416) (#2425)
* nmcli: Compare MAC addresses case insensitively

* Update changelogs/fragments/2416-nmcli_compare_mac_addresses_case_insensitively.yml

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

* Update plugins/modules/net_tools/nmcli.py

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

* Add mac to TESTCASE_BRIDGE so test_bridge_connection_unchanged covers case sensitive mac address comparison

* Update plugins/modules/net_tools/nmcli.py

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

* Convert current_value to uppercase as well in case nmcli changes behaviour

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

Co-authored-by: spike77453 <spike77453@users.noreply.github.com>
2021-05-03 08:17:32 +02:00
patchback[bot]
5691e3aff3 nmcli: Add 'slave-type bridge' to nmcli command if type is bridge-slave (#2409) (#2423)
(cherry picked from commit b5f8ae4320)

Co-authored-by: spike77453 <spike77453@users.noreply.github.com>
2021-05-03 08:16:19 +02:00
patchback[bot]
007333dbfe 📝 Document nested node addition with "_" in xml module (#2371) (#2427)
* 📝 Document nested node addition with "_" in xml module

Nested node addition using "_" to indicate sub nodes, and attributes are only documented in tests and issues, where is hard to find.

* 🚨 Fix trailing space

* Apply suggestions from code review

Add missing collection prefix for modules.

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

* Add missing comments

* Update xml.py

* Fix linter warnings

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

Co-authored-by: Daniel-Sanchez-Fabregas <33929811+Daniel-Sanchez-Fabregas@users.noreply.github.com>
2021-05-03 08:12:31 +02:00
patchback[bot]
05666b0e4d puppet - replace stdout with console in logdest option (#2407) (#2421)
* Change stdout to console

* readd stdout, resulting in console

* add changelog

* readd stdout to docs and add a warning when it is used

* version of what???

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

* postpone deprecation in another PR

* remove console option, so it can be backported

* change changelog respectively

* Fix changelog formatting

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

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

Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-05-03 07:59:33 +02:00
patchback[bot]
c934d9aeb5 Fix #2373 - TypeError: a bytes-like object is required, not 'str' (#2375) (#2419)
* Fix #2373

* Changelog fragment for #2373

* Update changelogs/fragments/2373-svr4pkg-fix-typeerror.yml

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update changelogs/fragments/2373-svr4pkg-fix-typeerror.yml

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

Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
(cherry picked from commit cd957fae4c)

Co-authored-by: Daniel Werner <srmlsrml@gmail.com>
2021-05-03 07:56:05 +02:00
Felix Fontein
5b15e4089a Revert "spotinst_aws_elastigroup - fixed elements for many lists (#2355) (#2363)"
This reverts commit ea42b75378.

(Will be re-reverted after the 3.0.1 release.)
2021-05-03 07:48:12 +02:00
Felix Fontein
a6379e45ce Prepare 3.0.1 release. 2021-05-03 07:47:48 +02:00
Felix Fontein
b95176dbc8 Copy schedule to stable branches. 2021-05-02 13:35:45 +02:00
patchback[bot]
b752fea121 BOTMETA.yml: terraform - add a new maintainer (#2290) (#2413)
(cherry picked from commit c0221b75af)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2021-05-02 13:27:43 +02:00
patchback[bot]
cf50990fed Add ansible-test config file. (#2404) (#2406)
(cherry picked from commit 4e90ee752e)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-05-01 22:54:44 +02:00
patchback[bot]
45343e6bc0 composer: --no-interaction when discovering available options (#2348) (#2403)
The composer module always uses the no-interaction option if it
discovers it _after_ calling "composer help ..." but not on the help
call itself. The lack of this option caused composer to not exit when
called through the ansible module.

The same example command when ran interactively does not prompt for user
interaction and exits immediately. It is therefore currently unknown why
the same command hangs when called through the ansible composer module
or even directly with the command module.

Example command which hangs:
php /usr/local/bin/composer help install --format=json

(cherry picked from commit eb455c69a2)

Co-authored-by: George Angelopoulos <george@usermod.net>
2021-05-01 18:46:16 +02:00
patchback[bot]
51540f6345 influxdb_retention_policy: fix duration parsing to support INF values (#2396) (#2401)
* influxdb_retention_policy: fix duration parsing to support INF values

* add changelog

(cherry picked from commit 26c3bd25f6)

Co-authored-by: Xabier Napal <xabiernapal@pm.me>
2021-05-01 14:37:19 +02:00
patchback[bot]
74eba52028 Remove resmo as composer maintainer. (#2392) (#2395)
(cherry picked from commit 276880aac1)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-05-01 14:11:16 +02:00
patchback[bot]
b920e8abf2 Add Fedora 34 to CI (#2384) (#2391)
* Add fedora 34 and fix typo

* Remove Fedora 32 from devel testing

* Use one newer version of Fedora for fixed ansible versions

* Revert "Use one newer version of Fedora for fixed ansible versions"

This reverts commit cbd006bd38.

* Try to skip task.

* Revert "Try to skip task."

This reverts commit ff0c899a86.

* Temporary disable Fedora 34 on setup_postgresql_db

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

Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-05-01 14:10:07 +02:00
patchback[bot]
75c0004e1e Use Ansible's codecov uploader. (#2377) (#2380)
(cherry picked from commit b3f436aa63)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-30 05:32:23 +02:00
patchback[bot]
be42fd4af7 No longer required for devel's ansible-test. (#2365) (#2367)
ci_complete

(cherry picked from commit 77d4bc2942)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-27 22:44:21 +02:00
patchback[bot]
1c05908ff6 BOTMETA.yml: team_suse - add a maintainer (#2354) (#2362)
(cherry picked from commit 9d13acd68e)

Co-authored-by: Andrew Klychkov <aklychko@redhat.com>
2021-04-27 13:46:28 +02:00
patchback[bot]
ea42b75378 spotinst_aws_elastigroup - fixed elements for many lists (#2355) (#2363)
* fixed elements for many lists

* added changelog fragment

* Removed verbose types in description - still missing formatting and properly documenting dicts

(cherry picked from commit 48ef05def3)

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-04-27 11:46:17 +00:00
patchback[bot]
0330f4b52c Make inventory scripts executable (#2337) (#2359)
* Make inventory scripts executable

* Mark inventory scripts in vault folder as executable

* Add changelog entry for making inventory scripts exectuable

* Update changelogs/fragments/2337-mark-inventory-scripts-executable.yml

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

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

Co-authored-by: Alan Rominger <arominge@redhat.com>
2021-04-26 22:24:42 +02:00
Felix Fontein
1d8c659ba2 Next expected release is 3.0.0. 2021-04-26 21:19:55 +02:00
Felix Fontein
e784254679 Release 3.0.0. 2021-04-26 18:40:14 +02:00
Felix Fontein
d5e1edd284 Add release summary. 2021-04-26 18:38:45 +02:00
Frank Dornheim
2cc3ce0230 lxd: dynamic inventory (#2026)
* lxd dynamic inventory and test data

* added ``merge_profile`` parameter to merge configurations from the play to an existing profile

* cosmetic changes

* added ``merge_profile`` parameter to merge configurations from the play to an existing profile

* cosmetic changes

* fix pylint errors

* fix flake8 warnings

* fix pep8 errors without "line to long"

* fix ansible tests

* fix typo

* fix version added

* fix lost of suggestions from felixfontein

* fix filter
fix ansible test errors

* delete test config

* delete 'notes:' and copy content to description

* move testdata
load testdata by path from config

* updated documentation

* fix test data and remove inventory branch

* fix spellings and rename lxd to community.general.lxd

* fix documentation

* remove selftest

* strip example data

* add unit test

* switch to ansible.module_utils.common.dict_transformations

* documentation cleanup

* move lxd_inventory.atd from files to fixtures

* update documentation
move lxd_inventory.atd

* rename self.groups to self dispose
remove dumpdata

* cleanup

* fix unittests
comment out dump_data, it breaks the unit tests

* fix pep8

* Apply suggestions from code review

* Update plugins/inventory/lxd.py

* add test if no groupby is selected

* rename disposed to groupby
remove unused constant
other suggested cleanups

* Use bundled ipaddress instead of own code.

* Update plugins/inventory/lxd.py

* Exceptions should not be eaten.

* Improve error handling for network range/address parsing.

* Fix typo.

* Make network range valid.

* Do not error when groupby is not a dict.

Co-authored-by: Frank Dornheim <“dornheim@posteo.de@users.noreply.github.com”>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-26 18:25:45 +02:00
Alexei Znamensky
99c564398a jira - module revamp -> moved code to class (#2208)
* rebased after jira bugfix

* Update plugins/modules/web_infrastructure/jira.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-26 13:09:19 +02:00
zigaSRC
ffd73296de lvol - extending volumes with '+' only work for percentages (#2267)
* Merged changes from old PR

* Added suppurt for -, other adaptations regarding size.

* Implementing +- support for -l

* Added changelog

* Renamed changelog

* Apply suggestions from code review

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-26 07:07:14 +02:00
Xabier Napal
aea12899cc influxdb_retention_policy: ensure duration parameters are idempotent (#2281) (#2284)
* influxdb_retention_policy: ensure duration parameters are idempotent (#2281)

* add changelog for pr #2284
2021-04-26 07:03:36 +02:00
Gaetan2907
1b79440896 Fix incorrect URL_REALMS in keycloak.py (#2335)
* Allow keycloak_group.py to take token as parameter for the authentification

* Refactor get_token to pass module.params + Documentation

* Fix unit test and add new one for token as param

* Fix identation

* Update plugins/modules/identity/keycloak/keycloak_client.py

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

* Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py

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

* Allow keycloak_group.py to take token as parameter for the authentification

* Refactor get_token to pass module.params + Documentation

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

* Check if base_url is None before to check format

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

* Update plugins/modules/identity/keycloak/keycloak_client.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Switch to modern syntax for the documentation (e.g. community.general.keycloak_client)

* Fix URL_REALMS in keycloak.py

* Update keycloak_client.py

* Update keycloak_clienttemplate.py

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-04-26 07:01:43 +02:00
Felix Fontein
5195536bd8 Fix Python 2 compatibility issue. (#2340) 2021-04-26 06:56:21 +02:00
Alexei Znamensky
8ddb81a36f jira - fixed base64 decode bug (#2349)
* fixed base64 decode bug

* added changelog fragment
2021-04-26 06:54:57 +02:00
Colin Nolan
399c0ef849 Removes colin-nolan as Consul maintainer (#2342) 2021-04-25 22:48:25 +02:00
Anubhav Chakraborty
f11f6595cc convert string returned by plugin to unicode (#2329)
* convert string returned by plugin to unicode

* add changelog fragment

* fix changelog format

* fix changelog format yet again

Co-authored-by: Anubhav Chakraborty <anubchak@cisco.com>
2021-04-24 12:20:11 +02:00
Andrew Klychkov
2799cd4ac7 BOTMETA.yml: haproxy - add a maintainer (#2331) 2021-04-22 20:52:06 +02:00
Felix Fontein
e0b731e76f Replace FreeBSD 11.4 with 13.0 for devel testing. (#2324) 2021-04-22 07:08:25 +02:00
TeamOSEOLA
fb1b756d48 Add plugin_paths parameter to terraform module. (#2308)
* Add plugin_paths parameter to terraform module.

The list `plugin_paths` is used in the init phase of terraform by setting
the `-plugin-dir` command line argument to a path that contains
terraform plugins. When the `-plugin-dir` argument is set, the plugin
discovery and auto-download of terraform is disabled. This is useful when
running terraform in automation environments for testing where the production
endpoints are strictly controlled or on air-gapped networks and you need
to control the versions of plugins available.

* Added `elements: path` to `plugin_paths` parameter documentation in terraform.py

* added changelog fragment

* Update changelogs/fragments/2308-terraform-add-plugin_paths-parameter.yaml

use double back-ticks vice single

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

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

Add version added to documentation

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

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

Add markup around URL in documentation

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

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

remove `required=False` and `default=None` from module argument spec, as that is the default behavior and redundant

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Added directory structure info to documentation and examples sections.

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

grammatical fix

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

* grammar fix to description.

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-04-21 16:05:37 -07:00
Dennis Israelsson
31c9ed0fe6 nmap: fix cache support (#2282)
* add cache support

* pep8 e501 fix

* revert verify_file function

* revert description update

* add changelog fragment

Co-authored-by: Dennis Israelsson <github@mdh.nu>
2021-04-21 16:00:03 -07:00
Felix Fontein
6f6b80fd89 Add PR URLs to changelog fragment. 2021-04-21 22:41:55 +02:00
Ilija Matoski
ca48917b4f inventory/proxmox: Added some cases for unsupported network interface and multi-nic and unsupported guest error (#2263)
* added some cases for unsupported network interface and multi-nic without IP addresses

* added changelog fragment

* lint fixes and added option for ansible_host setting

* added description about the new option

* lint fix too long line

* Update changelogs/fragments/2259-proxmox-multi-nic-and-unsupported.yml

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

* Update plugins/inventory/proxmox.py

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

* Update plugins/inventory/proxmox.py

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

* Update plugins/inventory/proxmox.py

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

* Added CommandDisabled

* refactored to code and added a test case where an interfaces doesnt have a mac address or is invalid to reset it to 00:00:00:00:00:00

* Update tests/unit/plugins/inventory/test_proxmox.py

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

* Update tests/unit/plugins/inventory/test_proxmox.py

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

* mac-address is set to None instead of 00:00:... when not defined

* changed None to empty string for mac-address

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
2021-04-21 19:52:45 +02:00
John R Barker
5ca19086a4 Update feature_request.yml 2021-04-21 13:34:30 +01:00
John R Barker
96ad40ac1c Update documentation_report.yml 2021-04-21 13:34:13 +01:00
John R Barker
32d071e349 Update bug_report.yml 2021-04-21 13:33:43 +01:00
Nick
8db59ff02d with_filetree:: use splitext for compatibility with template: (#2285)
* with_filetree: use splitext for compatibility with template

The example code given deploys files with their .j2 extensions intact, which is probably not what you want.

* Explain how templates interact with splitext|first

* Update plugins/lookup/filetree.py

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

* Don't encourage setting the mode of symlinks

On ext4, maybe most filesystems, symlinks always have the artificial mode of 0777, and `chmod $mode $symlink` *writes through* the symlink to its target file.

An effect of this is that if you deploy a file and a symlink to it (e.g. this common situation: /etc/nginx/sites-available/default and /etc/nginx/sites-enabled/default -> ../sites-available/default) then `with_filetree` will forever first deploy the file with the right mode, then corrupt its mode to 0777, and every redeploy will see a change to fix, forever in a loop.

Probably `file:` should refuse `mode:` on `state: link`s, but in the meantime, avoid recommending it in `filetree`

* Use `follow: false` instead of just the mode.

This should be more cross-compatible.

https://github.com/ansible-collections/community.general/pull/2285#discussion_r616571873

* Update plugins/lookup/filetree.py

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-21 14:04:46 +02:00
Felix Fontein
e1d28cf052 Temporarily disable sysrc tests since they fail often. (#2318) 2021-04-21 13:37:10 +02:00
Felix Fontein
c768060d95 More renames. (#2307) 2021-04-21 12:54:35 +02:00
Ajpantuso
68243063d1 pids module: Adding pattern and ignore_case options (#2280)
* Adding pattern and ignore_case options

* Adding changelog fragment

* Fixing changelog fragment

* Addressing FreeBSD 11.4/python 3 errors with explicit conversion

* Correcting descriptions

* Reverting back to regex input

* Fixing test syntax errors
2021-04-21 12:45:09 +02:00
kris2kris
cecbc2be2d Keycloak: add realm management (#2155)
* Keycloak: add realm management

* Minor fixes

* Review fixes

* Remove sort of list because condition on list cannot be true

* Add exception in fail_json and update test

* Change output for secret and version in comments

* Update copyright

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

* Add missing traceback

* Apply suggestions from code review

* Update plugins/modules/identity/keycloak/keycloak_realm.py

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-21 12:42:28 +02:00
Andrew Klychkov
fe2757f057 BOTMETA.yml: lxd/lxd_profile - add a new maintainer (#2310) 2021-04-21 12:40:45 +02:00
Andrew Klychkov
8ab19fc50b BOTMETA.yml: github/github_repo - add a new maintainer (#2309) 2021-04-21 11:22:06 +02:00
Andrew Klychkov
f7928d3eb7 Update BOTMETA.yml (#2304) 2021-04-20 22:31:59 +02:00
Andrew Klychkov
fc12eca65d BOTMETA.yml: update team_virt (#2286) 2021-04-20 15:15:10 +02:00
Andrew Klychkov
0231dad3e8 BOTMETA.yml: update team_ipa (#2289) 2021-04-20 14:36:25 +02:00
Gaetan2907
6ab9b05da3 Allow keycloak modules to take token as parameter for the auth. (#2250)
* Allow keycloak_group.py to take token as parameter for the authentification

* Fix some pep8 issues

* Add changelog fragment

* Refactor get_token to pass module.params + Documentation

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

* Fix unit test and add new one for token as param

* Fix identation

* Check base_url format also if token is given

* Update plugins/doc_fragments/keycloak.py

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

* Update plugins/modules/identity/keycloak/keycloak_client.py

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

* Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py

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

* Allow keycloak_group.py to take token as parameter for the authentification

* Refactor get_token to pass module.params + Documentation

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

* Update plugins/modules/identity/keycloak/keycloak_group.py

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

* Check if base_url is None before to check format

* Fix unit test: rename base_url parameter to auth_keycloak_url

* Update plugins/module_utils/identity/keycloak/keycloak.py

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

* Update changelogs/fragments/2250-allow-keycloak-modules-to-take-token-as-param.yml

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/module_utils/identity/keycloak/keycloak.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_client.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_client.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update changelogs/fragments/2250-allow-keycloak-modules-to-take-token-as-param.yml

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/module_utils/identity/keycloak/keycloak.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_clienttemplate.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_group.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/identity/keycloak/keycloak_group.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Switch to modern syntax for the documentation (e.g. community.general.keycloak_client)

* Add check either creds or token as argument of all keyloak_* modules

* Update plugins/modules/identity/keycloak/keycloak_client.py

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-04-20 13:20:46 +02:00
Andrew Klychkov
5b4fab80e2 BOTMETA.yml: expand team_opennebula (#2292) 2021-04-20 13:03:01 +02:00
Andrew Klychkov
84a79c3da4 BOTMETA.yml: inventory/proxmox - add a new maintainer (#2291) 2021-04-20 13:01:29 +02:00
Andrew Klychkov
49c07dc18b BOTMETA.yml: java_cert - add a new maintainer (#2288) 2021-04-20 12:58:56 +02:00
Andrew Klychkov
7aaa26b591 BOTMETA.yml: update team_gitlab (#2287) 2021-04-20 12:55:58 +02:00
Andrew Klychkov
412b4711c3 BOTMETA: hashids plugin - add a maintainer (#2270) 2021-04-20 08:03:28 +02:00
Gonéri Le Bouder
b10d707a8b nmcli: manual overwrite MAC address for any devices (#2224)
* nmcli: manual overwrite MAC address for any devices

Don't restrict the ability to manually set the MAC address to the
bridge. NetworkManager is able to set a static MAC address to the
vaste majority of the device types.

* Update changelogs/fragments/2224_nmcli_allow_MAC_overwrite.yaml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-19 19:02:48 +02:00
Andrew Klychkov
04bf8137fa BOTMETA.yml: callback/loganalytics - add a maintainer (#2271) 2021-04-19 18:53:48 +02:00
Andrew Klychkov
20401c63cd BOTMETA.yml: filter/from_csv - add a maintainer (#2272) 2021-04-19 18:53:36 +02:00
Andrew Klychkov
eb3ee83146 BOTMETA.yml: filter/version_sort - add a maintainer (#2273) 2021-04-19 18:53:21 +02:00
Andrew Klychkov
93046e0350 BOTMETA.yml: monitoring/spectrum_model_attrs - add a maintainer (#2274) 2021-04-19 18:53:08 +02:00
Alexei Znamensky
fbbd8ecd6f Fixed validations in a couple of modules (#2268)
* fixed udm_dns_zone

* fixed udm_dns_record

* removed lines from ignore files

* added changelog fragment

* moved the list elements fix to minor_changes
2021-04-19 18:48:16 +02:00
Andrew Klychkov
d5c26b6f70 filesize: add maintainer (#2264) 2021-04-19 08:14:21 +02:00
quidame
f87a39b21d new module: filesize - create or resize a file, given its size (#2232)
* new module: filesize

* description: create or resize a file, given its size
* with integration tests

* Update plugins/modules/files/filesize.py (version_added)

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

* Update filesize.py (extends_documentation_fragment: use fqcn)

Co-authored-by: Amin Vakil <info@aminvakil.com>

* doc: use strict lowercase booleans (true/false) rather than other variants

* use *raw* type to manage size values

* drop 'miB' unit family

* Apply suggestions from code review

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

* add more details

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-19 07:04:29 +02:00
Felix Fontein
91a0264f38 java_keystore: overwrite instead of fail when password or alias does not match (#2262)
* Overwrite instead of fail when password does not match.

* Update documentation.

* Fix tests.

* Update plugins/modules/system/java_keystore.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Fix documentation.

* Apply suggestions from code review

Co-authored-by: quidame <quidame@poivron.org>

* Update tests/unit/plugins/modules/system/test_java_keystore.py

* One more.

Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: quidame <quidame@poivron.org>
2021-04-19 06:59:52 +02:00
Gonéri Le Bouder
6a8eb7b388 nmcli: add wifi support (#2220)
* nmcli: add wifi support

Ability to:

- connect NetworkManager to a Wifi network
- attach the connection to a master (bond).

* target 3.0.0
2021-04-18 10:59:23 +02:00
Alexei Znamensky
ec9c23437c cpanm - revamp module (#2218)
* copying from the previous branch

* passing sanity - docs incomplete

* adjusted parameter name

* adjusted unit tests for mode=new

* adjusted integration tests for mode=new

* added 'russoz' to list of maintainers for cpanm

* Update tests/integration/targets/cpanm/tasks/main.yml

* Update tests/integration/targets/cpanm/tasks/main.yml

* ensuring backward compatibility + tests

* added changelog fragment

* version for new parameter and adjusted example

* typo and formatting

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

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

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

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

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

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

* multiple changes

- some fixes from PR
- supporting tests
- integration is no longer unsupported => destructive, should run on
  apt- and rpm-based systems only

* only run integration tests in redhat-family > v7 or debian-family

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-18 10:55:47 +02:00
Felix Fontein
721589827e Remove ldap_entry's params option completely (#2257)
* Remove ldap_entry's params option completely.

* Regular sanity error fixing.
2021-04-18 10:09:46 +02:00
Fabio Sangiovanni
f9e3e229dd linode_v4: add support for private_ip option. (#2249)
* linode_v4: add support for `private_ip` option.

* linode_v4: remove `required` attribute from `private_ip` parameter.

* linode_v4: add changelog fragment.

* linode_v4: add PR link to changelog fragment.

Co-authored-by: Amin Vakil <info@aminvakil.com>

* linode_v4: add the `version_added` attribute to the `private_ip` section of module documentation

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

* linode_v4: improve styling of `private_ip` docs

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

Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-18 09:45:26 +02:00
rainerleber
1400051890 Fix for Terraform 0.15 (#2246)
* Fix for Terraform 0.15

removed the append of variables in terraform validate because this is deprecated in Terraform 0.15. See: https://github.com/hashicorp/terraform/blob/v0.15/CHANGELOG.md >> The -var and -var-file options are no longer available on terraform validate. These were deprecated and have had no effect since Terraform v0.12

* Create terraform-validate.yaml

* Update and rename terraform-validate.yaml to 2246-terraform-validate.yaml

* Update changelogs/fragments/2246-terraform-validate.yaml

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update terraform.py

remove `-force` add `-auto-approve` on destroy as described in issue #2247

* Update and rename 2246-terraform-validate.yaml to 2246-terraform.yaml

* Update 2246-terraform.yaml

* add a function which check the used tf version

* add a function which check the used tf version

* Update changelogs/fragments/2246-terraform.yaml

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update changelogs/fragments/2246-terraform.yaml

Co-authored-by: Amin Vakil <info@aminvakil.com>

* add version return to function

* changed it to pass sanity check

* change variable name

* changed to a more specialized data types

* remove use_unsafe_shell=True

* Update changelogs/fragments/2246-terraform.yaml

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

* add description

* Update changelogs/fragments/2246-terraform.yaml

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

Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: Rainer Leber <rainer.leber@sva.de>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-17 22:32:54 +02:00
Ajpantuso
118d903e7d New filter plugins: hashids_encode and hashids_decode (#2244)
* New filters hashids_encode and hashids_decode

* Adding changelog

* Correcting whitespace issue in vars file

* Attempt to fix integration test failures

* Correcting copyright

* Addressing initial review comments

* Updating decoded sequence return from tuple to list

* Correcting capitilization and spelling
2021-04-17 20:00:03 +02:00
Felix Fontein
d09bc2525b Fix problems with pip2.6 included in CentOS 6. (#2256) 2021-04-17 19:11:08 +02:00
quidame
3a8206fe62 java_keystore: add certificate_path and private_key_path options (#2230)
* java_keystore: add `certificate_path` and `private_key_path` options

* Update DOCUMENTATION and EXAMPLES accordingly.
* Refactor integration tests to play the same tasks twice.
* Add a changelog fragment (minor_changes).

refactor DOCUMENTATION

* Add useful info for better understanding of what options allow
  keystore regeneration on the fly, and what other options lead the
  module to fail, if their values change.
* Fix indentation and tenses.
* Add myself as author.

* readability-related stuff + changelog fragment
2021-04-17 18:17:53 +02:00
Gonéri Le Bouder
f77aa51ab8 nmcli: do not set IP configuration bond slaves (#2223)
The master interface holds the IP configuration. If we try to update the
IP configuration of the slaves, NetworkManager raises an error.
2021-04-17 10:33:40 +02:00
Jeffrey van Pelt
123b5a9a3c Proxmox inv fix agent string parsing (#2245)
* Added handling for commas in the agent field for agent configuration

* Removed test statement

* Added changelog fragment

* Fixed spelling on fragment :-)
2021-04-17 10:08:41 +02:00
Amin Vakil
085c43b76b Remove unnecessary required=False s in linode_v4 module (#2251) 2021-04-17 09:51:03 +02:00
Alexei Znamensky
69a9a77b65 xenserver_guest - fixed validations (#2243)
* fixed cdrom parameter

* fixed networks and custom_params parameters

* fixed disks parameter

* fixed remaining parameters

* removed plugins/modules/cloud/xenserver/xenserver_guest.py from ignore list

* comments from PR

* comments from PR

* more adjustments from the PR
2021-04-17 09:32:13 +02:00
Brian Coca
f4858d64f4 funcd connection plugin is now usable/loadable (#2235)
* funcd connection plugin is now usable/loadable

* Update changelogs/fragments/allow_funcd_to_load.yml

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-14 09:25:57 +02:00
Amin Vakil
f97d5ca701 Use nomad_job_info in nomad_job_info examples (#2233) 2021-04-14 09:25:40 +02:00
Alexei Znamensky
bfd6d2b3aa jira - fixed isinstance error (#2236)
* fixed isinstance error

* added changelog fragment
2021-04-14 09:11:31 +02:00
Felix Fontein
081c534d40 Remove deprecated modules scheduled for removal in 3.0.0 (#1924)
* Remove deprecated modules scheduled for removal in 3.0.0.

* Update BOTMETA.

* Update ignore-2.12.txt.

* Next release will be 3.0.0.
2021-04-13 11:19:25 +00:00
Brandon McNama
98af8161b2 Add Jira attach operation (#2192)
* Add Jira attach operation

Adds the `attach` operation to the `web_infrastructure.jira` module,
which allows a user to attach a file to an issue. The user can supply
either the path to a file, which will be read from storage, or a file
name and content (as bytes).

* Apply suggestions from code review

* Update plugins/modules/web_infrastructure/jira.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-04-13 07:41:31 +02:00
tgates81
1f001cafd9 spectrum_model_attrs: Initial commit (#1802)
* spectrum_model_attrs: Initial commit

* spectrum_model_attrs: sanity check fixes (1)

* Apply suggestions from code review

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

* Apply suggestions from code review:
 * Removed ANSIBLE_METADATA.
 * List all currently supported names in DOCUMENTATION block.
 * Don't escape declarations that are long enough to fit on one
 line.

* Apply suggestions from code review:
  * YAML bools in DOCUMENTATION block.
  * Various DOCUMENTATION block aesthetics.
  * RETURN block proper format.
  * 'yes' -> True declaration in argument spec.
  * import urlencode from python 2 and 3 changed to
    six.moves.urllib.quote.

* spectrum_model_attrs: integration test added.

* Update plugins/modules/monitoring/spectrum_model_attrs.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Update plugins/modules/monitoring/spectrum_model_attrs.py

Co-authored-by: Amin Vakil <info@aminvakil.com>

* spectrum_model_attrs: lint error fixes.

Co-authored-by: Tyler Gates <tgates@citco.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-04-12 22:26:43 +02:00
Ajpantuso
8ab356520d Proxmox_Inv: Adding agent network interaces fact (#2148)
* Added agent network interaces fact

* Adding changelog fragment

* More concise looping over interfaces

* Adding unit test case for agent interfaces

* Correcting whitespace issue

* Commented new dummy json returns with corresponding method
2021-04-12 22:24:33 +02:00
quidame
89b7e7191f java_keystore: improve error handling and returned results (#2183)
* java_keystore - improve error handling and returned results

* set check_rc=False to return results as documented when module fails
* set LANG, LC_ALL and LC_MESSAGES to C to rely keytool output parsing
* fix pylint's `no-else-return` and `unused-variable` hints
* update related unit tests accordingly
* add a changelog fragment

update unit test (remove stdout_lines from returned dict)

fix unit test: failure is now expected when alias does not exist

* Update changelogs/fragments/2183-java_keystore_improve_error_handling.yml

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

* fix integration test: overwrite keystore at the same location

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-12 21:23:43 +02:00
Andrew Klychkov
7356451aa1 Grant supershipit to new maintainers (#2214)
* Grant supershipit to a new maintainer

* Add maintainer
2021-04-12 10:26:00 +02:00
justchris1
31645ded11 Added modules ipa_otpconfig and ipa_otptoken (#2122)
* Added module for ipa_otpconfig

* Make no_log=False explicit.

* Updated inputs to be int type instead of strings to align to expected inputs.  Updated output message

* Add changelog fragment

* Remove changelog fragment as this is a new module

* Update plugins/modules/identity/ipa/ipa_otpconfig.py

Add version_added field to module description.

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

* Updated punctuation in examples

* Add unit test for ipa_otpconfig

* Add ipa_otptoken module with unit test

* Updated documentation in unit test

* Update plugins/modules/identity/ipa/ipa_otpconfig.py

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

* Update plugins/modules/identity/ipa/ipa_otpconfig.py

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

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Added some documentation updates to make it conform to ansible standards

* Update plugins/modules/identity/ipa/ipa_otptoken.py

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

* Address review comments

Co-authored-by: Chris Costa <chris.costa@compellingtech.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-11 15:25:03 +02:00
Amin Vakil
fa13826273 npm: Add no_bin_links option (#2146)
* Add no-bin-links option to npm

* Add changelog

* Fix changelog format

* Add integration test

* Change node package from thelounge to ncp
2021-04-11 05:58:48 +02:00
justchris1
5502e4ec17 Added fields to the ipa_config module (#2116)
* Added fields to the ipa_config module: ipadefaultprimarygroup, ipagroupsearchfields, ipahomesrootdir, ipamaxusernamelength, ipapwdexpadvnotify, ipasearchrecordslimit, ipasearchtimelimit, ipauserauthtype, ipausersearchfields

* Fixed typos in documentation spec

* Updated a field that was missing the version_added decoration

* Add changelog fragment

* Update plugins/modules/identity/ipa/ipa_config.py

Cleanup example to be consistent with others.

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

* Cleanup example to be consistent with others.

* Fixed changelog fragment

* Updated punctuation in examples

* Switched some elements to use int instead of str, and fixed duplicated example

* Change type of field for ipauserauthtype to list of str, add support for ipaconfigstring and ipakrbauthzdata

* Update fragment to represent adding support for ipaconfigstring and ipakrbauthzdata

* Update changelogs/fragments/2116-add-fields-to-ipa-config-module.yml

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

* Update plugins/modules/identity/ipa/ipa_config.py

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

* Update plugins/modules/identity/ipa/ipa_config.py

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

* Address review comments by making inputs into group search and user search fields a list of strings, even though IPA does not treat it as a multiselect field

* Update plugins/modules/identity/ipa/ipa_config.py

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

* Update plugins/modules/identity/ipa/ipa_config.py

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

Co-authored-by: Chris Costa <chris.costa@compellingtech.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-11 05:57:36 +02:00
Álvaro Torres Cogollo
8eb2331aea Bugfix: PyGithub does not support explicit port in base_url (#2204)
* Bugfix: PyGithub does not support explicit port in base_url

* Fix unit tests

* Fix unit tests

* Added changelog

* Update changelogs/fragments/2204-github_repo-fix-baseurl_port.yml

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

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2021-04-09 06:00:21 +02:00
Florian Dambrine
f0b7c6351e New module: Add Pritunl VPN organization module (net_tools/pritunl/) (#804) 2021-04-08 22:37:06 +02:00
Ilija Matoski
4b71e088c7 inventory/proxmox: added constructable and added keyed_groups, groups and compose (#2180)
* added constructable and added keyed_groups, groups and compose

* Update changelogs/fragments/2162-proxmox-constructable.yml

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

* added constructed to extends_documentation_fragment and version_added to all the items

* renamed _apply_rules to _apply_constructable for more clarity

* Update changelogs/fragments/2162-proxmox-constructable.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-08 22:32:18 +02:00
Alexei Znamensky
0cd0f0eaf6 module_helper - fixed decorator cause_changes (#2203)
* fixed decorator cause_changes

* added changelog fragment

* typo
2021-04-08 07:58:39 +02:00
Felix Fontein
b6ae47c455 Add dict filter (#2171)
* Add dict and list_to_dict filters.

* Remove list_to_dict filter.

* Add myself as maintainer.
2021-04-08 07:50:28 +02:00
Felix Fontein
4b6722d938 Add path_join compatibility shim (#2172)
* Add path_join compatibility shim.

* Add myself as maintainer.
2021-04-08 07:49:57 +02:00
justchris1
595d590862 Fix issue where multiselect field in userauthtype did not allow multiple values (#2174)
* Fix issue where multiselect field in userauthtype did not allow multiple values

* Add changelogfragment for change

* Update changelogs/fragments/2174-ipa-user-userauthtype-multiselect.yml

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

* Update plugins/modules/identity/ipa/ipa_user.py

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

* Update changelogs/fragments/2174-ipa-user-userauthtype-multiselect.yml

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

Co-authored-by: Chris Costa <chris.costa@compellingtech.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-07 20:57:04 +02:00
justchris1
7f91821bcc ipa_user sshpubkey can now support multi word comments in the key (#2159)
* ipa_user sshpubkey can now support multi word comments in the key

* Add documentation fragment for pull request

* Update changelogs/fragments/2159-ipa-user-sshpubkey-multi-word-comments.yaml

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

* Cleaner implementation of multi word comments

Co-authored-by: Chris Costa <chris.costa@compellingtech.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-07 19:32:45 +02:00
absynth76
40ce0f995b Update java_cert module (#2008)
* porting https://github.com/ansible/ansible/pull/56778 as requested in https://github.com/ansible-collections/community.general/issues/821

* fix imports, add back trust_cacerts option

* try to fix import, ansible-lint fixes

* modify import to use ansible.module_utils.six instead

* cleanup indentation for tests/integration/targets/java_cert/tasks/main.yml file

* remove external crypto dependency - switch to openssl, work on password obfuscation, using files compare to reduce logic

* java_cert - remove latest run_command using password in arguments

* fix sanity check

* rename changelog fragment file - wrong extension

* add openssl dependency

* fix openssl_bin parameter missing on _get_digest_from_x509_file function call

* remove useless close files, fix paragraph, fix changelog, clean import re

* fix missing dots at end-of-line in changelogs fragments

* fix reminder case

* fix changelog

* restore .gitignore

* fix indentation on integration test files, delete useless json file

* fix typo importing tasks in tests/integration/targets/java_cert/tasks/main.yml

* Update changelogs/fragments/2008-update-java-cert-replace-cert-when-changed.yml

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

* Update tests/integration/targets/java_cert/tasks/state_change.yml

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

* Update plugins/modules/system/java_cert.py

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

* Update plugins/modules/system/java_cert.py

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

* Update plugins/modules/system/java_cert.py

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

* Update plugins/modules/system/java_cert.py

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

* Update plugins/modules/system/java_cert.py

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

* Update plugins/modules/system/java_cert.py

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

* Update plugins/modules/system/java_cert.py

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

* fix hardcoded executable keytool, use re.sub instead of import, add required cert_url or cert_alias parameter when absent, fix python script and cert_url test

* fix pylint issue with setupSSLServeR.py

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-07 19:31:58 +02:00
Norman Ziegner
7145204594 Fix HAProxy draining (#1993)
* Fix HAProxy draining by manually entering the 'MAINT' state

Inspired by rldleblanc: https://github.com/ansible/ansible/issues/37591#issuecomment-610130611

Signed-off-by: Norman Ziegner <norman.ziegner@ufz.de>

* Add changelog fragment

Signed-off-by: Norman Ziegner <norman.ziegner@ufz.de>

* Fix drain function docstring

Signed-off-by: Norman Ziegner <norman.ziegner@ufz.de>

* Fix typos

Signed-off-by: Norman Ziegner <norman.ziegner@ufz.de>

* Update changelog fragment

Signed-off-by: Norman Ziegner <norman.ziegner@ufz.de>
2021-04-07 19:29:10 +02:00
Alexei Znamensky
beb3b85a4f jira - changing the logic for transition (#1978)
* attempt at fixing the issue

* Update plugins/modules/web_infrastructure/jira.py

* Fixed setting of "fields" element in the payload

* added changelog fragment

* added accountId parameter + minor fixes in docs

* added integration test for jira

* adjustments per PR

* Update plugins/modules/web_infrastructure/jira.py

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

* Update plugins/modules/web_infrastructure/jira.py

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

* Update plugins/modules/web_infrastructure/jira.py

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

* Update plugins/modules/web_infrastructure/jira.py

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

* Update plugins/modules/web_infrastructure/jira.py

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

* adjustments per PR

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-07 08:14:03 +02:00
Alexei Znamensky
9aec9b502e Applying ModuleHelper variable mgmt to xfconf -> improvements on MH (#2188)
* applying MH variable mgmt to xfconf - improvements on MH

* added changelog fragment
2021-04-06 21:21:05 +02:00
Alexei Znamensky
9a5191d1f9 xfconf - state absent was not honoring check_mode (#2185)
* state absent was not honoring check_mode

* added changelog fragment
2021-04-06 12:49:50 +02:00
Felix Fontein
6bea8215c9 ansible/ansible's stable-2.11 branch has been created. (#2130) 2021-04-06 07:32:59 +02:00
quidame
eb851d4208 replace inline clear password by environment variable (#2177)
* replace inline clear password by environment variable on a per-command basis.

* add changelog fragment
* update related unit tests

* Update changelogs/fragments/2177-java_keystore_1668_dont_expose_secrets_on_cmdline.yml

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

* fix unit test: force result without lambda

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-05 16:37:13 +02:00
Alexei Znamensky
d2070277e8 ModuleHelper variables management (#2162)
* added metadata for variables in module helper

* adjustments

* added separate support for tracking changes

* rewrote the diff code

* added integration test for module_helper

* using ansible.module_utils.common.dict_transformations.dict_merge

* improved dependency management

* restore ModuleHelper to base classes of CmdStateModuleHelper

* added assertions to ensure the failing module name appears in the error messages

* added test code for state-based modules

* fixed test name

* renamed class to VarMeta

* small fixes

* fixes from the PR

* fixed VarDict.__set_attr__

* added VarDict.__getitem__()

* added changelog fragment

* adjustments per PR

* ModuleHelper.output is now aware of conflicting variable names

* Update plugins/module_utils/module_helper.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-05 15:19:13 +02:00
quidame
533e01a3f9 java_keystore/fix 1667 improve temp files storage (#2163)
* improve temporary files storage (naming/removal)

* update unit tests

* Update changelogs/fragments/2163-java_keystore_1667_improve_temp_files_storage.yml

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

* add dedicated function to randomize PKCS#12 filename

fix unit tests (also mock the new function)

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-05 14:40:36 +02:00
Alexei Znamensky
b81a7cdd16 removed unreachable code (#2157)
* removed unreachable code

* added changelog fragment
2021-04-05 09:22:28 +02:00
Alexei Znamensky
b97e31dd55 rewritten as list literals (#2160)
* rewritten as list literals

* added changelog fragment
2021-04-05 09:22:06 +02:00
Alexei Znamensky
d92d0632eb fixed calls to list.extend() (#2161)
* fixed calls to list.extend()

* added changelog fragment

* Update changelogs/fragments/2161-pkgutil-list-extend.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-04 23:38:59 +02:00
Felix Fontein
95156a11a1 [WIP] Committer guidelines (#2077)
* First idea for committer guidelines.

* Update commit-rights.md

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

* Update commit-rights.md

Co-authored-by: John R Barker <john@johnrbarker.com>

* Apply suggestions from code review

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

* Improve 'do not' list.

* Add improvements from ansible/ansible#73782.

* Apply suggestions from code review

Co-authored-by: Amin Vakil <info@aminvakil.com>

* Apply suggestions from code review

* Update commit-rights.md

* Update commit-rights.md

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
Co-authored-by: John R Barker <john@johnrbarker.com>
Co-authored-by: Amin Vakil <info@aminvakil.com>
2021-04-03 11:46:02 +02:00
Alexei Znamensky
c8885fdfbd using get_bin_path() on atomic modules (#2144)
* using get_bin_path() on atomic modules

* added changelog fragment

* Update changelogs/fragments/2144-atomic_get_bin_path.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-02 22:06:07 +02:00
Alexei Znamensky
3312ae08af kibana_plugin: fixed remove call + run_command with list instead of str (#2143)
* fixed remove call + run_command with list instead of str

* fixed the other calls to run_command()

* added changelog fragment

* adjustment on run_command params

* Update changelogs/fragments/2143-kibana_plugin-fixed-function-calls.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-04-02 22:05:28 +02:00
Alexei Znamensky
1d1cbc4f56 apache2_mod_proxy - minor improvements/fixes (#2142)
* minor improvements/fixes

- moved imports from the bottom of the code to the top (ansible-style).
- pythonified/simplified get_member_status()/set_member_status()
- reduced clutter in Balancer.__init__()

* added changelog fragment
2021-04-01 07:50:24 +02:00
Alexei Znamensky
f1dbef4143 fixed str formatting (#2139) 2021-03-31 13:05:40 +02:00
Gaige B Paulsen
604a5dbf49 fix type information for vmadm.resolvers (#2136)
* fix type information for vmadm.resolvers

* Update changelogs/fragments/2135-vmadm-resolvers-type-fix.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-31 09:25:28 +00:00
Felix Fontein
3355e65781 Next release will be 2.5.0. 2021-03-30 12:19:34 +02:00
zhcli
19db6f24f7 Callback plugin: Azure Log Analytics (#2091)
* adding plugins/callback/loganalytics.py

* * fixed sanity check issues
* adjusted documentation and license sections

* added changelogs fragment

* * added unit test
* documentation updated

* updated changelogs

* further docuement update

* minor fixes

* updated unittest

* suggested updates from community

* remove AnsibleError section
2021-03-30 12:12:26 +02:00
Alexei Znamensky
eb24e33666 git_config - fixed bug with scope file (#2125)
* fixed bug

- scope file was not working
- added test to guarantee that behaviour
- marked integration test as destructive, because it overwrites ~/.gitconfig

* added changelog fragment

* Update tests/integration/targets/git_config/tasks/setup_no_value.yml

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

* Update tests/integration/targets/git_config/tasks/get_set_state_present_file.yml

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

* Update tests/integration/targets/git_config/tasks/get_set_state_present_file.yml

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

* Update tests/integration/targets/git_config/aliases

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

* Update changelogs/fragments/2125-git-config-scope-file.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-30 08:24:08 +02:00
Amin Vakil
73bb0f1900 vdo: add force option (#2110)
* vdo: add force option

* Add changelog

* Improve the diff the next time something is added :)

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

* Add warning text placeholder by felixfontein

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

* Add warning text

* Apply suggestion for warning text from rhawalsh

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-27 15:13:53 +01:00
Bill Dodd
0de196413f remove billdodd from team_redfish (#2118) 2021-03-26 19:02:59 +01:00
Andrew Klychkov
0bc76c98b0 AZP: update default container version (#2112) 2021-03-26 12:33:11 +01:00
Georg Gadinger
cdc415ea1f opennebula: add one_template module (#2046)
* opennebula: add one_template module

A basic module for maintaining VM templates which should be flexible enough
for most needs ...

* fixup! opennebula: add one_template module

* fixup! fixup! opennebula: add one_template module
2021-03-26 07:24:24 +01:00
Stefan Richter
e7a0a12c3f Bugfix: Respect PATH env variable in zypper modules (#2094)
* Bugfix: Respect PATH env variable in zypper modules

* Improve changelogs/fragments/2094-bugfix-respect-PATH-env-variable-in-zypper-modules.yaml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-25 22:30:35 +01:00
Andy Walsh
62cd38a9a0 Updated vdo maintainer to rhawalsh. (#2102)
bgurney-rh does not work with VDO projects anymore.  This change re-points
maintainer pings to rhawalsh instead.
2021-03-25 22:22:50 +01:00
Alex Willmer
2558cd3f01 ipa_service - Correct pluralisation of "hosts" in example (#2103) 2021-03-25 22:19:48 +01:00
Matthias Dellweg
de8e2a83e2 Add a Pulp 2 related note to pulp_repo (#2096) 2021-03-24 17:48:24 +01:00
Dag Wieers
db26514bf1 Add support for sudo su - using password auth (#2054)
* Add support for `sudo su -` using password auth

Allow users to run Ansible tasks through `sudo su -` using password auth

- Feature Pull Request

sudosu

So I have been using this at various customers for bootstrapping Ansible mostly.

Often you have an existing setup where there is a user that has root-access enabled through sudo, but only to run `su` to log using the user's password.
In these specific cases the root password is unique to the system and therefore not an easy way to automate bootstrapping.

Having a `sudo su -` become option **with password prompt** is not possible with the existing become methods (neither sudo nor su can be used) by abusing `become_exe` or `become_flags`.

This fixes ansible/ansible#12686

* Fix all reported issues

* Add unit tests

* Apply suggestions from code review

* Update plugins/become/sudosu.py

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

* Update tests/unit/plugins/become/test_sudosu.py

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

* Update tests/unit/plugins/become/test_sudosu.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-24 17:20:26 +01:00
panyy3
04f46f0435 add new module xcc_redfish_command to manage Lenovo servers using Redfish APIs (#2007)
* add new module xcc_redfish_command to manage Lenovo servers using Redfish APIs

* Update plugins/modules/remote_management/lenovoxcc/xcc_redfish_command.py

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

* fix some errors detected by ansible-test sanity

* end all descriptions (except short_description) with a period

* fix return definition problem and other errors detected by ansible-test sanity

* Always use true/false for booleans in YAML

* It is usually a good idea to leave away required: false

* fix errors detected by ansible-test sanity

* fix elements of command is not defined

* check whether resource_uri is specified for Raw commands

* if no Members property, return false; if empty array, return true

* get @odata.etag from patch body instead of getting again

* add request_body checking

* add unit test for the module

* fix errors detected by ansible-test sanity --test pep8

* update class name xcc_RedfishUtils to XCCRedfishUtils to follow convention; import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args, exit_json, fail_json from ansible_collections.community.general.tests.unit.plugins.modules.utils instead of inline them

* support using security token for auth

* fix line too long error

* As 2.3.0 got released yesterday, move to 2.4.0

* add maintainers for lenovoxcc

* update to make sure that it's sorted alphabetically

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-24 15:07:17 +01:00
Alexei Znamensky
94cf07efbf fixed documentation for oneview modules (#2092) 2021-03-24 14:16:33 +01:00
Georg Gadinger
926c0a71d0 opennebula: port one_image to pyone (#2032) 2021-03-24 07:32:12 +01:00
Felix Fontein
be13f41b30 Next expected release is 2.4.0. 2021-03-23 13:30:44 +01:00
Felix Fontein
7fe9dd7a60 Install collections in CI directly with git to work around the Galaxy CloudFlare PITA. (#2082) 2021-03-23 07:08:04 +01:00
Felix Fontein
09351d9010 Temporarily disable copr integration tests due to failures with remote repository. (#2083) 2021-03-23 06:50:50 +01:00
Alexei Znamensky
88994ef2b7 Fixed documentation (#2062) 2021-03-22 18:51:27 +01:00
Triantafyllos
af441aecfc improve force_archive parameter documentation of archive module (#2052)
* improve documentation for force_archive parameter

* add link to unarchive module

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-22 18:27:55 +01:00
Alexei Znamensky
5fc56676c2 stacki_host - configured params to use fallback instead of default (#2072)
* configuredd params to use fallback instead of default

* added changelog fragment
2021-03-21 13:56:41 +01:00
Ajpantuso
6529390901 New Filter plugin from_csv (#2037)
* Added from_csv filter and integration tests

* Cleaning up whitespace

* Adding changelog fragment

* Updated changelog fragment name

* Removed temp fragment

* Refactoring csv functions Part 1

* Syncing refactored csv modules/filters

* Adding unit tests for csv Module_Util

* Updating changelog fragment

* Correcting whitespace in unit test

* Improving changelog fragment

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

* Update changelogs/fragments/2037-add-from-csv-filter.yml

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-21 13:21:54 +01:00
Alexei Znamensky
c147d2fb98 snmp_facts - added timeout and retries params to module (#2065)
* added timeout and retries params to module

* added changelog fragment

* Update plugins/modules/net_tools/snmp_facts.py

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

* Update plugins/modules/net_tools/snmp_facts.py

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

* removed default for retries per suggestion in PR

* Update plugins/modules/net_tools/snmp_facts.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-21 11:37:12 +01:00
Florian Dambrine
68fc48cd1f New module: Add Pritunl VPN user module (net_tools/pritunl/) (#803) 2021-03-21 11:30:16 +01:00
Gregory Thiemonge
81f3ad45c9 Add gandi_livedns module (#328)
* Add gandi_livedns module

This module uses REST API to register, update and delete domain name
entries in Gandi DNS service (https://www.gandi.net/en/domain).

* Apply suggestions from code review

* Update plugins/module_utils/gandi_livedns_api.py

Co-authored-by: Gregory Thiemonge <greg@thiemonge.org>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-21 11:25:24 +01:00
Alexei Znamensky
606eb0df15 archive - a first refactoring (#2061)
* a first refactoring on archive

* added changelog fragment

* suggestion from PR
2021-03-21 11:12:41 +01:00
quasd
ff9f98795e ipa_sudorule add support for setting runasextusers (#2031)
* Add support for setting runasextusers

* fix formatting

* add changelog fragment

* Update plugins/modules/identity/ipa/ipa_sudorule.py

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

* Update changelogs/fragments/2031-ipa_sudorule_add_runasextusers.yml

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

Co-authored-by: quasd <qquasd@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-21 11:09:49 +01:00
Alexei Znamensky
f5a9584ae6 archive - created an integration test that archives broken links (#2063)
* created an integration test that archives broken links

* sanity fix
2021-03-21 09:51:07 +01:00
Felix Fontein
24f8be834a Fix nios modules to work with ansible-core 2.11 (#2057)
* Fix nios modules to work with ansible-core 2.11.

* Adjust tests.
2021-03-20 13:45:42 +01:00
Mike Raineri
a23fc67f1f Adding xmadsen and renxulei as Redfish maintainers (#2047) 2021-03-20 09:57:38 +01:00
Bill Dodd
efd441407f Add support for Redfish session create, delete, and authenticate (#2027)
* Add support for Redfish session create and delete

* add changelog fragment

* Apply suggestions from code review

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-19 13:14:33 -07:00
Ajpantuso
79fb3e9852 Adding purge parameter to proxmox for use with lxc delete requests (#2013)
* added purge as optional module parameter

* Adding changelog fragment

* Adding version to documentation for purge

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

* Updating changelog

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-19 19:18:05 +01:00
Bill Dodd
0b2ebabd29 Fix IndexError in SetManagerNic (#2040)
* fix IndexError in SetManagerNic

* add changelog fragment
2021-03-19 19:06:57 +01:00
Charlie Kenney
8225b745f3 update linode team (#2039) 2021-03-18 23:20:12 +01:00
abarbare
fe61be3e11 fix: scaleway inventory pagination (#2036)
* fix: scaleway inventory pagination

* add changelog

* Update changelogs/fragments/2036-scaleway-inventory.yml

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

Co-authored-by: Antoine Barbare <abarbare@online.net>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-18 23:06:56 +01:00
Alexei Znamensky
4fbef900e1 Improvements and fixes to ModuleHelper, with (some) tests. (#2024)
* Improvements and fixes to ModuleHelper, with (some) tests.

* added changelog fragment

* adjusted changelog frag - get_bin_path() handling is actually a bugfix
2021-03-17 13:39:49 +01:00
Ajpantuso
0f61ae4841 Adding tags as module parameter to proxmox_kvm (#2000)
* Adding tags as module parameter

* Added changelog fragment

* Correcting typo in changelog fragment

* Correcting punctuation in docs

* Including version to tags parameter description

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

* Correct tag validation and parsing logic condition

Original test was for key and not value

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

* Improving usability with default null behavior

* Removing default case and related unneccessary complexity

* Display regex in tags description as code

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-15 13:39:23 +01:00
dacodas
3162ed6795 allow passing the --allow-root flag to kibana_plugin module (#2014)
* kibana_plugin module parameter force is a boolean

* allow passing the --allow-root flag to kibana_plugin module

* add changelog fragment for kibana_plugin --allow-root

Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: Felix Fontein <felix@fontein.de>

Co-authored-by: Amin Vakil <info@aminvakil.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-15 13:35:34 +01:00
Alexei Znamensky
84b54ad6a2 rax - removed service parameter (#2020)
* removed service parameter

* added changelog fragment
2021-03-14 12:22:03 +01:00
Alexei Znamensky
f8859af377 Tidy up sanity checks ignore lines modules (batch 8) (#2006)
* fixed validation-modules for plugins/modules/cloud/smartos/smartos_image_info.py

* fixed validation-modules for plugins/modules/cloud/rackspace/rax_scaling_group.py

* fixed validation-modules for plugins/modules/cloud/rackspace/rax_cdb_user.py

* fixed validation-modules for plugins/modules/cloud/rackspace/rax.py

* Tidy up sanity checks ignore lines modules (batch 8)

* added changelog fragment

* rolled back removal of parameter from rax.py
2021-03-14 10:53:49 +01:00
Felix Fontein
49d9a257ef More false-positives (not flagged by sanity tests yet). (#2010) 2021-03-13 13:24:26 +01:00
Ajpantuso
4676ca584b Remove password requirement when creating lxc containers (#1999)
* Removed requirement for password

* Updated documentation for password

* Adding changelog fragment

* Update changelogs/fragments/1999-proxmox-fix-issue-1955.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-12 19:17:41 +01:00
Felix Fontein
1ea080762b Mark non-secret leaking module options with no_log=False (#2001)
* Mark non-secret leaking module options with no_log=False.

* Add changelog fragment.
2021-03-12 08:51:47 +01:00
Jeffrey van Pelt
178209be27 Excluded qemu templates in pools (#1991)
* Excluded qemu templates in pools

* Added changelog fragment

* Made check more robust
2021-03-12 08:00:03 +01:00
Ajpantuso
d0bb74a03b Allow tags strings containing commas in proxmox inventory plug-in (#1949)
* Included explicit parsing for proxmox guest tags and updated corresponding unit test with tags key

* Including changelog fragment for PR 1949

* Removed ellipsis from test

Proxmox only permits periods when surrounded by alphanumeric characters

* Corrected punctuation for changelog entry

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

* Allowing tags string to contain commas

* Incorporated new parsed tags fact with bugfix

* Correcting whitespace issues

* Update changelogs/fragments/1949-proxmox-inventory-tags.yml

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

* Update plugins/inventory/proxmox.py

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

* Update changelogs/fragments/1949-proxmox-inventory-tags.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-11 22:08:11 +01:00
Alexei Znamensky
7452a53647 jenkins_job - added validate_certs parameter, setting the PYTHONHTTPSVERIFY env var (#1977)
* added validate_certs parameter, setting the PYTHONHTTPSVERIFY env var

* added changelog fragment

* Update plugins/modules/web_infrastructure/jenkins_job.py

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

* Update plugins/modules/web_infrastructure/jenkins_job.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-11 22:06:57 +01:00
Felix Fontein
36daa7c48e Remove deprecated features scheduled for removal in 3.0.0 (#1926)
* Remove deprecated features.

* Remove ignore.txt entries.

* Update changelogs/fragments/remove-deprecated-features.yml

Co-authored-by: Joe Adams <adams10301@gmail.com>

Co-authored-by: Joe Adams <adams10301@gmail.com>
2021-03-09 21:23:20 +01:00
Felix Fontein
1ca9229c66 Next expected release is 2.3.0. 2021-03-08 12:40:13 +01:00
Alexei Znamensky
2906591c08 Tidy up sanity checks ignore lines modules (batch 7) (#1970)
* fixed validation-modules for plugins/modules/cloud/heroku/heroku_collaborator.py

* fixed validation-modules for plugins/modules/cloud/linode/linode_v4.py

* fixed validation-modules for plugins/modules/remote_management/manageiq/manageiq_provider.py

* fixed validation-modules for plugins/modules/remote_management/manageiq/manageiq_policies.py

* fixed validation-modules for plugins/modules/remote_management/manageiq/manageiq_alert_profiles.py

* fixed validation-modules for plugins/modules/remote_management/manageiq/manageiq_tags.py

* fixed validation-modules for plugins/modules/cloud/opennebula/one_host.py

* fixed validation-modules for plugins/modules/cloud/opennebula/one_image_info.py

* fixed validation-modules for plugins/modules/cloud/opennebula/one_vm.py

* fixed validation-modules for plugins/modules/cloud/scaleway/scaleway_lb.py

* fixed validation-modules for plugins/modules/cloud/scaleway/scaleway_compute.py

* fixed validation-modules for plugins/modules/remote_management/oneview/oneview_network_set_info.py

* fixed validation-modules for plugins/modules/remote_management/oneview/oneview_ethernet_network_info.py

* fixed validation-modules for plugins/modules/remote_management/oneview/oneview_datacenter_info.py

* fixed validation-modules for plugins/modules/remote_management/oneview/oneview_enclosure_info.py

* Tidy up sanity checks ignore lines modules (batch 7)

* added changelog fragment

* Missed a couple of lines in ingnore-2.11.txt

* fixed validation-modules for plugins/modules/cloud/packet/packet_volume_attachment.py

* Adjusted ignore files and changelog for packet_volume_attachment.py

* Rolled back ignore line for linode module

* Update plugins/modules/cloud/opennebula/one_image_info.py

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

* fixes from the PR

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-08 08:35:09 +01:00
Alexei Znamensky
088743749b ini_file - allows adding empty string as a value (#1972)
* Added integration test and fixed bug

* added changelog fragment

* Update changelogs/fragments/1972-ini_file-empty-str-value.yml

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

* Update tests/integration/targets/ini_file/tasks/main.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-08 06:57:40 +01:00
phospi
a013e69d67 Extending manageiq modules with parameter resource_id (#719)
* Extending modules with resource_id

* Added documentation

* Fixed syntax
Changed resource_type back to required true
Added description identifier

* Added changelog fragment.

* fixed syntax

* Improved changelog fragment content.

* Updated description

* Changed if statement

* Changed changelog fragement filename

* version bump

* removed duplicate type

* Apply suggestions from code review

* Update plugins/modules/remote_management/manageiq/manageiq_tags.py

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-08 06:56:34 +01:00
Felix Fontein
ff4e4c055c Remove part of shippable config that's not needed for bot. 2021-03-06 14:05:56 +01:00
Álvaro Torres Cogollo
53c6b49673 Added new module github_repo (#1683)
* Added new module github_repo

* Fixed sanity errors

* Fixed sanity errors

* Unit tests for github_repo module

* Fixed import-before-documentation

* Added PyGithub dependency for unit tests

* Fixed errata

* Require Python >= 2.7

* Support for check_mode and other improvements

* Fixed import-before-documentation

* Improved module parameter requirements, check mode and docs

* Code improvements

* Fixed version tag
2021-03-06 13:30:43 +01:00
Alexei Znamensky
7425e9840d Deprecation of parameters triggering the parameter-invalid sanity-check (#1927)
* fixed validation-modules for plugins/modules/packaging/language/composer.py

* fixed validation-modules for plugins/modules/packaging/os/apt_rpm.py

* fixed validation-modules for plugins/modules/packaging/os/homebrew.py

* fixed validation-modules for plugins/modules/packaging/os/homebrew_cask.py

* fixed validation-modules for plugins/modules/packaging/os/opkg.py

* fixed validation-modules for plugins/modules/packaging/os/pacman.py

* fixed validation-modules for plugins/modules/packaging/os/slackpkg.py

* fixed validation-modules for plugins/modules/packaging/os/urpmi.py

* fixed validation-modules for plugins/modules/packaging/os/xbps.py

* fixed validation-modules for plugins/modules/source_control/github/github_deploy_key.py

* fixed validation-modules for plugins/modules/system/puppet.py

* added changelog fragment

* adjustments from PR + fixes in changelog frag

* fixed deprecation of param "show_diff" in module "puppet"

* Update changelogs/fragments/1927-removed-parameter-invalid.yml

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

* Update plugins/modules/system/puppet.py

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

* removed unnecessary ignore lines, adjustment in changelog frag

* no need to explicitly call deprecate() when param marked for removal

* Update changelogs/fragments/1927-removed-parameter-invalid.yml

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

* Adjustments in changelog fragment, per PR

* bumping deprecation to 7.0.0

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-05 08:17:36 +01:00
John Barker
1133e5c865 Fix CoC links 2021-03-04 19:56:29 +00:00
John R Barker
f49cf2c22d Add other GH Issue forms (#1968)
* Add other GH Issue forms

* review comments
2021-03-04 18:34:04 +00:00
John R Barker
5fdbe084e7 bug_report: missing label 2021-03-04 12:08:16 +00:00
John R Barker
e9866a2ccd bug_report issue form (#1966)
* Bring inline with ansible/ansible's issue template.
* Add more placeholders
2021-03-04 12:06:25 +00:00
phospi
ac95ff5b45 Bugfix/manageiq inventory (#720)
* Extending modules with resource_id

* Added documentation

* Revert previous PR

* Added filter for active vm's

* Added changelog fragment

* Update changelogs/fragments/720-cloudforms_inventory.yml

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-04 07:51:39 +01:00
Ox
dec345b818 Fix: nmcli - Ensure slave-type for bond-slave (#1882)
* Fix: nmcli - Ensure slave-type for bond-slave

Hello 🙂 

When using bond-slave type, by default command sent to nmcl is:

['/usr/bin/nmcli', 'con', 'add', 'type', 'bond-slave', 'con-name', 'enp129s0f0', 'connection.interface-name', 'enp129s0f0', 'connection.autoconnect', 'yes', 'connection.master', 'bond0']

Which is not enough, nmcli will complain that connection.slave-type is missing. This small fix solve this issue.

If this change is approved, I will add the changelog fragment.

* Fix: nmcli - Adding changelog fragment for 1882

* Update changelogs/fragments/1882-fix-nmcli-ensure-slave-type-for-bond-slave.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-04 07:49:38 +01:00
sam-lunt
ce5aea790d zfs: avoid errors with creation-only properties (#1833)
* avoid errors with creation-only properties

* add changelog fragment

* Apply suggestion to changelog fragment

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-04 07:47:45 +01:00
Brett
ad8aa1b1e6 Jenkins build module (#745)
* Jenkins build module

A module for queuing and deleting jenkins builds.

* CI fixes

* More CI fixes.

* Even more CI fixes

* Fixing symlink

* Update plugins/modules/web_infrastructure/jenkins_build.py

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

* removed ansible meta section

* Added unit tests.

* fix tests

* more test fixes.

* Completed tests.

Mocked jenkins api calls.
Fixed some logging.

* Update plugins/modules/web_infrastructure/jenkins_build.py

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

* Update plugins/modules/web_infrastructure/jenkins_build.py

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

* Update plugins/modules/web_infrastructure/jenkins_build.py

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

* Update plugins/modules/web_infrastructure/jenkins_build.py

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

* Cleaned up default items

And removed supports check mode flag.

* setting name param required

* Update plugins/modules/web_infrastructure/jenkins_build.py

Co-authored-by: Brett Milford <brettmilford@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
2021-03-04 07:46:37 +01:00
atbore-phx
3f882ee6a2 consul_io inventory script: fixes awx and python 3 kv_group (#620)
* feat(env): extending configuration options by env variables

* feat(env): extending config option docs

* feat(consul_io): fix byte chain decoding for python3

* fix(pep8): E128

* changelog added

* Update changelogs/fragments/620-consul_io-env-variables-conf-based.yml

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

* Update changelogs/fragments/620-consul_io-env-variables-conf-based.yml

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

* Update changelogs/fragments/620-consul_io-env-variables-conf-based.yml

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

* fix(typo): adding dots

Co-authored-by: Davy Bondeau <davy.bondeau@gmail.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-04 07:45:05 +01:00
Jim Speir
677ab8e383 Adding another example for tss lookup (#1945)
* Adding another example for tss lookup

A more detailed example using self-hosted secrets server as investigated in #1943

* Update plugins/lookup/tss.py

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

* Better line breaking

* Update plugins/lookup/tss.py

Seconded!

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

* Remove newline to pass tests

* Update plugins/lookup/tss.py

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-02 21:58:55 +01:00
Ox
4f98136771 Feat: nmcli - Add method4 and method6 (#1894)
* Feat: nmcli - Add method4 and method6

Allows to manipulate ipv4.method and ipv6.method.

Is mandatory to manage Bond interfaces with no native vlans but only tagged vlans.

* Fix: nmcli - Add changelog fragment for 1894

* Fix: nmcli - Add choices for method4 and method6

* Update plugins/modules/net_tools/nmcli.py

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

* Update changelogs/fragments/1894-feat-nmcli-add-method4-and-method6.yml

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

* Update plugins/modules/net_tools/nmcli.py

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

* Fix: nmcli - Update documentation

* Update plugins/modules/net_tools/nmcli.py

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

* Update plugins/modules/net_tools/nmcli.py

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

* Fix: nmcli - Simplify code

* Update plugins/modules/net_tools/nmcli.py

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

* Fix: nmcli - Update ip6 documentation

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-03-02 12:46:21 +01:00
Alexei Znamensky
585dd0b6ed Improved ModuleHelper.run_command() (#1867)
* Improved run_command signature and behaviour

- extra_params has been removed from the signature
- params now can be either str or dict (containing the param value)

* Reverted the removal of the method parameter, and added changelog fragment

* Update changelogs/fragments/1867-modhelper-cmdmixin-dict-params.yml

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

* Update plugins/module_utils/module_helper.py

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

* adjustement per PR

* Update plugins/module_utils/module_helper.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-28 15:06:36 +01:00
Felix Fontein
bec43041a9 grove: message -> message_content (#1929)
* grove: message -> message_content

* Remove no longer needed ignore.txt lines.
2021-02-27 21:23:14 +01:00
Felix Fontein
b4c136125e Actually use option. (#1928) 2021-02-27 21:23:07 +01:00
Eric L
4a8d6cf7cc Add version_sort filter to properly sort list of versions (#1916)
* Add version_sort filter to properly sort list of versions

* Fix all comments from Felix

- add changelog fragment
- fix test by removing runme.sh/yml and renaming to filter_version_sort
- use fully qualified name of filter in test case

* Remove wrong plugin.test changelog fragment

Ups...

* Properly name the file version_sort.py

* Update changelogs/fragments/1916-add-version-sort-filter.yml

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

Co-authored-by: Eric L <ewl+git@lavar.de>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-27 17:42:45 +01:00
Jeffrey van Pelt
20bd065e77 Proxmox inventory: Add some sanitization to url parameter (#1914)
* Added rstrip to the URL field to prevent issues when users add a trailing / in the config of this module

* Added changelog fragment

* Sorry Mr. Linter, I have removed the empty line :-)

* Fixed punctuation

* Fixed punctuation
2021-02-27 17:06:00 +01:00
William Leemans
ea65ce8e0d bugfix: xfs_quota feedback on projects not initialized has changed (#1596)
* bugfix: xfs_quota feedback on projects not initialized has changed

* changelog fragment

* Update changelogs/fragments/1596-xfs_quota-feedback_on_projects_not_initialized_has_changed.yml

Thanks for this, felixfontein

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

* xfs_quota is not necessarily in PATH

* pep8 and formatting

* Test was wrong. It needs to be changed

* formatting

* pep8 and formatting

* xfs_quota is not necessarily in PATH

* pep8 and formatting

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-27 08:38:24 +01:00
Amin Vakil
811b609b05 yum_versionlock: do lock/unlock concurrently (#1912)
* Change all packages at once in yum_versionlock module

* Re-enable tests

* Convert package list to packages string

* Fix module

* Change variable name to make it appropriate

* Fix module check_mode

* Revert "Fix module" and apply felixfontein suggestion

This reverts commit 5936da3198.

* Rename package to packages

* Only change packages which are needed

* Ignore if list is empty

* Add changelog
2021-02-26 23:16:40 +01:00
Felix Fontein
5447910a0b Improve meta/runtime.yml deprecation messages. (#1918) 2021-02-26 21:35:53 +01:00
Alexei Znamensky
76d9fe4ec6 Tidy up sanity checks (2021/Batch 3 - who's counting) (#1885)
* fixed validation-modules for plugins/modules/packaging/language/pip_package_info.py

* fixed validation-modules for plugins/modules/packaging/language/maven_artifact.py

* fixed validation-modules for plugins/modules/packaging/language/bundler.py

* fixed validation-modules for plugins/modules/notification/pushbullet.py

* fixed validation-modules for plugins/modules/monitoring/sensu/sensu_handler.py

* fixed validation-modules for plugins/modules/monitoring/sensu/sensu_check.py

* fixed validation-modules for plugins/modules/monitoring/sensu/sensu_client.py

* fixed validation-modules for plugins/modules/monitoring/icinga2_host.py

* fixed validation-modules for plugins/modules/monitoring/datadog/datadog_monitor.py

* fixed validation-modules for plugins/modules/monitoring/datadog/datadog_event.py

* fixed validation-modules for plugins/modules/clustering/znode.py

* fixed validation-modules for plugins/modules/clustering/etcd3.py

* fixed validation-modules for plugins/modules/clustering/consul/consul_session.py

* fixed validation-modules for plugins/modules/clustering/consul/consul_kv.py

* fixed validation-modules for plugins/modules/clustering/consul/consul.py

* fixed validation-modules for plugins/modules/cloud/profitbricks/profitbricks.py

* fixed validation-modules for plugins/modules/cloud/profitbricks/profitbricks_volume.py

* fixed validation-modules for plugins/modules/cloud/packet/packet_sshkey.py

* fixed validation-modules for plugins/modules/cloud/oneandone/oneandone_server.py

* fixed validation-modules for plugins/modules/cloud/oneandone/oneandone_private_network.py

* fixed validation-modules for plugins/modules/cloud/oneandone/oneandone_monitoring_policy.py

* fixed validation-modules for plugins/modules/cloud/oneandone/oneandone_load_balancer.py

* fixed validation-modules for plugins/modules/cloud/oneandone/oneandone_firewall_policy.py

* fixed validation-modules for plugins/modules/cloud/webfaction/webfaction_app.py

* fixed validation-modules for plugins/modules/cloud/webfaction/webfaction_db.py

* fixed validation-modules for plugins/modules/cloud/webfaction/webfaction_domain.py

* fixed validation-modules for plugins/modules/cloud/webfaction/webfaction_mailbox.py

* fixed validation-modules for plugins/modules/cloud/webfaction/webfaction_site.py

* fixed validation-modules for plugins/modules/remote_management/lxca/lxca_cmms.py

* fixed validation-modules for plugins/modules/remote_management/lxca/lxca_nodes.py

* missed one "elements" in sensu_handler

* Tidy up batch of sanity checks ignore lines

* missed lines in ignore-2.9.txt

* fixed validation-modules for plugins/modules/clustering/consul/consul_acl.py

* Update ignore-2.9.txt

Removed consul_acl.py from ignore-2.9.txt

* Apply suggestions from code review

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

* Update plugins/modules/notification/pushbullet.py

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

* Update plugins/modules/cloud/oneandone/oneandone_monitoring_policy.py

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

* added changelog fragment

* Update plugins/modules/cloud/oneandone/oneandone_monitoring_policy.py

* Update changelogs/fragments/1885-sanity-check-fixes-batch3.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
2021-02-26 11:37:23 +01:00
Felix Fontein
afe9d0fdb3 Disable way too slow yum_versionlock tests. (#1907) 2021-02-26 11:17:39 +01:00
Victor Fauth
71706031c7 BOTMETA.yml: Remove vfauth as etcd3 maintainer (#1900) 2021-02-25 15:17:48 +01:00
Nicolas Marcq
36dea9ab97 cobbler_sync cobbler_system fix TLS check when validate_certs (#1880)
Ref: https://www.python.org/dev/peps/pep-0476/
Issue #1878
add changelog fragment

Co-authored-by: Nicolas Marcq <nicolas.marcq@hpe.com>
2021-02-25 14:55:45 +01:00
Tristan Le Guern
bb7ce740fe proxmox_kvm: trivial patch for github issue #1875 (#1895)
* proxmox_kvm: trivial patch for Github issue #1875

* proxmox_kvm: add a changelog fragment

* Update changelogs/fragments/1895-proxmox-kvm-fix-issue-1875.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-25 12:34:53 +01:00
Alexei Znamensky
cf5e9bf44c Tidy up sanity checks ignore lines for source_control/* modules (#1893)
* fixed validation-modules for plugins/modules/source_control/git*

* Tidy up sanity checks ignore lines for source_control/* modules

* removed unnecessary ignore lines from 2.9 and 2.11
2021-02-25 09:39:48 +01:00
Alexei Znamensky
434f383ae9 fixed Python 3 keys() usage (#1861)
* fixed python3 keys()

* added changelog fragment

* Update plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py

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

* Update plugins/cache/redis.py

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

* rolledback redis.py per PR

* Update plugins/modules/monitoring/sensu/sensu_check.py

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

* removed unnecessary ignore lines

* adding memcached and one case in redis is indeed necessary

* Update changelogs/fragments/1861-python3-keys.yml

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

* Update changelogs/fragments/1861-python3-keys.yml

* Update changelogs/fragments/1861-python3-keys.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-23 23:19:27 +01:00
Felix Fontein
e353390e6c Remove unneeded fields (with typos). (#1887) 2021-02-23 23:11:49 +01:00
Felix Fontein
0b9893959f Since gitlab_project_members is a copy if gitlab_group_members with small modifications, it needs to contain the copyright notices of that module as well as the authors. (#1874) 2021-02-23 08:57:40 +01:00
Tristan Le Guern
305748b333 New module proxmox_storage_info (#1844)
* proxmox_storage_info: new module

Simple info module dedicated to the retrieval of information about the
storages available on a Proxmox VE cluster.

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

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

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

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

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

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

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

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

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-22 17:59:27 +01:00
Ramon de la Fuente
abfbe2a48d deploy_helper: fix a bug when not defining release on state=clean (#1859)
* Fix a bug when not defining release on state=clean

* Add changelog fragment
2021-02-22 09:12:21 +01:00
Felix Fontein
c0f3a63e18 Improve infoblox inventory script dependencies. (#1871) 2021-02-21 21:11:54 +01:00
Sergey Mikhaltsov
389b004879 added gitlab_project_members (#1829)
* added gitlab_project_members

* fix code style

* added some arg to doc

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

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

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

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

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

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

* integration test for gitlab_project_members module

Co-authored-by: Sergey Mikhaltsov <s.mikhaltsov@leadgid.ru>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-21 17:56:53 +01:00
Alexei Znamensky
fdb66d5567 Fixed imc_rest session logout (#1743)
* Fixed imc_rest session logout

* Update plugins/modules/remote_management/imc/imc_rest.py

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

* Update changelogs/fragments/1735-imc-sessions.yml

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

* Trying with try/finally

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-21 17:53:30 +01:00
Felix Fontein
57f56b02d8 Disable flatpack_remote test due to expired key. (#1862) 2021-02-20 10:20:23 +01:00
Denise Yu
a44ffdc20d Update bug_report.yml (#1858)
This fixes the bug report template.
2021-02-20 09:51:43 +01:00
Tristan Le Guern
682674dd5f proxmox_kvm: add integration tests (#1849) 2021-02-18 11:54:30 +01:00
Tristan Le Guern
5135587c16 proxmox_kvm: fix undefined local variable status (#1847)
* proxmox_kvm: undefined local variable status

* proxmox_kvm: Add a changelog fragment

* Update changelogs/fragments/1847-proxmox-kvm-fix-status.yml

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

* proxmox_kvm: fix the RETURN document

* proxmox_kvm: fix name variable when state=current

The variable name is not always defined as the module can be called with
just a vmid.

Before:

> "msg": "VM None with vmid = 118 is stopped"

After:

> "msg": "VM test-instance with vmid = 118 is stopped"

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-17 21:25:05 +01:00
Alexei Znamensky
e0dd4b240f runit - deprecate param dist (#1838)
* Deprecate param dist

* added changelog fragment

* added changelog fragment

* Removed ignore lines for runit
2021-02-16 21:45:25 +01:00
Felix Fontein
a1badbb5b2 Next release will be 2.2.0. 2021-02-16 13:00:53 +01:00
Mark Mercado
6165438689 StatsD Module (#1793)
* Pushing my WIP

* Update DOCUMENTATION

* Update EXAMPLES

* More friendly name

* Finish up the counter and gauge logic

* Cleanup DOCUMENTATION and add metric_type

* Apply autopep8

* Fixup the exits

* Stubbing out unit tests

* Whitespace

* Whitespace

* Removing unused modules

* Remove unused modules

* Might have have a prefix

* Rearrange imported modules

* Cleanup the if/elif blob

* Require python >= 2.7

* Update DOCUMENTATION

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

* Update DOCUMENTATION

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

* Add import guarding on statsd

* Add missing future import

* Include missing_required_lib

* Fixing sanity tests

* Fixing delta default and choices

* Formatting

* Close tcp connection

* Refactoring and unit tests

* Fix pep8 sanity tests

* Putting requirements.txt back to main

* Apply suggestions from code review

Co-authored-by: Mark Mercado <mmercado@digitalocean.com>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-16 11:46:39 +01:00
Alexei Znamensky
3778eac1ba Tidy up validations + bug fixes + deprecations (#1830)
* fixed validation-modules for plugins/modules/database/misc/elasticsearch_plugin.py

* fixed validation-modules for plugins/modules/database/misc/kibana_plugin.py

* fixed validation-modules for plugins/modules/database/misc/riak.py

* fixed validation-modules for plugins/modules/database/vertica/vertica_info.py

* fixed validation-modules for plugins/modules/database/vertica/vertica_role.py

* fixed validation-modules for plugins/modules/database/vertica/vertica_schema.py

* fixed validation-modules for plugins/modules/database/vertica/vertica_user.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_domain.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_host_ports.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_host.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_pool.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_vol.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_vol_map.py

* fixed validation-modules for plugins/modules/storage/ibm/ibm_sa_host_ports.py

* fixed validation-modules for plugins/modules/system/runit.py

* fixed validation-modules for plugins/modules/source_control/bzr.py

* fixed validation-modules for plugins/modules/source_control/hg.py

* fixed validation-modules for plugins/modules/storage/emc/emc_vnx_sg_member.py

* fixed validation-modules for plugins/modules/identity/opendj/opendj_backendprop.py

* fixed validation-modules for plugins/modules/files/iso_extract.py

* fixed validation-modules for plugins/modules/monitoring/logstash_plugin.py

* fixed validation-modules for plugins/modules/database/aerospike/aerospike_migrations.py

* Tidy up a number of sanity checks for some modules

* added changelog fragment

* Some parameters in vertica_* had their aliases documented as the name, and sometimes vice-versa as well

* Adjustments per PR

* Rolled back sanity ignores for runit

* Update changelogs/fragments/1830-valmod_docmissingtype_batch1.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-16 09:27:24 +01:00
Alexei Znamensky
03b7b39424 Tidy up all pylint:blacklisted-name ignore lines (#1819)
* fixed validation-modules for plugins/callback/hipchat.py

* fixed validation-modules for plugins/connection/lxc.py

* fixed validation-modules for plugins/modules/cloud/lxc/lxc_container.py

* fixed validation-modules for plugins/modules/monitoring/statusio_maintenance.py

* fixed validation-modules for plugins/modules/system/alternatives.py

* fixed validation-modules for plugins/modules/system/beadm.py

* fixed validation-modules for plugins/modules/system/cronvar.py

* fixed validation-modules for plugins/modules/system/dconf.py

* fixed validation-modules for plugins/modules/system/interfaces_file.py

* fixed validation-modules for plugins/modules/system/java_cert.py

* fixed validation-modules for plugins/modules/system/lvg.py

* fixed validation-modules for plugins/modules/system/lvol.py

* fixed validation-modules for plugins/modules/system/parted.py

* fixed validation-modules for plugins/modules/system/timezone.py

* fixed validation-modules for plugins/modules/web_infrastructure/rundeck_acl_policy.py

* Tidy up all pylint:blacklisted-name sanity checks ignore lines

* Missed one in statusio_maintenace.py

* fixed validation-modules for plugins/modules/system/filesystem.py

* Missed one in gconftool2.py

* Missed one in alternatives.py

* Using dummies now

* fixed indentation

* Made all the changes about replacing _ with dummy

* Rollback bug fixed

* Rollback bug fixed, part II

* added changelog fragment

* Improved changelog frag message per PR
2021-02-16 08:03:51 +01:00
Frank Dornheim
6dd4cd0eb7 Previously LXD profiles were overwritten, now these are merged. (#1813)
* added ``merge_profile`` parameter to merge configurations from the play to an existing profile

* add fragment

* cosmetic changes

Co-authored-by: Frank Dornheim <“dornheim@posteo.de@users.noreply.github.com”>
2021-02-16 07:15:50 +01:00
Craig Roberts
03fd6bd008 dnsimple: Add support for CAA records (#1814) 2021-02-16 07:15:19 +01:00
Alexei Znamensky
f33323ca89 Tidy up validate-modules:parameter-list-no-elements (batch 1) (#1795)
* fixed validation-modules for plugins/modules/storage/netapp/na_ontap_gather_facts.py

* fixed validation-modules for plugins/modules/source_control/gitlab/gitlab_runner.py

* fixed validation-modules for plugins/modules/packaging/os/redhat_subscription.py

* fixed validation-modules for plugins/modules/notification/twilio.py

* fixed validation-modules for plugins/modules/notification/slack.py

* fixed validation-modules for plugins/modules/notification/sendgrid.py

* fixed validation-modules for plugins/modules/notification/rocketchat.py

* fixed validation-modules for plugins/modules/notification/office_365_connector_card.py

* fixed validation-modules for plugins/modules/notification/nexmo.py

* fixed validation-modules for plugins/modules/notification/mail.py

* fixed validation-modules for plugins/modules/net_tools/omapi_host.py

* fixed validation-modules for plugins/modules/net_tools/nsupdate.py

* fixed validation-modules for plugins/modules/net_tools/dnsimple.py

* fixed validation-modules for plugins/modules/monitoring/pagerduty.py

* fixed validation-modules for plugins/modules/monitoring/librato_annotation.py

* fixed validation-modules for plugins/modules/identity/onepassword_info.py

* fixed validation-modules for plugins/modules/identity/keycloak/keycloak_client.py

* fixed validation-modules for plugins/modules/files/xml.py

* fixed validation-modules for plugins/modules/cloud/softlayer/sl_vm.py

* fixed validation-modules for plugins/modules/cloud/smartos/vmadm.py

* fixed validation-modules for plugins/modules/cloud/pubnub/pubnub_blocks.py

* fixed validation-modules for plugins/modules/cloud/packet/packet_device.py

* fixed validation-modules for plugins/modules/cloud/lxd/lxd_container.py

* fixed validation-modules for plugins/module_utils/oracle/oci_utils.py

* fixed validation-modules for plugins/doc_fragments/oracle_creatable_resource.py

* Tidy up validate-modules:parameter-list-no-elements for some modules

* fixed validation-modules for plugins/modules/monitoring/statusio_maintenance.py

* Fixed pending issues from CI validation

* Fixed xml module elements for add_children & set_children

* added changelog fragment

* typo

* fix wording in changelog frag
2021-02-16 07:11:37 +01:00
almdudler777
5aac81bdd1 proxmox_kvm: 500 error args parameter (#1783)
* don't add args if set to 'no_defaults'

* never add force even if false, will require archive parameter other which is not implemented

* remove trailing whitespace

* add changelog fragment

* Update changelogs/fragments/1783-proxmox-kvm-fix-args-500-error.yaml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-15 22:40:51 +01:00
shieni
8fae693d9c ease limitation for nios_host_record DNS Bypass (#1788)
* ease limitation for nios_host_record DNS Bypass, the bypass should be allowed when configure_dns is disabled and view is set other than default

* add changelog fragment

Co-authored-by: Nils <ext-nils.haglund@elisa.fi>
2021-02-15 08:29:04 +01:00
Jeffrey van Pelt
1cce279424 Added VMID to all returns (#1715)
* Added VMID to all returns

Also added in the docs promised return of MAC and devices when state ==
current.

Fixes: #1641

* Revert devices and mac as get_vminfo works differently then I expected

* Added status output to a few more calls

* Update RETURNS docs

* Remove vmid where it is not available

* Added changelog fragment

* Update changelogs/fragments/1715-proxmox_kvm-add-vmid-to-returns.yml

You're right, this message is way better then mine.. :-)

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-15 07:27:43 +00:00
Víctor Suárez Fernández
d09a558fda Fix undeclared result for nomad_job_info module (#1721)
* Fix unassigned variable

* Create 1721-fix-nomad_job_info-no-jobs-failure.yml

* refactor usage of variables in nomad_job_info

* Update changelogs/fragments/1721-fix-nomad_job_info-no-jobs-failure.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-15 07:27:33 +00:00
Alexei Znamensky
bd372939bc Tidy up validation for storage/zfs modules (#1766)
* fixed validation-modules for plugins/modules/storage/zfs/zfs.py

* fixed validation-modules for plugins/modules/storage/zfs/zfs_delegate_admin.py

* fixed validation-modules for plugins/modules/storage/zfs/zfs_facts.py

* fixed validation-modules for plugins/modules/storage/zfs/zpool_facts.py

* Tidy up validate-modules ignores for storage/zfs modules

* removed ignore lines in 2.11 files as well

* added changelog fragment per PR

* Update changelogs/fragments/1766-zfs-fixed-sanity.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-15 07:16:08 +01:00
Alexei Znamensky
41bc7816f3 Implemented fix pointed in issue (#1760) 2021-02-14 15:08:32 +01:00
Alexei Znamensky
865acdd4cf Tidy up validate-modules ignores for cloud/centurylink modules (#1771)
* fixed validation-modules for plugins/modules/cloud/centurylink/clc_alert_policy.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_blueprint_package.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_firewall_policy.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_loadbalancer.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_modify_server.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_publicip.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_server_snapshot.py

* fixed validation-modules for plugins/modules/cloud/centurylink/clc_server.py

* Tidy up validate-modules ignores for cloud/centurylink modules

* added changelog fragment per PR
2021-02-13 12:14:06 +01:00
Alexei Znamensky
e247300523 Adjusted django_manage integration test files to reduce ignore lines in sanity tests (#1805) 2021-02-13 12:09:54 +01:00
Felix Fontein
367c3c43ff Make selective callback work with ansible-core 2.11. (#1807) 2021-02-13 11:09:40 +01:00
Alexei Znamensky
0a5f79724c Improved parameter handling on proxmox modules (#1765)
* Improved parameter handling on proxmox modules

* removed unused imports

* rollback change in plugins/modules/cloud/misc/proxmox_user_info.py

* added changelog fragment

* Update changelogs/fragments/1765-proxmox-params.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-12 13:01:52 +01:00
Felix Fontein
f12df1d21b Change type str -> path in argspec. (#1741) 2021-02-12 07:58:30 +01:00
Alexei Znamensky
ba4a98b1be Removed parameter-list-no-elements validation errors from redfish modules (#1761)
* Removed parameter-list-no-elements validation errors from redfish modules

* added changelog fragment per PR

* Update changelogs/fragments/1761-redfish-tidy-up-validation.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-12 06:47:23 +01:00
Alexei Znamensky
436bbb0077 Prevented the expansion of parameters in run_command() (#1794) 2021-02-12 06:13:05 +01:00
Felix Fontein
e9551df5ed Update CI (#1782)
* Update targets for CI for devel branch; move some targets to stable-2.10.

* Skipping test on RHEL 8.3 (it is already skipped on RHEL 8.2).

* Linting.

* Shut 2.9/2.10 pylint complaints up.
2021-02-11 10:24:58 +01:00
Felix Fontein
9a6031ab4e Fix typo in aerospike_migration module. (#1740) 2021-02-11 07:04:45 +01:00
topperharly
562ff7efb7 only set param features when variable is not empty (#1763)
* only set param features when variable is not empty

* Apply suggestions from code review

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

Co-authored-by: Topper Harly <topperharly@gmx.net>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-10 22:07:27 +01:00
lukurde
93e0aa7557 redfish module_utils: case insesitive search for situations where the hostname/FQDN on iLO is in caps (#1744)
* case insesitive search for situations where the hostname/FQDN on iLO is in caps

* handle explicit and implicit nic_addr variable, added changelog fragment

* changelog linter fix

* changelog typo fix

Co-authored-by: Łukasz Kurdziel <Lukasz.Kurdziel@GAINCapital.com>
2021-02-10 19:47:09 +01:00
Anatoly Pugachev
9aef0ed17e timezone: add gentoo and alpine linux support (#1722)
* add alpine linux as NosystemdTimezone

* syntax error fix and more self variables, so it works now...

* use timezone name instead of timezone path for setup-timezone command

* alpine linux zoneinfo links to /etc/zoneinfo instead of /usr/share/zoneinfo, so correct re.search() pattern

* add changelog fragment

* add gentoo linux support

* Update 1722_timezone.yml

* refactor code
2021-02-10 19:44:09 +01:00
Alexei Znamensky
af64c9a432 xfconf: feature flag to disable facts and deprecation warning (#1747)
* Added feature flag to disable facts and its associated deprecatoin warning

* added changelog fragment

* Update plugins/modules/system/xfconf.py

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

* Update plugins/modules/system/xfconf.py

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

* Update plugins/modules/system/xfconf.py

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

* Fixed deprecation message

* Fixed changelog frag

* Update changelogs/fragments/1475-xfconf-facts.yml

* Update plugins/modules/system/xfconf.py

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-10 19:42:40 +01:00
quidame
d1e54d2fd1 document what filesystem types are supported by 'resizefs' option (#1753)
* document what filesystem types are supported by 'resizefs' option

* add changelog fragment

* remove info about lvol documentation changes in changelog fragment
2021-02-10 03:50:43 -08:00
Andrew Klychkov
e898e52d1b azure-pipelines: update container version (#1770) 2021-02-10 08:52:04 +01:00
Alexei Znamensky
89ffb04dff Tidy up validate-modules ignores for remote_management/ipmi modules (#1767)
* fixed validation-modules for plugins/modules/remote_management/ipmi/ipmi_boot.py

* fixed validation-modules for plugins/modules/remote_management/ipmi/ipmi_power.py

* Tidy up validate-modules ignores for remote_management/ipmi modules
2021-02-10 07:19:32 +01:00
morco
c03ae754d2 Various fixes for updating existing gitlab users (#1724)
* fixes various issues related to updating an ...

... existing gitlab user, in detail:

  - fixes updating admin status not working
  - fixes user passwords not updated
  - fixes confirmation skipping param ignored for user updates
  - added tests for code changes

* fixing sanity issues

* fixing sanity issues 02

* fixing sanity issues 03

* fixing sanity issues 04

* fixing unit test failures

* fixing unit test failures 02

* add changelog fragment

* fixing unit test failures 03

* forgot to add changelog fragment

* fix changelog sanity issues

* fix changelog sanity issues 02

* incorporate review suggestions

Co-authored-by: Mirko Wilhelmi <Mirko.Wilhelmi@sma.de>
2021-02-09 10:29:13 +01:00
Felix Fontein
909ac92fe2 Fix CI (#1752)
* Limit cryptography to < 3.4 for Python < 3.6.

* Work around old pip versions.

* Use constraints file when installing stormssh.

* Work around old pip in RHEL8.2, CentOS 8, Ubuntu 18.04, and OpenSuSE 15
2021-02-09 07:47:08 +01:00
Felix Fontein
29bd5a9486 Fix a bunch of potential security issues (secret leaking) (#1736)
* Fix a bunch of potential security issues (secret leaking).

* oneandone_server was already ok.

* Add more parameters for pagerduty_alert.

* Add more no_log=True.
2021-02-08 16:33:18 +01:00
John R Barker
f4e60e09ac bug_report.yml: inline comments to docs 2021-02-05 14:36:35 +00:00
John R Barker
d4f3a47d48 Update bug_report.yml 2021-02-05 14:07:30 +00:00
Felix Fontein
701a89eb1c Make sure mercurial is also installed on OpenSuSE. (#1734) 2021-02-05 08:08:06 +01:00
morco
dd0b54b9b5 Feature/gitlab deploykey updkey (#1661)
* feat(gitlab-deploy-key): automatically update ...

... the public key

* add integrity test

* fix sanity issues

* added changelog fragment

Co-authored-by: Mirko Wilhelmi <Mirko.Wilhelmi@sma.de>
2021-02-05 07:30:05 +01:00
Rick Sherman
f509f2c896 datadog_monitor: Add missing monitor types query alert, trace-analytics alert, rum alert (#1723)
* Add missing Datadog monitor types
This commit adds the following monitor types: query alert, trace-analytics alert, rum alert

* changelog PR1723 datadog_monitor types

* datadog_monitor 1723 Apply suggestions from code review

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-05 07:22:49 +01:00
Anatoly Pugachev
43da5b88db parted: fix regex for version match and partition size output (#1695)
* Fix 2 regex in parted related to parted version string and to parsing partition size output.

* Added changelog fragment.

* Updated changelog as per recommendation.

* Fix the regex matching the parted version. The space character at the end of the string may or may not be always present

* provided sample version output and corrected regex to match

* add/correct changelog fragment

* split parted_version function to allow creating a test unit

* test unit for parted version info

* ansible-test sanity fixes

* review fix

* Update changelogs/fragments/1695-parted-updatedregex.yaml

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

* comment fixes

* better function name

* Update plugins/modules/system/parted.py

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

* comment fixes

Co-authored-by: Claude Robitaille <claude@cbcr.me>
Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-05 07:21:57 +01:00
David Moreau Simard
ae8edc02e1 Add no_log to some module arguments (#1725)
* Add no_log to some module arguments

This will prevent potentially sensitive information from being printed to
the console.

See: CVE-2021-20191

* Update changelogs/fragments/CVE-2021-20191_no_log.yml

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-04 21:02:21 +01:00
John R Barker
2297f2f802 AZP nightly at 0800 2021-02-03 12:06:03 +00:00
quidame
aa95d8a5b7 module filesystem: partially fix idempotency issue #1457 (resizefs) (#1478)
* Use 'xfs_info' to query fs size, that doesn't always require the device be
  mounted. Although still query mountpoint first for backward compatibility.
* Do not fail whith fstype=xfs and resizefs=yes if filesystem already fills
  its underlying device.
* Include xfs in the tasks that test idempotency of resizefs option
* Add changelogs/fragments/1478-filesystem-fix-1457-resizefs-idempotency.yml
2021-02-03 07:54:04 +01:00
Stefan Walluhn
b40a5ef09a sensu-silence: fix json parsing of sensu API response (#1703)
* sensu-silence: fix json parsing of sensu API response

* use ansible helper function to decode bytestream

* add changelog fragment

* Update changelogs, link to PR

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-02 17:45:14 +01:00
John R Barker
4e70c0c55a Update bug_report.yml 2021-02-01 18:55:36 +00:00
John R Barker
e8886fa711 Update bug_report.yml 2021-02-01 18:53:06 +00:00
John R Barker
8dbb13edd4 Update bug_report.yml 2021-02-01 18:51:10 +00:00
John R Barker
6d86564308 Update bug_report.yml 2021-02-01 18:46:32 +00:00
John R Barker
165719d084 Update bug_report.yml 2021-02-01 18:41:44 +00:00
John R Barker
1591d52b78 Create bug_report.yml 2021-02-01 18:27:15 +00:00
Rajeev Arakkal
8afdd23be4 Migrating DellEMC collections from community.general collections (#1699)
* migration modification for dellemc collections

* removing dellemc collections

* Update changelogs/fragments/948-dellemc-migration-removal.yml

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

* Update 948-dellemc-migration-removal.yml

* Update 948-dellemc-migration-removal.yml

* Update runtime.yml

* Update meta/runtime.yml

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

* Update runtime.yml

* deleted symlink entries

* Update 948-dellemc-migration-removal.yml

* Update changelogs/fragments/948-dellemc-migration-removal.yml

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

* Update changelogs/fragments/948-dellemc-migration-removal.yml

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-01 18:06:58 +01:00
Felix Fontein
6af3c96d8e Fedora 30 and 31 are EOL and will eventually be removed from devel. (#1705) 2021-02-01 15:30:32 +00:00
Abhijeet Kasurde
d0f097c871 homebrew_tap: Add support for brew search path (#1708)
* homebrew_tap: Add support for brew search path

User can specify search path for brew executable.

Fixes: #1702

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>

* Change version

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

Co-authored-by: Felix Fontein <felix@fontein.de>
2021-02-01 08:42:32 +01:00
Andrew Klychkov
9c648c8e3a BOTMETA.yml: add logstash plugin maintainer (#1700)
Co-authored-by: Andrew Klychkov <andrew.klychkov@gmail.com>
2021-01-29 10:45:06 +01:00
Bill Dodd
00f5f7dfe7 Add Name and/or Id properties to resource inventory output (#1691)
* add Name and/or Id properties to resource inventory output

* add changelog fragment
2021-01-29 07:17:57 +01:00
Felix Fontein
b6774971a6 Clear changelog for 3.0.0. 2021-01-28 13:40:56 +01:00
Felix Fontein
83afe0f868 Next expected release is 2.1.0. Next changelog ancestor is 2.0.0. 2021-01-28 13:40:13 +01:00
497 changed files with 19674 additions and 16010 deletions

View File

@@ -13,13 +13,25 @@ pr:
- stable-*
schedules:
- cron: 0 9 * * *
displayName: Nightly
- cron: 0 8 * * *
displayName: Nightly (main)
always: true
branches:
include:
- main
- stable-*
- cron: 0 10 * * *
displayName: Nightly (active stable branches)
always: true
branches:
include:
- stable-2
- stable-3
- cron: 0 11 * * 0
displayName: Weekly (old stable branches)
always: true
branches:
include:
- stable-1
variables:
- name: checkoutPath
@@ -36,7 +48,7 @@ variables:
resources:
containers:
- container: default
image: quay.io/ansible/azure-pipelines-test-container:1.8.0
image: quay.io/ansible/azure-pipelines-test-container:1.9.0
pool: Standard
@@ -56,6 +68,19 @@ stages:
- test: 3
- test: 4
- test: extra
- stage: Sanity_2_11
displayName: Sanity 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Test {0}
testFormat: 2.11/sanity/{0}
targets:
- test: 1
- test: 2
- test: 3
- test: 4
- stage: Sanity_2_10
displayName: Sanity 2.10
dependsOn: []
@@ -99,6 +124,22 @@ stages:
- test: 3.7
- test: 3.8
- test: 3.9
- stage: Units_2_11
displayName: Units 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.11/units/{0}/1
targets:
- test: 2.6
- test: 2.7
- test: 3.5
- test: 3.6
- test: 3.7
- test: 3.8
- test: 3.9
- stage: Units_2_10
displayName: Units 2.10
dependsOn: []
@@ -146,14 +187,33 @@ stages:
test: rhel/7.9
- name: RHEL 8.3
test: rhel/8.3
- name: FreeBSD 11.4
test: freebsd/11.4
- name: FreeBSD 12.2
test: freebsd/12.2
- name: FreeBSD 13.0
test: freebsd/13.0
groups:
- 1
- 2
- 3
- stage: Remote_2_11
displayName: Remote 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.11/{0}
targets:
- name: macOS 11.1
test: macos/11.1
- name: RHEL 7.9
test: rhel/7.9
- name: RHEL 8.3
test: rhel/8.3
- name: FreeBSD 12.2
test: freebsd/12.2
groups:
- 1
- 2
- 3
- stage: Remote_2_10
displayName: Remote 2.10
dependsOn: []
@@ -208,10 +268,10 @@ stages:
test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 32
test: fedora32
- name: Fedora 33
test: fedora33
- name: Fedora 34
test: fedora34
- name: openSUSE 15 py2
test: opensuse15py2
- name: openSUSE 15 py3
@@ -224,6 +284,25 @@ stages:
- 1
- 2
- 3
- stage: Docker_2_11
displayName: Docker 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.11/linux/{0}
targets:
- name: CentOS 8
test: centos8
- name: Fedora 33
test: fedora33
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 20.04
test: ubuntu2004
groups:
- 2
- 3
- stage: Docker_2_10
displayName: Docker 2.10
dependsOn: []
@@ -270,6 +349,16 @@ stages:
parameters:
nameFormat: Python {0}
testFormat: devel/cloud/{0}/1
targets:
- test: 3.8
- stage: Cloud_2_11
displayName: Cloud 2.11
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
nameFormat: Python {0}
testFormat: 2.11/cloud/{0}/1
targets:
- test: 2.7
- test: 3.6
@@ -299,17 +388,22 @@ stages:
- Sanity_devel
- Sanity_2_9
- Sanity_2_10
- Sanity_2_11
- Units_devel
- Units_2_9
- Units_2_10
- Units_2_11
- Remote_devel
- Remote_2_9
- Remote_2_10
- Remote_2_11
- Docker_devel
- Docker_2_9
- Docker_2_10
- Docker_2_11
- Cloud_devel
- Cloud_2_9
- Cloud_2_10
- Cloud_2_11
jobs:
- template: templates/coverage.yml

View File

@@ -7,7 +7,7 @@ set -o pipefail -eu
output_path="$1"
curl --silent --show-error https://codecov.io/bash > codecov.sh
curl --silent --show-error https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh
for file in "${output_path}"/reports/coverage*.xml; do
name="${file}"

89
.github/BOTMETA.yml vendored
View File

@@ -1,5 +1,7 @@
automerge: true
files:
plugins/:
supershipit: aminvakil russoz
changelogs/fragments/:
support: community
$actions:
@@ -15,6 +17,8 @@ files:
labels: become
$callbacks/:
labels: callbacks
$callbacks/loganalytics.py:
maintainers: zhcli
$callbacks/logstash.py:
maintainers: ujenmr
$callbacks/say.py:
@@ -53,14 +57,24 @@ files:
$doc_fragments/xenserver.py:
maintainers: bvitnik
labels: xenserver
$filters/dict.py:
maintainers: felixfontein
$filters/dict_kv.py:
maintainers: giner
$filters/from_csv.py:
maintainers: Ajpantuso
$filters/hashids:
maintainers: Ajpantuso
$filters/jc.py:
maintainers: kellyjonbrazil
$filters/list.py:
maintainers: vbotka
$filters/path_join_shim.py:
maintainers: felixfontein
$filters/time.py:
maintainers: resmo
$filters/version_sort.py:
maintainers: ericzolf
$httpapis/:
maintainers: $team_networking
labels: networking
@@ -74,6 +88,8 @@ files:
maintainers: $team_linode
labels: cloud linode
keywords: linode dynamic inventory script
$inventories/proxmox.py:
maintainers: $team_virt ilijamt
$inventories/scaleway.py:
maintainers: $team_scaleway
labels: cloud scaleway
@@ -139,7 +155,6 @@ files:
$module_utils/redfish_utils.py:
maintainers: $team_redfish
labels: redfish_utils
$module_utils/remote_management/dellemc/: rajeevarakkal
$module_utils/remote_management/lxca/common.py: navalkp prabhosa
$module_utils/scaleway.py:
maintainers: $team_scaleway
@@ -175,14 +190,14 @@ files:
maintainers: zbal
$modules/cloud/lxc/lxc_container.py:
maintainers: cloudnull
$modules/cloud/lxc/lxc_profile.py:
maintainers: conloos
$modules/cloud/lxd/:
ignore: hnakamur
$modules/cloud/memset/:
maintainers: glitchcrab
$modules/cloud/misc/cloud_init_data_facts.py:
maintainers: resmo
$modules/cloud/misc/helm.py:
maintainers: flaper87
$modules/cloud/misc/proxmox.py:
maintainers: $team_virt UnderGreen
labels: proxmox virt
@@ -210,7 +225,7 @@ files:
$modules/cloud/misc/:
ignore: ryansb
$modules/cloud/misc/terraform.py:
maintainers: m-yosefpor
maintainers: m-yosefpor rainerleber
$modules/cloud/misc/xenserver_facts.py:
maintainers: caphrim007 cheese
labels: xenserver_facts
@@ -294,6 +309,7 @@ files:
maintainers: bvitnik
$modules/clustering/consul/:
maintainers: $team_consul
ignore: colin-nolan
$modules/clustering/etcd3.py:
maintainers: evrardjp
ignore: vfauth
@@ -330,6 +346,8 @@ files:
maintainers: dareko
$modules/files/archive.py:
maintainers: bendoh
$modules/files/filesize.py:
maintainers: quidame
$modules/files/ini_file.py:
maintainers: jpmens noseka1
$modules/files/iso_extract.py:
@@ -343,8 +361,6 @@ files:
maintainers: dagwieers magnus919 tbielawa cmprescott sm4rk0
labels: m:xml xml
ignore: magnus919
$modules/identity/onepassword_facts.py:
maintainers: Rylon
$modules/identity/ipa/:
maintainers: $team_ipa
$modules/identity/ipa/ipa_pwpolicy.py:
@@ -418,6 +434,8 @@ files:
maintainers: andsens
$modules/monitoring/spectrum_device.py:
maintainers: orgito
$modules/monitoring/spectrum_model_attrs.py:
maintainers: tgates81
$modules/monitoring/stackdriver.py:
maintainers: bwhaley
$modules/monitoring/statsd.py:
@@ -434,7 +452,7 @@ files:
$modules/net_tools/dnsmadeeasy.py:
maintainers: briceburg
$modules/net_tools/haproxy.py:
maintainers: ravibhure
maintainers: ravibhure Normo
$modules/net_tools/:
maintainers: nerzhul
$modules/net_tools/infinity/infinity.py:
@@ -447,8 +465,6 @@ files:
maintainers: akostyuk
$modules/net_tools/ipwcli_dns.py:
maintainers: cwollinger
$modules/net_tools/ldap/ldap_attr.py:
maintainers: jtyr
$modules/net_tools/ldap/ldap_attrs.py:
maintainers: drybjed jtyr noles
$modules/net_tools/ldap/ldap_entry.py:
@@ -544,9 +560,10 @@ files:
$modules/packaging/language/bundler.py:
maintainers: thoiberg
$modules/packaging/language/composer.py:
maintainers: dmtrs resmo
maintainers: dmtrs
ignore: resmo
$modules/packaging/language/cpanm.py:
maintainers: fcuny
maintainers: fcuny russoz
$modules/packaging/language/easy_install.py:
maintainers: mattupstate
$modules/packaging/language/gem.py:
@@ -691,15 +708,10 @@ files:
labels: zypper
ignore: dirtyharrycallahan robinro
$modules/packaging/os/zypper_repository.py:
maintainers: matze
maintainers: $team_suse matze
labels: zypper
$modules/remote_management/cobbler/:
maintainers: dagwieers
$modules/remote_management/dellemc/:
maintainers: rajeevarakkal
$modules/remote_management/dellemc/idrac_server_config_profile.py:
maintainers: jagadeeshnv
$modules/remote_management/dellemc/ome_device_info.py:
maintainers: Sajna-Shetty
$modules/remote_management/hpilo/:
maintainers: haad
ignore: dagwieers
@@ -708,6 +720,8 @@ files:
labels: cisco
$modules/remote_management/ipmi/:
maintainers: bgaifullin cloudnull
$modules/remote_management/lenovoxcc/:
maintainers: panyy3 renxulei
$modules/remote_management/lxca/:
maintainers: navalkp prabhosa
$modules/remote_management/manageiq/:
@@ -717,8 +731,6 @@ files:
maintainers: evertmulder
$modules/remote_management/manageiq/manageiq_tenant.py:
maintainers: evertmulder
$modules/remote_management/oneview/oneview_datacenter_facts.py:
maintainers: aalexmonteiro madhav-bharadwaj ricardogpsf soodpr
$modules/remote_management/oneview/:
maintainers: adriane-cardozo fgbulsoni tmiotto
$modules/remote_management/oneview/oneview_datacenter_info.py:
@@ -728,7 +740,7 @@ files:
$modules/remote_management/oneview/oneview_fcoe_network.py:
maintainers: fgbulsoni
$modules/remote_management/redfish/:
maintainers: $team_redfish billdodd
maintainers: $team_redfish
ignore: jose-delarosa
$modules/remote_management/stacki/stacki_host.py:
maintainers: bsanders bbyhuy
@@ -751,6 +763,8 @@ files:
ignore: erydo
$modules/source_control/github/github_release.py:
maintainers: adrianmoisey
$modules/source_control/github/github_repo.py:
maintainers: atorrescogollo
$modules/source_control/github/:
maintainers: stpierre
$modules/source_control/gitlab/:
@@ -765,12 +779,6 @@ files:
maintainers: yeukhon
$modules/storage/emc/emc_vnx_sg_member.py:
maintainers: remixtj
$modules/storage/glusterfs/:
maintainers: devyanikota
$modules/storage/glusterfs/gluster_peer.py:
maintainers: sac
$modules/storage/glusterfs/gluster_volume.py:
maintainers: rosmo
$modules/storage/hpe3par/ss_3par_cpg.py:
maintainers: farhan7500 gautamphegde
$modules/storage/ibm/:
@@ -792,9 +800,6 @@ files:
maintainers: johanwiren
$modules/storage/zfs/zfs_delegate_admin.py:
maintainers: natefoo
$modules/system/python_requirements_facts.py:
maintainers: willthames
ignore: ryansb
$modules/system/aix:
maintainers: $team_aix
labels: aix
@@ -834,7 +839,7 @@ files:
$modules/system/iptables_state.py:
maintainers: quidame
$modules/system/java_cert.py:
maintainers: haad
maintainers: haad absynth76
$modules/system/java_keystore.py:
maintainers: Mogztter
$modules/system/kernel_blacklist.py:
@@ -919,16 +924,12 @@ files:
maintainers: ahtik ovcharenko pyykkis
labels: ufw
$modules/system/vdo.py:
maintainers: bgurney-rh
maintainers: rhawalsh
$modules/system/xfconf.py:
maintainers: russoz jbenden
labels: xfconf
$modules/system/xfs_quota.py:
maintainers: bushvin
$modules/web_infrastructure/jenkins_job_facts.py:
maintainers: stpierre
$modules/web_infrastructure/nginx_status_facts.py:
maintainers: resmo
$modules/web_infrastructure/apache2_mod_proxy.py:
maintainers: oboukili
$modules/web_infrastructure/apache2_module.py:
@@ -1004,27 +1005,27 @@ macros:
terminals: plugins/terminal
team_aix: MorrisA bcoca d-little flynn1973 gforster kairoaraujo marvin-sinister mator molekuul ramooncamacho wtcross
team_bsd: JoergFiedler MacLemon bcoca dch jasperla mekanix opoplawski overhacked tuxillo
team_consul: colin-nolan sgargan
team_consul: sgargan
team_cyberark_conjur: jvanderhoof ryanprior
team_e_spirit: MatrixCrawler getjack
team_flatpak: JayKayy oolongbrothers
team_gitlab: Lunik Shaps dj-wasabi marwatk waheedi zanssa scodeman
team_gitlab: Lunik Shaps dj-wasabi marwatk waheedi zanssa scodeman metanovii
team_hpux: bcoca davx8342
team_huawei: QijunPan TommyLike edisonxiang freesky-edward hwDCN niuzhenguo xuxiaowei0512 yanzhangi zengchen1024 zhongjun2
team_ipa: Akasurde Nosmoht fxfitz
team_ipa: Akasurde Nosmoht fxfitz justchris1
team_jboss: Wolfant jairojunior wbrefvem
team_keycloak: eikef ndclt
team_linode: InTheCloudDan decentral1se displague rmcintosh
team_linode: InTheCloudDan decentral1se displague rmcintosh Charliekenney23 LBGarber
team_macos: Akasurde kyleabenson martinm82 danieljaouen indrajitr
team_manageiq: abellotti cben gtanzillo yaacov zgalor dkorn evertmulder
team_netapp: amit0701 carchi8py hulquest lmprice lonico ndswartz schmots1
team_networking: NilashishC Qalthos danielmellado ganeshrn justjais trishnaguha sganesh-infoblox privateip
team_opennebula: ilicmilan meerkampdvv rsmontero xorel
team_opennebula: ilicmilan meerkampdvv rsmontero xorel nilsding
team_oracle: manojmeda mross22 nalsaber
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
team_redfish: billdodd mraineri tomasg2012
team_redfish: mraineri tomasg2012 xmadsen renxulei
team_rhn: FlossWare alikins barnabycourt vritant
team_scaleway: QuentinBrosse abarbare jerome-quere kindermoumoute remyleone sieben
team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l
team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom
team_virt: joshainglis karmab Aversiste Thulium-Drake
team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom sealor
team_virt: joshainglis karmab tleguern Thulium-Drake Ajpantuso

135
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,135 @@
---
name: Bug report
description: Create a report to help us improve
body:
- type: markdown
attributes:
value: |
Verify first that your issue is not [already reported on GitHub][issue search].
Also test if the latest release and devel branch are affected too.
*Complete **all** sections as described, this form is processed automatically.*
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
- type: textarea
attributes:
label: Summary
description: Explain the problem briefly below.
placeholder: >-
When I try to do X with the collection from the main branch on GitHub, Y
breaks in a way Z under the env E. Here are all the details I know
about this problem...
validations:
required: true
- type: dropdown
attributes:
label: Issue Type
# FIXME: Once GitHub allows defining the default choice, update this
options:
- Bug Report
validations:
required: true
- type: textarea
attributes:
# For smaller collections we could use a multi-select and hardcode the list
# May generate this list via GitHub action and walking files under https://github.com/ansible-collections/community.general/tree/main/plugins
# Select from list, filter as you type (`mysql` would only show the 3 mysql components)
# OR freeform - doesn't seem to be supported in adaptivecards
label: Component Name
description: >-
Write the short name of the module, plugin, task or feature below,
*use your best guess if unsure*.
placeholder: dnf, apt, yum, pip, user etc.
validations:
required: true
- type: textarea
attributes:
label: Ansible Version
description: >-
Paste verbatim output from `ansible --version` between
tripple backticks.
value: |
```console (paste below)
$ ansible --version
```
validations:
required: true
- type: textarea
attributes:
label: Configuration
description: >-
If this issue has an example piece of YAML that can help to reproduce this problem, please provide it.
This can be a piece of YAML from, e.g., an automation, script, scene or configuration.
Paste verbatim output from `ansible-config dump --only-changed` between quotes
value: |
```console (paste below)
$ ansible-config dump --only-changed
```
- type: textarea
attributes:
label: OS / Environment
description: >-
Provide all relevant information below, e.g. target OS versions,
network device firmware, etc.
placeholder: RHEL 8, CentOS Stream etc.
validations:
required: false
- type: textarea
attributes:
label: Steps to Reproduce
description: |
Describe exactly how to reproduce the problem, using a minimal test-case. It would *really* help us understand your problem if you could also pased any playbooks, configs and commands you used.
**HINT:** You can paste https://gist.github.com links for larger files.
value: |
<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
```
validations:
required: true
- type: textarea
attributes:
label: Expected Results
description: >-
Describe what you expected to happen when running the steps above.
placeholder: >-
I expected X to happen because I assumed Y.
that it did not.
validations:
required: true
- type: textarea
attributes:
label: Actual Results
description: |
Describe what actually happened. If possible run with extra verbosity (`-vvvv`).
Paste verbatim command output between quotes.
value: |
```console (paste below)
```
- type: checkboxes
attributes:
label: Code of Conduct
description: |
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
options:
- label: I agree to follow the Ansible Code of Conduct
required: true
...

27
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
---
# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: false # default: true
contact_links:
- name: Security bug report
url: https://docs.ansible.com/ansible-core/devel/community/reporting_bugs_and_features.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
about: |
Please learn how to report security vulnerabilities here.
For all security related bugs, email security@ansible.com
instead of using this issue tracker and you will receive
a prompt response.
For more information, see
https://docs.ansible.com/ansible/latest/community/reporting_bugs_and_features.html
- name: Ansible Code of Conduct
url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
about: Be nice to other members of the community.
- name: Talks to the community
url: https://docs.ansible.com/ansible/latest/community/communication.html?utm_medium=github&utm_source=issue_template_chooser#mailing-list-information
about: Please ask and answer usage questions here
- name: Working groups
url: https://github.com/ansible/community/wiki
about: Interested in improving a specific area? Become a part of a working group!
- name: For Enterprise
url: https://www.ansible.com/products/engine?utm_medium=github&utm_source=issue_template_chooser_ansible_collections
about: Red Hat offers support for the Ansible Automation Platform

View File

@@ -0,0 +1,111 @@
---
name: Documentation Report
description: Ask us about docs
# NOTE: issue body is enabled to allow screenshots
body:
- type: markdown
attributes:
value: |
Verify first that your issue is not [already reported on GitHub][issue search].
Also test if the latest release and devel branch are affected too.
*Complete **all** sections as described, this form is processed automatically.*
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
- type: textarea
attributes:
label: Summary
description: |
Explain the problem briefly below, add suggestions to wording or structure.
**HINT:** Did you know the documentation has an `Edit on GitHub` link on every page?
placeholder: >-
I was reading the Collection documentation of version X and I'm having
problems understanding Y. It would be very helpful if that got
rephrased as Z.
validations:
required: true
- type: dropdown
attributes:
label: Issue Type
# FIXME: Once GitHub allows defining the default choice, update this
options:
- Documentation Report
validations:
required: true
- type: input
attributes:
label: Component Name
description: >-
Write the short name of the rst file, module, plugin, task or
feature below, *use your best guess if unsure*.
placeholder: mysql_user
validations:
required: true
- type: textarea
attributes:
label: Ansible Version
description: >-
Paste verbatim output from `ansible --version` between
tripple backticks.
value: |
```console (paste below)
$ ansible --version
```
validations:
required: false
- type: textarea
attributes:
label: Configuration
description: >-
Paste verbatim output from `ansible-config dump --only-changed` between quotes.
value: |
```console (paste below)
$ ansible-config dump --only-changed
```
validations:
required: false
- type: textarea
attributes:
label: OS / Environment
description: >-
Provide all relevant information below, e.g. OS version,
browser, etc.
placeholder: Fedora 33, Firefox etc.
validations:
required: false
- type: textarea
attributes:
label: Additional Information
description: |
Describe how this improves the documentation, e.g. before/after situation or screenshots.
**Tip:** It's not possible to upload the screenshot via this field directly but you can use the last textarea in this form to attach them.
**HINT:** You can paste https://gist.github.com links for larger files.
placeholder: >-
When the improvement is applied, it makes it more straightforward
to understand X.
validations:
required: false
- type: checkboxes
attributes:
label: Code of Conduct
description: |
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
options:
- label: I agree to follow the Ansible Code of Conduct
required: true
...

View File

@@ -0,0 +1,69 @@
---
name: Feature request
description: Suggest an idea for this project
body:
- type: markdown
attributes:
value: |
Verify first that your issue is not [already reported on GitHub][issue search].
Also test if the latest release and devel branch are affected too.
*Complete **all** sections as described, this form is processed automatically.*
[issue search]: https://github.com/ansible-collections/community.general/search?q=is%3Aissue&type=issues
- type: textarea
attributes:
label: Summary
description: Describe the new feature/improvement briefly below.
placeholder: >-
I am trying to do X with the collection from the main branch on GitHub and
I think that implementing a feature Y would be very helpful for me and
every other user of ansible-core because of Z.
validations:
required: true
- type: dropdown
attributes:
label: Issue Type
# FIXME: Once GitHub allows defining the default choice, update this
options:
- Feature Idea
validations:
required: true
- type: input
attributes:
label: Component Name
description: >-
Write the short name of the module, plugin, task or feature below,
*use your best guess if unsure*.
placeholder: dnf, apt, yum, pip, user etc.
validations:
required: true
- type: textarea
attributes:
label: Additional Information
description: |
Describe how the feature would be used, why it is needed and what it would solve.
**HINT:** You can paste https://gist.github.com links for larger files.
value: |
<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
```
validations:
required: false
- type: checkboxes
attributes:
label: Code of Conduct
description: |
Read the [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_form--ansible-collections) first.
options:
- label: I agree to follow the Ansible Code of Conduct
required: true
...

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# Community General Collection
[![Build Status](https://dev.azure.com/ansible/community.general/_apis/build/status/CI?branchName=stable-2)](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
[![Build Status](https://dev.azure.com/ansible/community.general/_apis/build/status/CI?branchName=stable-3)](https://dev.azure.com/ansible/community.general/_build?definitionId=31)
[![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.general)](https://codecov.io/gh/ansible-collections/community.general)
This repo contains the `community.general` Ansible Collection. The collection includes many modules and plugins supported by Ansible community which are not part of more specialized community collections.
@@ -9,7 +9,7 @@ You can find [documentation for this collection on the Ansible docs site](https:
## Tested with Ansible
Tested with the current Ansible 2.9 and 2.10 releases and the current development version of Ansible. Ansible versions before 2.9.10 are not supported.
Tested with the current Ansible 2.9, ansible-base 2.10 and ansible-core 2.11 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
## External requirements
@@ -76,7 +76,7 @@ Basic instructions without release branches:
## Release notes
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-2/CHANGELOG.rst).
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-3/CHANGELOG.rst).
## Roadmap

File diff suppressed because it is too large Load Diff

72
commit-rights.md Normal file
View File

@@ -0,0 +1,72 @@
Committers Guidelines for community.general
===========================================
This document is based on the [Ansible committer guidelines](https://github.com/ansible/ansible/blob/b57444af14062ec96e0af75fdfc2098c74fe2d9a/docs/docsite/rst/community/committer_guidelines.rst) ([latest version](https://docs.ansible.com/ansible/devel/community/committer_guidelines.html)).
These are the guidelines for people with commit privileges on the Ansible Community General Collection GitHub repository. Please read the guidelines before you commit.
These guidelines apply to everyone. At the same time, this is NOT a process document. So just use good judgment. You have been given commit access because we trust your judgment.
That said, use the trust wisely.
If you abuse the trust and break components and builds, and so on, the trust level falls and you may be asked not to commit or you may lose your commit privileges.
Our workflow on GitHub
----------------------
As a committer, you may already know this, but our workflow forms a lot of our team policies. Please ensure you are aware of the following workflow steps:
* Fork the repository upon which you want to do some work to your own personal repository
* Work on the specific branch upon which you need to commit
* Create a Pull Request back to the collection repository and await reviews
* Adjust code as necessary based on the Comments provided
* Ask someone from the other committers to do a final review and merge
Sometimes, committers merge their own pull requests. This section is a set of guidelines. If you are changing a comma in a doc or making a very minor change, you can use your best judgement. This is another trust thing. The process is critical for any major change, but for little things or getting something done quickly, use your best judgement and make sure people on the team are aware of your work.
Roles
-----
* Release managers: Merge pull requests to `stable-X` branches, create tags to do releases.
* Committers: Fine to do PRs for most things, but we should have a timebox. Hanging PRs may merge on the judgement of these devs.
* Module maintainers: Module maintainers own specific modules and have indirect commit access through the current module PR mechanisms. This is primary [ansibullbot](https://github.com/ansibullbot)'s `shipit` mechanism.
General rules
-------------
Individuals with direct commit access to this collection repository are entrusted with powers that allow them to do a broad variety of things--probably more than we can write down. Rather than rules, treat these as general *guidelines*, individuals with this power are expected to use their best judgement.
* Do NOTs:
- Do not commit directly.
- Do not merge your own PRs. Someone else should have a chance to review and approve the PR merge. You have a small amount of leeway here for very minor changes.
- Do not forget about non-standard / alternate environments. Consider the alternatives. Yes, people have bad/unusual/strange environments (like binaries from multiple init systems installed), but they are the ones who need us the most.
- Do not drag your community team members down. Discuss the technical merits of any pull requests you review. Avoid negativity and personal comments. For more guidance on being a good community member, read the [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html).
- Do not forget about the maintenance burden. High-maintenance features may not be worth adding.
- Do not break playbooks. Always keep backwards compatibility in mind.
- Do not forget to keep it simple. Complexity breeds all kinds of problems.
- Do not merge to branches other than `main`, especially not to `stable-X`, if you do not have explicit permission to do so.
- Do not create tags. Tags are used in the release process, and should only be created by the people responsible for managing the stable branches.
* Do:
- Squash, avoid merges whenever possible, use GitHub's squash commits or cherry pick if needed (bisect thanks you).
- Be active. Committers who have no activity on the project (through merges, triage, commits, and so on) will have their permissions suspended.
- Consider backwards compatibility (goes back to "do not break existing playbooks").
- Write tests. PRs with tests are looked at with more priority than PRs without tests that should have them included. While not all changes require tests, be sure to add them for bug fixes or functionality changes.
- Discuss with other committers, specially when you are unsure of something.
- Document! If your PR is a new feature or a change to behavior, make sure you've updated all associated documentation or have notified the right people to do so.
- Consider scope, sometimes a fix can be generalized.
- Keep it simple, then things are maintainable, debuggable and intelligible.
Committers are expected to continue to follow the same community and contribution guidelines followed by the rest of the Ansible community.
People
------
Individuals who have been asked to become a part of this group have generally been contributing in significant ways to the community.general collection for some time. Should they agree, they are requested to add their names and GitHub IDs to this file, in the section below, through a pull request. Doing so indicates that these individuals agree to act in the ways that their fellow committers trust that they will act.
| Name | GitHub ID | IRC Nick | Other |
| ------------------- | -------------------- | ------------------ | -------------------- |
| Andrew Klychkov | andersson007 | andersson007_ | |
| Felix Fontein | felixfontein | felixfontein | |
| John R Barker | gundalow | gundalow | |

View File

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

View File

@@ -39,7 +39,7 @@ plugin_routing:
redirect: community.hashi_vault.hashi_vault
modules:
ali_instance_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.ali_instance_info instead.
docker_compose:
@@ -159,8 +159,7 @@ plugin_routing:
gcpubsub_info:
redirect: community.google.gcpubsub_info
gcpubsub_facts:
redirect: community.google.gcpubsub_info
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.google.gcpubsub_info instead.
gcspanner:
@@ -171,22 +170,23 @@ plugin_routing:
tombstone:
removal_version: 2.0.0
warning_text: Use community.general.github_webhook and community.general.github_webhook_info instead.
gluster_heal_info:
deprecation:
removal_version: 3.0.0
warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_heal_info instead.
gluster_peer:
deprecation:
removal_version: 3.0.0
warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_peer instead.
gluster_volume:
deprecation:
removal_version: 3.0.0
warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_volume instead.
helm:
deprecation:
removal_version: 3.0.0
warning_text: The helm module in community.general has been deprecated. Use community.kubernetes.helm instead.
# Adding tombstones burns the old name, so we simply remove the entries:
# gluster_heal_info:
# tombstone:
# removal_version: 3.0.0
# warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_heal_info instead.
# gluster_peer:
# tombstone:
# removal_version: 3.0.0
# warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_peer instead.
# gluster_volume:
# tombstone:
# removal_version: 3.0.0
# warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_volume instead.
# helm:
# tombstone:
# removal_version: 3.0.0
# warning_text: Use community.kubernetes.helm instead.
hetzner_failover_ip:
redirect: community.hrobot.failover_ip
hetzner_failover_ip_info:
@@ -196,15 +196,19 @@ plugin_routing:
hetzner_firewall_info:
redirect: community.hrobot.firewall_info
hpilo_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.hpilo_info instead.
idrac_firmware:
redirect: dellemc.openmanage.idrac_firmware
idrac_redfish_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.idrac_redfish_info instead.
idrac_server_config_profile:
redirect: dellemc.openmanage.idrac_server_config_profile
jenkins_job_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.jenkins_job_info instead.
katello:
@@ -224,7 +228,7 @@ plugin_routing:
kubevirt_vm:
redirect: community.kubevirt.kubevirt_vm
ldap_attr:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.ldap_attrs instead.
logicmonitor:
@@ -236,11 +240,11 @@ plugin_routing:
removal_version: 1.0.0
warning_text: The logicmonitor_facts module is no longer maintained and the API used has been disabled in 2017.
memset_memstore_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.memset_memstore_info instead.
memset_server_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.memset_server_info instead.
na_cdot_aggregate:
@@ -276,159 +280,161 @@ plugin_routing:
removal_version: 2.0.0
warning_text: Use netapp.ontap.na_ontap_volume instead.
na_ontap_gather_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use netapp.ontap.na_ontap_info instead.
nginx_status_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.nginx_status_info instead.
ome_device_info:
redirect: dellemc.openmanage.ome_device_info
one_image_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.one_image_info instead.
onepassword_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.onepassword_info instead.
oneview_datacenter_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_datacenter_info instead.
oneview_enclosure_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_enclosure_info instead.
oneview_ethernet_network_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_ethernet_network_info instead.
oneview_fc_network_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_fc_network_info instead.
oneview_fcoe_network_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_fcoe_network_info instead.
oneview_logical_interconnect_group_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_logical_interconnect_group_info instead.
oneview_network_set_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_network_set_info instead.
oneview_san_manager_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.oneview_san_manager_info instead.
online_server_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.online_server_info instead.
online_user_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.online_user_info instead.
ovirt:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_vm instead.
ovirt_affinity_label_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_affinity_label_info instead.
ovirt_api_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_api_info instead.
ovirt_cluster_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_cluster_info instead.
ovirt_datacenter_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_datacenter_info instead.
ovirt_disk_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_disk_info instead.
ovirt_event_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_event_info instead.
ovirt_external_provider_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_external_provider_info instead.
ovirt_group_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_group_info instead.
ovirt_host_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_host_info instead.
ovirt_host_storage_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_host_storage_info instead.
ovirt_network_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_network_info instead.
ovirt_nic_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_nic_info instead.
ovirt_permission_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_permission_info instead.
ovirt_quota_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_quota_info instead.
ovirt_scheduling_policy_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_scheduling_policy_info instead.
ovirt_snapshot_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_snapshot_info instead.
ovirt_storage_domain_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_storage_domain_info instead.
ovirt_storage_template_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_storage_template_info instead.
ovirt_storage_vm_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_storage_vm_info instead.
ovirt_tag_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_tag_info instead.
ovirt_template_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_template_info instead.
ovirt_user_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_user_info instead.
ovirt_vm_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_vm_info instead.
ovirt_vmpool_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_vmpool_info instead.
postgresql_copy:
@@ -476,47 +482,47 @@ plugin_routing:
postgresql_user:
redirect: community.postgresql.postgresql_user
purefa_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use purestorage.flasharray.purefa_info instead.
purefb_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use purestorage.flashblade.purefb_info instead.
python_requirements_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.python_requirements_info instead.
redfish_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.redfish_info instead.
scaleway_image_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_image_info instead.
scaleway_ip_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_ip_info instead.
scaleway_organization_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_organization_info instead.
scaleway_security_group_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_security_group_info instead.
scaleway_server_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_server_info instead.
scaleway_snapshot_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_snapshot_info instead.
scaleway_volume_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_volume_info instead.
sf_account_manager:
@@ -540,15 +546,15 @@ plugin_routing:
removal_version: 2.0.0
warning_text: Use netapp.elementsw.na_elementsw_volume instead.
smartos_image_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.smartos_image_info instead.
vertica_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.vertica_info instead.
xenserver_guest_facts:
deprecation:
tombstone:
removal_version: 3.0.0
warning_text: Use community.general.xenserver_guest_info instead.
doc_fragments:
@@ -565,6 +571,8 @@ plugin_routing:
postgresql:
redirect: community.postgresql.postgresql
module_utils:
remote_management.dellemc.dellemc_idrac:
redirect: dellemc.openmanage.dellemc_idrac
docker.common:
redirect: community.docker.common
docker.swarm:
@@ -579,6 +587,8 @@ plugin_routing:
redirect: community.hrobot.robot
kubevirt:
redirect: community.kubevirt.kubevirt
remote_management.dellemc.ome:
redirect: dellemc.openmanage.ome
postgresql:
redirect: community.postgresql.postgresql
callback:
@@ -601,3 +611,10 @@ plugin_routing:
redirect: community.docker.docker_swarm
kubevirt:
redirect: community.kubevirt.kubevirt
filter:
path_join:
# The ansible.builtin.path_join filter has been added in ansible-base 2.10.
# Since plugin routing is only available since ansible-base 2.10, this
# redirect will be used for ansible-base 2.10 or later, and the included
# path_join filter will be used for Ansible 2.9 or earlier.
redirect: ansible.builtin.path_join

91
plugins/become/sudosu.py Normal file
View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
name: sudosu
short_description: Run tasks using sudo su -
description:
- This become plugins allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined.
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
"""
from ansible.plugins.become import BecomeBase
class BecomeModule(BecomeBase):
name = 'community.general.sudosu'
# messages for detecting prompted password issues
fail = ('Sorry, try again.',)
missing = ('Sorry, a password is required to run sudo', 'sudo: a password is required')
def build_become_command(self, cmd, shell):
super(BecomeModule, self).build_become_command(cmd, shell)
if not cmd:
return cmd
becomecmd = 'sudo'
flags = self.get_option('become_flags') or ''
prompt = ''
if self.get_option('become_pass'):
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 = '-p "%s"' % (self.prompt)
user = self.get_option('become_user') or ''
if user:
user = '%s' % (user)
return ' '.join([becomecmd, flags, prompt, 'su -l', user, self._build_success_command(cmd, shell)])

View File

@@ -0,0 +1,234 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
name: loganalytics
type: aggregate
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 = '''
examples: |
Whitelist the plugin in ansible.cfg:
[defaults]
callback_whitelist = community.general.loganalytics
Set the environment variable:
export WORKSPACE_ID=01234567-0123-0123-0123-01234567890a
export WORKSPACE_SHARED_KEY=dZD0kCbKl3ehZG6LHFMuhtE0yHiFCmetzFMc2u+roXIUQuatqU924SsAAAAPemhjbGlAemhjbGktTUJQAQIDBA==
Or configure the plugin in ansible.cfg in the callback_loganalytics block:
[callback_loganalytics]
workspace_id = 01234567-0123-0123-0123-01234567890a
shared_key = dZD0kCbKl3ehZG6LHFMuhtE0yHiFCmetzFMc2u+roXIUQuatqU924SsAAAAPemhjbGlAemhjbGktTUJQAQIDBA==
'''
import hashlib
import hmac
import base64
import logging
import json
import uuid
import socket
import getpass
from datetime import datetime
from os.path import basename
from ansible.module_utils.urls import open_url
from ansible.parsing.ajson import AnsibleJSONEncoder
from ansible.plugins.callback import CallbackBase
class AzureLogAnalyticsSource(object):
def __init__(self):
self.ansible_check_mode = False
self.ansible_playbook = ""
self.ansible_version = ""
self.session = str(uuid.uuid4())
self.host = socket.gethostname()
self.user = getpass.getuser()
self.extra_vars = ""
def __build_signature(self, date, workspace_id, shared_key, content_length):
# Build authorisation signature for Azure log analytics API call
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 = "SharedKey {0}:{1}".format(workspace_id, encoded_hash)
return signature
def __build_workspace_url(self, workspace_id):
return "https://{0}.ods.opinsights.azure.com/api/logs?api-version=2016-04-01".format(workspace_id)
def __rfc1123date(self):
return datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
def send_event(self, workspace_id, shared_key, state, result, runtime):
if result._task_fields['args'].get('_ansible_check_mode') is True:
self.ansible_check_mode = True
if result._task_fields['args'].get('_ansible_version'):
self.ansible_version = \
result._task_fields['args'].get('_ansible_version')
if result._task._role:
ansible_role = str(result._task._role)
else:
ansible_role = None
data = {}
data['uuid'] = result._task._uuid
data['session'] = self.session
data['status'] = state
data['timestamp'] = self.__rfc1123date()
data['host'] = self.host
data['user'] = self.user
data['runtime'] = runtime
data['ansible_version'] = self.ansible_version
data['ansible_check_mode'] = self.ansible_check_mode
data['ansible_host'] = result._host.name
data['ansible_playbook'] = self.ansible_playbook
data['ansible_role'] = ansible_role
data['ansible_task'] = result._task_fields
# Removing args since it can contain sensitive data
if 'args' in data['ansible_task']:
data['ansible_task'].pop('args')
data['ansible_result'] = result._result
if 'content' in data['ansible_result']:
data['ansible_result'].pop('content')
# Adding extra vars info
data['extra_vars'] = self.extra_vars
# Preparing the playbook logs as JSON format and send to Azure log analytics
jsondata = json.dumps({'event': data}, cls=AnsibleJSONEncoder, sort_keys=True)
content_length = len(jsondata)
rfc1123date = self.__rfc1123date()
signature = self.__build_signature(rfc1123date, workspace_id, shared_key, content_length)
workspace_url = self.__build_workspace_url(workspace_id)
open_url(
workspace_url,
jsondata,
headers={
'content-type': 'application/json',
'Authorization': signature,
'Log-Type': 'ansible_playbook',
'x-ms-date': rfc1123date
},
method='POST'
)
class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'aggregate'
CALLBACK_NAME = 'loganalytics'
CALLBACK_NEEDS_WHITELIST = True
def __init__(self, display=None):
super(CallbackModule, self).__init__(display=display)
self.start_datetimes = {} # Collect task start times
self.workspace_id = None
self.shared_key = None
self.loganalytics = AzureLogAnalyticsSource()
def _seconds_since_start(self, result):
return (
datetime.utcnow() -
self.start_datetimes[result._task._uuid]
).total_seconds()
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.workspace_id = self.get_option('workspace_id')
self.shared_key = self.get_option('shared_key')
def v2_playbook_on_play_start(self, play):
vm = play.get_variable_manager()
extra_vars = vm.extra_vars
self.loganalytics.extra_vars = extra_vars
def v2_playbook_on_start(self, playbook):
self.loganalytics.ansible_playbook = basename(playbook._file_name)
def v2_playbook_on_task_start(self, task, is_conditional):
self.start_datetimes[task._uuid] = datetime.utcnow()
def v2_playbook_on_handler_task_start(self, task):
self.start_datetimes[task._uuid] = datetime.utcnow()
def v2_runner_on_ok(self, result, **kwargs):
self.loganalytics.send_event(
self.workspace_id,
self.shared_key,
'OK',
result,
self._seconds_since_start(result)
)
def v2_runner_on_skipped(self, result, **kwargs):
self.loganalytics.send_event(
self.workspace_id,
self.shared_key,
'SKIPPED',
result,
self._seconds_since_start(result)
)
def v2_runner_on_failed(self, result, **kwargs):
self.loganalytics.send_event(
self.workspace_id,
self.shared_key,
'FAILED',
result,
self._seconds_since_start(result)
)
def runner_on_async_failed(self, result, **kwargs):
self.loganalytics.send_event(
self.workspace_id,
self.shared_key,
'FAILED',
result,
self._seconds_since_start(result)
)
def v2_runner_on_unreachable(self, result, **kwargs):
self.loganalytics.send_event(
self.workspace_id,
self.shared_key,
'UNREACHABLE',
result,
self._seconds_since_start(result)
)

View File

@@ -37,12 +37,13 @@ import tempfile
import shutil
from ansible.errors import AnsibleError
from ansible.plugins.connection import ConnectionBase
from ansible.utils.display import Display
display = Display()
class Connection(object):
class Connection(ConnectionBase):
''' Func-based connections '''
has_pipelining = False

View File

@@ -30,7 +30,6 @@ options:
description:
- Keycloak realm name to authenticate to for API access.
type: str
required: true
auth_client_secret:
description:
@@ -41,7 +40,6 @@ options:
description:
- Username to authenticate for API access with.
type: str
required: true
aliases:
- username
@@ -49,10 +47,15 @@ options:
description:
- Password to authenticate for API access with.
type: str
required: true
aliases:
- password
token:
description:
- Authentication token for Keycloak API.
type: str
version_added: 3.0.0
validate_certs:
description:
- Verify TLS certificates (do not disable this in production).

View File

@@ -13,12 +13,32 @@ class ModuleDocFragment(object):
DOCUMENTATION = r'''
options:
config:
description:
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
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

View File

@@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2016, Red Hat, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
class ModuleDocFragment(object):
# info standard oVirt documentation fragment
DOCUMENTATION = r'''
options:
fetch_nested:
description:
- If I(yes) the module will fetch additional data from the API.
- It will fetch only IDs of nested entity. It doesn't fetch multiple levels of nested attributes.
Only the attributes of the current entity. User can configure to fetch other
attributes of the nested entities by specifying C(nested_attributes).
type: bool
default: false
nested_attributes:
description:
- Specifies list of the attributes which should be fetched from the API.
- This parameter apply only when C(fetch_nested) is I(true).
type: list
auth:
description:
- "Dictionary with values needed to create HTTP/HTTPS connection to oVirt:"
- C(username)[I(required)] - The name of the user, something like I(admin@internal).
Default value is set by I(OVIRT_USERNAME) environment variable.
- "C(password)[I(required)] - The password of the user. Default value is set by I(OVIRT_PASSWORD) environment variable."
- "C(url)- A string containing the API URL of the server, usually
something like `I(https://server.example.com/ovirt-engine/api)`. Default value is set by I(OVIRT_URL) environment variable.
Either C(url) or C(hostname) is required."
- "C(hostname) - A string containing the hostname of the server, usually
something like `I(server.example.com)`. Default value is set by I(OVIRT_HOSTNAME) environment variable.
Either C(url) or C(hostname) is required."
- "C(token) - Token to be used instead of login with username/password. Default value is set by I(OVIRT_TOKEN) environment variable."
- "C(insecure) - A boolean flag that indicates if the server TLS
certificate and host name should be checked."
- "C(ca_file) - A PEM file containing the trusted CA certificates. The
certificate presented by the server will be verified using these CA
certificates. If `C(ca_file)` parameter is not set, system wide
CA certificate store is used. Default value is set by I(OVIRT_CAFILE) environment variable."
- "C(kerberos) - A boolean flag indicating if Kerberos authentication
should be used instead of the default basic authentication."
- "C(headers) - Dictionary of HTTP headers to be added to each API call."
type: dict
required: true
requirements:
- python >= 2.7
- ovirt-engine-sdk-python >= 4.3.0
notes:
- "In order to use this module you have to install oVirt Python SDK.
To ensure it's installed with correct version you can create the following task:
ansible.builtin.pip: name=ovirt-engine-sdk-python version=4.3.0"
'''

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Florian Dambrine <android.florian@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r"""
options:
pritunl_url:
type: str
required: true
description:
- URL and port of the Pritunl server on which the API is enabled.
pritunl_api_token:
type: str
required: true
description:
- API Token of a Pritunl admin user.
- It needs to be enabled in Administrators > USERNAME > Enable Token Authentication.
pritunl_api_secret:
type: str
required: true
description:
- API Secret found in Administrators > USERNAME > API Secret.
validate_certs:
type: bool
required: false
default: true
description:
- If certificates should be validated or not.
- This should never be set to C(false), except if you are very sure that
your connection to the server can not be subject to a Man In The Middle
attack.
"""

24
plugins/filter/dict.py Normal file
View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
def dict_filter(sequence):
'''Convert a list of tuples to a dictionary.
Example: ``[[1, 2], ['a', 'b']] | community.general.dict`` results in ``{1: 2, 'a': 'b'}``
'''
return dict(sequence)
class FilterModule(object):
'''Ansible jinja2 filters'''
def filters(self):
return {
'dict': dict_filter,
}

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@gmail.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.errors import AnsibleFilterError
from ansible.module_utils._text import to_native
from ansible_collections.community.general.plugins.module_utils.csv import (initialize_dialect, read_csv, CSVError,
DialectNotAvailableError,
CustomDialectFailureError)
def from_csv(data, dialect='excel', fieldnames=None, delimiter=None, skipinitialspace=None, strict=None):
dialect_params = {
"delimiter": delimiter,
"skipinitialspace": skipinitialspace,
"strict": strict,
}
try:
dialect = initialize_dialect(dialect, **dialect_params)
except (CustomDialectFailureError, DialectNotAvailableError) as e:
raise AnsibleFilterError(to_native(e))
reader = read_csv(data, dialect, fieldnames)
data_list = []
try:
for row in reader:
data_list.append(row)
except CSVError as e:
raise AnsibleFilterError("Unable to process file: %s" % to_native(e))
return data_list
class FilterModule(object):
def filters(self):
return {
'from_csv': from_csv
}

97
plugins/filter/hashids.py Normal file
View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.errors import (
AnsibleError,
AnsibleFilterError,
AnsibleFilterTypeError,
)
from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.common.collections import is_sequence
try:
from hashids import Hashids
HAS_HASHIDS = True
except ImportError:
HAS_HASHIDS = False
def initialize_hashids(**kwargs):
if not HAS_HASHIDS:
raise AnsibleError("The hashids library must be installed in order to use this plugin")
params = dict((k, v) for k, v in kwargs.items() if v)
try:
return Hashids(**params)
except TypeError as e:
raise AnsibleFilterError(
"The provided parameters %s are invalid: %s" % (
', '.join(["%s=%s" % (k, v) for k, v in params.items()]),
to_native(e)
)
)
def hashids_encode(nums, salt=None, alphabet=None, min_length=None):
"""Generates a YouTube-like hash from a sequence of ints
:nums: Sequence of one or more ints to hash
:salt: String to use as salt when hashing
:alphabet: String of 16 or more unique characters to produce a hash
:min_length: Minimum length of hash produced
"""
hashids = initialize_hashids(
salt=salt,
alphabet=alphabet,
min_length=min_length
)
# Handles the case where a single int is not encapsulated in a list or tuple.
# User convenience seems preferable to strict typing in this case
# Also avoids obfuscated error messages related to single invalid inputs
if not is_sequence(nums):
nums = [nums]
try:
hashid = hashids.encode(*nums)
except TypeError as e:
raise AnsibleFilterTypeError(
"Data to encode must by a tuple or list of ints: %s" % to_native(e)
)
return hashid
def hashids_decode(hashid, salt=None, alphabet=None, min_length=None):
"""Decodes a YouTube-like hash to a sequence of ints
:hashid: Hash string to decode
:salt: String to use as salt when hashing
:alphabet: String of 16 or more unique characters to produce a hash
:min_length: Minimum length of hash produced
"""
hashids = initialize_hashids(
salt=salt,
alphabet=alphabet,
min_length=min_length
)
nums = hashids.decode(hashid)
return list(nums)
class FilterModule(object):
def filters(self):
return {
'hashids_encode': hashids_encode,
'hashids_decode': hashids_decode,
}

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2020-2021, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os.path
def path_join(list):
'''Join list of paths.
This is a minimal shim for ansible.builtin.path_join included in ansible-base 2.10.
This should only be called by Ansible 2.9 or earlier. See meta/runtime.yml for details.
'''
return os.path.join(*list)
class FilterModule(object):
'''Ansible jinja2 filters'''
def filters(self):
return {
'path_join': path_join,
}

950
plugins/inventory/lxd.py Normal file
View File

@@ -0,0 +1,950 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Frank Dornheim <dornheim@posteo.de>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
name: lxd
short_description: Returns Ansible inventory from lxd host
description:
- Get inventory from the lxd.
- Uses a YAML configuration file that ends with 'lxd.(yml|yaml)'.
version_added: "3.0.0"
author: "Frank Dornheim (@conloos)"
options:
plugin:
description: Token that ensures this is a source file for the 'lxd' plugin.
required: true
choices: [ 'community.general.lxd' ]
url:
description:
- The unix domain socket path or the https URL for the lxd server.
- Sockets in filesystem have to start with C(unix:).
- Mostly C(unix:/var/lib/lxd/unix.socket) or C(unix:/var/snap/lxd/common/lxd/unix.socket).
default: unix:/var/snap/lxd/common/lxd/unix.socket
type: str
client_key:
description:
- The client certificate key file path.
aliases: [ key_file ]
default: $HOME/.config/lxc/client.key
type: path
client_cert:
description:
- The client certificate file path.
aliases: [ cert_file ]
default: $HOME/.config/lxc/client.crt
type: path
trust_password:
description:
- The client trusted password.
- You need to set this password on the lxd server before
running this module using the following command
C(lxc config set core.trust_password <some random password>)
See U(https://www.stgraber.org/2016/04/18/lxd-api-direct-interaction/).
- If I(trust_password) is set, this module send a request for authentication before sending any requests.
type: str
state:
description: Filter the container according to the current status.
type: str
default: none
choices: [ 'STOPPED', 'STARTING', 'RUNNING', 'none' ]
prefered_container_network_interface:
description:
- If a container has multiple network interfaces, select which one is the prefered as pattern.
- Combined with the first number that can be found e.g. 'eth' + 0.
type: str
default: eth
prefered_container_network_family:
description:
- If a container has multiple network interfaces, which one is the prefered by family.
- Specify C(inet) for IPv4 and C(inet6) for IPv6.
type: str
default: inet
choices: [ 'inet', 'inet6' ]
groupby:
description:
- Create groups by the following keywords C(location), C(pattern), C(network_range), C(os), C(release), C(profile), C(vlanid).
- See example for syntax.
type: dict
'''
EXAMPLES = '''
# simple lxd.yml
plugin: community.general.lxd
url: unix:/var/snap/lxd/common/lxd/unix.socket
# simple lxd.yml including filter
plugin: community.general.lxd
url: unix:/var/snap/lxd/common/lxd/unix.socket
state: RUNNING
# grouping lxd.yml
groupby:
testpattern:
type: pattern
attribute: test
vlan666:
type: vlanid
attribute: 666
locationBerlin:
type: location
attribute: Berlin
osUbuntu:
type: os
attribute: ubuntu
releaseFocal:
type: release
attribute: focal
releaseBionic:
type: release
attribute: bionic
profileDefault:
type: profile
attribute: default
profileX11:
type: profile
attribute: x11
netRangeIPv4:
type: network_range
attribute: 10.98.143.0/24
netRangeIPv6:
type: network_range
attribute: fd42:bd00:7b11:2167:216:3eff::/24
'''
import binascii
import json
import re
import time
import os
import socket
from ansible.plugins.inventory import BaseInventoryPlugin
from ansible.module_utils._text import to_native, to_text
from ansible.module_utils.common.dict_transformations import dict_merge
from ansible.errors import AnsibleError, AnsibleParserError
from ansible_collections.community.general.plugins.module_utils.compat import ipaddress
from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException
class InventoryModule(BaseInventoryPlugin):
DEBUG = 4
NAME = 'community.general.lxd'
SNAP_SOCKET_URL = 'unix:/var/snap/lxd/common/lxd/unix.socket'
SOCKET_URL = 'unix:/var/lib/lxd/unix.socket'
@staticmethod
def load_json_data(path):
"""Load json data
Load json data from file
Args:
list(path): Path elements
str(file_name): Filename of data
Kwargs:
None
Raises:
None
Returns:
dict(json_data): json data"""
try:
with open(path, 'r') as json_file:
return json.load(json_file)
except (IOError, json.decoder.JSONDecodeError) as err:
raise AnsibleParserError('Could not load the test data from {0}: {1}'.format(to_native(path), to_native(err)))
def save_json_data(self, path, file_name=None):
"""save data as json
Save data as json file
Args:
list(path): Path elements
str(file_name): Filename of data
Kwargs:
None
Raises:
None
Returns:
None"""
if file_name:
path.append(file_name)
else:
prefix = 'lxd_data-'
time_stamp = time.strftime('%Y%m%d-%H%M%S')
suffix = '.atd'
path.append(prefix + time_stamp + suffix)
try:
cwd = os.path.abspath(os.path.dirname(__file__))
with open(os.path.abspath(os.path.join(cwd, *path)), 'w') as json_file:
json.dump(self.data, json_file)
except IOError as err:
raise AnsibleParserError('Could not save data: {0}'.format(to_native(err)))
def verify_file(self, path):
"""Check the config
Return true/false if the config-file is valid for this plugin
Args:
str(path): path to the config
Kwargs:
None
Raises:
None
Returns:
bool(valid): is valid"""
valid = False
if super(InventoryModule, self).verify_file(path):
if path.endswith(('lxd.yaml', 'lxd.yml')):
valid = True
else:
self.display.vvv('Inventory source not ending in "lxd.yaml" or "lxd.yml"')
return valid
@staticmethod
def validate_url(url):
"""validate url
check whether the url is correctly formatted
Args:
url
Kwargs:
None
Raises:
AnsibleError
Returns:
bool"""
if not isinstance(url, str):
return False
if not url.startswith(('unix:', 'https:')):
raise AnsibleError('URL is malformed: {0}'.format(to_native(url)))
return True
def _connect_to_socket(self):
"""connect to lxd socket
Connect to lxd socket by provided url or defaults
Args:
None
Kwargs:
None
Raises:
AnsibleError
Returns:
None"""
error_storage = {}
url_list = [self.get_option('url'), self.SNAP_SOCKET_URL, self.SOCKET_URL]
urls = (url for url in url_list if self.validate_url(url))
for url in urls:
try:
socket_connection = LXDClient(url, self.client_key, self.client_cert, self.debug)
return socket_connection
except LXDClientException as err:
error_storage[url] = err
raise AnsibleError('No connection to the socket: {0}'.format(to_native(error_storage)))
def _get_networks(self):
"""Get Networknames
Returns all network config names
Args:
None
Kwargs:
None
Raises:
None
Returns:
list(names): names of all network_configs"""
# e.g. {'type': 'sync',
# 'status': 'Success',
# 'status_code': 200,
# 'operation': '',
# 'error_code': 0,
# 'error': '',
# 'metadata': ['/1.0/networks/lxdbr0']}
network_configs = self.socket.do('GET', '/1.0/networks')
return [m.split('/')[3] for m in network_configs['metadata']]
def _get_containers(self):
"""Get Containernames
Returns all containernames
Args:
None
Kwargs:
None
Raises:
None
Returns:
list(names): names of all containers"""
# e.g. {'type': 'sync',
# 'status': 'Success',
# 'status_code': 200,
# 'operation': '',
# 'error_code': 0,
# 'error': '',
# 'metadata': ['/1.0/containers/udemy-ansible-ubuntu-2004']}
containers = self.socket.do('GET', '/1.0/containers')
return [m.split('/')[3] for m in containers['metadata']]
def _get_config(self, branch, name):
"""Get inventory of container
Get config of container
Args:
str(branch): Name oft the API-Branch
str(name): Name of Container
Kwargs:
None
Source:
https://github.com/lxc/lxd/blob/master/doc/rest-api.md
Raises:
None
Returns:
dict(config): Config of the container"""
config = {}
if isinstance(branch, (tuple, list)):
config[name] = {branch[1]: self.socket.do('GET', '/1.0/{0}/{1}/{2}'.format(to_native(branch[0]), to_native(name), to_native(branch[1])))}
else:
config[name] = {branch: self.socket.do('GET', '/1.0/{0}/{1}'.format(to_native(branch), to_native(name)))}
return config
def get_container_data(self, names):
"""Create Inventory of the container
Iterate through the different branches of the containers and collect Informations.
Args:
list(names): List of container names
Kwargs:
None
Raises:
None
Returns:
None"""
# tuple(('instances','metadata/templates')) to get section in branch
# e.g. /1.0/instances/<name>/metadata/templates
branches = ['containers', ('instances', 'state')]
container_config = {}
for branch in branches:
for name in names:
container_config['containers'] = self._get_config(branch, name)
self.data = dict_merge(container_config, self.data)
def get_network_data(self, names):
"""Create Inventory of the container
Iterate through the different branches of the containers and collect Informations.
Args:
list(names): List of container names
Kwargs:
None
Raises:
None
Returns:
None"""
# tuple(('instances','metadata/templates')) to get section in branch
# e.g. /1.0/instances/<name>/metadata/templates
branches = [('networks', 'state')]
network_config = {}
for branch in branches:
for name in names:
try:
network_config['networks'] = self._get_config(branch, name)
except LXDClientException:
network_config['networks'] = {name: None}
self.data = dict_merge(network_config, self.data)
def extract_network_information_from_container_config(self, container_name):
"""Returns the network interface configuration
Returns the network ipv4 and ipv6 config of the container without local-link
Args:
str(container_name): Name oft he container
Kwargs:
None
Raises:
None
Returns:
dict(network_configuration): network config"""
container_network_interfaces = self._get_data_entry('containers/{0}/state/metadata/network'.format(container_name))
network_configuration = None
if container_network_interfaces:
network_configuration = {}
gen_interface_names = [interface_name for interface_name in container_network_interfaces if interface_name != 'lo']
for interface_name in gen_interface_names:
gen_address = [address for address in container_network_interfaces[interface_name]['addresses'] if address.get('scope') != 'link']
network_configuration[interface_name] = []
for address in gen_address:
address_set = {}
address_set['family'] = address.get('family')
address_set['address'] = address.get('address')
address_set['netmask'] = address.get('netmask')
address_set['combined'] = address.get('address') + '/' + address.get('netmask')
network_configuration[interface_name].append(address_set)
return network_configuration
def get_prefered_container_network_interface(self, container_name):
"""Helper to get the prefered interface of thr container
Helper to get the prefered interface provide by neme pattern from 'prefered_container_network_interface'.
Args:
str(containe_name): name of container
Kwargs:
None
Raises:
None
Returns:
str(prefered_interface): None or interface name"""
container_network_interfaces = self._get_data_entry('inventory/{0}/network_interfaces'.format(container_name))
prefered_interface = None # init
if container_network_interfaces: # container have network interfaces
# generator if interfaces which start with the desired pattern
net_generator = [interface for interface in container_network_interfaces if interface.startswith(self.prefered_container_network_interface)]
selected_interfaces = [] # init
for interface in net_generator:
selected_interfaces.append(interface)
if len(selected_interfaces) > 0:
prefered_interface = sorted(selected_interfaces)[0]
return prefered_interface
def get_container_vlans(self, container_name):
"""Get VLAN(s) from container
Helper to get the VLAN_ID from the container
Args:
str(containe_name): name of container
Kwargs:
None
Raises:
None
Returns:
None"""
# get network device configuration and store {network: vlan_id}
network_vlans = {}
for network in self._get_data_entry('networks'):
if self._get_data_entry('state/metadata/vlan/vid', data=self.data['networks'].get(network)):
network_vlans[network] = self._get_data_entry('state/metadata/vlan/vid', data=self.data['networks'].get(network))
# get networkdevices of container and return
# e.g.
# "eth0":{ "name":"eth0",
# "network":"lxdbr0",
# "type":"nic"},
vlan_ids = {}
devices = self._get_data_entry('containers/{0}/containers/metadata/expanded_devices'.format(to_native(container_name)))
for device in devices:
if 'network' in devices[device]:
if devices[device]['network'] in network_vlans:
vlan_ids[devices[device].get('network')] = network_vlans[devices[device].get('network')]
return vlan_ids if vlan_ids else None
def _get_data_entry(self, path, data=None, delimiter='/'):
"""Helper to get data
Helper to get data from self.data by a path like 'path/to/target'
Attention: Escaping of the delimiter is not (yet) provided.
Args:
str(path): path to nested dict
Kwargs:
dict(data): datastore
str(delimiter): delimiter in Path.
Raises:
None
Returns:
*(value)"""
try:
if not data:
data = self.data
if delimiter in path:
path = path.split(delimiter)
if isinstance(path, list) and len(path) > 1:
data = data[path.pop(0)]
path = delimiter.join(path)
return self._get_data_entry(path, data, delimiter) # recursion
return data[path]
except KeyError:
return None
def _set_data_entry(self, container_name, key, value, path=None):
"""Helper to save data
Helper to save the data in self.data
Detect if data is allready in branch and use dict_merge() to prevent that branch is overwritten.
Args:
str(container_name): name of container
str(key): same as dict
*(value): same as dict
Kwargs:
str(path): path to branch-part
Raises:
AnsibleParserError
Returns:
None"""
if not path:
path = self.data['inventory']
if container_name not in path:
path[container_name] = {}
try:
if isinstance(value, dict) and key in path[container_name]:
path[container_name] = dict_merge(value, path[container_name][key])
else:
path[container_name][key] = value
except KeyError as err:
raise AnsibleParserError("Unable to store Informations: {0}".format(to_native(err)))
def extract_information_from_container_configs(self):
"""Process configuration information
Preparation of the data
Args:
dict(configs): Container configurations
Kwargs:
None
Raises:
None
Returns:
None"""
# create branch "inventory"
if 'inventory' not in self.data:
self.data['inventory'] = {}
for container_name in self.data['containers']:
self._set_data_entry(container_name, 'os', self._get_data_entry(
'containers/{0}/containers/metadata/config/image.os'.format(container_name)))
self._set_data_entry(container_name, 'release', self._get_data_entry(
'containers/{0}/containers/metadata/config/image.release'.format(container_name)))
self._set_data_entry(container_name, 'version', self._get_data_entry(
'containers/{0}/containers/metadata/config/image.version'.format(container_name)))
self._set_data_entry(container_name, 'profile', self._get_data_entry(
'containers/{0}/containers/metadata/profiles'.format(container_name)))
self._set_data_entry(container_name, 'location', self._get_data_entry(
'containers/{0}/containers/metadata/location'.format(container_name)))
self._set_data_entry(container_name, 'state', self._get_data_entry(
'containers/{0}/containers/metadata/config/volatile.last_state.power'.format(container_name)))
self._set_data_entry(container_name, 'network_interfaces', self.extract_network_information_from_container_config(container_name))
self._set_data_entry(container_name, 'preferred_interface', self.get_prefered_container_network_interface(container_name))
self._set_data_entry(container_name, 'vlan_ids', self.get_container_vlans(container_name))
def build_inventory_network(self, container_name):
"""Add the network interfaces of the container to the inventory
Logic:
- if the container have no interface -> 'ansible_connection: local'
- get preferred_interface & prefered_container_network_family -> 'ansible_connection: ssh' & 'ansible_host: <IP>'
- first Interface from: network_interfaces prefered_container_network_family -> 'ansible_connection: ssh' & 'ansible_host: <IP>'
Args:
str(container_name): name of container
Kwargs:
None
Raises:
None
Returns:
None"""
def interface_selection(container_name):
"""Select container Interface for inventory
Logic:
- get preferred_interface & prefered_container_network_family -> str(IP)
- first Interface from: network_interfaces prefered_container_network_family -> str(IP)
Args:
str(container_name): name of container
Kwargs:
None
Raises:
None
Returns:
dict(interface_name: ip)"""
prefered_interface = self._get_data_entry('inventory/{0}/preferred_interface'.format(container_name)) # name or None
prefered_container_network_family = self.prefered_container_network_family
ip_address = ''
if prefered_interface:
interface = self._get_data_entry('inventory/{0}/network_interfaces/{1}'.format(container_name, prefered_interface))
for config in interface:
if config['family'] == prefered_container_network_family:
ip_address = config['address']
break
else:
interface = self._get_data_entry('inventory/{0}/network_interfaces'.format(container_name))
for config in interface:
if config['family'] == prefered_container_network_family:
ip_address = config['address']
break
return ip_address
if self._get_data_entry('inventory/{0}/network_interfaces'.format(container_name)): # container have network interfaces
if self._get_data_entry('inventory/{0}/preferred_interface'.format(container_name)): # container have a preferred interface
self.inventory.set_variable(container_name, 'ansible_connection', 'ssh')
self.inventory.set_variable(container_name, 'ansible_host', interface_selection(container_name))
else:
self.inventory.set_variable(container_name, 'ansible_connection', 'local')
def build_inventory_hosts(self):
"""Build host-part dynamic inventory
Build the host-part of the dynamic inventory.
Add Hosts and host_vars to the inventory.
Args:
None
Kwargs:
None
Raises:
None
Returns:
None"""
for container_name in self.data['inventory']:
# Only consider containers that match the "state" filter, if self.state is not None
if self.filter:
if self.filter.lower() != self._get_data_entry('inventory/{0}/state'.format(container_name)).lower():
continue
# add container
self.inventory.add_host(container_name)
# add network informations
self.build_inventory_network(container_name)
# add os
self.inventory.set_variable(container_name, 'ansible_lxd_os', self._get_data_entry('inventory/{0}/os'.format(container_name)).lower())
# add release
self.inventory.set_variable(container_name, 'ansible_lxd_release', self._get_data_entry('inventory/{0}/release'.format(container_name)).lower())
# add profile
self.inventory.set_variable(container_name, 'ansible_lxd_profile', self._get_data_entry('inventory/{0}/profile'.format(container_name)))
# add state
self.inventory.set_variable(container_name, 'ansible_lxd_state', self._get_data_entry('inventory/{0}/state'.format(container_name)).lower())
# add location information
if self._get_data_entry('inventory/{0}/location'.format(container_name)) != "none": # wrong type by lxd 'none' != 'None'
self.inventory.set_variable(container_name, 'ansible_lxd_location', self._get_data_entry('inventory/{0}/location'.format(container_name)))
# add VLAN_ID information
if self._get_data_entry('inventory/{0}/vlan_ids'.format(container_name)):
self.inventory.set_variable(container_name, 'ansible_lxd_vlan_ids', self._get_data_entry('inventory/{0}/vlan_ids'.format(container_name)))
def build_inventory_groups_location(self, group_name):
"""create group by attribute: location
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
for container_name in self.inventory.hosts:
if 'ansible_lxd_location' in self.inventory.get_host(container_name).get_vars():
self.inventory.add_child(group_name, container_name)
def build_inventory_groups_pattern(self, group_name):
"""create group by name pattern
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
regex_pattern = self.groupby[group_name].get('attribute')
for container_name in self.inventory.hosts:
result = re.search(regex_pattern, container_name)
if result:
self.inventory.add_child(group_name, container_name)
def build_inventory_groups_network_range(self, group_name):
"""check if IP is in network-class
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
try:
network = ipaddress.ip_network(to_text(self.groupby[group_name].get('attribute')))
except ValueError as err:
raise AnsibleParserError(
'Error while parsing network range {0}: {1}'.format(self.groupby[group_name].get('attribute'), to_native(err)))
for container_name in self.inventory.hosts:
if self.data['inventory'][container_name].get('network_interfaces') is not None:
for interface in self.data['inventory'][container_name].get('network_interfaces'):
for interface_family in self.data['inventory'][container_name].get('network_interfaces')[interface]:
try:
address = ipaddress.ip_address(to_text(interface_family['address']))
if address.version == network.version and address in network:
self.inventory.add_child(group_name, container_name)
except ValueError:
# Ignore invalid IP addresses returned by lxd
pass
def build_inventory_groups_os(self, group_name):
"""create group by attribute: os
Args:
str(group_name): Group name
Kwargs:
Noneself.data['inventory'][container_name][interface]
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
gen_containers = [
container_name for container_name in self.inventory.hosts
if 'ansible_lxd_os' in self.inventory.get_host(container_name).get_vars()]
for container_name in gen_containers:
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(container_name).get_vars().get('ansible_lxd_os'):
self.inventory.add_child(group_name, container_name)
def build_inventory_groups_release(self, group_name):
"""create group by attribute: release
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
gen_containers = [
container_name for container_name in self.inventory.hosts
if 'ansible_lxd_release' in self.inventory.get_host(container_name).get_vars()]
for container_name in gen_containers:
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(container_name).get_vars().get('ansible_lxd_release'):
self.inventory.add_child(group_name, container_name)
def build_inventory_groups_profile(self, group_name):
"""create group by attribute: profile
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
gen_containers = [
container_name for container_name in self.inventory.hosts.keys()
if 'ansible_lxd_profile' in self.inventory.get_host(container_name).get_vars().keys()]
for container_name in gen_containers:
if self.groupby[group_name].get('attribute').lower() in self.inventory.get_host(container_name).get_vars().get('ansible_lxd_profile'):
self.inventory.add_child(group_name, container_name)
def build_inventory_groups_vlanid(self, group_name):
"""create group by attribute: vlanid
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# maybe we just want to expand one group
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
gen_containers = [
container_name for container_name in self.inventory.hosts.keys()
if 'ansible_lxd_vlan_ids' in self.inventory.get_host(container_name).get_vars().keys()]
for container_name in gen_containers:
if self.groupby[group_name].get('attribute') in self.inventory.get_host(container_name).get_vars().get('ansible_lxd_vlan_ids').values():
self.inventory.add_child(group_name, container_name)
def build_inventory_groups(self):
"""Build group-part dynamic inventory
Build the group-part of the dynamic inventory.
Add groups to the inventory.
Args:
None
Kwargs:
None
Raises:
None
Returns:
None"""
def group_type(group_name):
"""create groups defined by lxd.yml or defaultvalues
create groups defined by lxd.yml or defaultvalues
supportetd:
* 'location'
* 'pattern'
* 'network_range'
* 'os'
* 'release'
* 'profile'
* 'vlanid'
Args:
str(group_name): Group name
Kwargs:
None
Raises:
None
Returns:
None"""
# Due to the compatibility with python 2 no use of map
if self.groupby[group_name].get('type') == 'location':
self.build_inventory_groups_location(group_name)
elif self.groupby[group_name].get('type') == 'pattern':
self.build_inventory_groups_pattern(group_name)
elif self.groupby[group_name].get('type') == 'network_range':
self.build_inventory_groups_network_range(group_name)
elif self.groupby[group_name].get('type') == 'os':
self.build_inventory_groups_os(group_name)
elif self.groupby[group_name].get('type') == 'release':
self.build_inventory_groups_release(group_name)
elif self.groupby[group_name].get('type') == 'profile':
self.build_inventory_groups_profile(group_name)
elif self.groupby[group_name].get('type') == 'vlanid':
self.build_inventory_groups_vlanid(group_name)
else:
raise AnsibleParserError('Unknown group type: {0}'.format(to_native(group_name)))
if self.groupby:
for group_name in self.groupby:
if not group_name.isalnum():
raise AnsibleParserError('Invalid character(s) in groupname: {0}'.format(to_native(group_name)))
group_type(group_name)
def build_inventory(self):
"""Build dynamic inventory
Build the dynamic inventory.
Args:
None
Kwargs:
None
Raises:
None
Returns:
None"""
self.build_inventory_hosts()
self.build_inventory_groups()
def _populate(self):
"""Return the hosts and groups
Returns the processed container configurations from the lxd import
Args:
None
Kwargs:
None
Raises:
None
Returns:
None"""
if len(self.data) == 0: # If no data is injected by unittests open socket
self.socket = self._connect_to_socket()
self.get_container_data(self._get_containers())
self.get_network_data(self._get_networks())
self.extract_information_from_container_configs()
# self.display.vvv(self.save_json_data([os.path.abspath(__file__)]))
self.build_inventory()
def parse(self, inventory, loader, path, cache):
"""Return dynamic inventory from source
Returns the processed inventory from the lxd import
Args:
str(inventory): inventory object with existing data and
the methods to add hosts/groups/variables
to inventory
str(loader): Ansible's DataLoader
str(path): path to the config
bool(cache): use or avoid caches
Kwargs:
None
Raises:
AnsibleParserError
Returns:
None"""
super(InventoryModule, self).parse(inventory, loader, path, cache=False)
# Read the inventory YAML file
self._read_config_data(path)
try:
self.client_key = self.get_option('client_key')
self.client_cert = self.get_option('client_cert')
self.debug = self.DEBUG
self.data = {} # store for inventory-data
self.groupby = self.get_option('groupby')
self.plugin = self.get_option('plugin')
self.prefered_container_network_family = self.get_option('prefered_container_network_family')
self.prefered_container_network_interface = self.get_option('prefered_container_network_interface')
if self.get_option('state').lower() == 'none': # none in config is str()
self.filter = None
else:
self.filter = self.get_option('state').lower()
self.trust_password = self.get_option('trust_password')
self.url = self.get_option('url')
except Exception as err:
raise AnsibleParserError(
'All correct options required: {0}'.format(to_native(err)))
# Call our internal helper to populate the dynamic inventory
self._populate()

View File

@@ -71,6 +71,25 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._nmap = None
super(InventoryModule, self).__init__()
def _populate(self, hosts):
# Use constructed if applicable
strict = self.get_option('strict')
for host in hosts:
hostname = host['name']
self.inventory.add_host(hostname)
for var, value in host.items():
self.inventory.set_variable(hostname, var, value)
# Composed variables
self._set_composite_vars(self.get_option('compose'), host, hostname, strict=strict)
# Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group
self._add_host_to_composed_groups(self.get_option('groups'), host, hostname, strict=strict)
# Create groups based on variable values and add the corresponding hosts to it
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), host, hostname, strict=strict)
def verify_file(self, path):
valid = False
@@ -82,7 +101,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return valid
def parse(self, inventory, loader, path, cache=False):
def parse(self, inventory, loader, path, cache=True):
try:
self._nmap = get_bin_path('nmap')
@@ -93,75 +112,101 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._read_config_data(path)
# setup command
cmd = [self._nmap]
if not self._options['ports']:
cmd.append('-sP')
cache_key = self.get_cache_key(path)
if self._options['ipv4'] and not self._options['ipv6']:
cmd.append('-4')
elif self._options['ipv6'] and not self._options['ipv4']:
cmd.append('-6')
elif not self._options['ipv6'] and not self._options['ipv4']:
raise AnsibleParserError('One of ipv4 or ipv6 must be enabled for this plugin')
# cache may be True or False at this point to indicate if the inventory is being refreshed
# get the user's cache option too to see if we should save the cache if it is changing
user_cache_setting = self.get_option('cache')
if self._options['exclude']:
cmd.append('--exclude')
cmd.append(','.join(self._options['exclude']))
cmd.append(self._options['address'])
try:
# execute
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleParserError('Failed to run nmap, rc=%s: %s' % (p.returncode, to_native(stderr)))
# parse results
host = None
ip = None
ports = []
# read if the user has caching enabled and the cache isn't being refreshed
attempt_to_read_cache = user_cache_setting and cache
# update if the user has caching enabled and the cache is being refreshed; update this value to True if the cache has expired below
cache_needs_update = user_cache_setting and not cache
if attempt_to_read_cache:
try:
t_stdout = to_text(stdout, errors='surrogate_or_strict')
except UnicodeError as e:
raise AnsibleParserError('Invalid (non unicode) input returned: %s' % to_native(e))
results = self._cache[cache_key]
except KeyError:
# This occurs if the cache_key is not in the cache or if the cache_key expired, so the cache needs to be updated
cache_needs_update = True
for line in t_stdout.splitlines():
hits = self.find_host.match(line)
if hits:
if host is not None:
self.inventory.set_variable(host, 'ports', ports)
if cache_needs_update:
# setup command
cmd = [self._nmap]
if not self._options['ports']:
cmd.append('-sP')
# if dns only shows arpa, just use ip instead as hostname
if hits.group(1).endswith('.in-addr.arpa'):
host = hits.group(2)
else:
host = hits.group(1)
if self._options['ipv4'] and not self._options['ipv6']:
cmd.append('-4')
elif self._options['ipv6'] and not self._options['ipv4']:
cmd.append('-6')
elif not self._options['ipv6'] and not self._options['ipv4']:
raise AnsibleParserError('One of ipv4 or ipv6 must be enabled for this plugin')
# if no reverse dns exists, just use ip instead as hostname
if hits.group(2) is not None:
ip = hits.group(2)
else:
ip = hits.group(1)
if self._options['exclude']:
cmd.append('--exclude')
cmd.append(','.join(self._options['exclude']))
if host is not None:
# update inventory
self.inventory.add_host(host)
self.inventory.set_variable(host, 'ip', ip)
ports = []
continue
cmd.append(self._options['address'])
try:
# execute
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleParserError('Failed to run nmap, rc=%s: %s' % (p.returncode, to_native(stderr)))
host_ports = self.find_port.match(line)
if host is not None and host_ports:
ports.append({'port': host_ports.group(1), 'protocol': host_ports.group(2), 'state': host_ports.group(3), 'service': host_ports.group(4)})
continue
# parse results
host = None
ip = None
ports = []
results = []
# TODO: parse more data, OS?
try:
t_stdout = to_text(stdout, errors='surrogate_or_strict')
except UnicodeError as e:
raise AnsibleParserError('Invalid (non unicode) input returned: %s' % to_native(e))
# if any leftovers
if host and ports:
self.inventory.set_variable(host, 'ports', ports)
for line in t_stdout.splitlines():
hits = self.find_host.match(line)
if hits:
if host is not None and ports:
results[-1]['ports'] = ports
except Exception as e:
raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))
# if dns only shows arpa, just use ip instead as hostname
if hits.group(1).endswith('.in-addr.arpa'):
host = hits.group(2)
else:
host = hits.group(1)
# if no reverse dns exists, just use ip instead as hostname
if hits.group(2) is not None:
ip = hits.group(2)
else:
ip = hits.group(1)
if host is not None:
# update inventory
results.append(dict())
results[-1]['name'] = host
results[-1]['ip'] = ip
ports = []
continue
host_ports = self.find_port.match(line)
if host is not None and host_ports:
ports.append({'port': host_ports.group(1),
'protocol': host_ports.group(2),
'state': host_ports.group(3),
'service': host_ports.group(4)})
continue
# if any leftovers
if host and ports:
results[-1]['ports'] = ports
except Exception as e:
raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))
self._cache[cache_key] = results
self._populate(results)

View File

@@ -19,6 +19,7 @@ DOCUMENTATION = '''
- Will retrieve the first network interface with an IP for Proxmox nodes.
- Can retrieve LXC/QEMU configuration as facts.
extends_documentation_fragment:
- constructed
- inventory_cache
options:
plugin:
@@ -69,6 +70,21 @@ DOCUMENTATION = '''
description: Gather LXC/QEMU configuration facts.
default: no
type: bool
want_proxmox_nodes_ansible_host:
version_added: 3.0.0
description:
- Whether to set C(ansbile_host) for proxmox nodes.
- When set to C(true) (default), will use the first available interface. This can be different from what you expect.
default: true
type: bool
strict:
version_added: 2.5.0
compose:
version_added: 2.5.0
groups:
version_added: 2.5.0
keyed_groups:
version_added: 2.5.0
'''
EXAMPLES = '''
@@ -78,6 +94,15 @@ url: http://localhost:8006
user: ansible@pve
password: secure
validate_certs: no
keyed_groups:
- key: proxmox_tags_parsed
separator: ""
prefix: group
groups:
webservers: "'web' in (proxmox_tags_parsed|list)"
mailservers: "'mail' in (proxmox_tags_parsed|list)"
compose:
ansible_port: 2222
'''
import re
@@ -86,7 +111,7 @@ from ansible.module_utils.common._collections_compat import MutableMapping
from distutils.version import LooseVersion
from ansible.errors import AnsibleError
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
from ansible.module_utils.six.moves.urllib.parse import urlencode
# 3rd party imports
@@ -99,7 +124,7 @@ except ImportError:
HAS_REQUESTS = False
class InventoryModule(BaseInventoryPlugin, Cacheable):
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
''' Host inventory parser for ansible using Proxmox as source. '''
NAME = 'community.general.proxmox'
@@ -206,9 +231,45 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
except Exception:
return None
def _get_agent_network_interfaces(self, node, vmid, vmtype):
result = []
try:
ifaces = self._get_json(
"%s/api2/json/nodes/%s/%s/%s/agent/network-get-interfaces" % (
self.proxmox_url, node, vmtype, vmid
)
)['result']
if "error" in ifaces:
if "class" in ifaces["error"]:
# This happens on Windows, even though qemu agent is running, the IP address
# cannot be fetched, as it's unsupported, also a command disabled can happen.
errorClass = ifaces["error"]["class"]
if errorClass in ["Unsupported"]:
self.display.v("Retrieving network interfaces from guest agents on windows with older qemu-guest-agents is not supported")
elif errorClass in ["CommandDisabled"]:
self.display.v("Retrieving network interfaces from guest agents has been disabled")
return result
for iface in ifaces:
result.append({
'name': iface['name'],
'mac-address': iface['hardware-address'] if 'hardware-address' in iface else '',
'ip-addresses': ["%s/%s" % (ip['ip-address'], ip['prefix']) for ip in iface['ip-addresses']] if 'ip-addresses' in iface else []
})
except requests.HTTPError:
pass
return result
def _get_vm_config(self, node, vmid, vmtype, name):
ret = self._get_json("%s/api2/json/nodes/%s/%s/%s/config" % (self.proxmox_url, node, vmtype, vmid))
node_key = 'node'
node_key = self.to_safe('%s%s' % (self.get_option('facts_prefix'), node_key.lower()))
self.inventory.set_variable(name, node_key, node)
vmid_key = 'vmid'
vmid_key = self.to_safe('%s%s' % (self.get_option('facts_prefix'), vmid_key.lower()))
self.inventory.set_variable(name, vmid_key, vmid)
@@ -217,6 +278,10 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
vmtype_key = self.to_safe('%s%s' % (self.get_option('facts_prefix'), vmtype_key.lower()))
self.inventory.set_variable(name, vmtype_key, vmtype)
plaintext_configs = [
'tags',
]
for config in ret:
key = config
key = self.to_safe('%s%s' % (self.get_option('facts_prefix'), key.lower()))
@@ -226,6 +291,20 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
if config == 'rootfs' or config.startswith(('virtio', 'sata', 'ide', 'scsi')):
value = ('disk_image=' + value)
# Additional field containing parsed tags as list
if config == 'tags':
parsed_key = self.to_safe('%s%s' % (key, "_parsed"))
parsed_value = [tag.strip() for tag in value.split(",")]
self.inventory.set_variable(name, parsed_key, parsed_value)
# The first field in the agent string tells you whether the agent is enabled
# the rest of the comma separated string is extra config for the agent
if config == 'agent' and int(value.split(',')[0]):
agent_iface_key = self.to_safe('%s%s' % (key, "_interfaces"))
agent_iface_value = self._get_agent_network_interfaces(node, vmid, vmtype)
if agent_iface_value:
self.inventory.set_variable(name, agent_iface_key, agent_iface_value)
if not (isinstance(value, int) or ',' not in value):
# split off strings with commas to a dict
# skip over any keys that cannot be processed
@@ -254,6 +333,12 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
regex = r"[^A-Za-z0-9\_]"
return re.sub(regex, "_", word.replace(" ", ""))
def _apply_constructable(self, name, variables):
strict = self.get_option('strict')
self._add_host_to_composed_groups(self.get_option('groups'), variables, name, strict=strict)
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), variables, name, strict=strict)
self._set_composite_vars(self.get_option('compose'), variables, name, strict=strict)
def _populate(self):
self._get_auth()
@@ -285,8 +370,9 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
self.inventory.add_child(nodes_group, node['node'])
# get node IP address
ip = self._get_node_ip(node['node'])
self.inventory.set_variable(node['node'], 'ansible_host', ip)
if self.get_option("want_proxmox_nodes_ansible_host"):
ip = self._get_node_ip(node['node'])
self.inventory.set_variable(node['node'], 'ansible_host', ip)
# get LXC containers for this node
node_lxc_group = self.to_safe('%s%s' % (self.get_option('group_prefix'), ('%s_lxc' % node['node']).lower()))
@@ -308,6 +394,8 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
if self.get_option('want_facts'):
self._get_vm_config(node['node'], lxc['vmid'], 'lxc', lxc['name'])
self._apply_constructable(lxc["name"], self.inventory.get_host(lxc['name']).get_vars())
# get QEMU vm's for this node
node_qemu_group = self.to_safe('%s%s' % (self.get_option('group_prefix'), ('%s_qemu' % node['node']).lower()))
self.inventory.add_group(node_qemu_group)
@@ -330,6 +418,8 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
if self.get_option('want_facts'):
self._get_vm_config(node['node'], qemu['vmid'], 'qemu', qemu['name'])
self._apply_constructable(qemu["name"], self.inventory.get_host(qemu['name']).get_vars())
# gather vm's in pools
for pool in self._get_pools():
if pool.get('poolid'):
@@ -339,7 +429,8 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
for member in self._get_members_per_pool(pool['poolid']):
if member.get('name'):
self.inventory.add_child(pool_group, member['name'])
if not member.get('template'):
self.inventory.add_child(pool_group, member['name'])
def parse(self, inventory, loader, path, cache=True):
if not HAS_REQUESTS:

View File

@@ -31,7 +31,9 @@ EXAMPLES = r"""
- name: Template files (explicitly skip directories in order to use the 'src' attribute)
ansible.builtin.template:
src: '{{ item.src }}'
dest: /web/{{ item.path }}
# Your template files should be stored with a .j2 file extension,
# but should not be deployed with it. splitext|first removes it.
dest: /web/{{ item.path | splitext | first }}
mode: '{{ item.mode }}'
with_community.general.filetree: web/
when: item.state == 'file'
@@ -41,6 +43,7 @@ EXAMPLES = r"""
src: '{{ item.src }}'
dest: /web/{{ item.path }}
state: link
follow: false # avoid corrupting target files if the link already exists
force: yes
mode: '{{ item.mode }}'
with_community.general.filetree: web/

View File

@@ -63,6 +63,7 @@ import os
from ansible.plugins.lookup import LookupBase
from ansible.utils.cmd_functions import run_cmd
from ansible.module_utils._text import to_text
ANSIBLE_HIERA_CFG = os.getenv('ANSIBLE_HIERA_CFG', '/etc/hiera.yaml')
ANSIBLE_HIERA_BIN = os.getenv('ANSIBLE_HIERA_BIN', '/usr/bin/hiera')
@@ -78,13 +79,11 @@ class Hiera(object):
rc, output, err = run_cmd("{0} -c {1} {2}".format(
ANSIBLE_HIERA_BIN, ANSIBLE_HIERA_CFG, hiera_key[0]))
return output.strip()
return to_text(output.strip())
class LookupModule(LookupBase):
def run(self, terms, variables=''):
hiera = Hiera()
ret = []
ret.append(hiera.get(terms))
ret = [hiera.get(terms)]
return ret

View File

@@ -1,871 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import inspect
import os
import time
from abc import ABCMeta, abstractmethod
from datetime import datetime
from distutils.version import LooseVersion
from ansible_collections.community.general.plugins.module_utils.cloud import CloudRetry
from ansible.module_utils.common._collections_compat import Mapping
try:
from enum import Enum # enum is a ovirtsdk4 requirement
import ovirtsdk4 as sdk
import ovirtsdk4.version as sdk_version
import ovirtsdk4.types as otypes
HAS_SDK = LooseVersion(sdk_version.VERSION) >= LooseVersion('4.3.0')
except ImportError:
HAS_SDK = False
BYTES_MAP = {
'kib': 2**10,
'mib': 2**20,
'gib': 2**30,
'tib': 2**40,
'pib': 2**50,
}
def check_sdk(module):
if not HAS_SDK:
module.fail_json(
msg='ovirtsdk4 version 4.3.0 or higher is required for this module'
)
def get_dict_of_struct(struct, connection=None, fetch_nested=False, attributes=None):
"""
Convert SDK Struct type into dictionary.
"""
res = {}
def resolve_href(value):
# Fetch nested values of struct:
try:
value = connection.follow_link(value)
except sdk.Error:
value = None
nested_obj = dict(
(attr, convert_value(getattr(value, attr)))
for attr in attributes if getattr(value, attr, None) is not None
)
nested_obj['id'] = getattr(value, 'id', None)
nested_obj['href'] = getattr(value, 'href', None)
return nested_obj
def remove_underscore(val):
if val.startswith('_'):
val = val[1:]
remove_underscore(val)
return val
def convert_value(value):
nested = False
if isinstance(value, sdk.Struct):
if not fetch_nested or not value.href:
return get_dict_of_struct(value)
return resolve_href(value)
elif isinstance(value, Enum) or isinstance(value, datetime):
return str(value)
elif isinstance(value, list) or isinstance(value, sdk.List):
if isinstance(value, sdk.List) and fetch_nested and value.href:
try:
value = connection.follow_link(value)
nested = True
except sdk.Error:
value = []
ret = []
for i in value:
if isinstance(i, sdk.Struct):
if not nested and fetch_nested and i.href:
ret.append(resolve_href(i))
elif not nested:
ret.append(get_dict_of_struct(i))
else:
nested_obj = dict(
(attr, convert_value(getattr(i, attr)))
for attr in attributes if getattr(i, attr, None)
)
nested_obj['id'] = getattr(i, 'id', None)
ret.append(nested_obj)
elif isinstance(i, Enum):
ret.append(str(i))
else:
ret.append(i)
return ret
else:
return value
if struct is not None:
for key, value in struct.__dict__.items():
if value is None:
continue
key = remove_underscore(key)
res[key] = convert_value(value)
return res
def engine_version(connection):
"""
Return string representation of oVirt engine version.
"""
engine_api = connection.system_service().get()
engine_version = engine_api.product_info.version
return '%s.%s' % (engine_version.major, engine_version.minor)
def create_connection(auth):
"""
Create a connection to Python SDK, from task `auth` parameter.
If user doesnt't have SSO token the `auth` dictionary has following parameters mandatory:
url, username, password
If user has SSO token the `auth` dictionary has following parameters mandatory:
url, token
The `ca_file` parameter is mandatory in case user want to use secure connection,
in case user want to use insecure connection, it's mandatory to send insecure=True.
:param auth: dictionary which contains needed values for connection creation
:return: Python SDK connection
"""
url = auth.get('url')
if url is None and auth.get('hostname') is not None:
url = 'https://{0}/ovirt-engine/api'.format(auth.get('hostname'))
return sdk.Connection(
url=url,
username=auth.get('username'),
password=auth.get('password'),
ca_file=auth.get('ca_file', None),
insecure=auth.get('insecure', False),
token=auth.get('token', None),
kerberos=auth.get('kerberos', None),
headers=auth.get('headers', None),
)
def convert_to_bytes(param):
"""
This method convert units to bytes, which follow IEC standard.
:param param: value to be converted
"""
if param is None:
return None
# Get rid of whitespaces:
param = ''.join(param.split())
# Convert to bytes:
if len(param) > 3 and param[-3].lower() in ['k', 'm', 'g', 't', 'p']:
return int(param[:-3]) * BYTES_MAP.get(param[-3:].lower(), 1)
elif param.isdigit():
return int(param) * 2**10
else:
raise ValueError(
"Unsupported value(IEC supported): '{value}'".format(value=param)
)
def follow_link(connection, link):
"""
This method returns the entity of the element which link points to.
:param connection: connection to the Python SDK
:param link: link of the entity
:return: entity which link points to
"""
if link:
return connection.follow_link(link)
else:
return None
def get_link_name(connection, link):
"""
This method returns the name of the element which link points to.
:param connection: connection to the Python SDK
:param link: link of the entity
:return: name of the entity, which link points to
"""
if link:
return connection.follow_link(link).name
else:
return None
def equal(param1, param2, ignore_case=False):
"""
Compare two parameters and return if they are equal.
This parameter doesn't run equal operation if first parameter is None.
With this approach we don't run equal operation in case user don't
specify parameter in their task.
:param param1: user inputted parameter
:param param2: value of entity parameter
:return: True if parameters are equal or first parameter is None, otherwise False
"""
if param1 is not None:
if ignore_case:
return param1.lower() == param2.lower()
return param1 == param2
return True
def search_by_attributes(service, list_params=None, **kwargs):
"""
Search for the entity by attributes. Nested entities don't support search
via REST, so in case using search for nested entity we return all entities
and filter them by specified attributes.
"""
list_params = list_params or {}
# Check if 'list' method support search(look for search parameter):
if 'search' in inspect.getargspec(service.list)[0]:
res = service.list(
# There must be double quotes around name, because some oVirt resources it's possible to create then with space in name.
search=' and '.join('{0}="{1}"'.format(k, v) for k, v in kwargs.items()),
**list_params
)
else:
res = [
e for e in service.list(**list_params) if len([
k for k, v in kwargs.items() if getattr(e, k, None) == v
]) == len(kwargs)
]
res = res or [None]
return res[0]
def search_by_name(service, name, **kwargs):
"""
Search for the entity by its name. Nested entities don't support search
via REST, so in case using search for nested entity we return all entities
and filter them by name.
:param service: service of the entity
:param name: name of the entity
:return: Entity object returned by Python SDK
"""
# Check if 'list' method support search(look for search parameter):
if 'search' in inspect.getargspec(service.list)[0]:
res = service.list(
# There must be double quotes around name, because some oVirt resources it's possible to create then with space in name.
search='name="{name}"'.format(name=name)
)
else:
res = [e for e in service.list() if e.name == name]
if kwargs:
res = [
e for e in service.list() if len([
k for k, v in kwargs.items() if getattr(e, k, None) == v
]) == len(kwargs)
]
res = res or [None]
return res[0]
def get_entity(service, get_params=None):
"""
Ignore SDK Error in case of getting an entity from service.
"""
entity = None
try:
if get_params is not None:
entity = service.get(**get_params)
else:
entity = service.get()
except sdk.Error:
# We can get here 404, we should ignore it, in case
# of removing entity for example.
pass
return entity
def get_id_by_name(service, name, raise_error=True, ignore_case=False):
"""
Search an entity ID by it's name.
"""
entity = search_by_name(service, name)
if entity is not None:
return entity.id
if raise_error:
raise Exception("Entity '%s' was not found." % name)
def wait(
service,
condition,
fail_condition=lambda e: False,
timeout=180,
wait=True,
poll_interval=3,
):
"""
Wait until entity fulfill expected condition.
:param service: service of the entity
:param condition: condition to be fulfilled
:param fail_condition: if this condition is true, raise Exception
:param timeout: max time to wait in seconds
:param wait: if True wait for condition, if False don't wait
:param poll_interval: Number of seconds we should wait until next condition check
"""
# Wait until the desired state of the entity:
if wait:
start = time.time()
while time.time() < start + timeout:
# Exit if the condition of entity is valid:
entity = get_entity(service)
if condition(entity):
return
elif fail_condition(entity):
raise Exception("Error while waiting on result state of the entity.")
# Sleep for `poll_interval` seconds if none of the conditions apply:
time.sleep(float(poll_interval))
raise Exception("Timeout exceed while waiting on result state of the entity.")
def __get_auth_dict():
OVIRT_URL = os.environ.get('OVIRT_URL')
OVIRT_HOSTNAME = os.environ.get('OVIRT_HOSTNAME')
OVIRT_USERNAME = os.environ.get('OVIRT_USERNAME')
OVIRT_PASSWORD = os.environ.get('OVIRT_PASSWORD')
OVIRT_TOKEN = os.environ.get('OVIRT_TOKEN')
OVIRT_CAFILE = os.environ.get('OVIRT_CAFILE')
OVIRT_INSECURE = OVIRT_CAFILE is None
env_vars = None
if OVIRT_URL is None and OVIRT_HOSTNAME is not None:
OVIRT_URL = 'https://{0}/ovirt-engine/api'.format(OVIRT_HOSTNAME)
if OVIRT_URL and ((OVIRT_USERNAME and OVIRT_PASSWORD) or OVIRT_TOKEN):
env_vars = {
'url': OVIRT_URL,
'username': OVIRT_USERNAME,
'password': OVIRT_PASSWORD,
'insecure': OVIRT_INSECURE,
'token': OVIRT_TOKEN,
'ca_file': OVIRT_CAFILE,
}
if env_vars is not None:
auth = dict(default=env_vars, type='dict')
else:
auth = dict(required=True, type='dict')
return auth
def ovirt_info_full_argument_spec(**kwargs):
"""
Extend parameters of info module with parameters which are common to all
oVirt info modules.
:param kwargs: kwargs to be extended
:return: extended dictionary with common parameters
"""
spec = dict(
auth=__get_auth_dict(),
fetch_nested=dict(default=False, type='bool'),
nested_attributes=dict(type='list', default=list()),
)
spec.update(kwargs)
return spec
# Left for third-party module compatibility
def ovirt_facts_full_argument_spec(**kwargs):
"""
This is deprecated. Please use ovirt_info_full_argument_spec instead!
:param kwargs: kwargs to be extended
:return: extended dictionary with common parameters
"""
return ovirt_info_full_argument_spec(**kwargs)
def ovirt_full_argument_spec(**kwargs):
"""
Extend parameters of module with parameters which are common to all oVirt modules.
:param kwargs: kwargs to be extended
:return: extended dictionary with common parameters
"""
spec = dict(
auth=__get_auth_dict(),
timeout=dict(default=180, type='int'),
wait=dict(default=True, type='bool'),
poll_interval=dict(default=3, type='int'),
fetch_nested=dict(default=False, type='bool'),
nested_attributes=dict(type='list', default=list()),
)
spec.update(kwargs)
return spec
def check_params(module):
"""
Most modules must have either `name` or `id` specified.
"""
if module.params.get('name') is None and module.params.get('id') is None:
module.fail_json(msg='"name" or "id" is required')
def engine_supported(connection, version):
return LooseVersion(engine_version(connection)) >= LooseVersion(version)
def check_support(version, connection, module, params):
"""
Check if parameters used by user are supported by oVirt Python SDK
and oVirt engine.
"""
api_version = LooseVersion(engine_version(connection))
version = LooseVersion(version)
for param in params:
if module.params.get(param) is not None:
return LooseVersion(sdk_version.VERSION) >= version and api_version >= version
return True
class BaseModule(object):
"""
This is base class for oVirt modules. oVirt modules should inherit this
class and override method to customize specific needs of the module.
The only abstract method of this class is `build_entity`, which must
to be implemented in child class.
"""
__metaclass__ = ABCMeta
def __init__(self, connection, module, service, changed=False):
self._connection = connection
self._module = module
self._service = service
self._changed = changed
self._diff = {'after': dict(), 'before': dict()}
@property
def changed(self):
return self._changed
@changed.setter
def changed(self, changed):
if not self._changed:
self._changed = changed
@abstractmethod
def build_entity(self):
"""
This method should return oVirt Python SDK type, which we want to
create or update, initialized by values passed by Ansible module.
For example if we want to create VM, we will return following:
types.Vm(name=self._module.params['vm_name'])
:return: Specific instance of sdk.Struct.
"""
pass
def param(self, name, default=None):
"""
Return a module parameter specified by it's name.
"""
return self._module.params.get(name, default)
def update_check(self, entity):
"""
This method handle checks whether the entity values are same as values
passed to ansible module. By default we don't compare any values.
:param entity: Entity we want to compare with Ansible module values.
:return: True if values are same, so we don't need to update the entity.
"""
return True
def pre_create(self, entity):
"""
This method is called right before entity is created.
:param entity: Entity to be created or updated.
"""
pass
def post_create(self, entity):
"""
This method is called right after entity is created.
:param entity: Entity which was created.
"""
pass
def post_update(self, entity):
"""
This method is called right after entity is updated.
:param entity: Entity which was updated.
"""
pass
def diff_update(self, after, update):
for k, v in update.items():
if isinstance(v, Mapping):
after[k] = self.diff_update(after.get(k, dict()), v)
else:
after[k] = update[k]
return after
def create(
self,
entity=None,
result_state=None,
fail_condition=lambda e: False,
search_params=None,
update_params=None,
_wait=None,
force_create=False,
**kwargs
):
"""
Method which is called when state of the entity is 'present'. If user
don't provide `entity` parameter the entity is searched using
`search_params` parameter. If entity is found it's updated, whether
the entity should be updated is checked by `update_check` method.
The corresponding updated entity is build by `build_entity` method.
Function executed after entity is created can optionally be specified
in `post_create` parameter. Function executed after entity is updated
can optionally be specified in `post_update` parameter.
:param entity: Entity we want to update, if exists.
:param result_state: State which should entity has in order to finish task.
:param fail_condition: Function which checks incorrect state of entity, if it returns `True` Exception is raised.
:param search_params: Dictionary of parameters to be used for search.
:param update_params: The params which should be passed to update method.
:param kwargs: Additional parameters passed when creating entity.
:return: Dictionary with values returned by Ansible module.
"""
if entity is None and not force_create:
entity = self.search_entity(search_params)
self.pre_create(entity)
if entity:
# Entity exists, so update it:
entity_service = self._service.service(entity.id)
if not self.update_check(entity):
new_entity = self.build_entity()
if not self._module.check_mode:
update_params = update_params or {}
updated_entity = entity_service.update(
new_entity,
**update_params
)
self.post_update(entity)
# Update diffs only if user specified --diff parameter,
# so we don't useless overload API:
if self._module._diff:
before = get_dict_of_struct(
entity,
self._connection,
fetch_nested=True,
attributes=['name'],
)
after = before.copy()
self.diff_update(after, get_dict_of_struct(new_entity))
self._diff['before'] = before
self._diff['after'] = after
self.changed = True
else:
# Entity don't exists, so create it:
if not self._module.check_mode:
entity = self._service.add(
self.build_entity(),
**kwargs
)
self.post_create(entity)
self.changed = True
if not self._module.check_mode:
# Wait for the entity to be created and to be in the defined state:
entity_service = self._service.service(entity.id)
def state_condition(entity):
return entity
if result_state:
def state_condition(entity):
return entity and entity.status == result_state
wait(
service=entity_service,
condition=state_condition,
fail_condition=fail_condition,
wait=_wait if _wait is not None else self._module.params['wait'],
timeout=self._module.params['timeout'],
poll_interval=self._module.params['poll_interval'],
)
return {
'changed': self.changed,
'id': getattr(entity, 'id', None),
type(entity).__name__.lower(): get_dict_of_struct(
struct=entity,
connection=self._connection,
fetch_nested=self._module.params.get('fetch_nested'),
attributes=self._module.params.get('nested_attributes'),
),
'diff': self._diff,
}
def pre_remove(self, entity):
"""
This method is called right before entity is removed.
:param entity: Entity which we want to remove.
"""
pass
def entity_name(self, entity):
return "{e_type} '{e_name}'".format(
e_type=type(entity).__name__.lower(),
e_name=getattr(entity, 'name', None),
)
def remove(self, entity=None, search_params=None, **kwargs):
"""
Method which is called when state of the entity is 'absent'. If user
don't provide `entity` parameter the entity is searched using
`search_params` parameter. If entity is found it's removed.
Function executed before remove is executed can optionally be specified
in `pre_remove` parameter.
:param entity: Entity we want to remove.
:param search_params: Dictionary of parameters to be used for search.
:param kwargs: Additional parameters passed when removing entity.
:return: Dictionary with values returned by Ansible module.
"""
if entity is None:
entity = self.search_entity(search_params)
if entity is None:
return {
'changed': self.changed,
'msg': "Entity wasn't found."
}
self.pre_remove(entity)
entity_service = self._service.service(entity.id)
if not self._module.check_mode:
entity_service.remove(**kwargs)
wait(
service=entity_service,
condition=lambda entity: not entity,
wait=self._module.params['wait'],
timeout=self._module.params['timeout'],
poll_interval=self._module.params['poll_interval'],
)
self.changed = True
return {
'changed': self.changed,
'id': entity.id,
type(entity).__name__.lower(): get_dict_of_struct(
struct=entity,
connection=self._connection,
fetch_nested=self._module.params.get('fetch_nested'),
attributes=self._module.params.get('nested_attributes'),
),
}
def action(
self,
action,
entity=None,
action_condition=lambda e: e,
wait_condition=lambda e: e,
fail_condition=lambda e: False,
pre_action=lambda e: e,
post_action=lambda e: None,
search_params=None,
**kwargs
):
"""
This method is executed when we want to change the state of some oVirt
entity. The action to be executed on oVirt service is specified by
`action` parameter. Whether the action should be executed can be
specified by passing `action_condition` parameter. State which the
entity should be in after execution of the action can be specified
by `wait_condition` parameter.
Function executed before an action on entity can optionally be specified
in `pre_action` parameter. Function executed after an action on entity can
optionally be specified in `post_action` parameter.
:param action: Action which should be executed by service on entity.
:param entity: Entity we want to run action on.
:param action_condition: Function which is executed when checking if action should be executed.
:param fail_condition: Function which checks incorrect state of entity, if it returns `True` Exception is raised.
:param wait_condition: Function which is executed when waiting on result state.
:param pre_action: Function which is executed before running the action.
:param post_action: Function which is executed after running the action.
:param search_params: Dictionary of parameters to be used for search.
:param kwargs: Additional parameters passed to action.
:return: Dictionary with values returned by Ansible module.
"""
if entity is None:
entity = self.search_entity(search_params)
entity = pre_action(entity)
if entity is None:
self._module.fail_json(
msg="Entity not found, can't run action '{0}'.".format(
action
)
)
entity_service = self._service.service(entity.id)
entity = entity_service.get()
if action_condition(entity):
if not self._module.check_mode:
getattr(entity_service, action)(**kwargs)
self.changed = True
post_action(entity)
wait(
service=self._service.service(entity.id),
condition=wait_condition,
fail_condition=fail_condition,
wait=self._module.params['wait'],
timeout=self._module.params['timeout'],
poll_interval=self._module.params['poll_interval'],
)
return {
'changed': self.changed,
'id': entity.id,
type(entity).__name__.lower(): get_dict_of_struct(
struct=entity,
connection=self._connection,
fetch_nested=self._module.params.get('fetch_nested'),
attributes=self._module.params.get('nested_attributes'),
),
'diff': self._diff,
}
def wait_for_import(self, condition=lambda e: True):
if self._module.params['wait']:
start = time.time()
timeout = self._module.params['timeout']
poll_interval = self._module.params['poll_interval']
while time.time() < start + timeout:
entity = self.search_entity()
if entity and condition(entity):
return entity
time.sleep(poll_interval)
def search_entity(self, search_params=None, list_params=None):
"""
Always first try to search by `ID`, if ID isn't specified,
check if user constructed special search in `search_params`,
if not search by `name`.
"""
entity = None
if 'id' in self._module.params and self._module.params['id'] is not None:
entity = get_entity(self._service.service(self._module.params['id']), get_params=list_params)
elif search_params is not None:
entity = search_by_attributes(self._service, list_params=list_params, **search_params)
elif self._module.params.get('name') is not None:
entity = search_by_attributes(self._service, list_params=list_params, name=self._module.params['name'])
return entity
def _get_major(self, full_version):
if full_version is None or full_version == "":
return None
if isinstance(full_version, otypes.Version):
return int(full_version.major)
return int(full_version.split('.')[0])
def _get_minor(self, full_version):
if full_version is None or full_version == "":
return None
if isinstance(full_version, otypes.Version):
return int(full_version.minor)
return int(full_version.split('.')[1])
def _sdk4_error_maybe():
"""
Allow for ovirtsdk4 not being installed.
"""
if HAS_SDK:
return sdk.Error
return type(None)
class OvirtRetry(CloudRetry):
base_class = _sdk4_error_maybe()
@staticmethod
def status_code_from_exception(error):
return error.code
@staticmethod
def found(response_code, catch_extra_error_codes=None):
# This is a list of error codes to retry.
retry_on = [
# HTTP status: Conflict
409,
]
if catch_extra_error_codes:
retry_on.extend(catch_extra_error_codes)
return response_code in retry_on

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Andrew Pantuso (@ajpantuso) <ajpantuso@gmail.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import csv
from io import BytesIO, StringIO
from ansible.module_utils._text import to_native
from ansible.module_utils.six import PY3
class CustomDialectFailureError(Exception):
pass
class DialectNotAvailableError(Exception):
pass
CSVError = csv.Error
def initialize_dialect(dialect, **kwargs):
# Add Unix dialect from Python 3
class unix_dialect(csv.Dialect):
"""Describe the usual properties of Unix-generated CSV files."""
delimiter = ','
quotechar = '"'
doublequote = True
skipinitialspace = False
lineterminator = '\n'
quoting = csv.QUOTE_ALL
csv.register_dialect("unix", unix_dialect)
if dialect not in csv.list_dialects():
raise DialectNotAvailableError("Dialect '%s' is not supported by your version of python." % dialect)
# Create a dictionary from only set options
dialect_params = dict((k, v) for k, v in kwargs.items() if v is not None)
if dialect_params:
try:
csv.register_dialect('custom', dialect, **dialect_params)
except TypeError as e:
raise CustomDialectFailureError("Unable to create custom dialect: %s" % to_native(e))
dialect = 'custom'
return dialect
def read_csv(data, dialect, fieldnames=None):
data = to_native(data, errors='surrogate_or_strict')
if PY3:
fake_fh = StringIO(data)
else:
fake_fh = BytesIO(data)
reader = csv.DictReader(fake_fh, fieldnames=fieldnames, dialect=dialect)
return reader

View File

@@ -0,0 +1,234 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019 Gregory Thiemonge <gregory.thiemonge@gmail.com>
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
from ansible.module_utils._text import to_native, to_text
from ansible.module_utils.urls import fetch_url
class GandiLiveDNSAPI(object):
api_endpoint = 'https://api.gandi.net/v5/livedns'
changed = False
error_strings = {
400: 'Bad request',
401: 'Permission denied',
404: 'Resource not found',
}
attribute_map = {
'record': 'rrset_name',
'type': 'rrset_type',
'ttl': 'rrset_ttl',
'values': 'rrset_values'
}
def __init__(self, module):
self.module = module
self.api_key = module.params['api_key']
def _build_error_message(self, module, info):
s = ''
body = info.get('body')
if body:
errors = module.from_json(body).get('errors')
if errors:
error = errors[0]
name = error.get('name')
if name:
s += '{0} :'.format(name)
description = error.get('description')
if description:
s += description
return s
def _gandi_api_call(self, api_call, method='GET', payload=None, error_on_404=True):
headers = {'Authorization': 'Apikey {0}'.format(self.api_key),
'Content-Type': 'application/json'}
data = None
if payload:
try:
data = json.dumps(payload)
except Exception as e:
self.module.fail_json(msg="Failed to encode payload as JSON: %s " % to_native(e))
resp, info = fetch_url(self.module,
self.api_endpoint + api_call,
headers=headers,
data=data,
method=method)
error_msg = ''
if info['status'] >= 400 and (info['status'] != 404 or error_on_404):
err_s = self.error_strings.get(info['status'], '')
error_msg = "API Error {0}: {1}".format(err_s, self._build_error_message(self.module, info))
result = None
try:
content = resp.read()
except AttributeError:
content = None
if content:
try:
result = json.loads(to_text(content, errors='surrogate_or_strict'))
except (getattr(json, 'JSONDecodeError', ValueError)) as e:
error_msg += "; Failed to parse API response with error {0}: {1}".format(to_native(e), content)
if error_msg:
self.module.fail_json(msg=error_msg)
return result, info['status']
def build_result(self, result, domain):
if result is None:
return None
res = {}
for k in self.attribute_map:
v = result.get(self.attribute_map[k], None)
if v is not None:
if k == 'record' and v == '@':
v = ''
res[k] = v
res['domain'] = domain
return res
def build_results(self, results, domain):
if results is None:
return []
return [self.build_result(r, domain) for r in results]
def get_records(self, record, type, domain):
url = '/domains/%s/records' % (domain)
if record:
url += '/%s' % (record)
if type:
url += '/%s' % (type)
records, status = self._gandi_api_call(url, error_on_404=False)
if status == 404:
return []
if not isinstance(records, list):
records = [records]
# filter by type if record is not set
if not record and type:
records = [r
for r in records
if r['rrset_type'] == type]
return records
def create_record(self, record, type, values, ttl, domain):
url = '/domains/%s/records' % (domain)
new_record = {
'rrset_name': record,
'rrset_type': type,
'rrset_values': values,
'rrset_ttl': ttl,
}
record, status = self._gandi_api_call(url, method='POST', payload=new_record)
if status in (200, 201,):
return new_record
return None
def update_record(self, record, type, values, ttl, domain):
url = '/domains/%s/records/%s/%s' % (domain, record, type)
new_record = {
'rrset_values': values,
'rrset_ttl': ttl,
}
record = self._gandi_api_call(url, method='PUT', payload=new_record)[0]
return record
def delete_record(self, record, type, domain):
url = '/domains/%s/records/%s/%s' % (domain, record, type)
self._gandi_api_call(url, method='DELETE')
def delete_dns_record(self, record, type, values, domain):
if record == '':
record = '@'
records = self.get_records(record, type, domain)
if records:
cur_record = records[0]
self.changed = True
if values is not None and set(cur_record['rrset_values']) != set(values):
new_values = set(cur_record['rrset_values']) - set(values)
if new_values:
# Removing one or more values from a record, we update the record with the remaining values
self.update_record(record, type, list(new_values), cur_record['rrset_ttl'], domain)
records = self.get_records(record, type, domain)
return records[0], self.changed
if not self.module.check_mode:
self.delete_record(record, type, domain)
else:
cur_record = None
return None, self.changed
def ensure_dns_record(self, record, type, ttl, values, domain):
if record == '':
record = '@'
records = self.get_records(record, type, domain)
if records:
cur_record = records[0]
do_update = False
if ttl is not None and cur_record['rrset_ttl'] != ttl:
do_update = True
if values is not None and set(cur_record['rrset_values']) != set(values):
do_update = True
if do_update:
if self.module.check_mode:
result = dict(
rrset_type=type,
rrset_name=record,
rrset_values=values,
rrset_ttl=ttl
)
else:
self.update_record(record, type, values, ttl, domain)
records = self.get_records(record, type, domain)
result = records[0]
self.changed = True
return result, self.changed
else:
return cur_record, self.changed
if self.module.check_mode:
new_record = dict(
rrset_type=type,
rrset_name=record,
rrset_values=values,
rrset_ttl=ttl
)
result = new_record
else:
result = self.create_record(record, type, values, ttl, domain)
self.changed = True
return result, self.changed

View File

@@ -30,12 +30,16 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import traceback
from ansible.module_utils.urls import open_url
from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible.module_utils._text import to_native
URL_REALMS = "{url}/admin/realms"
URL_REALM = "{url}/admin/realms/{realm}"
URL_TOKEN = "{url}/realms/{realm}/protocol/openid-connect/token"
URL_CLIENT = "{url}/admin/realms/{realm}/clients/{id}"
URL_CLIENTS = "{url}/admin/realms/{realm}/clients"
@@ -55,13 +59,14 @@ def keycloak_argument_spec():
:return: argument_spec dict
"""
return dict(
auth_keycloak_url=dict(type='str', aliases=['url'], required=True),
auth_keycloak_url=dict(type='str', aliases=['url'], required=True, no_log=False),
auth_client_id=dict(type='str', default='admin-cli'),
auth_realm=dict(type='str', required=True),
auth_realm=dict(type='str'),
auth_client_secret=dict(type='str', default=None, no_log=True),
auth_username=dict(type='str', aliases=['username'], required=True),
auth_password=dict(type='str', aliases=['password'], required=True, no_log=True),
validate_certs=dict(type='bool', default=True)
auth_username=dict(type='str', aliases=['username']),
auth_password=dict(type='str', aliases=['password'], no_log=True),
validate_certs=dict(type='bool', default=True),
token=dict(type='str', no_log=True),
)
@@ -73,41 +78,58 @@ class KeycloakError(Exception):
pass
def get_token(base_url, validate_certs, auth_realm, client_id,
auth_username, auth_password, client_secret):
def get_token(module_params):
""" Obtains connection header with token for the authentication,
token already given or obtained from credentials
:param module_params: parameters of the module
:return: connection header
"""
token = module_params.get('token')
base_url = module_params.get('auth_keycloak_url')
if not base_url.lower().startswith(('http', 'https')):
raise KeycloakError("auth_url '%s' should either start with 'http' or 'https'." % base_url)
auth_url = URL_TOKEN.format(url=base_url, realm=auth_realm)
temp_payload = {
'grant_type': 'password',
'client_id': client_id,
'client_secret': client_secret,
'username': auth_username,
'password': auth_password,
}
# Remove empty items, for instance missing client_secret
payload = dict(
(k, v) for k, v in temp_payload.items() if v is not None)
try:
r = json.loads(to_native(open_url(auth_url, method='POST',
validate_certs=validate_certs,
data=urlencode(payload)).read()))
except ValueError as e:
raise KeycloakError(
'API returned invalid JSON when trying to obtain access token from %s: %s'
% (auth_url, str(e)))
except Exception as e:
raise KeycloakError('Could not obtain access token from %s: %s'
% (auth_url, str(e)))
try:
return {
'Authorization': 'Bearer ' + r['access_token'],
'Content-Type': 'application/json'
if token is None:
base_url = module_params.get('auth_keycloak_url')
validate_certs = module_params.get('validate_certs')
auth_realm = module_params.get('auth_realm')
client_id = module_params.get('auth_client_id')
auth_username = module_params.get('auth_username')
auth_password = module_params.get('auth_password')
client_secret = module_params.get('auth_client_secret')
auth_url = URL_TOKEN.format(url=base_url, realm=auth_realm)
temp_payload = {
'grant_type': 'password',
'client_id': client_id,
'client_secret': client_secret,
'username': auth_username,
'password': auth_password,
}
except KeyError:
raise KeycloakError(
'Could not obtain access token from %s' % auth_url)
# Remove empty items, for instance missing client_secret
payload = dict(
(k, v) for k, v in temp_payload.items() if v is not None)
try:
r = json.loads(to_native(open_url(auth_url, method='POST',
validate_certs=validate_certs,
data=urlencode(payload)).read()))
except ValueError as e:
raise KeycloakError(
'API returned invalid JSON when trying to obtain access token from %s: %s'
% (auth_url, str(e)))
except Exception as e:
raise KeycloakError('Could not obtain access token from %s: %s'
% (auth_url, str(e)))
try:
token = r['access_token']
except KeyError:
raise KeycloakError(
'Could not obtain access token from %s' % auth_url)
return {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
class KeycloakAPI(object):
@@ -120,6 +142,75 @@ class KeycloakAPI(object):
self.validate_certs = self.module.params.get('validate_certs')
self.restheaders = connection_header
def get_realm_by_id(self, realm='master'):
""" Obtain realm representation by id
:param realm: realm id
:return: dict of real, representation or None if none matching exist
"""
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
try:
return json.loads(to_native(open_url(realm_url, method='GET', headers=self.restheaders,
validate_certs=self.validate_certs).read()))
except HTTPError as e:
if e.code == 404:
return None
else:
self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)),
exception=traceback.format_exc())
except ValueError as e:
self.module.fail_json(msg='API returned incorrect JSON when trying to obtain realm %s: %s' % (realm, str(e)),
exception=traceback.format_exc())
except Exception as e:
self.module.fail_json(msg='Could not obtain realm %s: %s' % (realm, str(e)),
exception=traceback.format_exc())
def update_realm(self, realmrep, realm="master"):
""" Update an existing realm
:param realmrep: corresponding (partial/full) realm representation with updates
:param realm: realm to be updated in Keycloak
:return: HTTPResponse object on success
"""
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
try:
return open_url(realm_url, method='PUT', headers=self.restheaders,
data=json.dumps(realmrep), validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not update realm %s: %s' % (realm, str(e)),
exception=traceback.format_exc())
def create_realm(self, realmrep):
""" Create a realm in keycloak
:param realmrep: Realm representation of realm to be created.
:return: HTTPResponse object on success
"""
realm_url = URL_REALMS.format(url=self.baseurl)
try:
return open_url(realm_url, method='POST', headers=self.restheaders,
data=json.dumps(realmrep), validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not create realm %s: %s' % (realmrep['id'], str(e)),
exception=traceback.format_exc())
def delete_realm(self, realm="master"):
""" Delete a realm from Keycloak
:param realm: realm to be deleted
:return: HTTPResponse object on success
"""
realm_url = URL_REALM.format(url=self.baseurl, realm=realm)
try:
return open_url(realm_url, method='DELETE', headers=self.restheaders,
validate_certs=self.validate_certs)
except Exception as e:
self.module.fail_json(msg='Could not delete realm %s: %s' % (realm, str(e)),
exception=traceback.format_exc())
def get_clients(self, realm='master', filter=None):
""" Obtains client representations for clients in a realm

View File

@@ -119,9 +119,9 @@ class IPAClient(object):
data = dict(method=method)
# TODO: We should probably handle this a little better.
if method in ('ping', 'config_show'):
if method in ('ping', 'config_show', 'otpconfig_show'):
data['params'] = [[], {}]
elif method == 'config_mod':
elif method in ('config_mod', 'otpconfig_mod'):
data['params'] = [[], item]
else:
data['params'] = [[name], item]

View File

@@ -87,11 +87,12 @@ def not_in_host_file(self, host):
user_host_file = "~/.ssh/known_hosts"
user_host_file = os.path.expanduser(user_host_file)
host_file_list = []
host_file_list.append(user_host_file)
host_file_list.append("/etc/ssh/ssh_known_hosts")
host_file_list.append("/etc/ssh/ssh_known_hosts2")
host_file_list.append("/etc/openssh/ssh_known_hosts")
host_file_list = [
user_host_file,
"/etc/ssh/ssh_known_hosts",
"/etc/ssh/ssh_known_hosts2",
"/etc/openssh/ssh_known_hosts",
]
hfiles_not_found = 0
for hf in host_file_list:

View File

@@ -10,6 +10,7 @@ from functools import partial, wraps
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.dict_transformations import dict_merge
class ModuleHelperException(Exception):
@@ -24,12 +25,12 @@ class ModuleHelperException(Exception):
def __init__(self, *args, **kwargs):
self.msg = self._get_remove('msg', kwargs) or "Module failed with exception: {0}".format(self)
self.update_output = self._get_remove('update_output', kwargs) or {}
super(ModuleHelperException, self).__init__(*args, **kwargs)
super(ModuleHelperException, self).__init__(*args)
class ArgFormat(object):
"""
Argument formatter
Argument formatter for use as a command line parameter. Used in CmdMixin.
"""
BOOLEAN = 0
PRINTF = 1
@@ -50,7 +51,8 @@ class ArgFormat(object):
def __init__(self, name, fmt=None, style=FORMAT, stars=0):
"""
Creates a new formatter
Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for
the CLI command execution.
:param name: Name of the argument to be formatted
:param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that
:param style: Whether arg_format (as str) should use printf-style formatting.
@@ -93,22 +95,33 @@ class ArgFormat(object):
self.arg_format = (self.stars_deco(stars))(self.arg_format)
def to_text(self, value):
if value is None:
return []
func = self.arg_format
return [str(p) for p in func(value)]
def cause_changes(func, on_success=True, on_failure=False):
@wraps(func)
def wrapper(self, *args, **kwargs):
try:
func(*args, **kwargs)
if on_success:
self.changed = True
except Exception as e:
if on_failure:
self.changed = True
raise
return wrapper
def cause_changes(on_success=None, on_failure=None):
def deco(func):
if on_success is None and on_failure is None:
return func
@wraps(func)
def wrapper(*args, **kwargs):
try:
self = args[0]
func(*args, **kwargs)
if on_success is not None:
self.changed = on_success
except Exception:
if on_failure is not None:
self.changed = on_failure
raise
return wrapper
return deco
def module_fails_on_exception(func):
@@ -121,10 +134,12 @@ def module_fails_on_exception(func):
except ModuleHelperException as e:
if e.update_output:
self.update_output(e.update_output)
self.module.fail_json(msg=e.msg, exception=traceback.format_exc(),
output=self.output, vars=self.vars.output(), **self.output)
except Exception as e:
self.vars.msg = "Module failed with exception: {0}".format(str(e).strip())
self.vars.exception = traceback.format_exc()
self.module.fail_json(changed=False, msg=self.vars.msg, exception=self.vars.exception, output=self.output, vars=self.vars)
msg = "Module failed with exception: {0}".format(str(e).strip())
self.module.fail_json(msg=msg, exception=traceback.format_exc(),
output=self.output, vars=self.vars.output(), **self.output)
return wrapper
@@ -138,7 +153,7 @@ class DependencyCtxMgr(object):
self.exc_tb = None
def __enter__(self):
pass
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.has_it = exc_type is None
@@ -152,32 +167,157 @@ class DependencyCtxMgr(object):
return self.msg or str(self.exc_val)
class ModuleHelper(object):
_dependencies = []
module = {}
facts_name = None
class VarMeta(object):
NOTHING = object()
def __init__(self, diff=False, output=True, change=None, fact=False):
self.init = False
self.initial_value = None
self.value = None
self.diff = diff
self.change = diff if change is None else change
self.output = output
self.fact = fact
def set(self, diff=None, output=None, change=None, fact=None, initial_value=NOTHING):
if diff is not None:
self.diff = diff
if output is not None:
self.output = output
if change is not None:
self.change = change
if fact is not None:
self.fact = fact
if initial_value is not self.NOTHING:
self.initial_value = initial_value
def set_value(self, value):
if not self.init:
self.initial_value = value
self.init = True
self.value = value
return self
@property
def has_changed(self):
return self.change and (self.initial_value != self.value)
@property
def diff_result(self):
return None if not (self.diff and self.has_changed) else {
'before': self.initial_value,
'after': self.value,
}
def __str__(self):
return "<VarMeta: value={0}, initial={1}, diff={2}, output={3}, change={4}>".format(
self.value, self.initial_value, self.diff, self.output, self.change
)
class ModuleHelper(object):
_output_conflict_list = ('msg', 'exception', 'output', 'vars', 'changed')
_dependencies = []
module = None
facts_name = None
output_params = ()
diff_params = ()
change_params = ()
facts_params = ()
class VarDict(object):
def __init__(self):
self._data = dict()
self._meta = dict()
def __getitem__(self, item):
return self._data[item]
def __setitem__(self, key, value):
self.set(key, value)
class AttrDict(dict):
def __getattr__(self, item):
return self[item]
try:
return self._data[item]
except KeyError:
return getattr(self._data, item)
def __setattr__(self, key, value):
if key in ('_data', '_meta'):
super(ModuleHelper.VarDict, self).__setattr__(key, value)
else:
self.set(key, value)
def meta(self, name):
return self._meta[name]
def set_meta(self, name, **kwargs):
self.meta(name).set(**kwargs)
def set(self, name, value, **kwargs):
if name in ('_data', '_meta'):
raise ValueError("Names _data and _meta are reserved for use by ModuleHelper")
self._data[name] = value
if name in self._meta:
meta = self.meta(name)
else:
meta = VarMeta(**kwargs)
meta.set_value(value)
self._meta[name] = meta
def output(self):
return dict((k, v) for k, v in self._data.items() if self.meta(k).output)
def diff(self):
diff_results = [(k, self.meta(k).diff_result) for k in self._data]
diff_results = [dr for dr in diff_results if dr[1] is not None]
if diff_results:
before = dict((dr[0], dr[1]['before']) for dr in diff_results)
after = dict((dr[0], dr[1]['after']) for dr in diff_results)
return {'before': before, 'after': after}
return None
def facts(self):
facts_result = dict((k, v) for k, v in self._data.items() if self._meta[k].fact)
return facts_result if facts_result else None
def change_vars(self):
return [v for v in self._data if self.meta(v).change]
def has_changed(self, v):
return self._meta[v].has_changed
def __init__(self, module=None):
self.vars = ModuleHelper.AttrDict()
self.output_dict = dict()
self.facts_dict = dict()
self.vars = ModuleHelper.VarDict()
self._changed = False
if module:
self.module = module
if isinstance(self.module, dict):
if not isinstance(self.module, AnsibleModule):
self.module = AnsibleModule(**self.module)
for name, value in self.module.params.items():
self.vars.set(
name, value,
diff=name in self.diff_params,
output=name in self.output_params,
change=None if not self.change_params else name in self.change_params,
fact=name in self.facts_params,
)
def update_vars(self, meta=None, **kwargs):
if meta is None:
meta = {}
for k, v in kwargs.items():
self.vars.set(k, v, **meta)
def update_output(self, **kwargs):
self.output_dict.update(kwargs)
self.update_vars(meta={"output": True}, **kwargs)
def update_facts(self, **kwargs):
self.facts_dict.update(kwargs)
self.update_vars(meta={"fact": True}, **kwargs)
def __init_module__(self):
pass
@@ -188,6 +328,9 @@ class ModuleHelper(object):
def __quit_module__(self):
pass
def _vars_changed(self):
return any(self.vars.has_changed(v) for v in self.vars.change_vars())
@property
def changed(self):
return self._changed
@@ -196,12 +339,25 @@ class ModuleHelper(object):
def changed(self, value):
self._changed = value
def has_changed(self):
return self.changed or self._vars_changed()
@property
def output(self):
result = dict(self.vars)
result.update(self.output_dict)
result = dict(self.vars.output())
if self.facts_name:
result['ansible_facts'] = {self.facts_name: self.facts_dict}
facts = self.vars.facts()
if facts is not None:
result['ansible_facts'] = {self.facts_name: facts}
if self.module._diff:
diff = result.get('diff', {})
vars_diff = self.vars.diff() or {}
result['diff'] = dict_merge(dict(diff), vars_diff)
for varname in result:
if varname in self._output_conflict_list:
result["_" + varname] = result[varname]
del result[varname]
return result
@module_fails_on_exception
@@ -210,7 +366,7 @@ class ModuleHelper(object):
self.__init_module__()
self.__run__()
self.__quit_module__()
self.module.exit_json(changed=self.changed, **self.output_dict)
self.module.exit_json(changed=self.has_changed(), **self.output)
@classmethod
def dependency(cls, name, msg):
@@ -221,9 +377,9 @@ class ModuleHelper(object):
for d in self._dependencies:
if not d.has_it:
self.module.fail_json(changed=False,
exception=d.exc_val.__traceback__.format_exc(),
exception="\n".join(traceback.format_exception(d.exc_type, d.exc_val, d.exc_tb)),
msg=d.text,
**self.output_dict)
**self.output)
class StateMixin(object):
@@ -292,7 +448,10 @@ class CmdMixin(object):
extra_params = extra_params or dict()
cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command)
cmd_args[0] = self.module.get_bin_path(cmd_args[0])
try:
cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True)
except ValueError:
pass
param_list = params if params else self.module.params.keys()
for param in param_list:
@@ -326,7 +485,7 @@ class CmdMixin(object):
return rc, out, err
def run_command(self, extra_params=None, params=None, *args, **kwargs):
self.vars['cmd_args'] = self._calculate_args(extra_params, params)
self.vars.cmd_args = self._calculate_args(extra_params, params)
options = dict(self.run_command_fixed_options)
env_update = dict(options.get('environ_update', {}))
options['check_rc'] = options.get('check_rc', self.check_rc)
@@ -335,7 +494,7 @@ class CmdMixin(object):
self.update_output(force_lang=self.force_lang)
options['environ_update'] = env_update
options.update(kwargs)
rc, out, err = self.module.run_command(self.vars['cmd_args'], *args, **options)
rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)
self.update_output(rc=rc, stdout=out, stderr=err)
return self.process_command_output(rc, out, err)

View File

@@ -18,6 +18,7 @@ from ansible.module_utils._text import to_native
from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.common.validation import check_type_dict
try:
from infoblox_client.connector import Connector
@@ -399,11 +400,11 @@ class WapiModule(WapiBase):
if 'ipv4addrs' in proposed_object:
if 'nios_next_ip' in proposed_object['ipv4addrs'][0]['ipv4addr']:
ip_range = self.module._check_type_dict(proposed_object['ipv4addrs'][0]['ipv4addr'])['nios_next_ip']
ip_range = check_type_dict(proposed_object['ipv4addrs'][0]['ipv4addr'])['nios_next_ip']
proposed_object['ipv4addrs'][0]['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range
elif 'ipv4addr' in proposed_object:
if 'nios_next_ip' in proposed_object['ipv4addr']:
ip_range = self.module._check_type_dict(proposed_object['ipv4addr'])['nios_next_ip']
ip_range = check_type_dict(proposed_object['ipv4addr'])['nios_next_ip']
proposed_object['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range
return proposed_object
@@ -485,7 +486,7 @@ class WapiModule(WapiBase):
if ('name' in obj_filter):
# gets and returns the current object based on name/old_name passed
try:
name_obj = self.module._check_type_dict(obj_filter['name'])
name_obj = check_type_dict(obj_filter['name'])
old_name = name_obj['old_name']
new_name = name_obj['new_name']
except TypeError:
@@ -521,7 +522,7 @@ class WapiModule(WapiBase):
test_obj_filter['name'] = test_obj_filter['name'].lower()
# resolves issue where multiple a_records with same name and different IP address
try:
ipaddr_obj = self.module._check_type_dict(obj_filter['ipv4addr'])
ipaddr_obj = check_type_dict(obj_filter['ipv4addr'])
ipaddr = ipaddr_obj['old_ipv4addr']
except TypeError:
ipaddr = obj_filter['ipv4addr']
@@ -530,7 +531,7 @@ class WapiModule(WapiBase):
# resolves issue where multiple txt_records with same name and different text
test_obj_filter = obj_filter
try:
text_obj = self.module._check_type_dict(obj_filter['text'])
text_obj = check_type_dict(obj_filter['text'])
txt = text_obj['old_text']
except TypeError:
txt = obj_filter['text']
@@ -543,7 +544,7 @@ class WapiModule(WapiBase):
# resolves issue where multiple a_records with same name and different IP address
test_obj_filter = obj_filter
try:
ipaddr_obj = self.module._check_type_dict(obj_filter['ipv4addr'])
ipaddr_obj = check_type_dict(obj_filter['ipv4addr'])
ipaddr = ipaddr_obj['old_ipv4addr']
except TypeError:
ipaddr = obj_filter['ipv4addr']
@@ -553,7 +554,7 @@ class WapiModule(WapiBase):
# resolves issue where multiple txt_records with same name and different text
test_obj_filter = obj_filter
try:
text_obj = self.module._check_type_dict(obj_filter['text'])
text_obj = check_type_dict(obj_filter['text'])
txt = text_obj['old_text']
except TypeError:
txt = obj_filter['text']

View File

@@ -0,0 +1,370 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, Florian Dambrine <android.florian@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
Pritunl API that offers CRUD operations on Pritunl Organizations and Users
"""
from __future__ import absolute_import, division, print_function
import base64
import hashlib
import hmac
import json
import time
import uuid
from ansible.module_utils.six import iteritems
from ansible.module_utils.urls import open_url
__metaclass__ = type
class PritunlException(Exception):
pass
def pritunl_argument_spec():
return dict(
pritunl_url=dict(required=True, type="str"),
pritunl_api_token=dict(required=True, type="str", no_log=False),
pritunl_api_secret=dict(required=True, type="str", no_log=True),
validate_certs=dict(required=False, type="bool", default=True),
)
def get_pritunl_settings(module):
"""
Helper function to set required Pritunl request params from module arguments.
"""
return {
"api_token": module.params.get("pritunl_api_token"),
"api_secret": module.params.get("pritunl_api_secret"),
"base_url": module.params.get("pritunl_url"),
"validate_certs": module.params.get("validate_certs"),
}
def _get_pritunl_organizations(api_token, api_secret, base_url, validate_certs=True):
return pritunl_auth_request(
base_url=base_url,
api_token=api_token,
api_secret=api_secret,
method="GET",
path="/organization",
validate_certs=validate_certs,
)
def _delete_pritunl_organization(
api_token, api_secret, base_url, organization_id, validate_certs=True
):
return pritunl_auth_request(
base_url=base_url,
api_token=api_token,
api_secret=api_secret,
method="DELETE",
path="/organization/%s" % (organization_id),
validate_certs=validate_certs,
)
def _post_pritunl_organization(
api_token, api_secret, base_url, organization_data, validate_certs=True
):
return pritunl_auth_request(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
method="POST",
path="/organization/%s",
headers={"Content-Type": "application/json"},
data=json.dumps(organization_data),
validate_certs=validate_certs,
)
def _get_pritunl_users(
api_token, api_secret, base_url, organization_id, validate_certs=True
):
return pritunl_auth_request(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
method="GET",
path="/user/%s" % organization_id,
validate_certs=validate_certs,
)
def _delete_pritunl_user(
api_token, api_secret, base_url, organization_id, user_id, validate_certs=True
):
return pritunl_auth_request(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
method="DELETE",
path="/user/%s/%s" % (organization_id, user_id),
validate_certs=validate_certs,
)
def _post_pritunl_user(
api_token, api_secret, base_url, organization_id, user_data, validate_certs=True
):
return pritunl_auth_request(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
method="POST",
path="/user/%s" % organization_id,
headers={"Content-Type": "application/json"},
data=json.dumps(user_data),
validate_certs=validate_certs,
)
def _put_pritunl_user(
api_token,
api_secret,
base_url,
organization_id,
user_id,
user_data,
validate_certs=True,
):
return pritunl_auth_request(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
method="PUT",
path="/user/%s/%s" % (organization_id, user_id),
headers={"Content-Type": "application/json"},
data=json.dumps(user_data),
validate_certs=validate_certs,
)
def list_pritunl_organizations(
api_token, api_secret, base_url, validate_certs=True, filters=None
):
orgs = []
response = _get_pritunl_organizations(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
validate_certs=validate_certs,
)
if response.getcode() != 200:
raise PritunlException("Could not retrieve organizations from Pritunl")
else:
for org in json.loads(response.read()):
# No filtering
if filters is None:
orgs.append(org)
else:
if not any(
filter_val != org[filter_key]
for filter_key, filter_val in iteritems(filters)
):
orgs.append(org)
return orgs
def list_pritunl_users(
api_token, api_secret, base_url, organization_id, validate_certs=True, filters=None
):
users = []
response = _get_pritunl_users(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
validate_certs=validate_certs,
organization_id=organization_id,
)
if response.getcode() != 200:
raise PritunlException("Could not retrieve users from Pritunl")
else:
for user in json.loads(response.read()):
# No filtering
if filters is None:
users.append(user)
else:
if not any(
filter_val != user[filter_key]
for filter_key, filter_val in iteritems(filters)
):
users.append(user)
return users
def post_pritunl_organization(
api_token,
api_secret,
base_url,
organization_name,
validate_certs=True,
):
response = _post_pritunl_organization(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
organization_data={"name": organization_name},
validate_certs=True,
)
if response.getcode() != 200:
raise PritunlException(
"Could not add organization %s to Pritunl" % (organization_name)
)
# The user PUT request returns the updated user object
return json.loads(response.read())
def post_pritunl_user(
api_token,
api_secret,
base_url,
organization_id,
user_data,
user_id=None,
validate_certs=True,
):
# If user_id is provided will do PUT otherwise will do POST
if user_id is None:
response = _post_pritunl_user(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
organization_id=organization_id,
user_data=user_data,
validate_certs=True,
)
if response.getcode() != 200:
raise PritunlException(
"Could not remove user %s from organization %s from Pritunl"
% (user_id, organization_id)
)
# user POST request returns an array of a single item,
# so return this item instead of the list
return json.loads(response.read())[0]
else:
response = _put_pritunl_user(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
organization_id=organization_id,
user_data=user_data,
user_id=user_id,
validate_certs=True,
)
if response.getcode() != 200:
raise PritunlException(
"Could not update user %s from organization %s from Pritunl"
% (user_id, organization_id)
)
# The user PUT request returns the updated user object
return json.loads(response.read())
def delete_pritunl_organization(
api_token, api_secret, base_url, organization_id, validate_certs=True
):
response = _delete_pritunl_organization(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
organization_id=organization_id,
validate_certs=True,
)
if response.getcode() != 200:
raise PritunlException(
"Could not remove organization %s from Pritunl" % (organization_id)
)
return json.loads(response.read())
def delete_pritunl_user(
api_token, api_secret, base_url, organization_id, user_id, validate_certs=True
):
response = _delete_pritunl_user(
api_token=api_token,
api_secret=api_secret,
base_url=base_url,
organization_id=organization_id,
user_id=user_id,
validate_certs=True,
)
if response.getcode() != 200:
raise PritunlException(
"Could not remove user %s from organization %s from Pritunl"
% (user_id, organization_id)
)
return json.loads(response.read())
def pritunl_auth_request(
api_token,
api_secret,
base_url,
method,
path,
validate_certs=True,
headers=None,
data=None,
):
"""
Send an API call to a Pritunl server.
Taken from https://pritunl.com/api and adaped work with Ansible open_url
"""
auth_timestamp = str(int(time.time()))
auth_nonce = uuid.uuid4().hex
auth_string = "&".join(
[api_token, auth_timestamp, auth_nonce, method.upper(), path]
+ ([data] if data else [])
)
auth_signature = base64.b64encode(
hmac.new(
api_secret.encode("utf-8"), auth_string.encode("utf-8"), hashlib.sha256
).digest()
)
auth_headers = {
"Auth-Token": api_token,
"Auth-Timestamp": auth_timestamp,
"Auth-Nonce": auth_nonce,
"Auth-Signature": auth_signature,
}
if headers:
auth_headers.update(headers)
try:
uri = "%s%s" % (base_url, path)
return open_url(
uri,
method=method.upper(),
headers=auth_headers,
data=data,
validate_certs=validate_certs,
)
except Exception as e:
raise PritunlException(e)

View File

@@ -39,14 +39,16 @@ class OpenNebulaModule:
wait_timeout=dict(type='int', default=300),
)
def __init__(self, argument_spec, supports_check_mode=False, mutually_exclusive=None):
def __init__(self, argument_spec, supports_check_mode=False, mutually_exclusive=None, required_one_of=None, required_if=None):
module_args = OpenNebulaModule.common_args
module_args = OpenNebulaModule.common_args.copy()
module_args.update(argument_spec)
self.module = AnsibleModule(argument_spec=module_args,
supports_check_mode=supports_check_mode,
mutually_exclusive=mutually_exclusive)
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of,
required_if=required_if)
self.result = dict(changed=False,
original_message='',
message='')

View File

@@ -104,7 +104,7 @@ def get_common_arg_spec(supports_create=False, supports_wait=False):
if supports_create:
common_args.update(
key_by=dict(type="list", elements="str"),
key_by=dict(type="list", elements="str", no_log=False),
force_create=dict(type="bool", default=False),
)

View File

@@ -19,11 +19,10 @@ PATCH_HEADERS = {'content-type': 'application/json', 'accept': 'application/json
'OData-Version': '4.0'}
DELETE_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'}
DEPRECATE_MSG = 'Issuing a data modification command without specifying the '\
'ID of the target %(resource)s resource when there is more '\
'than one %(resource)s will use the first one in the '\
'collection. Use the `resource_id` option to specify the '\
'target %(resource)s ID'
FAIL_MSG = 'Issuing a data modification command without specifying the '\
'ID of the target %(resource)s resource when there is more '\
'than one %(resource)s is no longer allowed. Use the `resource_id` '\
'option to specify the target %(resource)s ID.'
class RedfishUtils(object):
@@ -39,13 +38,34 @@ class RedfishUtils(object):
self.data_modification = data_modification
self._init_session()
def _auth_params(self, headers):
"""
Return tuple of required authentication params based on the presence
of a token in the self.creds dict. If using a token, set the
X-Auth-Token header in the `headers` param.
:param headers: dict containing headers to send in request
:return: tuple of username, password and force_basic_auth
"""
if self.creds.get('token'):
username = None
password = None
force_basic_auth = False
headers['X-Auth-Token'] = self.creds['token']
else:
username = self.creds['user']
password = self.creds['pswd']
force_basic_auth = True
return username, password, force_basic_auth
# The following functions are to send GET/POST/PATCH/DELETE requests
def get_request(self, uri):
req_headers = dict(GET_HEADERS)
username, password, basic_auth = self._auth_params(req_headers)
try:
resp = open_url(uri, method="GET", headers=GET_HEADERS,
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, validate_certs=False,
resp = open_url(uri, method="GET", headers=req_headers,
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
data = json.loads(to_native(resp.read()))
@@ -66,14 +86,16 @@ class RedfishUtils(object):
return {'ret': True, 'data': data, 'headers': headers}
def post_request(self, uri, pyld):
req_headers = dict(POST_HEADERS)
username, password, basic_auth = self._auth_params(req_headers)
try:
resp = open_url(uri, data=json.dumps(pyld),
headers=POST_HEADERS, method="POST",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, validate_certs=False,
headers=req_headers, method="POST",
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
headers = dict((k.lower(), v) for (k, v) in resp.info().items())
except HTTPError as e:
msg = self._get_extended_message(e)
return {'ret': False,
@@ -87,10 +109,10 @@ class RedfishUtils(object):
except Exception as e:
return {'ret': False,
'msg': "Failed POST request to '%s': '%s'" % (uri, to_text(e))}
return {'ret': True, 'resp': resp}
return {'ret': True, 'headers': headers, 'resp': resp}
def patch_request(self, uri, pyld):
headers = PATCH_HEADERS
req_headers = dict(PATCH_HEADERS)
r = self.get_request(uri)
if r['ret']:
# Get etag from etag header or @odata.etag property
@@ -98,15 +120,13 @@ class RedfishUtils(object):
if not etag:
etag = r['data'].get('@odata.etag')
if etag:
# Make copy of headers and add If-Match header
headers = dict(headers)
headers['If-Match'] = etag
req_headers['If-Match'] = etag
username, password, basic_auth = self._auth_params(req_headers)
try:
resp = open_url(uri, data=json.dumps(pyld),
headers=headers, method="PATCH",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, validate_certs=False,
headers=req_headers, method="PATCH",
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
except HTTPError as e:
@@ -125,13 +145,14 @@ class RedfishUtils(object):
return {'ret': True, 'resp': resp}
def delete_request(self, uri, pyld=None):
req_headers = dict(DELETE_HEADERS)
username, password, basic_auth = self._auth_params(req_headers)
try:
data = json.dumps(pyld) if pyld else None
resp = open_url(uri, data=data,
headers=DELETE_HEADERS, method="DELETE",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, validate_certs=False,
headers=req_headers, method="DELETE",
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
except HTTPError as e:
@@ -245,8 +266,7 @@ class RedfishUtils(object):
'ret': False,
'msg': "System resource %s not found" % self.resource_id}
elif len(self.systems_uris) > 1:
self.module.deprecate(DEPRECATE_MSG % {'resource': 'System'},
version='3.0.0', collection_name='community.general') # was Ansible 2.14
self.module.fail_json(msg=FAIL_MSG % {'resource': 'System'})
return {'ret': True}
def _find_updateservice_resource(self):
@@ -296,8 +316,7 @@ class RedfishUtils(object):
'ret': False,
'msg': "Chassis resource %s not found" % self.resource_id}
elif len(self.chassis_uris) > 1:
self.module.deprecate(DEPRECATE_MSG % {'resource': 'Chassis'},
version='3.0.0', collection_name='community.general') # was Ansible 2.14
self.module.fail_json(msg=FAIL_MSG % {'resource': 'Chassis'})
return {'ret': True}
def _find_managers_resource(self):
@@ -326,8 +345,7 @@ class RedfishUtils(object):
'ret': False,
'msg': "Manager resource %s not found" % self.resource_id}
elif len(self.manager_uris) > 1:
self.module.deprecate(DEPRECATE_MSG % {'resource': 'Manager'},
version='3.0.0', collection_name='community.general') # was Ansible 2.14
self.module.fail_json(msg=FAIL_MSG % {'resource': 'Manager'})
return {'ret': True}
def _get_all_action_info_values(self, action):
@@ -1196,6 +1214,54 @@ class RedfishUtils(object):
return {'ret': True, 'changed': True, 'msg': "Clear all sessions successfully"}
def create_session(self):
if not self.creds.get('user') or not self.creds.get('pswd'):
return {'ret': False, 'msg':
'Must provide the username and password parameters for '
'the CreateSession command'}
payload = {
'UserName': self.creds['user'],
'Password': self.creds['pswd']
}
response = self.post_request(self.root_uri + self.sessions_uri, payload)
if response['ret'] is False:
return response
headers = response['headers']
if 'x-auth-token' not in headers:
return {'ret': False, 'msg':
'The service did not return the X-Auth-Token header in '
'the response from the Sessions collection POST'}
if 'location' not in headers:
self.module.warn(
'The service did not return the Location header for the '
'session URL in the response from the Sessions collection '
'POST')
session_uri = None
else:
session_uri = urlparse(headers.get('location')).path
session = dict()
session['token'] = headers.get('x-auth-token')
session['uri'] = session_uri
return {'ret': True, 'changed': True, 'session': session,
'msg': 'Session created successfully'}
def delete_session(self, session_uri):
if not session_uri:
return {'ret': False, 'msg':
'Must provide the session_uri parameter for the '
'DeleteSession command'}
response = self.delete_request(self.root_uri + session_uri)
if response['ret'] is False:
return response
return {'ret': True, 'changed': True,
'msg': 'Session deleted successfully'}
def get_firmware_update_capabilities(self):
result = {}
response = self.get_request(self.root_uri + self.update_uri)
@@ -2676,6 +2742,10 @@ class RedfishUtils(object):
need_change = True
# type is list
if isinstance(set_value, list):
if len(set_value) != len(cur_value):
# if arrays are not the same len, no need to check each element
need_change = True
continue
for i in range(len(set_value)):
for subprop in payload[property][i].keys():
if subprop not in target_ethernet_current_setting[property][i]:

View File

@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
#
# Dell EMC OpenManage Ansible Modules
# Version 1.0
# Copyright (C) 2018 Dell Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# All rights reserved. Dell, EMC, and other trademarks are trademarks of Dell Inc. or its subsidiaries.
# Other trademarks may be trademarks of their respective owners.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
try:
from omsdk.sdkinfra import sdkinfra
from omsdk.sdkcreds import UserCredentials
from omsdk.sdkfile import FileOnShare, file_share_manager
from omsdk.sdkprotopref import ProtoPreference, ProtocolEnum
from omsdk.http.sdkwsmanbase import WsManOptions
HAS_OMSDK = True
except ImportError:
HAS_OMSDK = False
class iDRACConnection:
def __init__(self, module_params):
if not HAS_OMSDK:
raise ImportError("Dell EMC OMSDK library is required for this module")
self.idrac_ip = module_params['idrac_ip']
self.idrac_user = module_params['idrac_user']
self.idrac_pwd = module_params['idrac_password']
self.idrac_port = module_params['idrac_port']
if not all((self.idrac_ip, self.idrac_user, self.idrac_pwd)):
raise ValueError("hostname, username and password required")
self.handle = None
self.creds = UserCredentials(self.idrac_user, self.idrac_pwd)
self.pOp = WsManOptions(port=self.idrac_port)
self.sdk = sdkinfra()
if self.sdk is None:
msg = "Could not initialize iDRAC drivers."
raise RuntimeError(msg)
def __enter__(self):
self.sdk.importPath()
self.handle = self.sdk.get_driver(self.sdk.driver_enum.iDRAC, self.idrac_ip, self.creds, pOptions=self.pOp)
if self.handle is None:
msg = "Could not find device driver for iDRAC with IP Address: {0}".format(self.idrac_ip)
raise RuntimeError(msg)
return self.handle
def __exit__(self, exc_type, exc_val, exc_tb):
self.handle.disconnect()
return False

View File

@@ -1,163 +0,0 @@
# -*- coding: utf-8 -*-
# Dell EMC OpenManage Ansible Modules
# Version 1.3
# Copyright (C) 2019 Dell Inc. or its subsidiaries. All Rights Reserved.
#
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError
from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError
from ansible.module_utils.six.moves.urllib.parse import urlencode
SESSION_RESOURCE_COLLECTION = {
"SESSION": "SessionService/Sessions",
"SESSION_ID": "SessionService/Sessions('{Id}')",
}
class OpenURLResponse(object):
"""Handles HTTPResponse"""
def __init__(self, resp):
self.body = None
self.resp = resp
if self.resp:
self.body = self.resp.read()
@property
def json_data(self):
try:
return json.loads(self.body)
except ValueError:
raise ValueError("Unable to parse json")
@property
def status_code(self):
return self.resp.getcode()
@property
def success(self):
return self.status_code in (200, 201, 202, 204)
@property
def token_header(self):
return self.resp.headers.get('X-Auth-Token')
class RestOME(object):
"""Handles OME API requests"""
def __init__(self, module_params=None, req_session=False):
self.module_params = module_params
self.hostname = self.module_params["hostname"]
self.username = self.module_params["username"]
self.password = self.module_params["password"]
self.port = self.module_params["port"]
self.req_session = req_session
self.session_id = None
self.protocol = 'https'
self._headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
def _get_base_url(self):
"""builds base url"""
return '{0}://{1}:{2}/api'.format(self.protocol, self.hostname, self.port)
def _build_url(self, path, query_param=None):
"""builds complete url"""
url = path
base_uri = self._get_base_url()
if path:
url = '{0}/{1}'.format(base_uri, path)
if query_param:
url += "?{0}".format(urlencode(query_param))
return url
def _url_common_args_spec(self, method, api_timeout, headers=None):
"""Creates an argument common spec"""
req_header = self._headers
if headers:
req_header.update(headers)
url_kwargs = {
"method": method,
"validate_certs": False,
"use_proxy": True,
"headers": req_header,
"timeout": api_timeout,
"follow_redirects": 'all',
}
return url_kwargs
def _args_without_session(self, method, api_timeout=30, headers=None):
"""Creates an argument spec in case of basic authentication"""
req_header = self._headers
if headers:
req_header.update(headers)
url_kwargs = self._url_common_args_spec(method, api_timeout, headers=headers)
url_kwargs["url_username"] = self.username
url_kwargs["url_password"] = self.password
url_kwargs["force_basic_auth"] = True
return url_kwargs
def _args_with_session(self, method, api_timeout=30, headers=None):
"""Creates an argument spec, in case of authentication with session"""
url_kwargs = self._url_common_args_spec(method, api_timeout, headers=headers)
url_kwargs["force_basic_auth"] = False
return url_kwargs
def invoke_request(self, method, path, data=None, query_param=None, headers=None,
api_timeout=30, dump=True):
"""
Sends a request via open_url
Returns :class:`OpenURLResponse` object.
:arg method: HTTP verb to use for the request
:arg path: path to request without query parameter
:arg data: (optional) Payload to send with the request
:arg query_param: (optional) Dictionary of query parameter to send with request
:arg headers: (optional) Dictionary of HTTP Headers to send with the
request
:arg api_timeout: (optional) How long to wait for the server to send
data before giving up
:arg dump: (Optional) boolean value for dumping payload data.
:returns: OpenURLResponse
"""
try:
if 'X-Auth-Token' in self._headers:
url_kwargs = self._args_with_session(method, api_timeout, headers=headers)
else:
url_kwargs = self._args_without_session(method, api_timeout, headers=headers)
if data and dump:
data = json.dumps(data)
url = self._build_url(path, query_param=query_param)
resp = open_url(url, data=data, **url_kwargs)
resp_data = OpenURLResponse(resp)
except (HTTPError, URLError, SSLValidationError, ConnectionError) as err:
raise err
return resp_data
def __enter__(self):
"""Creates sessions by passing it to header"""
if self.req_session:
payload = {'UserName': self.username,
'Password': self.password,
'SessionType': 'API', }
path = SESSION_RESOURCE_COLLECTION["SESSION"]
resp = self.invoke_request('POST', path, data=payload)
if resp and resp.success:
self.session_id = resp.json_data.get("Id")
self._headers["X-Auth-Token"] = resp.token_header
else:
msg = "Could not create the session"
raise ConnectionError(msg)
return self
def __exit__(self, exc_type, exc_value, traceback):
"""Deletes a session id, which is in use for request"""
if self.session_id:
path = SESSION_RESOURCE_COLLECTION["SESSION_ID"].format(Id=self.session_id)
self.invoke_request('DELETE', path)
return False

View File

@@ -39,7 +39,7 @@ class ScalewayException(Exception):
R_LINK_HEADER = r'''<[^>]+>;\srel="(first|previous|next|last)"
(,<[^>]+>;\srel="(first|previous|next|last)")*'''
# Specify a single relation, for iteration and string extraction purposes
R_RELATION = r'<(?P<target_IRI>[^>]+)>; rel="(?P<relation>first|previous|next|last)"'
R_RELATION = r'</?(?P<target_IRI>[^>]+)>; rel="(?P<relation>first|previous|next|last)"'
def parse_pagination_link(header):

View File

@@ -20,7 +20,6 @@ except ImportError:
XENAPI_IMP_ERR = traceback.format_exc()
from ansible.module_utils.basic import env_fallback, missing_required_lib
from ansible.module_utils.common.network import is_mac
from ansible.module_utils.ansible_release import __version__ as ANSIBLE_VERSION

View File

@@ -1 +0,0 @@
cloud/alicloud/ali_instance_facts.py

View File

@@ -1 +0,0 @@
ali_instance_info.py

View File

@@ -383,9 +383,6 @@ def main():
)
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('ali_instance_facts', 'community.general.ali_instance_facts'):
module.deprecate("The 'ali_instance_facts' module has been renamed to 'ali_instance_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
if HAS_FOOTMARK is False:
module.fail_json(msg=missing_required_lib('footmark'), exception=FOOTMARK_IMP_ERR)

View File

@@ -102,7 +102,8 @@ def do_install(module, mode, rootfs, container, image, values_list, backend):
system_list = ["--system"] if mode == 'system' else []
user_list = ["--user"] if mode == 'user' else []
rootfs_list = ["--rootfs=%s" % rootfs] if rootfs else []
args = ['atomic', 'install', "--storage=%s" % backend, '--name=%s' % container] + system_list + user_list + rootfs_list + values_list + [image]
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'install', "--storage=%s" % backend, '--name=%s' % container] + system_list + user_list + rootfs_list + values_list + [image]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
@@ -112,7 +113,8 @@ def do_install(module, mode, rootfs, container, image, values_list, backend):
def do_update(module, container, image, values_list):
args = ['atomic', 'containers', 'update', "--rebase=%s" % image] + values_list + [container]
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'containers', 'update', "--rebase=%s" % image] + values_list + [container]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
@@ -122,7 +124,8 @@ def do_update(module, container, image, values_list):
def do_uninstall(module, name, backend):
args = ['atomic', 'uninstall', "--storage=%s" % backend, name]
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'uninstall', "--storage=%s" % backend, name]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
@@ -130,7 +133,8 @@ def do_uninstall(module, name, backend):
def do_rollback(module, name):
args = ['atomic', 'containers', 'rollback', name]
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'containers', 'rollback', name]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
@@ -148,14 +152,12 @@ def core(module):
backend = module.params['backend']
state = module.params['state']
atomic_bin = module.get_bin_path('atomic')
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
out = {}
err = {}
rc = 0
values_list = ["--set=%s" % x for x in values] if values else []
args = ['atomic', 'containers', 'list', '--no-trunc', '-n', '--all', '-f', 'backend=%s' % backend, '-f', 'container=%s' % name]
args = [atomic_bin, 'containers', 'list', '--no-trunc', '-n', '--all', '-f', 'backend=%s' % backend, '-f', 'container=%s' % name]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
@@ -194,9 +196,7 @@ def main():
module.fail_json(msg="values is supported only with user or system mode")
# Verify that the platform supports atomic command
rc, out, err = module.run_command('atomic -v', check_rc=False)
if rc != 0:
module.fail_json(msg="Error in running atomic command", err=err)
dummy = module.get_bin_path('atomic', required=True)
try:
core(module)

View File

@@ -57,18 +57,14 @@ from ansible.module_utils._text import to_native
def core(module):
revision = module.params['revision']
args = []
atomic_bin = module.get_bin_path('atomic', required=True)
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
if revision == 'latest':
args = ['atomic', 'host', 'upgrade']
args = [atomic_bin, 'host', 'upgrade']
else:
args = ['atomic', 'host', 'deploy', revision]
out = {}
err = {}
rc = 0
args = [atomic_bin, 'host', 'deploy', revision]
rc, out, err = module.run_command(args, check_rc=False)

View File

@@ -73,7 +73,8 @@ from ansible.module_utils._text import to_native
def do_upgrade(module, image):
args = ['atomic', 'update', '--force', image]
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'update', '--force', image]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0: # something went wrong emit the msg
module.fail_json(rc=rc, msg=err)
@@ -91,20 +92,21 @@ def core(module):
is_upgraded = False
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
atomic_bin = module.get_bin_path('atomic')
out = {}
err = {}
rc = 0
if backend:
if state == 'present' or state == 'latest':
args = ['atomic', 'pull', "--storage=%s" % backend, image]
args = [atomic_bin, 'pull', "--storage=%s" % backend, image]
rc, out, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
else:
out_run = ""
if started:
args = ['atomic', 'run', "--storage=%s" % backend, image]
args = [atomic_bin, 'run', "--storage=%s" % backend, image]
rc, out_run, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
@@ -112,7 +114,7 @@ def core(module):
changed = "Extracting" in out or "Copying blob" in out
module.exit_json(msg=(out + out_run), changed=changed)
elif state == 'absent':
args = ['atomic', 'images', 'delete', "--storage=%s" % backend, image]
args = [atomic_bin, 'images', 'delete', "--storage=%s" % backend, image]
rc, out, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
@@ -126,11 +128,11 @@ def core(module):
is_upgraded = do_upgrade(module, image)
if started:
args = ['atomic', 'run', image]
args = [atomic_bin, 'run', image]
else:
args = ['atomic', 'install', image]
args = [atomic_bin, 'install', image]
elif state == 'absent':
args = ['atomic', 'uninstall', image]
args = [atomic_bin, 'uninstall', image]
rc, out, err = module.run_command(args, check_rc=False)
@@ -155,9 +157,7 @@ def main():
)
# Verify that the platform supports atomic command
rc, out, err = module.run_command('atomic -v', check_rc=False)
if rc != 0:
module.fail_json(msg="Error in running atomic command", err=err)
dummy = module.get_bin_path('atomic', required=True)
try:
core(module)

View File

@@ -30,10 +30,6 @@ options:
required: False
default: present
choices: ['present','absent']
wait:
description:
- This option does nothing and will be removed in community.general 3.0.0.
type: bool
requirements:
- python = 2.7
- requests >= 2.5.0
@@ -185,7 +181,6 @@ class ClcAntiAffinityPolicy:
argument_spec = dict(
name=dict(required=True),
location=dict(required=True),
wait=dict(type='bool', removed_in_version='3.0.0', removed_from_collection='community.general'), # was Ansible 2.14
state=dict(default='present', choices=['present', 'absent']),
)
return argument_spec

View File

@@ -260,7 +260,7 @@ class DimensionDataNetworkModule(DimensionDataModule):
)
self.module.fail_json(
"Unexpected failure deleting network with id %s", network.id
"Unexpected failure deleting network with id %s" % network.id
)
except DimensionDataAPIException as e:

View File

@@ -28,7 +28,6 @@ options:
- The region of the instance. This is a required parameter only when
creating Linode instances. See
U(https://www.linode.com/docs/api/regions/).
required: false
type: str
image:
description:
@@ -36,14 +35,12 @@ options:
creating Linode instances. See
U(https://www.linode.com/docs/api/images/).
type: str
required: false
type:
description:
- The type of the instance. This is a required parameter only when
creating Linode instances. See
U(https://www.linode.com/docs/api/linode-types/).
type: str
required: false
label:
description:
- The instance label. This label is used as the main determiner for
@@ -56,12 +53,17 @@ options:
group labelling is deprecated but still supported. The encouraged
method for marking instances is to use tags.
type: str
required: false
private_ip:
description:
- If C(true), the created Linode will have private networking enabled and
assigned a private IPv4 address.
type: bool
default: false
version_added: 3.0.0
tags:
description:
- The tags that the instance should be marked under. See
U(https://www.linode.com/docs/api/tags/).
required: false
type: list
elements: str
root_pass:
@@ -69,12 +71,10 @@ options:
- The password for the root user. If not specified, one will be
generated. This generated password will be available in the task
success JSON.
required: false
type: str
authorized_keys:
description:
- A list of SSH public key parts to deploy for the root user.
required: false
type: list
elements: str
state:
@@ -208,9 +208,8 @@ def create_linode(module, client, **kwargs):
else:
return response._raw_json
except TypeError:
module.fail_json(msg='Unable to parse Linode instance creation'
' response. Please raise a bug against this'
' module on https://github.com/ansible/ansible/issues'
module.fail_json(msg='Unable to parse Linode instance creation response. Please raise a bug against this'
' module on https://github.com/ansible-collections/community.general/issues'
)
@@ -242,15 +241,16 @@ def initialise_module():
no_log=True,
fallback=(env_fallback, ['LINODE_ACCESS_TOKEN']),
),
authorized_keys=dict(type='list', elements='str', required=False),
group=dict(type='str', required=False),
image=dict(type='str', required=False),
region=dict(type='str', required=False),
root_pass=dict(type='str', required=False, no_log=True),
tags=dict(type='list', elements='str', required=False),
type=dict(type='str', required=False),
stackscript_id=dict(type='int', required=False),
stackscript_data=dict(type='dict', required=False),
authorized_keys=dict(type='list', elements='str', no_log=False),
group=dict(type='str'),
image=dict(type='str'),
private_ip=dict(type='bool', default=False),
region=dict(type='str'),
root_pass=dict(type='str', no_log=True),
tags=dict(type='list', elements='str'),
type=dict(type='str'),
stackscript_id=dict(type='int'),
stackscript_data=dict(type='dict'),
),
supports_check_mode=False,
required_one_of=(
@@ -290,6 +290,7 @@ def main():
group=module.params['group'],
image=module.params['image'],
label=module.params['label'],
private_ip=module.params['private_ip'],
region=module.params['region'],
root_pass=module.params['root_pass'],
tags=module.params['tags'],

View File

@@ -1 +0,0 @@
memset_memstore_info.py

View File

@@ -151,9 +151,6 @@ def main():
),
supports_check_mode=False
)
if module._name in ('memset_memstore_facts', 'community.general.memset_memstore_facts'):
module.deprecate("The 'memset_memstore_facts' module has been renamed to 'memset_memstore_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
# populate the dict with the user-provided vars.
args = dict()

View File

@@ -1 +0,0 @@
memset_server_info.py

View File

@@ -276,9 +276,6 @@ def main():
),
supports_check_mode=False
)
if module._name in ('memset_server_facts', 'community.general.memset_server_facts'):
module.deprecate("The 'memset_server_facts' module has been renamed to 'memset_server_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
# populate the dict with the user-provided vars.
args = dict()

View File

@@ -1,216 +0,0 @@
#!/usr/bin/python
# (c) 2016, Flavio Percoco <flavio@redhat.com>
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
deprecated:
removed_in: 3.0.0 # was Ansible 2.14
why: For more details https://github.com/ansible/ansible/issues/61546.
alternative: Use M(community.kubernetes.helm) instead.
module: helm
short_description: Manages Kubernetes packages with the Helm package manager
author: "Flavio Percoco (@flaper87)"
description:
- Install, upgrade, delete and list packages with the Helm package manager.
requirements:
- "pyhelm"
- "grpcio"
options:
host:
description:
- Tiller's server host.
type: str
default: "localhost"
port:
description:
- Tiller's server port.
type: int
default: 44134
namespace:
description:
- Kubernetes namespace where the chart should be installed.
type: str
default: "default"
name:
description:
- Release name to manage.
type: str
state:
description:
- Whether to install C(present), remove C(absent), or purge C(purged) a package.
choices: ['absent', 'purged', 'present']
type: str
default: "present"
chart:
description:
- A map describing the chart to install. See examples for available options.
type: dict
default: {}
values:
description:
- A map of value options for the chart.
type: dict
default: {}
disable_hooks:
description:
- Whether to disable hooks during the uninstall process.
type: bool
default: 'no'
'''
RETURN = ''' # '''
EXAMPLES = '''
- name: Install helm chart
community.general.helm:
host: localhost
chart:
name: memcached
version: 0.4.0
source:
type: repo
location: https://kubernetes-charts.storage.googleapis.com
state: present
name: my-memcached
namespace: default
- name: Uninstall helm chart
community.general.helm:
host: localhost
state: absent
name: my-memcached
- name: Install helm chart from a git repo
community.general.helm:
host: localhost
chart:
source:
type: git
location: https://github.com/user/helm-chart.git
state: present
name: my-example
namespace: default
values:
foo: "bar"
- name: Install helm chart from a git repo specifying path
community.general.helm:
host: localhost
chart:
source:
type: git
location: https://github.com/helm/charts.git
path: stable/memcached
state: present
name: my-memcached
namespace: default
values: "{{ lookup('file', '/path/to/file/values.yaml') | from_yaml }}"
'''
import traceback
HELM_IMPORT_ERR = None
try:
import grpc
from pyhelm import tiller
from pyhelm import chartbuilder
except ImportError:
HELM_IMPORT_ERR = traceback.format_exc()
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
def install(module, tserver):
changed = False
params = module.params
name = params['name']
values = params['values']
chart = module.params['chart']
namespace = module.params['namespace']
chartb = chartbuilder.ChartBuilder(chart)
r_matches = (x for x in tserver.list_releases()
if x.name == name and x.namespace == namespace)
installed_release = next(r_matches, None)
if installed_release:
if installed_release.chart.metadata.version != chart['version']:
tserver.update_release(chartb.get_helm_chart(), False,
namespace, name=name, values=values)
changed = True
else:
tserver.install_release(chartb.get_helm_chart(), namespace,
dry_run=False, name=name,
values=values)
changed = True
return dict(changed=changed)
def delete(module, tserver, purge=False):
changed = False
params = module.params
if not module.params['name']:
module.fail_json(msg='Missing required field name')
name = module.params['name']
disable_hooks = params['disable_hooks']
try:
tserver.uninstall_release(name, disable_hooks, purge)
changed = True
except grpc._channel._Rendezvous as exc:
if 'not found' not in str(exc):
raise exc
return dict(changed=changed)
def main():
"""The main function."""
module = AnsibleModule(
argument_spec=dict(
host=dict(type='str', default='localhost'),
port=dict(type='int', default=44134),
name=dict(type='str', default=''),
chart=dict(type='dict'),
state=dict(
choices=['absent', 'purged', 'present'],
default='present'
),
# Install options
values=dict(type='dict'),
namespace=dict(type='str', default='default'),
# Uninstall options
disable_hooks=dict(type='bool', default=False),
),
supports_check_mode=True)
if HELM_IMPORT_ERR:
module.fail_json(msg=missing_required_lib('pyhelm'), exception=HELM_IMPORT_ERR)
host = module.params['host']
port = module.params['port']
state = module.params['state']
tserver = tiller.Tiller(host, port)
if state == 'present':
rst = install(module, tserver)
if state in 'absent':
rst = delete(module, tserver)
if state in 'purged':
rst = delete(module, tserver, True)
module.exit_json(**rst)
if __name__ == '__main__':
main()

View File

@@ -1,503 +0,0 @@
#!/usr/bin/python
# Copyright: (c) 2013, Vincent Van der Kussen <vincent at vanderkussen.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt
author:
- Vincent Van der Kussen (@vincentvdk)
short_description: oVirt/RHEV platform management
deprecated:
removed_in: 3.0.0 # was Ansible 2.14
why: This module is for deprecated version of ovirt.
alternative: Use C(ovirt_vm) from the C(ovirt.ovirt) collection instead
description:
- This module only supports oVirt/RHEV version 3. A newer module M(ovirt.ovirt.ovirt_vm) supports oVirt/RHV version 4.
- Allows you to create new instances, either from scratch or an image, in addition to deleting or stopping instances on the oVirt/RHEV platform.
options:
user:
description:
- The user to authenticate with.
type: str
required: true
url:
description:
- The url of the oVirt instance.
type: str
required: true
instance_name:
description:
- The name of the instance to use.
type: str
required: true
aliases: [ vmname ]
password:
description:
- Password of the user to authenticate with.
type: str
required: true
image:
description:
- The template to use for the instance.
type: str
resource_type:
description:
- Whether you want to deploy an image or create an instance from scratch.
type: str
choices: [ new, template ]
zone:
description:
- Deploy the image to this oVirt cluster.
type: str
instance_disksize:
description:
- Size of the instance's disk in GB.
type: str
aliases: [ vm_disksize]
instance_cpus:
description:
- The instance's number of CPUs.
type: str
default: 1
aliases: [ vmcpus ]
instance_nic:
description:
- The name of the network interface in oVirt/RHEV.
type: str
aliases: [ vmnic ]
instance_network:
description:
- The logical network the machine should belong to.
type: str
default: rhevm
aliases: [ vmnetwork ]
instance_mem:
description:
- The instance's amount of memory in MB.
type: str
aliases: [ vmmem ]
instance_type:
description:
- Define whether the instance is a server, desktop or high_performance.
- I(high_performance) is supported since Ansible 2.5 and oVirt/RHV 4.2.
type: str
choices: [ desktop, server, high_performance ]
default: server
aliases: [ vmtype ]
disk_alloc:
description:
- Define whether disk is thin or preallocated.
type: str
choices: [ preallocated, thin ]
default: thin
disk_int:
description:
- Interface type of the disk.
type: str
choices: [ ide, virtio ]
default: virtio
instance_os:
description:
- Type of Operating System.
type: str
aliases: [ vmos ]
instance_cores:
description:
- Define the instance's number of cores.
type: str
default: 1
aliases: [ vmcores ]
sdomain:
description:
- The Storage Domain where you want to create the instance's disk on.
type: str
region:
description:
- The oVirt/RHEV datacenter where you want to deploy to.
type: str
instance_dns:
description:
- Define the instance's Primary DNS server.
type: str
aliases: [ dns ]
instance_domain:
description:
- Define the instance's Domain.
type: str
aliases: [ domain ]
instance_hostname:
description:
- Define the instance's Hostname.
type: str
aliases: [ hostname ]
instance_ip:
description:
- Define the instance's IP.
type: str
aliases: [ ip ]
instance_netmask:
description:
- Define the instance's Netmask.
type: str
aliases: [ netmask ]
instance_gateway:
description:
- Define the instance's Gateway.
type: str
aliases: [ gateway ]
instance_rootpw:
description:
- Define the instance's Root password.
type: str
aliases: [ rootpw ]
instance_key:
description:
- Define the instance's Authorized key.
type: str
aliases: [ key ]
state:
description:
- Create, terminate or remove instances.
type: str
choices: [ absent, present, restart, shutdown, started ]
default: present
requirements:
- ovirt-engine-sdk-python
'''
EXAMPLES = '''
- name: Basic example to provision from image
community.general.ovirt:
user: admin@internal
url: https://ovirt.example.com
instance_name: ansiblevm04
password: secret
image: centos_64
zone: cluster01
resource_type: template
- name: Full example to create new instance from scratch
community.general.ovirt:
instance_name: testansible
resource_type: new
instance_type: server
user: admin@internal
password: secret
url: https://ovirt.example.com
instance_disksize: 10
zone: cluster01
region: datacenter1
instance_cpus: 1
instance_nic: nic1
instance_network: rhevm
instance_mem: 1000
disk_alloc: thin
sdomain: FIBER01
instance_cores: 1
instance_os: rhel_6x64
disk_int: virtio
- name: Stopping an existing instance
community.general.ovirt:
instance_name: testansible
state: stopped
user: admin@internal
password: secret
url: https://ovirt.example.com
- name: Start an existing instance
community.general.ovirt:
instance_name: testansible
state: started
user: admin@internal
password: secret
url: https://ovirt.example.com
- name: Start an instance with cloud init information
community.general.ovirt:
instance_name: testansible
state: started
user: admin@internal
password: secret
url: https://ovirt.example.com
hostname: testansible
domain: ansible.local
ip: 192.0.2.100
netmask: 255.255.255.0
gateway: 192.0.2.1
rootpw: bigsecret
'''
import time
try:
from ovirtsdk.api import API
from ovirtsdk.xml import params
HAS_OVIRTSDK = True
except ImportError:
HAS_OVIRTSDK = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.removed import removed_module
# ------------------------------------------------------------------- #
# create connection with API
#
def conn(url, user, password):
api = API(url=url, username=user, password=password, insecure=True)
try:
value = api.test()
except Exception:
raise Exception("error connecting to the oVirt API")
return api
# ------------------------------------------------------------------- #
# Create VM from scratch
def create_vm(conn, vmtype, vmname, zone, vmdisk_size, vmcpus, vmnic, vmnetwork, vmmem, vmdisk_alloc, sdomain, vmcores, vmos, vmdisk_int):
if vmdisk_alloc == 'thin':
# define VM params
vmparams = params.VM(name=vmname, cluster=conn.clusters.get(name=zone), os=params.OperatingSystem(type_=vmos),
template=conn.templates.get(name="Blank"), memory=1024 * 1024 * int(vmmem),
cpu=params.CPU(topology=params.CpuTopology(cores=int(vmcores), sockets=vmcpus)), type_=vmtype)
# define disk params
vmdisk = params.Disk(size=1024 * 1024 * 1024 * int(vmdisk_size), wipe_after_delete=True, sparse=True, interface=vmdisk_int, type_="System",
format='cow',
storage_domains=params.StorageDomains(storage_domain=[conn.storagedomains.get(name=sdomain)]))
# define network parameters
network_net = params.Network(name=vmnetwork)
nic_net1 = params.NIC(name='nic1', network=network_net, interface='virtio')
elif vmdisk_alloc == 'preallocated':
# define VM params
vmparams = params.VM(name=vmname, cluster=conn.clusters.get(name=zone), os=params.OperatingSystem(type_=vmos),
template=conn.templates.get(name="Blank"), memory=1024 * 1024 * int(vmmem),
cpu=params.CPU(topology=params.CpuTopology(cores=int(vmcores), sockets=vmcpus)), type_=vmtype)
# define disk params
vmdisk = params.Disk(size=1024 * 1024 * 1024 * int(vmdisk_size), wipe_after_delete=True, sparse=False, interface=vmdisk_int, type_="System",
format='raw', storage_domains=params.StorageDomains(storage_domain=[conn.storagedomains.get(name=sdomain)]))
# define network parameters
network_net = params.Network(name=vmnetwork)
nic_net1 = params.NIC(name=vmnic, network=network_net, interface='virtio')
try:
conn.vms.add(vmparams)
except Exception:
raise Exception("Error creating VM with specified parameters")
vm = conn.vms.get(name=vmname)
try:
vm.disks.add(vmdisk)
except Exception:
raise Exception("Error attaching disk")
try:
vm.nics.add(nic_net1)
except Exception:
raise Exception("Error adding nic")
# create an instance from a template
def create_vm_template(conn, vmname, image, zone):
vmparams = params.VM(name=vmname, cluster=conn.clusters.get(name=zone), template=conn.templates.get(name=image), disks=params.Disks(clone=True))
try:
conn.vms.add(vmparams)
except Exception:
raise Exception('error adding template %s' % image)
# start instance
def vm_start(conn, vmname, hostname=None, ip=None, netmask=None, gateway=None,
domain=None, dns=None, rootpw=None, key=None):
vm = conn.vms.get(name=vmname)
use_cloud_init = False
nics = None
nic = None
if hostname or ip or netmask or gateway or domain or dns or rootpw or key:
use_cloud_init = True
if ip and netmask and gateway:
ipinfo = params.IP(address=ip, netmask=netmask, gateway=gateway)
nic = params.GuestNicConfiguration(name='eth0', boot_protocol='STATIC', ip=ipinfo, on_boot=True)
nics = params.Nics()
nics = params.GuestNicsConfiguration(nic_configuration=[nic])
initialization = params.Initialization(regenerate_ssh_keys=True, host_name=hostname, domain=domain, user_name='root',
root_password=rootpw, nic_configurations=nics, dns_servers=dns,
authorized_ssh_keys=key)
action = params.Action(use_cloud_init=use_cloud_init, vm=params.VM(initialization=initialization))
vm.start(action=action)
# Stop instance
def vm_stop(conn, vmname):
vm = conn.vms.get(name=vmname)
vm.stop()
# restart instance
def vm_restart(conn, vmname):
state = vm_status(conn, vmname)
vm = conn.vms.get(name=vmname)
vm.stop()
while conn.vms.get(vmname).get_status().get_state() != 'down':
time.sleep(5)
vm.start()
# remove an instance
def vm_remove(conn, vmname):
vm = conn.vms.get(name=vmname)
vm.delete()
# ------------------------------------------------------------------- #
# VM statuses
#
# Get the VMs status
def vm_status(conn, vmname):
status = conn.vms.get(name=vmname).status.state
return status
# Get VM object and return it's name if object exists
def get_vm(conn, vmname):
vm = conn.vms.get(name=vmname)
if vm is None:
name = "empty"
else:
name = vm.get_name()
return name
# ------------------------------------------------------------------- #
# Hypervisor operations
#
# not available yet
# ------------------------------------------------------------------- #
# Main
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(type='str', default='present', choices=['absent', 'present', 'restart', 'shutdown', 'started']),
user=dict(type='str', required=True),
url=dict(type='str', required=True),
instance_name=dict(type='str', required=True, aliases=['vmname']),
password=dict(type='str', required=True, no_log=True),
image=dict(type='str'),
resource_type=dict(type='str', choices=['new', 'template']),
zone=dict(type='str'),
instance_disksize=dict(type='str', aliases=['vm_disksize']),
instance_cpus=dict(type='str', default=1, aliases=['vmcpus']),
instance_nic=dict(type='str', aliases=['vmnic']),
instance_network=dict(type='str', default='rhevm', aliases=['vmnetwork']),
instance_mem=dict(type='str', aliases=['vmmem']),
instance_type=dict(type='str', default='server', aliases=['vmtype'], choices=['desktop', 'server', 'high_performance']),
disk_alloc=dict(type='str', default='thin', choices=['preallocated', 'thin']),
disk_int=dict(type='str', default='virtio', choices=['ide', 'virtio']),
instance_os=dict(type='str', aliases=['vmos']),
instance_cores=dict(type='str', default=1, aliases=['vmcores']),
instance_hostname=dict(type='str', aliases=['hostname']),
instance_ip=dict(type='str', aliases=['ip']),
instance_netmask=dict(type='str', aliases=['netmask']),
instance_gateway=dict(type='str', aliases=['gateway']),
instance_domain=dict(type='str', aliases=['domain']),
instance_dns=dict(type='str', aliases=['dns']),
instance_rootpw=dict(type='str', aliases=['rootpw'], no_log=True),
instance_key=dict(type='str', aliases=['key'], no_log=True),
sdomain=dict(type='str'),
region=dict(type='str'),
),
)
if not HAS_OVIRTSDK:
module.fail_json(msg='ovirtsdk required for this module')
state = module.params['state']
user = module.params['user']
url = module.params['url']
vmname = module.params['instance_name']
password = module.params['password']
image = module.params['image'] # name of the image to deploy
resource_type = module.params['resource_type'] # template or from scratch
zone = module.params['zone'] # oVirt cluster
vmdisk_size = module.params['instance_disksize'] # disksize
vmcpus = module.params['instance_cpus'] # number of cpu
vmnic = module.params['instance_nic'] # network interface
vmnetwork = module.params['instance_network'] # logical network
vmmem = module.params['instance_mem'] # mem size
vmdisk_alloc = module.params['disk_alloc'] # thin, preallocated
vmdisk_int = module.params['disk_int'] # disk interface virtio or ide
vmos = module.params['instance_os'] # Operating System
vmtype = module.params['instance_type'] # server, desktop or high_performance
vmcores = module.params['instance_cores'] # number of cores
sdomain = module.params['sdomain'] # storage domain to store disk on
region = module.params['region'] # oVirt Datacenter
hostname = module.params['instance_hostname']
ip = module.params['instance_ip']
netmask = module.params['instance_netmask']
gateway = module.params['instance_gateway']
domain = module.params['instance_domain']
dns = module.params['instance_dns']
rootpw = module.params['instance_rootpw']
key = module.params['instance_key']
# initialize connection
try:
c = conn(url + "/api", user, password)
except Exception as e:
module.fail_json(msg='%s' % e)
if state == 'present':
if get_vm(c, vmname) == "empty":
if resource_type == 'template':
try:
create_vm_template(c, vmname, image, zone)
except Exception as e:
module.fail_json(msg='%s' % e)
module.exit_json(changed=True, msg="deployed VM %s from template %s" % (vmname, image))
elif resource_type == 'new':
# FIXME: refactor, use keyword args.
try:
create_vm(c, vmtype, vmname, zone, vmdisk_size, vmcpus, vmnic, vmnetwork, vmmem, vmdisk_alloc, sdomain, vmcores, vmos, vmdisk_int)
except Exception as e:
module.fail_json(msg='%s' % e)
module.exit_json(changed=True, msg="deployed VM %s from scratch" % vmname)
else:
module.exit_json(changed=False, msg="You did not specify a resource type")
else:
module.exit_json(changed=False, msg="VM %s already exists" % vmname)
if state == 'started':
if vm_status(c, vmname) == 'up':
module.exit_json(changed=False, msg="VM %s is already running" % vmname)
else:
# vm_start(c, vmname)
vm_start(c, vmname, hostname, ip, netmask, gateway, domain, dns, rootpw, key)
module.exit_json(changed=True, msg="VM %s started" % vmname)
if state == 'shutdown':
if vm_status(c, vmname) == 'down':
module.exit_json(changed=False, msg="VM %s is already shutdown" % vmname)
else:
vm_stop(c, vmname)
module.exit_json(changed=True, msg="VM %s is shutting down" % vmname)
if state == 'restart':
if vm_status(c, vmname) == 'up':
vm_restart(c, vmname)
module.exit_json(changed=True, msg="VM %s is restarted" % vmname)
else:
module.exit_json(changed=False, msg="VM %s is not running" % vmname)
if state == 'absent':
if get_vm(c, vmname) == "empty":
module.exit_json(changed=False, msg="VM %s does not exist" % vmname)
else:
vm_remove(c, vmname)
module.exit_json(changed=True, msg="VM %s removed" % vmname)
if __name__ == '__main__':
main()

View File

@@ -17,7 +17,6 @@ options:
password:
description:
- the instance root password
- required only for C(state=present)
type: str
hostname:
description:
@@ -124,6 +123,15 @@ options:
- with states C(stopped) , C(restarted) allow to force stop instance
type: bool
default: 'no'
purge:
description:
- Remove container from all related configurations.
- For example backup jobs, replication jobs, or HA.
- Related ACLs and Firewall entries will always be removed.
- Used with state C(absent).
type: bool
default: false
version_added: 2.3.0
state:
description:
- Indicate desired state of the instance
@@ -507,6 +515,7 @@ def main():
searchdomain=dict(),
timeout=dict(type='int', default=30),
force=dict(type='bool', default=False),
purge=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted']),
pubkey=dict(type='str', default=None),
unprivileged=dict(type='bool', default=False),
@@ -514,7 +523,7 @@ def main():
hookscript=dict(type='str'),
proxmox_default_behavior=dict(type='str', choices=['compatibility', 'no_defaults']),
),
required_if=[('state', 'present', ['node', 'hostname', 'password', 'ostemplate'])],
required_if=[('state', 'present', ['node', 'hostname', 'ostemplate'])],
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
)
@@ -687,7 +696,13 @@ def main():
if getattr(proxmox.nodes(vm[0]['node']), VZ_TYPE)(vmid).status.current.get()['status'] == 'mounted':
module.exit_json(changed=False, msg="VM %s is mounted. Stop it with force option before deletion." % vmid)
taskid = getattr(proxmox.nodes(vm[0]['node']), VZ_TYPE).delete(vmid)
delete_params = {}
if module.params['purge']:
delete_params['purge'] = 1
taskid = getattr(proxmox.nodes(vm[0]['node']), VZ_TYPE).delete(vmid, **delete_params)
while timeout:
if (proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['status'] == 'stopped' and
proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['exitstatus'] == 'OK'):

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Tristan Le Guern (@Aversiste) <tleguern at bouledef.eu>
# Copyright: Tristan Le Guern (@tleguern) <tleguern at bouledef.eu>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
@@ -21,7 +21,7 @@ options:
- Restrict results to a specific authentication realm.
aliases: ['realm', 'name']
type: str
author: Tristan Le Guern (@Aversiste)
author: Tristan Le Guern (@tleguern)
extends_documentation_fragment: community.general.proxmox.documentation
'''

View File

@@ -21,7 +21,7 @@ options:
- Restrict results to a specific group.
aliases: ['groupid', 'name']
type: str
author: Tristan Le Guern (@Aversiste)
author: Tristan Le Guern (@tleguern)
extends_documentation_fragment: community.general.proxmox.documentation
'''

View File

@@ -425,6 +425,14 @@ options:
option has a default of C(no). Note that the default value of I(proxmox_default_behavior)
changes in community.general 4.0.0.
type: bool
tags:
description:
- List of tags to apply to the VM instance.
- Tags must start with C([a-z0-9_]) followed by zero or more of the following characters C([a-z0-9_-+.]).
- Tags are only available in Proxmox 6+.
type: list
elements: str
version_added: 2.3.0
target:
description:
- Target node. Only allowed if the original VM is on shared storage.
@@ -858,7 +866,7 @@ def wait_for_task(module, proxmox, node, taskid):
def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sockets, update, **kwargs):
# Available only in PVE 4
only_v4 = ['force', 'protection', 'skiplock']
only_v6 = ['ciuser', 'cipassword', 'sshkeys', 'ipconfig']
only_v6 = ['ciuser', 'cipassword', 'sshkeys', 'ipconfig', 'tags']
# valide clone parameters
valid_clone_params = ['format', 'full', 'pool', 'snapname', 'storage', 'target']
@@ -928,6 +936,13 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock
if searchdomains:
kwargs['searchdomain'] = ' '.join(searchdomains)
# VM tags are expected to be valid and presented as a comma/semi-colon delimited string
if 'tags' in kwargs:
for tag in kwargs['tags']:
if not re.match(r'^[a-z0-9_][a-z0-9_\-\+\.]*$', tag):
module.fail_json(msg='%s is not a valid tag' % tag)
kwargs['tags'] = ",".join(kwargs['tags'])
# -args and skiplock require root@pam user - but can not use api tokens
if module.params['api_user'] == "root@pam" and module.params['args'] is None:
if not update and module.params['proxmox_default_behavior'] == 'compatibility':
@@ -1057,12 +1072,13 @@ def main():
smbios=dict(type='str'),
snapname=dict(type='str'),
sockets=dict(type='int'),
sshkeys=dict(type='str'),
sshkeys=dict(type='str', no_log=False),
startdate=dict(type='str'),
startup=dict(),
state=dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted', 'current']),
storage=dict(type='str'),
tablet=dict(type='bool'),
tags=dict(type='list', elements='str'),
target=dict(type='str'),
tdf=dict(type='bool'),
template=dict(type='bool'),
@@ -1267,6 +1283,7 @@ def main():
startdate=module.params['startdate'],
startup=module.params['startup'],
tablet=module.params['tablet'],
tags=module.params['tags'],
target=module.params['target'],
tdf=module.params['tdf'],
template=module.params['template'],

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Tristan Le Guern (@Aversiste) <tleguern at bouledef.eu>
# Copyright: Tristan Le Guern (@tleguern) <tleguern at bouledef.eu>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
@@ -25,7 +25,7 @@ options:
description:
- Filter on a specifc storage type.
type: str
author: Tristan Le Guern (@Aversiste)
author: Tristan Le Guern (@tleguern)
extends_documentation_fragment: community.general.proxmox.documentation
notes:
- Storage specific options can be returned by this module, please look at the documentation at U(https://pve.proxmox.com/wiki/Storage).

View File

@@ -30,7 +30,7 @@ options:
description:
- Restrict results to a specific user ID, which is a concatenation of a user and domain parts.
type: str
author: Tristan Le Guern (@Aversiste)
author: Tristan Le Guern (@tleguern)
extends_documentation_fragment: community.general.proxmox.documentation
'''

View File

@@ -1229,24 +1229,6 @@ class RHEV(object):
self.__get_conn()
return self.conn.set_VM_Host(vmname, vmhost)
# pylint: disable=unreachable
VM = self.conn.get_VM(vmname)
HOST = self.conn.get_Host(vmhost)
if VM.placement_policy.host is None:
self.conn.set_VM_Host(vmname, vmhost)
elif str(VM.placement_policy.host.id) != str(HOST.id):
self.conn.set_VM_Host(vmname, vmhost)
else:
setMsg("VM's startup host was already set to " + vmhost)
checkFail()
if str(VM.status.state) == "up":
self.conn.migrate_VM(vmname, vmhost)
checkFail()
return True
def setHost(self, hostname, cluster, ifaces):
self.__get_conn()
return self.conn.set_Host(hostname, cluster, ifaces)

View File

@@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
DOCUMENTATION = r'''
---
module: terraform
short_description: Manages a Terraform deployment (and plans)
@@ -33,6 +33,19 @@ options:
vars.tf/main.tf/etc to use.
type: path
required: true
plugin_paths:
description:
- List of paths containing Terraform plugin executable files.
- Plugin executables can be downloaded from U(https://releases.hashicorp.com/).
- When set, the plugin discovery and auto-download behavior of Terraform is disabled.
- The directory structure in the plugin path can be tricky. The Terraform docs
U(https://learn.hashicorp.com/tutorials/terraform/automate-terraform#pre-installed-plugins)
show a simple directory of files, but actually, the directory structure
has to follow the same structure you would see if Terraform auto-downloaded the plugins.
See the examples below for a tree output of an example plugin directory.
type: list
elements: path
version_added: 3.0.0
workspace:
description:
- The terraform workspace to work with.
@@ -141,6 +154,28 @@ EXAMPLES = """
backend_config_files:
- /path/to/backend_config_file_1
- /path/to/backend_config_file_2
- name: Disable plugin discovery and auto-download by setting plugin_paths
community.general.terraform:
project_path: 'project/'
state: "{{ state }}"
force_init: true
plugin_paths:
- /path/to/plugins_dir_1
- /path/to/plugins_dir_2
### Example directory structure for plugin_paths example
# $ tree /path/to/plugins_dir_1
# /path/to/plugins_dir_1/
# └── registry.terraform.io
# └── hashicorp
# └── vsphere
# ├── 1.24.0
# │ └── linux_amd64
# │ └── terraform-provider-vsphere_v1.24.0_x4
# └── 1.26.0
# └── linux_amd64
# └── terraform-provider-vsphere_v1.26.0_x4
"""
RETURN = """
@@ -177,24 +212,31 @@ command:
import os
import json
import tempfile
from distutils.version import LooseVersion
from ansible.module_utils.six.moves import shlex_quote
from ansible.module_utils.basic import AnsibleModule
DESTROY_ARGS = ('destroy', '-no-color', '-force')
APPLY_ARGS = ('apply', '-no-color', '-input=false', '-auto-approve=true')
module = None
def preflight_validation(bin_path, project_path, variables_args=None, plan_file=None):
def get_version(bin_path):
extract_version = module.run_command([bin_path, 'version', '-json'])
terraform_version = (json.loads(extract_version[1]))['terraform_version']
return terraform_version
def preflight_validation(bin_path, project_path, version, variables_args=None, plan_file=None):
if project_path in [None, ''] or '/' not in project_path:
module.fail_json(msg="Path for Terraform project can not be None or ''.")
if not os.path.exists(bin_path):
module.fail_json(msg="Path for Terraform binary '{0}' doesn't exist on this host - check the path and try again please.".format(bin_path))
if not os.path.isdir(project_path):
module.fail_json(msg="Path for Terraform project '{0}' doesn't exist on this host - check the path and try again please.".format(project_path))
rc, out, err = module.run_command([bin_path, 'validate'] + variables_args, check_rc=True, cwd=project_path, use_unsafe_shell=True)
if LooseVersion(version) < LooseVersion('0.15.0'):
rc, out, err = module.run_command([bin_path, 'validate'] + variables_args, check_rc=True, cwd=project_path)
else:
rc, out, err = module.run_command([bin_path, 'validate'], check_rc=True, cwd=project_path)
def _state_args(state_file):
@@ -205,7 +247,7 @@ def _state_args(state_file):
return []
def init_plugins(bin_path, project_path, backend_config, backend_config_files, init_reconfigure):
def init_plugins(bin_path, project_path, backend_config, backend_config_files, init_reconfigure, plugin_paths):
command = [bin_path, 'init', '-input=false']
if backend_config:
for key, val in backend_config.items():
@@ -218,6 +260,9 @@ def init_plugins(bin_path, project_path, backend_config, backend_config_files, i
command.extend(['-backend-config', f])
if init_reconfigure:
command.extend(['-reconfigure'])
if plugin_paths:
for plugin_path in plugin_paths:
command.extend(['-plugin-dir', plugin_path])
rc, out, err = module.run_command(command, check_rc=True, cwd=project_path)
@@ -267,7 +312,7 @@ def build_plan(command, project_path, variables_args, state_file, targets, state
plan_command.extend(_state_args(state_file))
rc, out, err = module.run_command(plan_command + variables_args, cwd=project_path, use_unsafe_shell=True)
rc, out, err = module.run_command(plan_command + variables_args, cwd=project_path)
if rc == 0:
# no changes
@@ -288,6 +333,7 @@ def main():
argument_spec=dict(
project_path=dict(required=True, type='path'),
binary_path=dict(type='path'),
plugin_paths=dict(type='list', elements='path'),
workspace=dict(required=False, type='str', default='default'),
purge_workspace=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent', 'planned']),
@@ -309,6 +355,7 @@ def main():
project_path = module.params.get('project_path')
bin_path = module.params.get('binary_path')
plugin_paths = module.params.get('plugin_paths')
workspace = module.params.get('workspace')
purge_workspace = module.params.get('purge_workspace')
state = module.params.get('state')
@@ -326,8 +373,17 @@ def main():
else:
command = [module.get_bin_path('terraform', required=True)]
checked_version = get_version(command[0])
if LooseVersion(checked_version) < LooseVersion('0.15.0'):
DESTROY_ARGS = ('destroy', '-no-color', '-force')
APPLY_ARGS = ('apply', '-no-color', '-input=false', '-auto-approve=true')
else:
DESTROY_ARGS = ('destroy', '-no-color', '-auto-approve')
APPLY_ARGS = ('apply', '-no-color', '-input=false', '-auto-approve')
if force_init:
init_plugins(command[0], project_path, backend_config, backend_config_files, init_reconfigure)
init_plugins(command[0], project_path, backend_config, backend_config_files, init_reconfigure, plugin_paths)
workspace_ctx = get_workspace_context(command[0], project_path)
if workspace_ctx["current"] != workspace:
@@ -351,7 +407,7 @@ def main():
for f in variables_files:
variables_args.extend(['-var-file', f])
preflight_validation(command[0], project_path, variables_args)
preflight_validation(command[0], project_path, checked_version, variables_args)
if module.params.get('lock') is not None:
if module.params.get('lock'):

View File

@@ -630,7 +630,7 @@ def main():
ram=dict(type='float'),
hdds=dict(type='list', elements='dict'),
count=dict(type='int', default=1),
ssh_key=dict(type='raw'),
ssh_key=dict(type='raw', no_log=False),
auto_increment=dict(type='bool', default=True),
server=dict(type='str'),
datacenter=dict(

View File

@@ -1,175 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: online_server_facts
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: Deprecated in favour of C(_info) module.
alternative: Use M(community.general.online_server_info) instead.
short_description: Gather facts about Online servers.
description:
- Gather facts about the servers.
- U(https://www.online.net/en/dedicated-server)
author:
- "Remy Leone (@sieben)"
extends_documentation_fragment:
- community.general.online
'''
EXAMPLES = r'''
- name: Gather Online server facts
community.general.online_server_facts:
api_token: '0d1627e8-bbf0-44c5-a46f-5c4d3aef033f'
'''
RETURN = r'''
---
online_server_facts:
description: Response from Online API
returned: success
type: complex
sample:
"online_server_facts": [
{
"abuse": "abuse@example.com",
"anti_ddos": false,
"bmc": {
"session_key": null
},
"boot_mode": "normal",
"contacts": {
"owner": "foobar",
"tech": "foobar"
},
"disks": [
{
"$ref": "/api/v1/server/hardware/disk/68452"
},
{
"$ref": "/api/v1/server/hardware/disk/68453"
}
],
"drive_arrays": [
{
"disks": [
{
"$ref": "/api/v1/server/hardware/disk/68452"
},
{
"$ref": "/api/v1/server/hardware/disk/68453"
}
],
"raid_controller": {
"$ref": "/api/v1/server/hardware/raidController/9910"
},
"raid_level": "RAID1"
}
],
"hardware_watch": true,
"hostname": "sd-42",
"id": 42,
"ip": [
{
"address": "195.154.172.149",
"mac": "28:92:4a:33:5e:c6",
"reverse": "195-154-172-149.rev.poneytelecom.eu.",
"switch_port_state": "up",
"type": "public"
},
{
"address": "10.90.53.212",
"mac": "28:92:4a:33:5e:c7",
"reverse": null,
"switch_port_state": "up",
"type": "private"
}
],
"last_reboot": "2018-08-23T08:32:03.000Z",
"location": {
"block": "A",
"datacenter": "DC3",
"position": 19,
"rack": "A23",
"room": "4 4-4"
},
"network": {
"ip": [
"195.154.172.149"
],
"ipfo": [],
"private": [
"10.90.53.212"
]
},
"offer": "Pro-1-S-SATA",
"os": {
"name": "FreeBSD",
"version": "11.1-RELEASE"
},
"power": "ON",
"proactive_monitoring": false,
"raid_controllers": [
{
"$ref": "/api/v1/server/hardware/raidController/9910"
}
],
"support": "Basic service level"
}
]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.online import (
Online, OnlineException, online_argument_spec
)
class OnlineServerFacts(Online):
def __init__(self, module):
super(OnlineServerFacts, self).__init__(module)
self.name = 'api/v1/server'
def _get_server_detail(self, server_path):
try:
return self.get(path=server_path).json
except OnlineException as exc:
self.module.fail_json(msg="A problem occurred while fetching: %s (%s)" % (server_path, exc))
def all_detailed_servers(self):
servers_api_path = self.get_resources()
server_data = (
self._get_server_detail(server_api_path)
for server_api_path in servers_api_path
)
return [s for s in server_data if s is not None]
def main():
module = AnsibleModule(
argument_spec=online_argument_spec(),
supports_check_mode=True,
)
try:
servers_facts = OnlineServerFacts(module).all_detailed_servers()
module.exit_json(
ansible_facts={'online_server_facts': servers_facts}
)
except OnlineException as exc:
module.fail_json(msg=exc.message)
if __name__ == '__main__':
main()

View File

@@ -1,76 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = r'''
---
module: online_user_facts
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: Deprecated in favour of C(_info) module.
alternative: Use M(community.general.online_user_info) instead.
short_description: Gather facts about Online user.
description:
- Gather facts about the user.
author:
- "Remy Leone (@sieben)"
extends_documentation_fragment:
- community.general.online
'''
EXAMPLES = r'''
- name: Gather Online user facts
community.general.online_user_facts:
'''
RETURN = r'''
---
online_user_facts:
description: Response from Online API
returned: success
type: complex
sample:
"online_user_facts": {
"company": "foobar LLC",
"email": "foobar@example.com",
"first_name": "foo",
"id": 42,
"last_name": "bar",
"login": "foobar"
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.online import (
Online, OnlineException, online_argument_spec
)
class OnlineUserFacts(Online):
def __init__(self, module):
super(OnlineUserFacts, self).__init__(module)
self.name = 'api/v1/user'
def main():
module = AnsibleModule(
argument_spec=online_argument_spec(),
supports_check_mode=True,
)
try:
module.exit_json(
ansible_facts={'online_user_facts': OnlineUserFacts(module).get_resources()}
)
except OnlineException as exc:
module.fail_json(msg=exc.message)
if __name__ == '__main__':
main()

View File

@@ -31,7 +31,7 @@ short_description: Manages OpenNebula images
description:
- Manages OpenNebula images
requirements:
- python-oca
- pyone
options:
api_url:
description:
@@ -88,7 +88,7 @@ EXAMPLES = '''
- name: Print the IMAGE properties
ansible.builtin.debug:
msg: result
var: result
- name: Rename existing IMAGE
community.general.one_image:
@@ -168,21 +168,20 @@ running_vms:
'''
try:
import oca
HAS_OCA = True
import pyone
HAS_PYONE = True
except ImportError:
HAS_OCA = False
HAS_PYONE = False
from ansible.module_utils.basic import AnsibleModule
import os
def get_image(module, client, predicate):
pool = oca.ImagePool(client)
# Filter -2 means fetch all images user can Use
pool.info(filter=-2)
pool = client.imagepool.info(-2, -1, -1, -1)
for image in pool:
for image in pool.IMAGE:
if predicate(image):
return image
@@ -190,11 +189,11 @@ def get_image(module, client, predicate):
def get_image_by_name(module, client, image_name):
return get_image(module, client, lambda image: (image.name == image_name))
return get_image(module, client, lambda image: (image.NAME == image_name))
def get_image_by_id(module, client, image_id):
return get_image(module, client, lambda image: (image.id == image_id))
return get_image(module, client, lambda image: (image.ID == image_id))
def get_image_instance(module, client, requested_id, requested_name):
@@ -208,30 +207,28 @@ IMAGE_STATES = ['INIT', 'READY', 'USED', 'DISABLED', 'LOCKED', 'ERROR', 'CLONE',
def get_image_info(image):
image.info()
info = {
'id': image.id,
'name': image.name,
'state': IMAGE_STATES[image.state],
'running_vms': image.running_vms,
'used': bool(image.running_vms),
'user_name': image.uname,
'user_id': image.uid,
'group_name': image.gname,
'group_id': image.gid,
'id': image.ID,
'name': image.NAME,
'state': IMAGE_STATES[image.STATE],
'running_vms': image.RUNNING_VMS,
'used': bool(image.RUNNING_VMS),
'user_name': image.UNAME,
'user_id': image.UID,
'group_name': image.GNAME,
'group_id': image.GID,
}
return info
def wait_for_state(module, image, wait_timeout, state_predicate):
def wait_for_state(module, client, image_id, wait_timeout, state_predicate):
import time
start_time = time.time()
while (time.time() - start_time) < wait_timeout:
image.info()
state = image.state
image = client.image.info(image_id)
state = image.STATE
if state_predicate(state):
return image
@@ -241,19 +238,19 @@ def wait_for_state(module, image, wait_timeout, state_predicate):
module.fail_json(msg="Wait timeout has expired!")
def wait_for_ready(module, image, wait_timeout=60):
return wait_for_state(module, image, wait_timeout, lambda state: (state in [IMAGE_STATES.index('READY')]))
def wait_for_ready(module, client, image_id, wait_timeout=60):
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('READY')]))
def wait_for_delete(module, image, wait_timeout=60):
return wait_for_state(module, image, wait_timeout, lambda state: (state in [IMAGE_STATES.index('DELETE')]))
def wait_for_delete(module, client, image_id, wait_timeout=60):
return wait_for_state(module, client, image_id, wait_timeout, lambda state: (state in [IMAGE_STATES.index('DELETE')]))
def enable_image(module, client, image, enable):
image.info()
image = client.image.info(image.ID)
changed = False
state = image.state
state = image.STATE
if state not in [IMAGE_STATES.index('READY'), IMAGE_STATES.index('DISABLED'), IMAGE_STATES.index('ERROR')]:
if enable:
@@ -266,7 +263,7 @@ def enable_image(module, client, image, enable):
changed = True
if changed and not module.check_mode:
client.call('image.enable', image.id, enable)
client.image.enable(image.ID, enable)
result = get_image_info(image)
result['changed'] = changed
@@ -276,7 +273,7 @@ def enable_image(module, client, image, enable):
def clone_image(module, client, image, new_name):
if new_name is None:
new_name = "Copy of " + image.name
new_name = "Copy of " + image.NAME
tmp_image = get_image_by_name(module, client, new_name)
if tmp_image:
@@ -284,13 +281,13 @@ def clone_image(module, client, image, new_name):
result['changed'] = False
return result
if image.state == IMAGE_STATES.index('DISABLED'):
if image.STATE == IMAGE_STATES.index('DISABLED'):
module.fail_json(msg="Cannot clone DISABLED image")
if not module.check_mode:
new_id = client.call('image.clone', image.id, new_name)
image = get_image_by_id(module, client, new_id)
wait_for_ready(module, image)
new_id = client.image.clone(image.ID, new_name)
wait_for_ready(module, client, new_id)
image = client.image.info(new_id)
result = get_image_info(image)
result['changed'] = True
@@ -302,7 +299,7 @@ def rename_image(module, client, image, new_name):
if new_name is None:
module.fail_json(msg="'new_name' option has to be specified when the state is 'renamed'")
if new_name == image.name:
if new_name == image.NAME:
result = get_image_info(image)
result['changed'] = False
return result
@@ -312,7 +309,7 @@ def rename_image(module, client, image, new_name):
module.fail_json(msg="Name '" + new_name + "' is already taken by IMAGE with id=" + str(tmp_image.id))
if not module.check_mode:
client.call('image.rename', image.id, new_name)
client.image.rename(image.ID, new_name)
result = get_image_info(image)
result['changed'] = True
@@ -324,12 +321,12 @@ def delete_image(module, client, image):
if not image:
return {'changed': False}
if image.running_vms > 0:
module.fail_json(msg="Cannot delete image. There are " + str(image.running_vms) + " VMs using it.")
if image.RUNNING_VMS > 0:
module.fail_json(msg="Cannot delete image. There are " + str(image.RUNNING_VMS) + " VMs using it.")
if not module.check_mode:
client.call('image.delete', image.id)
wait_for_delete(module, image)
client.image.delete(image.ID)
wait_for_delete(module, client, image.ID)
return {'changed': True}
@@ -378,8 +375,8 @@ def main():
mutually_exclusive=[['id', 'name']],
supports_check_mode=True)
if not HAS_OCA:
module.fail_json(msg='This module requires python-oca to work!')
if not HAS_PYONE:
module.fail_json(msg='This module requires pyone to work!')
auth = get_connection_info(module)
params = module.params
@@ -388,7 +385,7 @@ def main():
state = params.get('state')
enabled = params.get('enabled')
new_name = params.get('new_name')
client = oca.Client(auth.username + ':' + auth.password, auth.url)
client = pyone.OneServer(auth.url, session=auth.username + ':' + auth.password)
result = {}

View File

@@ -1 +0,0 @@
one_image_info.py

View File

@@ -261,9 +261,6 @@ def main():
module = AnsibleModule(argument_spec=fields,
mutually_exclusive=[['ids', 'name']],
supports_check_mode=True)
if module._name in ('one_image_facts', 'community.general.one_image_facts'):
module.deprecate("The 'one_image_facts' module has been renamed to 'one_image_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
if not HAS_PYONE:
module.fail_json(msg='This module requires pyone to work!')

View File

@@ -0,0 +1,276 @@
#!/usr/bin/python
#
# Copyright: (c) 2021, Georg Gadinger <nilsding@nilsding.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
module: one_template
short_description: Manages OpenNebula templates
version_added: 2.4.0
requirements:
- pyone
description:
- "Manages OpenNebula templates."
options:
id:
description:
- A I(id) of the template you would like to manage. If not set then a
- new template will be created with the given I(name).
type: int
name:
description:
- A I(name) of the template you would like to manage. If a template with
- the given name does not exist it will be created, otherwise it will be
- managed by this module.
type: str
template:
description:
- A string containing the template contents.
type: str
state:
description:
- C(present) - state that is used to manage the template.
- C(absent) - delete the template.
choices: ["present", "absent"]
default: present
type: str
notes:
- Supports C(check_mode). Note that check mode always returns C(changed=true) for existing templates, even if the template would not actually change.
extends_documentation_fragment:
- community.general.opennebula
author:
- "Georg Gadinger (@nilsding)"
'''
EXAMPLES = '''
- name: Fetch the TEMPLATE by id
community.general.one_template:
id: 6459
register: result
- name: Print the TEMPLATE properties
ansible.builtin.debug:
var: result
- name: Fetch the TEMPLATE by name
community.general.one_template:
name: tf-prd-users-workerredis-p6379a
register: result
- name: Create a new or update an existing TEMPLATE
community.general.one_template:
name: generic-opensuse
template: |
CONTEXT = [
HOSTNAME = "generic-opensuse"
]
CPU = "1"
CUSTOM_ATTRIBUTE = ""
DISK = [
CACHE = "writeback",
DEV_PREFIX = "sd",
DISCARD = "unmap",
IMAGE = "opensuse-leap-15.2",
IMAGE_UNAME = "oneadmin",
IO = "threads",
SIZE = "" ]
MEMORY = "2048"
NIC = [
MODEL = "virtio",
NETWORK = "testnet",
NETWORK_UNAME = "oneadmin" ]
OS = [
ARCH = "x86_64",
BOOT = "disk0" ]
SCHED_REQUIREMENTS = "CLUSTER_ID=\\"100\\""
VCPU = "2"
- name: Delete the TEMPLATE by id
community.general.one_template:
id: 6459
state: absent
'''
RETURN = '''
id:
description: template id
type: int
returned: when I(state=present)
sample: 153
name:
description: template name
type: str
returned: when I(state=present)
sample: app1
template:
description: the parsed template
type: dict
returned: when I(state=present)
group_id:
description: template's group id
type: int
returned: when I(state=present)
sample: 1
group_name:
description: template's group name
type: str
returned: when I(state=present)
sample: one-users
owner_id:
description: template's owner id
type: int
returned: when I(state=present)
sample: 143
owner_name:
description: template's owner name
type: str
returned: when I(state=present)
sample: ansible-test
'''
from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule
class TemplateModule(OpenNebulaModule):
def __init__(self):
argument_spec = dict(
id=dict(type='int', required=False),
name=dict(type='str', required=False),
state=dict(type='str', choices=['present', 'absent'], default='present'),
template=dict(type='str', required=False),
)
mutually_exclusive = [
['id', 'name']
]
required_one_of = [('id', 'name')]
required_if = [
['state', 'present', ['template']]
]
OpenNebulaModule.__init__(self,
argument_spec,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of,
required_if=required_if)
def run(self, one, module, result):
params = module.params
id = params.get('id')
name = params.get('name')
desired_state = params.get('state')
template_data = params.get('template')
self.result = {}
template = self.get_template_instance(id, name)
needs_creation = False
if not template and desired_state != 'absent':
if id:
module.fail_json(msg="There is no template with id=" + str(id))
else:
needs_creation = True
if desired_state == 'absent':
self.result = self.delete_template(template)
else:
if needs_creation:
self.result = self.create_template(name, template_data)
else:
self.result = self.update_template(template, template_data)
self.exit()
def get_template(self, predicate):
# -3 means "Resources belonging to the user"
# the other two parameters are used for pagination, -1 for both essentially means "return all"
pool = self.one.templatepool.info(-3, -1, -1)
for template in pool.VMTEMPLATE:
if predicate(template):
return template
return None
def get_template_by_id(self, template_id):
return self.get_template(lambda template: (template.ID == template_id))
def get_template_by_name(self, template_name):
return self.get_template(lambda template: (template.NAME == template_name))
def get_template_instance(self, requested_id, requested_name):
if requested_id:
return self.get_template_by_id(requested_id)
else:
return self.get_template_by_name(requested_name)
def get_template_info(self, template):
info = {
'id': template.ID,
'name': template.NAME,
'template': template.TEMPLATE,
'user_name': template.UNAME,
'user_id': template.UID,
'group_name': template.GNAME,
'group_id': template.GID,
}
return info
def create_template(self, name, template_data):
if not self.module.check_mode:
self.one.template.allocate("NAME = \"" + name + "\"\n" + template_data)
result = self.get_template_info(self.get_template_by_name(name))
result['changed'] = True
return result
def update_template(self, template, template_data):
if not self.module.check_mode:
# 0 = replace the whole template
self.one.template.update(template.ID, template_data, 0)
result = self.get_template_info(self.get_template_by_id(template.ID))
if self.module.check_mode:
# Unfortunately it is not easy to detect if the template would have changed, therefore always report a change here.
result['changed'] = True
else:
# if the previous parsed template data is not equal to the updated one, this has changed
result['changed'] = template.TEMPLATE != result['template']
return result
def delete_template(self, template):
if not template:
return {'changed': False}
if not self.module.check_mode:
self.one.template.delete(template.ID)
return {'changed': True}
def main():
TemplateModule().run_module()
if __name__ == '__main__':
main()

View File

@@ -752,11 +752,20 @@ def get_vm_info(client, vm):
if 'NIC' in vm.TEMPLATE:
if isinstance(vm.TEMPLATE['NIC'], list):
for nic in vm.TEMPLATE['NIC']:
networks_info.append({'ip': nic['IP'], 'mac': nic['MAC'], 'name': nic['NETWORK'], 'security_groups': nic['SECURITY_GROUPS']})
networks_info.append({
'ip': nic.get('IP', ''),
'mac': nic.get('MAC', ''),
'name': nic.get('NETWORK', ''),
'security_groups': nic.get('SECURITY_GROUPS', '')
})
else:
networks_info.append(
{'ip': vm.TEMPLATE['NIC']['IP'], 'mac': vm.TEMPLATE['NIC']['MAC'],
'name': vm.TEMPLATE['NIC']['NETWORK'], 'security_groups': vm.TEMPLATE['NIC']['SECURITY_GROUPS']})
networks_info.append({
'ip': vm.TEMPLATE['NIC'].get('IP', ''),
'mac': vm.TEMPLATE['NIC'].get('MAC', ''),
'name': vm.TEMPLATE['NIC'].get('NETWORK', ''),
'security_groups':
vm.TEMPLATE['NIC'].get('SECURITY_GROUPS', '')
})
import time
current_time = time.localtime()

View File

@@ -162,7 +162,6 @@ def waitForTaskDone(client, name, taskId, timeout):
currentTimeout -= 5
if currentTimeout < 0:
return False
return True
def main():

View File

@@ -1,196 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_affinity_label_facts
short_description: Retrieve information about one or more oVirt/RHV affinity labels
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_affinity_label_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV affinity labels."
notes:
- "This module returns a variable C(ovirt_affinity_labels), which
contains a list of affinity labels. You need to register the result with
the I(register) keyword to use it."
options:
name:
description:
- "Name of the affinity labels which should be listed."
vm:
description:
- "Name of the VM, which affinity labels should be listed."
host:
description:
- "Name of the host, which affinity labels should be listed."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all affinity labels, which names start with label
ovirt_affinity_label_info:
name: label*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_affinity_labels }}"
- name: >
Gather information about all affinity labels, which are assigned to VMs
which names start with postgres
ovirt_affinity_label_info:
vm: postgres*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_affinity_labels }}"
- name: >
Gather information about all affinity labels, which are assigned to hosts
which names start with west
ovirt_affinity_label_info:
host: west*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_affinity_labels }}"
- name: >
Gather information about all affinity labels, which are assigned to hosts
which names start with west or VMs which names start with postgres
ovirt_affinity_label_info:
host: west*
vm: postgres*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_affinity_labels }}"
'''
RETURN = '''
ovirt_affinity_labels:
description: "List of dictionaries describing the affinity labels. Affinity labels attributes are mapped to dictionary keys,
all affinity labels attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/affinity_label."
returned: On success.
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
search_by_name,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
name=dict(default=None),
host=dict(default=None),
vm=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_affinity_label_facts', 'community.general.ovirt_affinity_label_facts')
if is_old_facts:
module.deprecate("The 'ovirt_affinity_label_facts' module has been renamed to 'ovirt_affinity_label_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
affinity_labels_service = connection.system_service().affinity_labels_service()
labels = []
all_labels = affinity_labels_service.list()
if module.params['name']:
labels.extend([
l for l in all_labels
if fnmatch.fnmatch(l.name, module.params['name'])
])
if module.params['host']:
hosts_service = connection.system_service().hosts_service()
if search_by_name(hosts_service, module.params['host']) is None:
raise Exception("Host '%s' was not found." % module.params['host'])
labels.extend([
label
for label in all_labels
for host in connection.follow_link(label.hosts)
if fnmatch.fnmatch(hosts_service.service(host.id).get().name, module.params['host'])
])
if module.params['vm']:
vms_service = connection.system_service().vms_service()
if search_by_name(vms_service, module.params['vm']) is None:
raise Exception("Vm '%s' was not found." % module.params['vm'])
labels.extend([
label
for label in all_labels
for vm in connection.follow_link(label.vms)
if fnmatch.fnmatch(vms_service.service(vm.id).get().name, module.params['vm'])
])
if not (module.params['vm'] or module.params['host'] or module.params['name']):
labels = all_labels
result = dict(
ovirt_affinity_labels=[
get_dict_of_struct(
struct=l,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for l in labels
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,98 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_api_facts
short_description: Retrieve information about the oVirt/RHV API
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_api_info) instead.
description:
- "Retrieve information about the oVirt/RHV API."
notes:
- "This module returns a variable C(ovirt_api),
which contains a information about oVirt/RHV API. You need to register the result with
the I(register) keyword to use it."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information oVirt API
ovirt_api_info:
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_api }}"
'''
RETURN = '''
ovirt_api:
description: "Dictionary describing the oVirt API information.
Api attributes are mapped to dictionary keys,
all API attributes can be found at following
url: https://ovirt.example.com/ovirt-engine/api/model#types/api."
returned: On success.
type: dict
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec()
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_api_facts', 'community.general.ovirt_api_facts')
if is_old_facts:
module.deprecate("The 'ovirt_api_facts' module has been renamed to 'ovirt_api_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
api = connection.system_service().get()
result = dict(
ovirt_api=get_dict_of_struct(
struct=api,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
)
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,125 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_cluster_facts
short_description: Retrieve information about one or more oVirt/RHV clusters
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_cluster_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV clusters."
notes:
- "This module returns a variable C(ovirt_clusters), which
contains a list of clusters. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search cluster X from datacenter Y use following pattern:
name=X and datacenter=Y"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all clusters which names start with production
ovirt_cluster_info:
pattern:
name: 'production*'
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_clusters }}"
'''
RETURN = '''
ovirt_clusters:
description: "List of dictionaries describing the clusters. Cluster attributes are mapped to dictionary keys,
all clusters attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/cluster."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_cluster_facts', 'community.general.ovirt_cluster_facts')
if is_old_facts:
module.deprecate("The 'ovirt_cluster_facts' module has been renamed to 'ovirt_cluster_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
clusters_service = connection.system_service().clusters_service()
clusters = clusters_service.list(search=module.params['pattern'])
result = dict(
ovirt_clusters=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in clusters
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,108 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_datacenter_facts
short_description: Retrieve information about one or more oVirt/RHV datacenters
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_datacenter_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV datacenters."
notes:
- "This module returns a variable C(ovirt_datacenters), which
contains a list of datacenters. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search datacenter I(X) use following pattern: I(name=X)"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all data centers which names start with production
ovirt_datacenter_info:
pattern: name=production*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_datacenters }}"
'''
RETURN = '''
ovirt_datacenters:
description: "List of dictionaries describing the datacenters. Datacenter attributes are mapped to dictionary keys,
all datacenters attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/data_center."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_datacenter_facts', 'community.general.ovirt_datacenter_facts')
if is_old_facts:
module.deprecate("The 'ovirt_datacenter_facts' module has been renamed to 'ovirt_datacenter_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
datacenters_service = connection.system_service().data_centers_service()
datacenters = datacenters_service.list(search=module.params['pattern'])
result = dict(
ovirt_datacenters=[
get_dict_of_struct(
struct=d,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for d in datacenters
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,125 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_disk_facts
short_description: Retrieve information about one or more oVirt/RHV disks
author: "Katerina Koukiou (@KKoukiou)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_disk_info) instead
description:
- "Retrieve information about one or more oVirt/RHV disks."
notes:
- "This module returns a variable C(ovirt_disks), which
contains a list of disks. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search Disk X from storage Y use following pattern:
name=X and storage.name=Y"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all Disks which names start with centos
ovirt_disk_info:
pattern: name=centos*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_disks }}"
'''
RETURN = '''
ovirt_disks:
description: "List of dictionaries describing the Disks. Disk attributes are mapped to dictionary keys,
all Disks attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/disk."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_disk_facts', 'community.general.ovirt_disk_facts')
if is_old_facts:
module.deprecate("The 'ovirt_disk_facts' module has been renamed to 'ovirt_disk_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
disks_service = connection.system_service().disks_service()
disks = disks_service.list(
search=module.params['pattern'],
)
result = dict(
ovirt_disks=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in disks
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,170 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_event_facts
short_description: This module can be used to retrieve information about one or more oVirt/RHV events
author: "Chris Keller (@nasx)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_event_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV events."
options:
case_sensitive:
description:
- "Indicates if the search performed using the search parameter should be performed taking case
into account. The default value is true, which means that case is taken into account. If you
want to search ignoring case set it to false."
required: false
default: true
type: bool
from_:
description:
- "Indicates the event index after which events should be returned. The indexes of events are
strictly increasing, so when this parameter is used only the events with greater indexes
will be returned."
required: false
type: int
max:
description:
- "Sets the maximum number of events to return. If not specified all the events are returned."
required: false
type: int
search:
description:
- "Search term which is accepted by the oVirt/RHV API."
- "For example to search for events of severity alert use the following pattern: severity=alert"
required: false
type: str
headers:
description:
- "Additional HTTP headers."
required: false
type: str
query:
description:
- "Additional URL query parameters."
required: false
type: str
wait:
description:
- "If True wait for the response."
required: false
default: true
type: bool
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain the auth parameter for simplicity,
# look at the ovirt_auth module to see how to reuse authentication.
- name: Return all events
ovirt_event_info:
register: result
- name: Return the last 10 events
ovirt_event_info:
max: 10
register: result
- name: Return all events of type alert
ovirt_event_info:
search: "severity=alert"
register: result
- ansible.builtin.debug:
msg: "{{ result.ovirt_events }}"
'''
RETURN = '''
ovirt_events:
description: "List of dictionaries describing the events. Event attributes are mapped to dictionary keys.
All event attributes can be found at the following url:
http://ovirt.github.io/ovirt-engine-api-model/master/#types/event"
returned: On success."
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
case_sensitive=dict(default=True, type='bool', required=False),
from_=dict(default=None, type='int', required=False),
max=dict(default=None, type='int', required=False),
search=dict(default='', required=False),
headers=dict(default='', required=False),
query=dict(default='', required=False),
wait=dict(default=True, type='bool', required=False)
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_event_facts', 'community.general.ovirt_event_facts')
if is_old_facts:
module.deprecate("The 'ovirt_event_facts' module has been renamed to 'ovirt_event_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
events_service = connection.system_service().events_service()
events = events_service.list(
case_sensitive=module.params['case_sensitive'],
from_=module.params['from_'],
max=module.params['max'],
search=module.params['search'],
headers=module.params['headers'],
query=module.params['query'],
wait=module.params['wait']
)
result = dict(
ovirt_events=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in events
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,165 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_external_provider_facts
short_description: Retrieve information about one or more oVirt/RHV external providers
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_external_provider_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV external providers."
notes:
- "This module returns a variable C(ovirt_external_providers), which
contains a list of external_providers. You need to register the result with
the I(register) keyword to use it."
options:
type:
description:
- "Type of the external provider."
choices: ['os_image', 'os_network', 'os_volume', 'foreman']
required: true
type: str
name:
description:
- "Name of the external provider, can be used as glob expression."
type: str
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all image external providers named glance
ovirt_external_provider_info:
type: os_image
name: glance
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_external_providers }}"
'''
RETURN = '''
ovirt_external_providers:
description:
- "List of dictionaries. Content depends on I(type)."
- "For type C(foreman), attributes appearing in the dictionary can be found on your oVirt/RHV instance
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/external_host_provider."
- "For type C(os_image), attributes appearing in the dictionary can be found on your oVirt/RHV instance
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_image_provider."
- "For type C(os_volume), attributes appearing in the dictionary can be found on your oVirt/RHV instance
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_volume_provider."
- "For type C(os_network), attributes appearing in the dictionary can be found on your oVirt/RHV instance
at the following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/openstack_network_provider."
returned: On success
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def _external_provider_service(provider_type, system_service):
if provider_type == 'os_image':
return system_service.openstack_image_providers_service()
elif provider_type == 'os_network':
return system_service.openstack_network_providers_service()
elif provider_type == 'os_volume':
return system_service.openstack_volume_providers_service()
elif provider_type == 'foreman':
return system_service.external_host_providers_service()
def main():
argument_spec = ovirt_info_full_argument_spec(
name=dict(default=None, required=False),
type=dict(
required=True,
choices=['os_image', 'os_network', 'os_volume', 'foreman'],
aliases=['provider'],
),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_external_provider_facts', 'community.general.ovirt_external_provider_facts')
if is_old_facts:
module.deprecate("The 'ovirt_external_provider_facts' module has been renamed to 'ovirt_external_provider_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
external_providers_service = _external_provider_service(
provider_type=module.params.pop('type'),
system_service=connection.system_service(),
)
if module.params['name']:
external_providers = [
e for e in external_providers_service.list()
if fnmatch.fnmatch(e.name, module.params['name'])
]
else:
external_providers = external_providers_service.list()
result = dict(
ovirt_external_providers=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in external_providers
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,123 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_group_facts
short_description: Retrieve information about one or more oVirt/RHV groups
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_group_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV groups."
notes:
- "This module returns a variable C(ovirt_groups), which
contains a list of groups. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search group X use following pattern: name=X"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all groups which names start with admin
ovirt_group_info:
pattern: name=admin*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_groups }}"
'''
RETURN = '''
ovirt_groups:
description: "List of dictionaries describing the groups. Group attributes are mapped to dictionary keys,
all groups attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/group."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_group_facts', 'community.general.ovirt_group_facts')
if is_old_facts:
module.deprecate("The 'ovirt_group_facts' module has been renamed to 'ovirt_group_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
groups_service = connection.system_service().groups_service()
groups = groups_service.list(search=module.params['pattern'])
result = dict(
ovirt_groups=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in groups
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,149 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_host_facts
short_description: Retrieve information about one or more oVirt/RHV hosts
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_host_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV hosts."
notes:
- "This module returns a variable C(ovirt_hosts), which
contains a list of hosts. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search host X from datacenter Y use following pattern:
name=X and datacenter=Y"
all_content:
description:
- "If I(true) all the attributes of the hosts should be
included in the response."
default: False
type: bool
cluster_version:
description:
- "Filter the hosts based on the cluster version."
type: str
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all hosts which names start with host and belong to data center west
ovirt_host_info:
pattern: name=host* and datacenter=west
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_hosts }}"
- name: Gather information about all hosts with cluster version 4.2
ovirt_host_info:
pattern: name=host*
cluster_version: "4.2"
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_hosts }}"
'''
RETURN = '''
ovirt_hosts:
description: "List of dictionaries describing the hosts. Host attributes are mapped to dictionary keys,
all hosts attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def get_filtered_hosts(cluster_version, hosts, connection):
# Filtering by cluster version returns only those which have same cluster version as input
filtered_hosts = []
for host in hosts:
cluster = connection.follow_link(host.cluster)
cluster_version_host = str(cluster.version.major) + '.' + str(cluster.version.minor)
if cluster_version_host == cluster_version:
filtered_hosts.append(host)
return filtered_hosts
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
all_content=dict(default=False, type='bool'),
cluster_version=dict(default=None, type='str'),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_host_facts', 'community.general.ovirt_host_facts')
if is_old_facts:
module.deprecate("The 'ovirt_host_facts' module has been renamed to 'ovirt_host_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
hosts_service = connection.system_service().hosts_service()
hosts = hosts_service.list(
search=module.params['pattern'],
all_content=module.params['all_content']
)
cluster_version = module.params.get('cluster_version')
if cluster_version is not None:
hosts = get_filtered_hosts(cluster_version, hosts, connection)
result = dict(
ovirt_hosts=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in hosts
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,187 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Red Hat, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_host_storage_facts
short_description: Retrieve information about one or more oVirt/RHV HostStorages (applicable only for block storage)
author: "Daniel Erez (@derez)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_host_storage_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV HostStorages (applicable only for block storage)."
options:
host:
description:
- "Host to get device list from."
required: true
iscsi:
description:
- "Dictionary with values for iSCSI storage type:"
suboptions:
address:
description:
- "Address of the iSCSI storage server."
target:
description:
- "The target IQN for the storage device."
username:
description:
- "A CHAP user name for logging into a target."
password:
description:
- "A CHAP password for logging into a target."
portal:
description:
- "The portal being used to connect with iscsi."
fcp:
description:
- "Dictionary with values for fibre channel storage type:"
suboptions:
address:
description:
- "Address of the fibre channel storage server."
port:
description:
- "Port of the fibre channel storage server."
lun_id:
description:
- "LUN id."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about HostStorages with specified target and address
ovirt_host_storage_info:
host: myhost
iscsi:
target: iqn.2016-08-09.domain-01:nickname
address: 10.34.63.204
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_host_storages }}"
'''
RETURN = '''
ovirt_host_storages:
description: "List of dictionaries describing the HostStorage. HostStorage attributes are mapped to dictionary keys,
all HostStorage attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/host_storage."
returned: On success.
type: list
'''
import traceback
try:
import ovirtsdk4.types as otypes
except ImportError:
pass
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
get_id_by_name,
)
def _login(host_service, iscsi):
host_service.iscsi_login(
iscsi=otypes.IscsiDetails(
username=iscsi.get('username'),
password=iscsi.get('password'),
address=iscsi.get('address'),
target=iscsi.get('target'),
portal=iscsi.get('portal')
),
)
def _get_storage_type(params):
for sd_type in ['iscsi', 'fcp']:
if params.get(sd_type) is not None:
return sd_type
def main():
argument_spec = ovirt_info_full_argument_spec(
host=dict(required=True),
iscsi=dict(default=None, type='dict'),
fcp=dict(default=None, type='dict'),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_host_storage_facts', 'community.general.ovirt_host_storage_facts')
if is_old_facts:
module.deprecate("The 'ovirt_host_storage_facts' module has been renamed to 'ovirt_host_storage_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
# Get Host
hosts_service = connection.system_service().hosts_service()
host_id = get_id_by_name(hosts_service, module.params['host'])
storage_type = _get_storage_type(module.params)
host_service = hosts_service.host_service(host_id)
if storage_type == 'iscsi':
# Login
iscsi = module.params.get('iscsi')
_login(host_service, iscsi)
# Get LUNs exposed from the specified target
host_storages = host_service.storage_service().list()
if storage_type == 'iscsi':
filterred_host_storages = [host_storage for host_storage in host_storages
if host_storage.type == otypes.StorageType.ISCSI]
if 'target' in iscsi:
filterred_host_storages = [host_storage for host_storage in filterred_host_storages
if iscsi.get('target') == host_storage.logical_units[0].target]
elif storage_type == 'fcp':
filterred_host_storages = [host_storage for host_storage in host_storages
if host_storage.type == otypes.StorageType.FCP]
result = dict(
ovirt_host_storages=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in filterred_host_storages
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,125 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_network_facts
short_description: Retrieve information about one or more oVirt/RHV networks
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_network_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV networks."
notes:
- "This module returns a variable C(ovirt_networks), which
contains a list of networks. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search network starting with string vlan1 use: name=vlan1*"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all networks which names start with vlan1
ovirt_network_info:
pattern: name=vlan1*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_networks }}"
'''
RETURN = '''
ovirt_networks:
description: "List of dictionaries describing the networks. Network attributes are mapped to dictionary keys,
all networks attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/network."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_network_facts', 'community.general.ovirt_network_facts')
if is_old_facts:
module.deprecate("The 'ovirt_network_facts' module has been renamed to 'ovirt_network_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
networks_service = connection.system_service().networks_service()
networks = networks_service.list(search=module.params['pattern'])
result = dict(
ovirt_networks=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in networks
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,143 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_nic_facts
short_description: Retrieve information about one or more oVirt/RHV virtual machine network interfaces
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_nic_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV virtual machine network interfaces."
notes:
- "This module returns a variable C(ovirt_nics), which
contains a list of NICs. You need to register the result with
the I(register) keyword to use it."
options:
vm:
description:
- "Name of the VM where NIC is attached."
required: true
name:
description:
- "Name of the NIC, can be used as glob expression."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all NICs which names start with eth for VM named centos7
ovirt_nic_info:
vm: centos7
name: eth*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_nics }}"
'''
RETURN = '''
ovirt_nics:
description: "List of dictionaries describing the network interfaces. NIC attributes are mapped to dictionary keys,
all NICs attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/nic."
returned: On success.
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
search_by_name,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
vm=dict(required=True),
name=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_nic_facts', 'community.general.ovirt_nic_facts')
if is_old_facts:
module.deprecate("The 'ovirt_nic_facts' module has been renamed to 'ovirt_nic_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
vms_service = connection.system_service().vms_service()
vm_name = module.params['vm']
vm = search_by_name(vms_service, vm_name)
if vm is None:
raise Exception("VM '%s' was not found." % vm_name)
nics_service = vms_service.service(vm.id).nics_service()
if module.params['name']:
nics = [
e for e in nics_service.list()
if fnmatch.fnmatch(e.name, module.params['name'])
]
else:
nics = nics_service.list()
result = dict(
ovirt_nics=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in nics
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,166 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_permission_facts
short_description: Retrieve information about one or more oVirt/RHV permissions
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_permission_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV permissions."
notes:
- "This module returns a variable C(ovirt_permissions), which
contains a list of permissions. You need to register the result with
the I(register) keyword to use it."
options:
user_name:
description:
- "Username of the user to manage. In most LDAPs it's I(uid) of the user, but in Active Directory you must specify I(UPN) of the user."
group_name:
description:
- "Name of the group to manage."
authz_name:
description:
- "Authorization provider of the user/group. In previous versions of oVirt/RHV known as domain."
required: true
aliases: ['domain']
namespace:
description:
- "Namespace of the authorization provider, where user/group resides."
required: false
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all permissions of user with username john
ovirt_permission_info:
user_name: john
authz_name: example.com-authz
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_permissions }}"
'''
RETURN = '''
ovirt_permissions:
description: "List of dictionaries describing the permissions. Permission attributes are mapped to dictionary keys,
all permissions attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/permission."
returned: On success.
type: list
'''
import traceback
try:
import ovirtsdk4 as sdk
except ImportError:
pass
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_link_name,
ovirt_info_full_argument_spec,
search_by_name,
)
def _permissions_service(connection, module):
if module.params['user_name']:
service = connection.system_service().users_service()
entity = next(
iter(
service.list(
search='usrname={0}'.format(
'{0}@{1}'.format(module.params['user_name'], module.params['authz_name'])
)
)
),
None
)
else:
service = connection.system_service().groups_service()
entity = search_by_name(service, module.params['group_name'])
if entity is None:
raise Exception("User/Group wasn't found.")
return service.service(entity.id).permissions_service()
def main():
argument_spec = ovirt_info_full_argument_spec(
authz_name=dict(required=True, aliases=['domain']),
user_name=dict(default=None),
group_name=dict(default=None),
namespace=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_permission_facts', 'community.general.ovirt_permission_facts')
if is_old_facts:
module.deprecate("The 'ovirt_permission_facts' module has been renamed to 'ovirt_permission_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
permissions_service = _permissions_service(connection, module)
permissions = []
for p in permissions_service.list():
newperm = dict()
for key, value in p.__dict__.items():
if value and isinstance(value, sdk.Struct):
newperm[key[1:]] = get_link_name(connection, value)
newperm['%s_id' % key[1:]] = value.id
permissions.append(newperm)
result = dict(ovirt_permissions=permissions)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,143 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_quota_facts
short_description: Retrieve information about one or more oVirt/RHV quotas
author: "Maor Lipchuk (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_quota_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV quotas."
notes:
- "This module returns a variable C(ovirt_quotas), which
contains a list of quotas. You need to register the result with
the I(register) keyword to use it."
options:
data_center:
description:
- "Name of the datacenter where quota resides."
required: true
name:
description:
- "Name of the quota, can be used as glob expression."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about quota named C<myquota> in Default datacenter
ovirt_quota_info:
data_center: Default
name: myquota
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_quotas }}"
'''
RETURN = '''
ovirt_quotas:
description: "List of dictionaries describing the quotas. Quota attributes are mapped to dictionary keys,
all quotas attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/quota."
returned: On success.
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
search_by_name,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
data_center=dict(required=True),
name=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_quota_facts', 'community.general.ovirt_quota_facts')
if is_old_facts:
module.deprecate("The 'ovirt_quota_facts' module has been renamed to 'ovirt_quota_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
datacenters_service = connection.system_service().data_centers_service()
dc_name = module.params['data_center']
dc = search_by_name(datacenters_service, dc_name)
if dc is None:
raise Exception("Datacenter '%s' was not found." % dc_name)
quotas_service = datacenters_service.service(dc.id).quotas_service()
if module.params['name']:
quotas = [
e for e in quotas_service.list()
if fnmatch.fnmatch(e.name, module.params['name'])
]
else:
quotas = quotas_service.list()
result = dict(
ovirt_quotas=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in quotas
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,140 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_scheduling_policy_facts
short_description: Retrieve information about one or more oVirt scheduling policies
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_scheduling_policy_info) instead.
description:
- "Retrieve information about one or more oVirt scheduling policies."
notes:
- "This module returns a variable C(ovirt_scheduling_policies),
which contains a list of scheduling policies. You need to register the result with
the I(register) keyword to use it."
options:
id:
description:
- "ID of the scheduling policy."
name:
description:
- "Name of the scheduling policy, can be used as glob expression."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all scheduling policies with name InClusterUpgrade
ovirt_scheduling_policy_info:
name: InClusterUpgrade
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_scheduling_policies }}"
'''
RETURN = '''
ovirt_scheduling_policies:
description: "List of dictionaries describing the scheduling policies.
Scheduling policies attributes are mapped to dictionary keys,
all scheduling policies attributes can be found at following
url: https://ovirt.example.com/ovirt-engine/api/model#types/scheduling_policy."
returned: On success.
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
id=dict(default=None),
name=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_scheduling_policy_facts', 'community.general.ovirt_scheduling_policy_facts')
if is_old_facts:
module.deprecate("The 'ovirt_scheduling_policy_facts' module has been renamed to 'ovirt_scheduling_policy_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
system_service = connection.system_service()
sched_policies_service = system_service.scheduling_policies_service()
if module.params['name']:
sched_policies = [
e for e in sched_policies_service.list()
if fnmatch.fnmatch(e.name, module.params['name'])
]
elif module.params['id']:
sched_policies = [
sched_policies_service.service(module.params['id']).get()
]
else:
sched_policies = sched_policies_service.list()
result = dict(
ovirt_scheduling_policies=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in sched_policies
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,137 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_snapshot_facts
short_description: Retrieve information about one or more oVirt/RHV virtual machine snapshots
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_snapshot_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV virtual machine snapshots."
notes:
- "This module returns a variable C(ovirt_snapshots), which
contains a list of snapshots. You need to register the result with
the I(register) keyword to use it."
options:
vm:
description:
- "Name of the VM with snapshot."
required: true
description:
description:
- "Description of the snapshot, can be used as glob expression."
snapshot_id:
description:
- "Id of the snapshot we want to retrieve information about."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all snapshots which description start with update for VM named centos7
ovirt_snapshot_info:
vm: centos7
description: update*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_snapshots }}"
'''
RETURN = '''
ovirt_snapshots:
description: "List of dictionaries describing the snapshot. Snapshot attributes are mapped to dictionary keys,
all snapshot attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/snapshot."
returned: On success.
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
search_by_name,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
vm=dict(required=True),
description=dict(default=None),
snapshot_id=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_snapshot_facts', 'community.general.ovirt_snapshot_facts')
if is_old_facts:
module.deprecate("The 'ovirt_snapshot_facts' module has been renamed to 'ovirt_snapshot_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
vms_service = connection.system_service().vms_service()
vm_name = module.params['vm']
vm = search_by_name(vms_service, vm_name)
if vm is None:
raise Exception("VM '%s' was not found." % vm_name)
snapshots_service = vms_service.service(vm.id).snapshots_service()
if module.params['description']:
snapshots = [
e for e in snapshots_service.list()
if fnmatch.fnmatch(e.description, module.params['description'])
]
elif module.params['snapshot_id']:
snapshots = [
snapshots_service.snapshot_service(module.params['snapshot_id']).get()
]
else:
snapshots = snapshots_service.list()
result = dict(
ovirt_snapshots=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in snapshots
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,126 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_storage_domain_facts
short_description: Retrieve information about one or more oVirt/RHV storage domains
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_storage_domain_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV storage domains."
notes:
- "This module returns a variable C(ovirt_storage_domains), which
contains a list of storage domains. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search storage domain X from datacenter Y use following pattern:
name=X and datacenter=Y"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: >
Gather information about all storage domains which names
start with data and belong to data center west
ovirt_storage_domain_info:
pattern: name=data* and datacenter=west
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_storage_domains }}"
'''
RETURN = '''
ovirt_storage_domains:
description: "List of dictionaries describing the storage domains. Storage_domain attributes are mapped to dictionary keys,
all storage domains attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/storage_domain."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_storage_domain_facts', 'community.general.ovirt_storage_domain_facts')
if is_old_facts:
module.deprecate("The 'ovirt_storage_domain_facts' module has been renamed to 'ovirt_storage_domain_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
storage_domains_service = connection.system_service().storage_domains_service()
storage_domains = storage_domains_service.list(search=module.params['pattern'])
result = dict(
ovirt_storage_domains=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in storage_domains
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,142 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_storage_template_facts
short_description: Retrieve information about one or more oVirt/RHV templates relate to a storage domain.
author: "Maor Lipchuk (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_storage_template_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV templates relate to a storage domain."
notes:
- "This module returns a variable C(ovirt_storage_templates), which
contains a list of templates. You need to register the result with
the I(register) keyword to use it."
options:
unregistered:
description:
- "Flag which indicates whether to get unregistered templates which contain one or more
disks which reside on a storage domain or diskless templates."
type: bool
default: false
max:
description:
- "Sets the maximum number of templates to return. If not specified all the templates are returned."
storage_domain:
description:
- "The storage domain name where the templates should be listed."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all templates which relate to a storage domain and are unregistered
ovirt_storage_template_info:
unregistered: yes
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_storage_templates }}"
'''
RETURN = '''
ovirt_storage_templates:
description: "List of dictionaries describing the Templates. Template attributes are mapped to dictionary keys,
all Templates attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/template."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
get_id_by_name
)
def main():
argument_spec = ovirt_info_full_argument_spec(
storage_domain=dict(default=None),
max=dict(default=None, type='int'),
unregistered=dict(default=False, type='bool'),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_storage_template_facts', 'community.general.ovirt_storage_template_facts')
if is_old_facts:
module.deprecate("The 'ovirt_storage_template_facts' module has been renamed to 'ovirt_storage_template_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
storage_domains_service = connection.system_service().storage_domains_service()
sd_id = get_id_by_name(storage_domains_service, module.params['storage_domain'])
storage_domain_service = storage_domains_service.storage_domain_service(sd_id)
templates_service = storage_domain_service.templates_service()
# Find the unregistered Template we want to register:
if module.params.get('unregistered'):
templates = templates_service.list(unregistered=True)
else:
templates = templates_service.list(max=module.params['max'])
result = dict(
ovirt_storage_templates=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in templates
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,142 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_storage_vm_facts
short_description: Retrieve information about one or more oVirt/RHV virtual machines relate to a storage domain.
author: "Maor Lipchuk (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_storage_vm_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV virtual machines relate to a storage domain."
notes:
- "This module returns a variable C(ovirt_storage_vms), which
contains a list of virtual machines. You need to register the result with
the I(register) keyword to use it."
options:
unregistered:
description:
- "Flag which indicates whether to get unregistered virtual machines which contain one or more
disks which reside on a storage domain or diskless virtual machines."
type: bool
default: false
max:
description:
- "Sets the maximum number of virtual machines to return. If not specified all the virtual machines are returned."
storage_domain:
description:
- "The storage domain name where the virtual machines should be listed."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all VMs which relate to a storage domain and are unregistered
ovirt_vms_info:
unregistered: yes
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_storage_vms }}"
'''
RETURN = '''
ovirt_storage_vms:
description: "List of dictionaries describing the VMs. VM attributes are mapped to dictionary keys,
all VMs attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
get_id_by_name
)
def main():
argument_spec = ovirt_info_full_argument_spec(
storage_domain=dict(default=None),
max=dict(default=None, type='int'),
unregistered=dict(default=False, type='bool'),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_storage_vm_facts', 'community.general.ovirt_storage_vm_facts')
if is_old_facts:
module.deprecate("The 'ovirt_storage_vm_facts' module has been renamed to 'ovirt_storage_vm_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
storage_domains_service = connection.system_service().storage_domains_service()
sd_id = get_id_by_name(storage_domains_service, module.params['storage_domain'])
storage_domain_service = storage_domains_service.storage_domain_service(sd_id)
vms_service = storage_domain_service.vms_service()
# Find the unregistered VM we want to register:
if module.params.get('unregistered'):
vms = vms_service.list(unregistered=True)
else:
vms = vms_service.list()
result = dict(
ovirt_storage_vms=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in vms
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,172 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_tag_facts
short_description: Retrieve information about one or more oVirt/RHV tags
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_tag_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV tags."
notes:
- "This module returns a variable C(ovirt_tags), which
contains a list of tags. You need to register the result with
the I(register) keyword to use it."
options:
name:
description:
- "Name of the tag which should be listed."
vm:
description:
- "Name of the VM, which tags should be listed."
host:
description:
- "Name of the host, which tags should be listed."
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all tags, which names start with tag
ovirt_tag_info:
name: tag*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_tags }}"
- name: Gather information about all tags, which are assigned to VM postgres
ovirt_tag_info:
vm: postgres
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_tags }}"
- name: Gather information about all tags, which are assigned to host west
ovirt_tag_info:
host: west
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_tags }}"
'''
RETURN = '''
ovirt_tags:
description: "List of dictionaries describing the tags. Tags attributes are mapped to dictionary keys,
all tags attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/tag."
returned: On success.
type: list
'''
import fnmatch
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
search_by_name,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
name=dict(default=None),
host=dict(default=None),
vm=dict(default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_tag_facts', 'community.general.ovirt_tag_facts')
if is_old_facts:
module.deprecate("The 'ovirt_tag_facts' module has been renamed to 'ovirt_tag_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
tags_service = connection.system_service().tags_service()
tags = []
all_tags = tags_service.list()
if module.params['name']:
tags.extend([
t for t in all_tags
if fnmatch.fnmatch(t.name, module.params['name'])
])
if module.params['host']:
hosts_service = connection.system_service().hosts_service()
host = search_by_name(hosts_service, module.params['host'])
if host is None:
raise Exception("Host '%s' was not found." % module.params['host'])
tags.extend(hosts_service.host_service(host.id).tags_service().list())
if module.params['vm']:
vms_service = connection.system_service().vms_service()
vm = search_by_name(vms_service, module.params['vm'])
if vm is None:
raise Exception("Vm '%s' was not found." % module.params['vm'])
tags.extend(vms_service.vm_service(vm.id).tags_service().list())
if not (module.params['vm'] or module.params['host'] or module.params['name']):
tags = all_tags
result = dict(
ovirt_tags=[
get_dict_of_struct(
struct=t,
connection=connection,
fetch_nested=module.params['fetch_nested'],
attributes=module.params['nested_attributes'],
) for t in tags
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,124 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_template_facts
short_description: Retrieve information about one or more oVirt/RHV templates
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_template_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV templates."
notes:
- "This module returns a variable C(ovirt_templates), which
contains a list of templates. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search template X from datacenter Y use following pattern:
name=X and datacenter=Y"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all templates which names start with centos and belongs to data center west
ovirt_template_info:
pattern: name=centos* and datacenter=west
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_templates }}"
'''
RETURN = '''
ovirt_templates:
description: "List of dictionaries describing the templates. Template attributes are mapped to dictionary keys,
all templates attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/template."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_template_facts', 'community.general.ovirt_template_facts')
if is_old_facts:
module.deprecate("The 'ovirt_template_facts' module has been renamed to 'ovirt_template_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
templates_service = connection.system_service().templates_service()
templates = templates_service.list(search=module.params['pattern'])
result = dict(
ovirt_templates=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in templates
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

View File

@@ -1,123 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: ovirt_user_facts
short_description: Retrieve information about one or more oVirt/RHV users
author: "Ondra Machacek (@machacekondra)"
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: When migrating to collection we decided to use only _info modules.
alternative: Use M(ovirt.ovirt.ovirt_user_info) instead.
description:
- "Retrieve information about one or more oVirt/RHV users."
notes:
- "This module returns a variable C(ovirt_users), which
contains a list of users. You need to register the result with
the I(register) keyword to use it."
options:
pattern:
description:
- "Search term which is accepted by oVirt/RHV search backend."
- "For example to search user X use following pattern: name=X"
extends_documentation_fragment:
- community.general.ovirt_facts
'''
EXAMPLES = '''
# Examples don't contain auth parameter for simplicity,
# look at ovirt_auth module to see how to reuse authentication:
- name: Gather information about all users which first names start with john
ovirt_user_info:
pattern: name=john*
register: result
- name: Print gathered information
ansible.builtin.debug:
msg: "{{ result.ovirt_users }}"
'''
RETURN = '''
ovirt_users:
description: "List of dictionaries describing the users. User attributes are mapped to dictionary keys,
all users attributes can be found at following url: http://ovirt.github.io/ovirt-engine-api-model/master/#types/user."
returned: On success.
type: list
'''
import traceback
from ansible.module_utils.common.removed import removed_module
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils._ovirt import (
check_sdk,
create_connection,
get_dict_of_struct,
ovirt_info_full_argument_spec,
)
def main():
argument_spec = ovirt_info_full_argument_spec(
pattern=dict(default='', required=False),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name in ('ovirt_user_facts', 'community.general.ovirt_user_facts')
if is_old_facts:
module.deprecate("The 'ovirt_user_facts' module has been renamed to 'ovirt_user_info', "
"and the renamed one no longer returns ansible_facts",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
check_sdk(module)
try:
auth = module.params.pop('auth')
connection = create_connection(auth)
users_service = connection.system_service().users_service()
users = users_service.list(search=module.params['pattern'])
result = dict(
ovirt_users=[
get_dict_of_struct(
struct=c,
connection=connection,
fetch_nested=module.params.get('fetch_nested'),
attributes=module.params.get('nested_attributes'),
) for c in users
],
)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=result)
else:
module.exit_json(changed=False, **result)
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
finally:
connection.close(logout=auth.get('token') is None)
if __name__ == '__main__':
main()

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