Compare commits

..

3 Commits
3.0.2 ... 2.0.0

Author SHA1 Message Date
Felix Fontein
dc4222df0d Fix links. 2021-01-28 13:52:13 +01:00
Felix Fontein
b9a89d6d0f Release 2.0.0. 2021-01-28 13:43:42 +01:00
Felix Fontein
f48913d91b Add release summary. 2021-01-28 13:42:22 +01:00
682 changed files with 16851 additions and 24135 deletions

View File

@@ -13,25 +13,13 @@ pr:
- stable-*
schedules:
- cron: 0 8 * * *
displayName: Nightly (main)
- cron: 0 9 * * *
displayName: Nightly
always: true
branches:
include:
- main
- 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
- stable-*
variables:
- name: checkoutPath
@@ -48,7 +36,7 @@ variables:
resources:
containers:
- container: default
image: quay.io/ansible/azure-pipelines-test-container:1.9.0
image: quay.io/ansible/azure-pipelines-test-container:1.7.1
pool: Standard
@@ -68,19 +56,6 @@ 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: []
@@ -124,22 +99,6 @@ 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: []
@@ -180,47 +139,6 @@ stages:
- template: templates/matrix.yml
parameters:
testFormat: devel/{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
- 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
- stage: Remote_2_10
displayName: Remote 2.10
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.10/{0}
targets:
- name: OS X 10.11
test: osx/10.11
@@ -232,6 +150,26 @@ stages:
test: rhel/7.8
- name: RHEL 8.2
test: rhel/8.2
- name: FreeBSD 11.4
test: freebsd/11.4
- name: FreeBSD 12.2
test: freebsd/12.2
groups:
- 1
- 2
- 3
- stage: Remote_2_10
displayName: Remote 2.10
dependsOn: []
jobs:
- template: templates/matrix.yml
parameters:
testFormat: 2.10/{0}
targets:
- name: macOS 11.1
test: macos/11.1
- name: RHEL 8.2
test: rhel/8.2
- name: FreeBSD 12.1
test: freebsd/12.1
groups:
@@ -268,14 +206,16 @@ stages:
test: centos7
- name: CentOS 8
test: centos8
- name: Fedora 33
test: fedora33
- name: Fedora 34
test: fedora34
- name: Fedora 31
test: fedora31
- name: Fedora 32
test: fedora32
- name: openSUSE 15 py2
test: opensuse15py2
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 16.04
test: ubuntu1604
- name: Ubuntu 18.04
test: ubuntu1804
- name: Ubuntu 20.04
@@ -284,25 +224,6 @@ 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: []
@@ -317,8 +238,6 @@ stages:
test: fedora32
- name: openSUSE 15 py3
test: opensuse15
- name: Ubuntu 16.04
test: ubuntu1604
groups:
- 2
- 3
@@ -349,16 +268,6 @@ 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
@@ -388,22 +297,17 @@ 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://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh
curl --silent --show-error https://codecov.io/bash > codecov.sh
for file in "${output_path}"/reports/coverage*.xml; do
name="${file}"

100
.github/BOTMETA.yml vendored
View File

@@ -1,7 +1,5 @@
automerge: true
files:
plugins/:
supershipit: aminvakil russoz
changelogs/fragments/:
support: community
$actions:
@@ -17,10 +15,6 @@ files:
labels: become
$callbacks/:
labels: callbacks
$callbacks/loganalytics.py:
maintainers: zhcli
$callbacks/logstash.py:
maintainers: ujenmr
$callbacks/say.py:
notify: chris-short
maintainers: $team_macos
@@ -57,24 +51,14 @@ 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
@@ -88,10 +72,6 @@ files:
maintainers: $team_linode
labels: cloud linode
keywords: linode dynamic inventory script
$inventories/lxd.py:
maintainers: conloos
$inventories/proxmox.py:
maintainers: $team_virt ilijamt
$inventories/scaleway.py:
maintainers: $team_scaleway
labels: cloud scaleway
@@ -157,6 +137,7 @@ 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
@@ -192,14 +173,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
@@ -227,7 +208,7 @@ files:
$modules/cloud/misc/:
ignore: ryansb
$modules/cloud/misc/terraform.py:
maintainers: m-yosefpor rainerleber
maintainers: m-yosefpor
$modules/cloud/misc/xenserver_facts.py:
maintainers: caphrim007 cheese
labels: xenserver_facts
@@ -311,10 +292,8 @@ files:
maintainers: bvitnik
$modules/clustering/consul/:
maintainers: $team_consul
ignore: colin-nolan
$modules/clustering/etcd3.py:
maintainers: evrardjp
ignore: vfauth
maintainers: evrardjp vfauth
$modules/clustering/nomad/:
maintainers: chris93111
$modules/clustering/pacemaker_cluster.py:
@@ -348,8 +327,6 @@ 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:
@@ -363,6 +340,8 @@ 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:
@@ -375,8 +354,6 @@ files:
maintainers: $team_keycloak
$modules/identity/keycloak/keycloak_group.py:
maintainers: adamgoossens
$modules/identity/keycloak/keycloak_realm.py:
maintainers: kris2kris
$modules/identity/onepassword_info.py:
maintainers: Rylon
$modules/identity/opendj/opendj_backendprop.py:
@@ -438,12 +415,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:
maintainers: mamercad
$modules/monitoring/statusio_maintenance.py:
maintainers: bhcopeland
$modules/monitoring/uptimerobot.py:
@@ -456,7 +429,7 @@ files:
$modules/net_tools/dnsmadeeasy.py:
maintainers: briceburg
$modules/net_tools/haproxy.py:
maintainers: ravibhure Normo
maintainers: ravibhure
$modules/net_tools/:
maintainers: nerzhul
$modules/net_tools/infinity/infinity.py:
@@ -469,6 +442,8 @@ 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:
@@ -564,10 +539,9 @@ files:
$modules/packaging/language/bundler.py:
maintainers: thoiberg
$modules/packaging/language/composer.py:
maintainers: dmtrs
ignore: resmo
maintainers: dmtrs resmo
$modules/packaging/language/cpanm.py:
maintainers: fcuny russoz
maintainers: fcuny
$modules/packaging/language/easy_install.py:
maintainers: mattupstate
$modules/packaging/language/gem.py:
@@ -712,10 +686,15 @@ files:
labels: zypper
ignore: dirtyharrycallahan robinro
$modules/packaging/os/zypper_repository.py:
maintainers: $team_suse matze
labels: zypper
maintainers: matze
$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
@@ -724,8 +703,6 @@ 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/:
@@ -735,6 +712,8 @@ 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:
@@ -744,7 +723,7 @@ files:
$modules/remote_management/oneview/oneview_fcoe_network.py:
maintainers: fgbulsoni
$modules/remote_management/redfish/:
maintainers: $team_redfish
maintainers: $team_redfish billdodd
ignore: jose-delarosa
$modules/remote_management/stacki/stacki_host.py:
maintainers: bsanders bbyhuy
@@ -767,8 +746,6 @@ 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/:
@@ -783,6 +760,12 @@ 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/:
@@ -804,6 +787,9 @@ 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
@@ -843,7 +829,7 @@ files:
$modules/system/iptables_state.py:
maintainers: quidame
$modules/system/java_cert.py:
maintainers: haad absynth76
maintainers: haad
$modules/system/java_keystore.py:
maintainers: Mogztter
$modules/system/kernel_blacklist.py:
@@ -928,12 +914,16 @@ files:
maintainers: ahtik ovcharenko pyykkis
labels: ufw
$modules/system/vdo.py:
maintainers: rhawalsh
maintainers: bgurney-rh
$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:
@@ -1009,27 +999,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: sgargan
team_consul: colin-nolan 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 metanovii
team_gitlab: Lunik Shaps dj-wasabi marwatk waheedi zanssa scodeman
team_hpux: bcoca davx8342
team_huawei: QijunPan TommyLike edisonxiang freesky-edward hwDCN niuzhenguo xuxiaowei0512 yanzhangi zengchen1024 zhongjun2
team_ipa: Akasurde Nosmoht fxfitz justchris1
team_ipa: Akasurde Nosmoht fxfitz
team_jboss: Wolfant jairojunior wbrefvem
team_keycloak: eikef ndclt
team_linode: InTheCloudDan decentral1se displague rmcintosh Charliekenney23 LBGarber
team_linode: InTheCloudDan decentral1se displague rmcintosh
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 nilsding
team_opennebula: ilicmilan meerkampdvv rsmontero xorel
team_oracle: manojmeda mross22 nalsaber
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
team_redfish: mraineri tomasg2012 xmadsen renxulei
team_redfish: billdodd mraineri tomasg2012
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 sealor
team_virt: joshainglis karmab tleguern Thulium-Drake Ajpantuso
team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom
team_virt: joshainglis karmab Aversiste Thulium-Drake

View File

@@ -1,135 +0,0 @@
---
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
...

View File

@@ -1,27 +0,0 @@
---
# 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

@@ -1,111 +0,0 @@
---
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

@@ -1,69 +0,0 @@
---
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
...

View File

@@ -4,493 +4,435 @@ Community General Release Notes
.. contents:: Topics
This changelog describes changes after version 2.0.0.
This changelog describes changes after version 1.0.0.
v3.0.2
v2.0.0
======
Release Summary
---------------
Bugfix release for the first Ansible 4.0.0 release candidate.
This is release 2.0.0 of ``community.general``, released on 2021-01-28.
Bugfixes
--------
Major Changes
-------------
- stackpath_compute inventory script - fix broken validation checks for client ID and client secret (https://github.com/ansible-collections/community.general/pull/2448).
- zfs - certain ZFS properties, especially sizes, would lead to a task being falsely marked as "changed" even when no actual change was made (https://github.com/ansible-collections/community.general/issues/975, https://github.com/ansible-collections/community.general/pull/2454).
v3.0.1
======
Release Summary
---------------
Bugfix release for the next Ansible 4.0.0 beta.
Bugfixes
--------
- composer - use ``no-interaction`` option when discovering available options to avoid an issue where composer hangs (https://github.com/ansible-collections/community.general/pull/2348).
- influxdb_retention_policy - fix bug where ``INF`` duration values failed parsing (https://github.com/ansible-collections/community.general/pull/2385).
- inventory and vault scripts - change file permissions to make vendored inventory and vault scripts exectuable (https://github.com/ansible-collections/community.general/pull/2337).
- linode_v4 - changed the error message to point to the correct bugtracker URL (https://github.com/ansible-collections/community.general/pull/2430).
- lvol - fixed rounding errors (https://github.com/ansible-collections/community.general/issues/2370).
- lvol - fixed size unit capitalization to match units used between different tools for comparison (https://github.com/ansible-collections/community.general/issues/2360).
- nmcli - compare MAC addresses case insensitively to fix idempotency issue (https://github.com/ansible-collections/community.general/issues/2409).
- nmcli - if type is ``bridge-slave`` add ``slave-type bridge`` to ``nmcli`` command (https://github.com/ansible-collections/community.general/issues/2408).
- one_vm - Allow missing NIC keys (https://github.com/ansible-collections/community.general/pull/2435).
- puppet - replace ``console` with ``stdout`` in ``logdest`` option when ``all`` has been chosen (https://github.com/ansible-collections/community.general/issues/1190).
- svr4pkg - convert string to a bytes-like object to avoid ``TypeError`` with Python 3 (https://github.com/ansible-collections/community.general/issues/2373).
v3.0.0
======
Release Summary
---------------
This is release 3.0.0 of ``community.general``, released on 2021-04-26.
- The community.general collection no longer depends on the ansible.netcommon collection (https://github.com/ansible-collections/community.general/pull/1561).
- The community.general collection no longer depends on the ansible.posix collection (https://github.com/ansible-collections/community.general/pull/1157).
Minor Changes
-------------
- apache2_mod_proxy - refactored/cleaned-up part of the code (https://github.com/ansible-collections/community.general/pull/2142).
- archive - refactored some reused code out into a couple of functions (https://github.com/ansible-collections/community.general/pull/2061).
- atomic_container - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144).
- atomic_host - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144).
- atomic_image - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144).
- beadm - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- bitbucket_pipeline_variable - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157).
- bundler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- clc_* modules - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1771).
- consul - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- consul_acl - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- consul_io inventory script - conf options - allow custom configuration options via env variables (https://github.com/ansible-collections/community.general/pull/620).
- consul_session - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- cpanm - honor and install specified version when running in ``new`` mode; that feature is not available in ``compatibility`` mode (https://github.com/ansible-collections/community.general/issues/208).
- cpanm - rewritten using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2218).
- csv module utils - new module_utils for shared functions between ``from_csv`` filter and ``read_csv`` module (https://github.com/ansible-collections/community.general/pull/2037).
- datadog_monitor - add missing monitor types ``query alert``, ``trace-analytics alert``, ``rum alert`` (https://github.com/ansible-collections/community.general/pull/1723).
- datadog_monitor - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- dnsimple - add CAA records to the whitelist of valid record types (https://github.com/ansible-collections/community.general/pull/1814).
- dnsimple - elements of list parameters ``record_ids`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- gitlab_deploy_key - when the given key title already exists but has a different public key, the public key will now be updated to given value (https://github.com/ansible-collections/community.general/pull/1661).
- gitlab_runner - elements of list parameters ``tag_list`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- grove - the option ``message`` has been renamed to ``message_content``. The old name ``message`` is kept as an alias and will be removed for community.general 4.0.0. This was done because ``message`` is used internally by Ansible (https://github.com/ansible-collections/community.general/pull/1929).
- heroku_collaborator - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- hiera lookup - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- homebrew_tap - add support to specify search path for ``brew`` executable (https://github.com/ansible-collections/community.general/issues/1702).
- ipa_config - add new options ``ipaconfigstring``, ``ipadefaultprimarygroup``, ``ipagroupsearchfields``, ``ipahomesrootdir``, ``ipabrkauthzdata``, ``ipamaxusernamelength``, ``ipapwdexpadvnotify``, ``ipasearchrecordslimit``, ``ipasearchtimelimit``, ``ipauserauthtype``, and ``ipausersearchfields`` (https://github.com/ansible-collections/community.general/pull/2116).
- ipa_sudorule - add support for setting sudo runasuser (https://github.com/ansible-collections/community.general/pull/2031).
- ipa_user - fix ``userauthtype`` option to take in list of strings for the multi-select field instead of single string (https://github.com/ansible-collections/community.general/pull/2174).
- ipwcli_dns - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- java_cert - change ``state: present`` to check certificates by hash, not just alias name (https://github.com/ansible/ansible/issues/43249).
- java_keystore - add options ``certificate_path`` and ``private_key_path``, mutually exclusive with ``certificate`` and ``private_key`` respectively, and targetting files on remote hosts rather than their contents on the controller. (https://github.com/ansible-collections/community.general/issues/1669).
- jenkins_job - add a ``validate_certs`` parameter that allows disabling TLS/SSL certificate validation (https://github.com/ansible-collections/community.general/issues/255).
- jira - added ``attach`` operation, which allows a user to attach a file to an issue (https://github.com/ansible-collections/community.general/pull/2192).
- jira - added parameter ``account_id`` for compatibility with recent versions of JIRA (https://github.com/ansible-collections/community.general/issues/818, https://github.com/ansible-collections/community.general/pull/1978).
- jira - revamped the module as a class using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2208).
- keycloak_* modules - allow the keycloak modules to use a token for the authentication, the modules can take either a token or the credentials (https://github.com/ansible-collections/community.general/pull/2250).
- keycloak_client - elements of list parameters ``default_roles``, ``redirect_uris``, ``web_origins`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- kibana_plugin - add parameter for passing ``--allow-root`` flag to kibana and kibana-plugin commands (https://github.com/ansible-collections/community.general/pull/2014).
- known_hosts module utils - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- librato_annotation - elements of list parameters ``links`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- linode_v4 - add support for ``private_ip`` option (https://github.com/ansible-collections/community.general/pull/2249).
- linode_v4 - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- lvol - added proper support for ``+-`` options when extending or reducing the logical volume (https://github.com/ansible-collections/community.general/issues/1988).
- lxd_container - ``client_key`` and ``client_cert`` are now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741).
- lxd_container - elements of list parameter ``profiles`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- lxd_profile - ``client_key`` and ``client_cert`` are now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741).
- lxd_profile - added ``merge_profile`` parameter to merge configurations from the play to an existing profile (https://github.com/ansible-collections/community.general/pull/1813).
- mail - elements of list parameters ``to``, ``cc``, ``bcc``, ``attach``, ``headers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- manageiq_alert_profiles - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- manageiq_policies - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- manageiq_tags - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- manageiq_tags and manageiq_policies - added new parameter ``resource_id``. This parameter can be used instead of parameter ``resource_name`` (https://github.com/ansible-collections/community.general/pull/719).
- module_helper module utils - ``CmdMixin.run_command()`` now accepts ``dict`` command arguments, providing the parameter and its value (https://github.com/ansible-collections/community.general/pull/1867).
- module_helper module utils - added management of facts and adhoc setting of the initial value for variables (https://github.com/ansible-collections/community.general/pull/2188).
- module_helper module utils - added mechanism to manage variables, providing automatic output of variables, change status and diff information (https://github.com/ansible-collections/community.general/pull/2162).
- na_ontap_gather_facts - elements of list parameters ``gather_subset`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- nexmo - elements of list parameters ``dest`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- nictagadm - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- nmcli - add ability to connect to a Wifi network and also to attach it to a master (bond) (https://github.com/ansible-collections/community.general/pull/2220).
- nmcli - do not set IP configuration on slave connection (https://github.com/ansible-collections/community.general/pull/2223).
- nmcli - don't restrict the ability to manually set the MAC address to the bridge (https://github.com/ansible-collections/community.general/pull/2224).
- npm - add ``no_bin_links`` option (https://github.com/ansible-collections/community.general/issues/2128).
- nsupdate - elements of list parameters ``value`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- oci_vcn - ``api_user_key_file`` is now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741).
- omapi_host - elements of list parameters ``statements`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- one_host - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- one_image_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- one_vm - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- oneandone_firewall_policy - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- oneandone_load_balancer - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- oneandone_monitoring_policy - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- oneandone_private_network - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- oneandone_server - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- onepassword_info - elements of list parameters ``search_terms`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- oneview_datacenter_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- oneview_enclosure_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- oneview_ethernet_network_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- oneview_network_set_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- ovh_ip_failover - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157).
- packet_device - elements of list parameters ``device_ids``, ``hostnames`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- pagerduty - elements of list parameters ``service`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- pids - new options ``pattern`` and `ignore_case`` for retrieving PIDs of processes matching a supplied pattern (https://github.com/ansible-collections/community.general/pull/2280).
- plugins/module_utils/oracle/oci_utils.py - elements of list parameter ``key_by`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- profitbricks - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- profitbricks_volume - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- proxmox - added ``purge`` module parameter for use when deleting lxc's with HA options (https://github.com/ansible-collections/community.general/pull/2013).
- proxmox inventory plugin - added ``Constructable`` class to the inventory to provide options ``strict``, ``keyed_groups``, ``groups``, and ``compose`` (https://github.com/ansible-collections/community.general/pull/2180).
- proxmox inventory plugin - added ``proxmox_agent_interfaces`` fact describing network interfaces returned from a QEMU guest agent (https://github.com/ansible-collections/community.general/pull/2148).
- proxmox inventory plugin - added ``tags_parsed`` fact containing tags parsed as a list (https://github.com/ansible-collections/community.general/pull/1949).
- proxmox inventory plugin - allow to select whether ``ansible_host`` should be set for the proxmox nodes (https://github.com/ansible-collections/community.general/pull/2263).
- proxmox_kvm - added new module parameter ``tags`` for use with PVE 6+ (https://github.com/ansible-collections/community.general/pull/2000).
- proxmox_kvm module - actually implemented ``vmid`` and ``status`` return values. Updated documentation to reflect current situation (https://github.com/ansible-collections/community.general/issues/1410, https://github.com/ansible-collections/community.general/pull/1715).
- pubnub_blocks - elements of list parameters ``event_handlers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- rax - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006).
- rax_cdb_user - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006).
- rax_scaling_group - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006).
- read_csv - refactored read_csv module to use shared csv functions from csv module_utils (https://github.com/ansible-collections/community.general/pull/2037).
- redfish modules - explicitly setting lists' elements to ``str`` (https://github.com/ansible-collections/community.general/pull/1761).
- redfish_* modules, redfish_utils module utils - add support for Redfish session create, delete, and authenticate (https://github.com/ansible-collections/community.general/issues/1975).
- redfish_config - case insensitive search for situations where the hostname/FQDN case on iLO doesn't match variable's case (https://github.com/ansible-collections/community.general/pull/1744).
- redhat_subscription - elements of list parameters ``pool_ids``, ``addons`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- rhevm - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157).
- rocketchat - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- scaleway_compute - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- scaleway_lb - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970).
- sendgrid - elements of list parameters ``to_addresses``, ``cc``, ``bcc``, ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- sensu_check - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- sensu_client - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- sensu_handler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- sl_vm - elements of list parameters ``disks``, ``ssh_keys`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- slack - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- smartos_image_info - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- snmp_facts - added parameters ``timeout`` and ``retries`` to module (https://github.com/ansible-collections/community.general/issues/980).
- statusio_maintenance - elements of list parameters ``components``, ``containers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- svr4pkg - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- terraform - add ``plugin_paths`` parameter which allows disabling Terraform from performing plugin discovery and auto-download (https://github.com/ansible-collections/community.general/pull/2308).
- timezone - add Gentoo and Alpine Linux support (https://github.com/ansible-collections/community.general/issues/781).
- twilio - elements of list parameters ``to_numbers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- udm_dns_zone - elements of list parameters ``nameserver``, ``interfaces``, and ``mx`` are now validated (https://github.com/ansible-collections/community.general/pull/2268).
- vdo - add ``force`` option (https://github.com/ansible-collections/community.general/issues/2101).
- vmadm - elements of list parameters ``disks``, ``nics``, ``resolvers``, ``filesystems`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- webfaction_domain - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- webfaction_site - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885).
- xattr - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- xfconf - added option ``disable_facts`` to disable facts and its associated deprecation warning (https://github.com/ansible-collections/community.general/issues/1475).
- xfconf - changed implementation to use ``ModuleHelper`` new features (https://github.com/ansible-collections/community.general/pull/2188).
- xml - elements of list parameters ``add_children``, ``set_children`` are now validated (https://github.com/ansible-collections/community.general/pull/1795).
- yum_versionlock - Do the lock/unlock concurrently to speed up (https://github.com/ansible-collections/community.general/pull/1912).
- zfs_facts - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- zpool_facts - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160).
- A new filter ``lists_mergeby`` to merge two lists of dictionaries by an attribute.
For example:
.. code-block:: yaml
[{'n': 'n1', 'p1': 'A', 'p2': 'F'},
{'n': 'n2', 'p2': 'B'}] | community.general.lists_mergeby(
[{'n': 'n1', 'p1': 'C'},
{'n': 'n2', 'p2': 'D'},
{'n': 'n3', 'p3': 'E'}], 'n') | list
evaluates to
.. code-block:: yaml
[{'n': 'n1', 'p1': 'C', 'p2': 'F'},
{'n': 'n2', 'p2': 'D'},
{'n': 'n3', 'p3': 'E'}]
(https://github.com/ansible-collections/community.general/pull/604).
- Add new filter plugin ``dict_kv`` which returns a single key-value pair from two arguments. Useful for generating complex dictionaries without using loops. For example ``'value' | community.general.dict_kv('key'))`` evaluates to ``{'key': 'value'}`` (https://github.com/ansible-collections/community.general/pull/1264).
- The collection dependencies were adjusted so that ``community.kubernetes`` is required to be of version 1.0.0 or newer (https://github.com/ansible-collections/community.general/pull/774).
- The collection is now actively tested in CI with the latest Ansible 2.9 release.
- airbrake_deployment - add ``version`` param; clarified docs on ``revision`` param (https://github.com/ansible-collections/community.general/pull/583).
- apk - added ``no_cache`` option (https://github.com/ansible-collections/community.general/pull/548).
- archive - fix paramater types (https://github.com/ansible-collections/community.general/pull/1039).
- cloudflare_dns - add support for environment variable ``CLOUDFLARE_TOKEN`` (https://github.com/ansible-collections/community.general/pull/1238).
- consul - added support for tcp checks (https://github.com/ansible-collections/community.general/issues/1128).
- datadog - mark ``notification_message`` as ``no_log`` (https://github.com/ansible-collections/community.general/pull/1338).
- datadog_monitor - add ``include_tags`` option (https://github.com/ansible/ansible/issues/57441).
- dconf - update documentation and logic code refactor (https://github.com/ansible-collections/community.general/pull/1585).
- django_manage - renamed parameter ``app_path`` to ``project_path``, adding ``app_path`` and ``chdir`` as aliases (https://github.com/ansible-collections/community.general/issues/1044).
- facter - added option for ``arguments`` (https://github.com/ansible-collections/community.general/pull/768).
- firewalld - the module has been moved to the ``ansible.posix`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/623).
- git_config - added parameter and scope ``file`` allowing user to change parameters in a custom file (https://github.com/ansible-collections/community.general/issues/1021).
- gitlab_project - add parameter ``lfs_enabled`` to specify Git LFS (https://github.com/ansible-collections/community.general/issues/1506).
- gitlab_project - add support for merge_method on projects (https://github.com/ansible/ansible/pull/66813).
- gitlab_project_variable - add support for ``environment_scope`` on projects variables (https://github.com/ansible-collections/community.general/pull/1197).
- gitlab_runner - add ``owned`` option to allow non-admin use (https://github.com/ansible-collections/community.general/pull/1491).
- gitlab_runners inventory plugin - permit environment variable input for ``server_url``, ``api_token`` and ``filter`` options (https://github.com/ansible-collections/community.general/pull/611).
- haproxy - add options to dis/enable health and agent checks. When health and agent checks are enabled for a service, a disabled service will re-enable itself automatically. These options also change the state of the agent checks to match the requested state for the backend (https://github.com/ansible-collections/community.general/issues/684).
- homebrew_cask - Homebrew will be deprecating use of ``brew cask`` commands as of version 2.6.0, see https://brew.sh/2020/12/01/homebrew-2.6.0/. Added logic to stop using ``brew cask`` for brew version >= 2.6.0 (https://github.com/ansible-collections/community.general/pull/1481).
- homebrew_tap - provide error message to user when module fails (https://github.com/ansible-collections/community.general/issues/1411).
- influxdb_retention_policy - add shard group duration parameter ``shard_group_duration`` (https://github.com/ansible-collections/community.general/pull/1590).
- infoblox inventory script - use stderr for reporting errors, and allow use of environment for configuration (https://github.com/ansible-collections/community.general/pull/436).
- ini_file - module now can create an empty section (https://github.com/ansible-collections/community.general/issues/479).
- ipa_host - silence warning about non-secret ``random_password`` option not having ``no_log`` set (https://github.com/ansible-collections/community.general/pull/1339).
- ipa_sudorule - added option to use command groups inside sudo rules (https://github.com/ansible-collections/community.general/issues/1555).
- ipa_user - add ``userauthtype`` option (https://github.com/ansible-collections/community.general/pull/951).
- ipa_user - silence warning about non-secret ``krbpasswordexpiration`` and ``update_password`` options not having ``no_log`` set (https://github.com/ansible-collections/community.general/pull/1339).
- iptables_state - use FQCN when calling a module from action plugin (https://github.com/ansible-collections/community.general/pull/967).
- jc - new filter to convert the output of many shell commands and file-types to JSON. Uses the jc library at https://github.com/kellyjonbrazil/jc. For example, filtering the STDOUT output of ``uname -a`` via ``{{ result.stdout | community.general.jc('uname') }}``. Requires Python 3.6+ (https://github.com/ansible-collections/community.general/pull/750).
- jira - added the traceback output to ``fail_json()`` calls deriving from exceptions (https://github.com/ansible-collections/community.general/pull/1536).
- ldap modules - allow to configure referral chasing (https://github.com/ansible-collections/community.general/pull/1618).
- linode inventory plugin - add support for ``keyed_groups``, ``groups``, and ``compose`` options (https://github.com/ansible-collections/community.general/issues/1326).
- linode inventory plugin - add support for ``tags`` option to filter instances by tag (https://github.com/ansible-collections/community.general/issues/1549).
- linode_v4 - added support for Linode StackScript usage when creating instances (https://github.com/ansible-collections/community.general/issues/723).
- log_plays callback - use v2 methods (https://github.com/ansible-collections/community.general/pull/442).
- logstash callback - add ini config (https://github.com/ansible-collections/community.general/pull/610).
- logstash callback - improve logstash message structure, needs to be enabled with the ``format_version`` option (https://github.com/ansible-collections/community.general/pull/641).
- logstash callback - migrate to python3-logstash (https://github.com/ansible-collections/community.general/pull/641).
- lvol - fix idempotency issue when using lvol with ``%VG`` or ``%PVS`` size options and VG is fully allocated (https://github.com/ansible-collections/community.general/pull/229).
- lxd_container - added support of ``--target`` flag for cluster deployments (https://github.com/ansible-collections/community.general/issues/637).
- make - add ``jobs`` parameter to allow specification of number of simultaneous jobs for make to run (https://github.com/ansible-collections/community.general/pull/1550).
- maven_artifact - added ``client_cert`` and ``client_key`` parameters to the maven_artifact module (https://github.com/ansible-collections/community.general/issues/1123).
- module_helper - added ModuleHelper class and a couple of convenience tools for module developers (https://github.com/ansible-collections/community.general/pull/1322).
- module_helper module utils - multiple convenience features added (https://github.com/ansible-collections/community.general/pull/1480).
- nagios - add the ``acknowledge`` action (https://github.com/ansible-collections/community.general/pull/820).
- nagios - add the ``host`` and ``all`` values for the ``forced_check`` action (https://github.com/ansible-collections/community.general/pull/998).
- nagios - add the ``service_check`` action (https://github.com/ansible-collections/community.general/pull/820).
- nagios - rename the ``service_check`` action to ``forced_check`` since we now are able to check both a particular service, all services of a particular host and the host itself (https://github.com/ansible-collections/community.general/pull/998).
- nios modules - clean up module argument spec processing (https://github.com/ansible-collections/community.general/pull/1598).
- nios_network - no longer requires the ansible.netcommon collection (https://github.com/ansible-collections/community.general/pull/1561).
- nmcli - add ``ipv4.routes``, ``ipv4.route-metric`` and ``ipv4.never-default`` support (https://github.com/ansible-collections/community.general/pull/1260).
- nmcli - add ``zone`` parameter (https://github.com/ansible-collections/community.general/issues/949, https://github.com/ansible-collections/community.general/pull/1426).
- nmcli - add infiniband type support (https://github.com/ansible-collections/community.general/pull/1260).
- nmcli - refactor internal methods for simplicity and enhance reuse to support existing and future connection types (https://github.com/ansible-collections/community.general/pull/1113).
- nmcli - remove Python DBus and GTK Object library dependencies (https://github.com/ansible-collections/community.general/issues/1112).
- nmcli - the ``dns4``, ``dns4_search``, ``dns6``, and ``dns6_search`` arguments are retained internally as lists (https://github.com/ansible-collections/community.general/pull/1113).
- npm - add ``no-optional`` option (https://github.com/ansible-collections/community.general/issues/1421).
- odbc - added a parameter ``commit`` which allows users to disable the explicit commit after the execute call (https://github.com/ansible-collections/community.general/pull/1139).
- openbsd_pkg - added ``snapshot`` option (https://github.com/ansible-collections/community.general/pull/965).
- pacman - improve group expansion speed: query list of pacman groups once (https://github.com/ansible-collections/community.general/pull/349).
- pam_limits - add support for nice and priority limits (https://github.com/ansible/ansible/pull/47680).
- pam_limits - adds check mode (https://github.com/ansible-collections/community.general/issues/827).
- pam_limits - adds diff mode (https://github.com/ansible-collections/community.general/issues/828).
- parted - accept negative numbers in ``part_start`` and ``part_end``
- parted - add ``resize`` option to resize existing partitions (https://github.com/ansible-collections/community.general/pull/773).
- passwordstore lookup plugin - added ``umask`` option to set the desired file permisions on creation. This is done via the ``PASSWORD_STORE_UMASK`` environment variable (https://github.com/ansible-collections/community.general/pull/1156).
- pkgin - add support for installation of full versioned package names (https://github.com/ansible-collections/community.general/pull/1256).
- pkgng - added ``stdout`` and ``stderr`` attributes to the result (https://github.com/ansible-collections/community.general/pull/560).
- pkgng - added support for upgrading all packages using ``name: *, state: latest``, similar to other package providers (https://github.com/ansible-collections/community.general/pull/569).
- pkgng - present the ``ignore_osver`` option to pkg (https://github.com/ansible-collections/community.general/pull/1243).
- pkgutil - module can now accept a list of packages (https://github.com/ansible-collections/community.general/pull/799).
- pkgutil - module has a new option, ``force``, equivalent to the ``-f`` option to the `pkgutil <http://pkgutil.net/>`_ command (https://github.com/ansible-collections/community.general/pull/799).
- pkgutil - module now supports check mode (https://github.com/ansible-collections/community.general/pull/799).
- portage - add ``getbinpkgonly`` option, remove unnecessary note on internal portage behaviour (getbinpkg=yes), and remove the undocumented exclusiveness of the pkg options as portage makes no such restriction (https://github.com/ansible-collections/community.general/pull/1169).
- proxmox - add ``features`` option to LXC (https://github.com/ansible-collections/community.general/issues/816).
- proxmox - add new ``proxmox_default_behavior`` option (https://github.com/ansible-collections/community.general/pull/850).
- proxmox - add support for API tokens (https://github.com/ansible-collections/community.general/pull/1206).
- proxmox - extract common code and documentation (https://github.com/ansible-collections/community.general/pull/1331).
- proxmox - improve and extract more common documentation (https://github.com/ansible-collections/community.general/pull/1404).
- proxmox inventory plugin - add environment variable passthrough (https://github.com/ansible-collections/community.general/pull/1645).
- proxmox inventory plugin - ignore QEMU templates altogether instead of skipping the creation of the host in the inventory (https://github.com/ansible-collections/community.general/pull/1185).
- proxmox_kvm - add cloud-init support (new options: ``cicustom``, ``cipassword``, ``citype``, ``ciuser``, ``ipconfig``, ``nameservers``, ``searchdomains``, ``sshkeys``) (https://github.com/ansible-collections/community.general/pull/797).
- proxmox_kvm - add new ``proxmox_default_behavior`` option (https://github.com/ansible-collections/community.general/pull/850).
- proxmox_kvm - add support for API tokens (https://github.com/ansible-collections/community.general/pull/1206).
- proxmox_kvm - improve and extract more common documentation (https://github.com/ansible-collections/community.general/pull/1404).
- proxmox_kvm - improve code readability (https://github.com/ansible-collections/community.general/pull/934).
- proxmox_template - add support for API tokens (https://github.com/ansible-collections/community.general/pull/1206).
- proxmox_template - download proxmox applicance templates (pveam) (https://github.com/ansible-collections/community.general/pull/1046).
- proxmox_template - improve documentation (https://github.com/ansible-collections/community.general/pull/1404).
- pushover - add device parameter (https://github.com/ansible-collections/community.general/pull/802).
- redfish_command - add sub-command for ``EnableContinuousBootOverride`` and ``DisableBootOverride`` to allow setting BootSourceOverrideEnabled Redfish property (https://github.com/ansible-collections/community.general/issues/824).
- redfish_command - support same reset actions on Managers as on Systems (https://github.com/ansible-collections/community.general/issues/901).
- redis cache plugin - add redis sentinel functionality to cache plugin (https://github.com/ansible-collections/community.general/pull/1055).
- redis cache plugin - make the redis cache keyset name configurable (https://github.com/ansible-collections/community.general/pull/1036).
- rhn_register - added ``force`` parameter to allow forced registering (https://github.com/ansible-collections/community.general/issues/1454).
- rundeck_acl_policy - add check for rundeck_acl_policy name parameter (https://github.com/ansible-collections/community.general/pull/612).
- scaleway modules and inventory plugin - update regions and zones to add the new ones (https://github.com/ansible-collections/community.general/pull/1690).
- slack - add support for sending messages built with block kit (https://github.com/ansible-collections/community.general/issues/380).
- slack - add support for updating messages (https://github.com/ansible-collections/community.general/issues/304).
- splunk callback - add an option to allow not to validate certificate from HEC (https://github.com/ansible-collections/community.general/pull/596).
- splunk callback - new parameter ``include_milliseconds`` to add milliseconds to existing timestamp field (https://github.com/ansible-collections/community.general/pull/1462).
- telegram - now can call any methods in Telegram bot API. Previously this module was hardcoded to use "SendMessage" only. Usage of "SendMessage" API method was also librated, and now you can specify any arguments you need, for example, "disable_notificaton" (https://github.com/ansible-collections/community.general/pull/1642).
- terraform - add ``init_reconfigure`` option, which controls the ``-reconfigure`` flag (backend reconfiguration) (https://github.com/ansible-collections/community.general/pull/823).
- xfconf - add arrays support (https://github.com/ansible/ansible/issues/46308).
- xfconf - add support for ``double`` type (https://github.com/ansible-collections/community.general/pull/744).
- xfconf - add support for ``uint`` type (https://github.com/ansible-collections/community.general/pull/696).
- xfconf - removed unnecessary second execution of ``xfconf-query`` (https://github.com/ansible-collections/community.general/pull/1305).
- xml - fixed issue were changed was returned when removing non-existent xpath (https://github.com/ansible-collections/community.general/pull/1007).
- zypper_repository - proper failure when python-xml is missing (https://github.com/ansible-collections/community.general/pull/939).
Breaking Changes / Porting Guide
--------------------------------
- If you use Ansible 2.9 and these plugins or modules from this collection, community.general 3.0.0 results in errors when trying to use the DellEMC content by FQCN, like ``community.general.idrac_firmware``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``dellemc.openmanage.idrac_firmware`` for the previous example) and to make sure that you have ``dellemc.openmanage`` installed.
- If you use Ansible 2.9 and the Google cloud plugins or modules from this collection, community.general 2.0.0 results in errors when trying to use the Google cloud content by FQCN, like ``community.general.gce_img``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``community.google.gce_img`` for the previous example) and to make sure that you have ``community.google`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 4.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install the ``dellemc.openmanage`` collection if you are using any of these plugins or modules.
While ansible-base 2.10 or newer can use the redirects that community.general 3.0.0 adds, the collection they point to (such as dellemc.openmanage) must be installed for them to work.
- gitlab_deploy_key - if for an already existing key title a different public key was given as parameter nothing happened, now this changed so that the public key is updated to the new value (https://github.com/ansible-collections/community.general/pull/1661).
- java_keystore - instead of failing, now overwrites keystore if the alias (name) is changed. This was originally the intended behavior, but did not work due to a logic error. Make sure that your playbooks and roles do not depend on the old behavior of failing instead of overwriting (https://github.com/ansible-collections/community.general/issues/1671).
- java_keystore - instead of failing, now overwrites keystore if the passphrase is changed. Make sure that your playbooks and roles do not depend on the old behavior of failing instead of overwriting (https://github.com/ansible-collections/community.general/issues/1671).
- one_image - use pyone instead of python-oca (https://github.com/ansible-collections/community.general/pull/2032).
- utm_proxy_auth_profile - the ``frontend_cookie_secret`` return value now contains a placeholder string instead of the module's ``frontend_cookie_secret`` parameter (https://github.com/ansible-collections/community.general/pull/1736).
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install the ``community.google`` or ``google.cloud`` collections if you are using any of the Google cloud plugins or modules.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (such as community.google) must be installed for them to work.
- If you use Ansible 2.9 and the Kubevirt plugins or modules from this collection, community.general 2.0.0 results in errors when trying to use the Kubevirt content by FQCN, like ``community.general.kubevirt_vm``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``community.kubevirt.kubevirt_vm`` for the previous example) and to make sure that you have ``community.kubevirt`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install the ``community.kubevirt`` collection if you are using any of the Kubevirt plugins or modules.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (such as community.google) must be installed for them to work.
- If you use Ansible 2.9 and the ``docker`` plugins or modules from this collections, community.general 2.0.0 results in errors when trying to use the docker content by FQCN, like ``community.general.docker_container``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``community.docker.docker_container`` for the previous example) and to make sure that you have ``community.docker`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.docker`` if you are using any of the ``docker`` plugins or modules.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (community.docker) must be installed for them to work.
- If you use Ansible 2.9 and the ``hashi_vault`` lookup plugin from this collections, community.general 2.0.0 results in errors when trying to use the Hashi Vault content by FQCN, like ``community.general.hashi_vault``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your inventories, variable files, playbooks and roles manually to use the new FQCN (``community.hashi_vault.hashi_vault``) and to make sure that you have ``community.hashi_vault`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.hashi_vault`` if you are using the ``hashi_vault`` plugin.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (community.hashi_vault) must be installed for them to work.
- If you use Ansible 2.9 and the ``hetzner`` modules from this collections, community.general 2.0.0 results in errors when trying to use the hetzner content by FQCN, like ``community.general.hetzner_firewall``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``community.hrobot.firewall`` for the previous example) and to make sure that you have ``community.hrobot`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.hrobot`` if you are using any of the ``hetzner`` modules.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (community.hrobot) must be installed for them to work.
- If you use Ansible 2.9 and the ``oc`` connection plugin from this collections, community.general 2.0.0 results in errors when trying to use the oc content by FQCN, like ``community.general.oc``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your inventories, variable files, playbooks and roles manually to use the new FQCN (``community.okd.oc``) and to make sure that you have ``community.okd`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.okd`` if you are using the ``oc`` plugin.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (community.okd) must be installed for them to work.
- If you use Ansible 2.9 and the ``postgresql`` modules from this collections, community.general 2.0.0 results in errors when trying to use the postgresql content by FQCN, like ``community.general.postgresql_info``.
Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``community.postgresql.postgresql_info`` for the previous example) and to make sure that you have ``community.postgresql`` installed.
If you use ansible-base 2.10 or newer and did not install Ansible 3.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install ``community.postgresql`` if you are using any of the ``postgresql`` modules.
While ansible-base 2.10 or newer can use the redirects that community.general 2.0.0 adds, the collection they point to (community.postgresql) must be installed for them to work.
- The Google cloud inventory script ``gce.py`` has been migrated to the ``community.google`` collection. Install the ``community.google`` collection in order to continue using it.
- archive - remove path folder itself when ``remove`` paramater is true (https://github.com/ansible-collections/community.general/issues/1041).
- log_plays callback - add missing information to the logs generated by the callback plugin. This changes the log message format (https://github.com/ansible-collections/community.general/pull/442).
- passwordstore lookup plugin - now parsing a password store entry as YAML if possible, skipping the first line (which by convention only contains the password and nothing else). If it cannot be parsed as YAML, the old ``key: value`` parser will be used to process the entry. Can break backwards compatibility if YAML formatted code was parsed in a non-YAML interpreted way, e.g. ``foo: [bar, baz]`` will become a list with two elements in the new version, but a string ``'[bar, baz]'`` in the old (https://github.com/ansible-collections/community.general/issues/1673).
- pkgng - passing ``name: *`` with ``state: absent`` will no longer remove every installed package from the system. It is now a noop. (https://github.com/ansible-collections/community.general/pull/569).
- pkgng - passing ``name: *`` with ``state: latest`` or ``state: present`` will no longer install every package from the configured package repositories. Instead, ``name: *, state: latest`` will upgrade all already-installed packages, and ``name: *, state: present`` is a noop. (https://github.com/ansible-collections/community.general/pull/569).
- proxmox_kvm - recognize ``force=yes`` in conjunction with ``state=absent`` to forcibly remove a running VM (https://github.com/ansible-collections/community.general/pull/849).
Deprecated Features
-------------------
- apt_rpm - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- composer - deprecated invalid parameter aliases ``working-dir``, ``global-command``, ``prefer-source``, ``prefer-dist``, ``no-dev``, ``no-scripts``, ``no-plugins``, ``optimize-autoloader``, ``classmap-authoritative``, ``apcu-autoloader``, ``ignore-platform-reqs``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- cpanm - parameter ``system_lib`` deprecated in favor of using ``become`` (https://github.com/ansible-collections/community.general/pull/2218).
- github_deploy_key - deprecated invalid parameter alias ``2fa_token``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- grove - the option ``message`` will be removed in community.general 4.0.0. Use the new option ``message_content`` instead (https://github.com/ansible-collections/community.general/pull/1929).
- homebrew - deprecated invalid parameter alias ``update-brew``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- homebrew_cask - deprecated invalid parameter alias ``update-brew``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- opkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- pacman - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- puppet - deprecated undocumented parameter ``show_diff``, will be removed in 7.0.0. (https://github.com/ansible-collections/community.general/pull/1927).
- runit - unused parameter ``dist`` marked for deprecation (https://github.com/ansible-collections/community.general/pull/1830).
- slackpkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- urmpi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- xbps - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927).
- xfconf - returning output as facts is deprecated, this will be removed in community.general 4.0.0. Please register the task output in a variable and use it instead. You can already switch to the new behavior now by using the new ``disable_facts`` option (https://github.com/ansible-collections/community.general/pull/1747).
- The ``gluster_heal_info``, ``gluster_peer`` and ``gluster_volume`` modules have migrated to the `gluster.gluster <https://galaxy.ansible.com/gluster/gluster>`_ collection. Ansible-base 2.10.1 adjusted the routing target to point to the modules in that collection, so we will remove these modules in community.general 3.0.0. If you use Ansible 2.9, or use FQCNs ``community.general.gluster_*`` in your playbooks and/or roles, please update them to use the modules from ``gluster.gluster`` instead.
- The ldap_attr module has been deprecated and will be removed in a later release; use ldap_attrs instead.
- django_manage - the parameter ``liveserver`` relates to a no longer maintained third-party module for django. It is now deprecated, and will be remove in community.general 3.0.0 (https://github.com/ansible-collections/community.general/pull/1154).
- proxmox - the default of the new ``proxmox_default_behavior`` option will change from ``compatibility`` to ``no_defaults`` in community.general 4.0.0. Set the option to an explicit value to avoid a deprecation warning (https://github.com/ansible-collections/community.general/pull/850).
- proxmox_kvm - the default of the new ``proxmox_default_behavior`` option will change from ``compatibility`` to ``no_defaults`` in community.general 4.0.0. Set the option to an explicit value to avoid a deprecation warning (https://github.com/ansible-collections/community.general/pull/850).
- syspatch - deprecate the redundant ``apply`` argument (https://github.com/ansible-collections/community.general/pull/360).
- xbps - the ``force`` option never had any effect. It is now deprecated, and will be removed in 3.0.0 (https://github.com/ansible-collections/community.general/pull/568).
Removed Features (previously deprecated)
----------------------------------------
- The ``ome_device_info``, ``idrac_firmware`` and ``idrac_server_config_profile`` modules have now been migrated from community.general to the `dellemc.openmanage <https://galaxy.ansible.com/dellemc/openmanage>`_ Ansible collection.
- All Google cloud modules and plugins have now been migrated away from this collection.
They can be found in either the `community.google <https://galaxy.ansible.com/community/google>`_ or `google.cloud <https://galaxy.ansible.com/google/cloud>`_ collections.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.idrac_firmware````dellemc.openmanage.idrac_firmware``) and make sure to install the dellemc.openmanage collection.
- The deprecated ali_instance_facts module has been removed. Use ali_instance_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated gluster_heal_info module has been removed. Use gluster.gluster.gluster_heal_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated gluster_peer module has been removed. Use gluster.gluster.gluster_peer instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated gluster_volume module has been removed. Use gluster.gluster.gluster_volume instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated helm module has been removed. Use community.kubernetes.helm instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated hpilo_facts module has been removed. Use hpilo_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated idrac_redfish_facts module has been removed. Use idrac_redfish_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated jenkins_job_facts module has been removed. Use jenkins_job_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ldap_attr module has been removed. Use ldap_attrs instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated memset_memstore_facts module has been removed. Use memset_memstore_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated memset_server_facts module has been removed. Use memset_server_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated na_ontap_gather_facts module has been removed. Use netapp.ontap.na_ontap_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated nginx_status_facts module has been removed. Use nginx_status_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated one_image_facts module has been removed. Use one_image_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated onepassword_facts module has been removed. Use onepassword_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_datacenter_facts module has been removed. Use oneview_datacenter_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_enclosure_facts module has been removed. Use oneview_enclosure_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_ethernet_network_facts module has been removed. Use oneview_ethernet_network_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_fc_network_facts module has been removed. Use oneview_fc_network_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_fcoe_network_facts module has been removed. Use oneview_fcoe_network_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_logical_interconnect_group_facts module has been removed. Use oneview_logical_interconnect_group_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_network_set_facts module has been removed. Use oneview_network_set_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated oneview_san_manager_facts module has been removed. Use oneview_san_manager_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated online_server_facts module has been removed. Use online_server_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated online_user_facts module has been removed. Use online_user_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt module has been removed. Use ovirt.ovirt.ovirt_vm instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_affinity_label_facts module has been removed. Use ovirt.ovirt.ovirt_affinity_label_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_api_facts module has been removed. Use ovirt.ovirt.ovirt_api_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_cluster_facts module has been removed. Use ovirt.ovirt.ovirt_cluster_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_datacenter_facts module has been removed. Use ovirt.ovirt.ovirt_datacenter_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_disk_facts module has been removed. Use ovirt.ovirt.ovirt_disk_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_event_facts module has been removed. Use ovirt.ovirt.ovirt_event_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_external_provider_facts module has been removed. Use ovirt.ovirt.ovirt_external_provider_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_group_facts module has been removed. Use ovirt.ovirt.ovirt_group_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_host_facts module has been removed. Use ovirt.ovirt.ovirt_host_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_host_storage_facts module has been removed. Use ovirt.ovirt.ovirt_host_storage_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_network_facts module has been removed. Use ovirt.ovirt.ovirt_network_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_nic_facts module has been removed. Use ovirt.ovirt.ovirt_nic_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_permission_facts module has been removed. Use ovirt.ovirt.ovirt_permission_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_quota_facts module has been removed. Use ovirt.ovirt.ovirt_quota_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_scheduling_policy_facts module has been removed. Use ovirt.ovirt.ovirt_scheduling_policy_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_snapshot_facts module has been removed. Use ovirt.ovirt.ovirt_snapshot_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_storage_domain_facts module has been removed. Use ovirt.ovirt.ovirt_storage_domain_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_storage_template_facts module has been removed. Use ovirt.ovirt.ovirt_storage_template_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_storage_vm_facts module has been removed. Use ovirt.ovirt.ovirt_storage_vm_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_tag_facts module has been removed. Use ovirt.ovirt.ovirt_tag_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_template_facts module has been removed. Use ovirt.ovirt.ovirt_template_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_user_facts module has been removed. Use ovirt.ovirt.ovirt_user_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_vm_facts module has been removed. Use ovirt.ovirt.ovirt_vm_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated ovirt_vmpool_facts module has been removed. Use ovirt.ovirt.ovirt_vmpool_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated purefa_facts module has been removed. Use purestorage.flasharray.purefa_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated purefb_facts module has been removed. Use purestorage.flasharray.purefb_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated python_requirements_facts module has been removed. Use python_requirements_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated redfish_facts module has been removed. Use redfish_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_image_facts module has been removed. Use scaleway_image_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_ip_facts module has been removed. Use scaleway_ip_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_organization_facts module has been removed. Use scaleway_organization_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_security_group_facts module has been removed. Use scaleway_security_group_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_server_facts module has been removed. Use scaleway_server_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_snapshot_facts module has been removed. Use scaleway_snapshot_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated scaleway_volume_facts module has been removed. Use scaleway_volume_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated smartos_image_facts module has been removed. Use smartos_image_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated vertica_facts module has been removed. Use vertica_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The deprecated xenserver_guest_facts module has been removed. Use xenserver_guest_info instead (https://github.com/ansible-collections/community.general/pull/1924).
- The ovirt_facts docs fragment has been removed (https://github.com/ansible-collections/community.general/pull/1924).
- airbrake_deployment - removed deprecated ``token`` parameter. Use ``project_id`` and ``project_key`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- bigpanda - the alias ``message`` has been removed. Use ``deployment_message`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- cisco_spark, cisco_webex - the alias ``message`` has been removed. Use ``msg`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- clc_aa_policy - the ``wait`` parameter has been removed. It did not have any effect (https://github.com/ansible-collections/community.general/pull/1926).
- datadog_monitor - the alias ``message`` has been removed. Use ``notification_message`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- django_manage - the parameter ``liveserver`` has been removed (https://github.com/ansible-collections/community.general/pull/1926).
- idrac_redfish_config - the parameters ``manager_attribute_name`` and ``manager_attribute_value`` have been removed. Use ``manager_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- iso_extract - the alias ``thirsty`` has been removed. Use ``force`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- ldap_entry - the ``params`` parameter is now completely removed. Using it already triggered an error since community.general 0.1.2 (https://github.com/ansible-collections/community.general/pull/2257).
- pulp_repo - the ``feed_client_cert`` parameter no longer defaults to the value of the ``client_cert`` parameter (https://github.com/ansible-collections/community.general/pull/1926).
- pulp_repo - the ``feed_client_key`` parameter no longer defaults to the value of the ``client_key`` parameter (https://github.com/ansible-collections/community.general/pull/1926).
- pulp_repo - the alias ``ca_cert`` has been removed. Use ``feed_ca_cert`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- rax - unused parameter ``service`` removed (https://github.com/ansible-collections/community.general/pull/2020).
- redfish modules - issuing a data modification command without specifying the ID of the target System, Chassis or Manager resource when there is more than one is no longer allowed. Use the ``resource_id`` option to specify the target ID (https://github.com/ansible-collections/community.general/pull/1926).
- redfish_config - the parameters ``bios_attribute_name`` and ``bios_attribute_value`` have been removed. Use ``bios_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926).
- syspatch - the ``apply`` parameter has been removed. This is the default mode, so simply removing it will not change the behavior (https://github.com/ansible-collections/community.general/pull/1926).
- xbps - the ``force`` parameter has been removed. It did not have any effect (https://github.com/ansible-collections/community.general/pull/1926).
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.gce_img````community.google.gce_img``) and make sure to install the community.google or google.cloud collections as appropriate.
- All Kubevirt modules and plugins have now been migrated from community.general to the `community.kubevirt <https://galaxy.ansible.com/community/kubevirt>`_ Ansible collection.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.kubevirt_vm````community.kubevirt.kubevirt_vm``) and make sure to install the community.kubevirt collection.
- All ``docker`` modules and plugins have been removed from this collection.
They have been migrated to the `community.docker <https://galaxy.ansible.com/community/docker>`_ collection.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.docker_container````community.docker.docker_container``) and make sure to install the community.docker collection.
- All ``hetzner`` modules have been removed from this collection.
They have been migrated to the `community.hrobot <https://galaxy.ansible.com/community/hrobot>`_ collection.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.hetzner_firewall````community.hrobot.firewall``) and make sure to install the community.hrobot collection.
- All ``postgresql`` modules have been removed from this collection.
They have been migrated to the `community.postgresql <https://galaxy.ansible.com/community/postgresql>`_ collection.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.postgresql_info````community.postgresql.postgresql_info``) and make sure to install the community.postgresql collection.
- The Google cloud inventory script ``gce.py`` has been migrated to the ``community.google`` collection. Install the ``community.google`` collection in order to continue using it.
- The ``hashi_vault`` lookup plugin has been removed from this collection.
It has been migrated to the `community.hashi_vault <https://galaxy.ansible.com/community/hashi_vault>`_ collection.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.hashi_vault````community.hashi_vault.hashi_vault``) and make sure to install the community.hashi_vault collection.
- The ``oc`` connection plugin has been removed from this collection.
It has been migrated to the `community.okd <https://galaxy.ansible.com/community/okd>`_ collection.
If you use ansible-base 2.10 or newer, redirections have been provided.
If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.oc````community.okd.oc``) and make sure to install the community.okd collection.
- The deprecated ``actionable`` callback plugin has been removed. Use the ``ansible.builtin.default`` callback plugin with ``display_skipped_hosts = no`` and ``display_ok_hosts = no`` options instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``foreman`` module has been removed. Use the modules from the theforeman.foreman collection instead (https://github.com/ansible-collections/community.general/pull/1347) (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``full_skip`` callback plugin has been removed. Use the ``ansible.builtin.default`` callback plugin with ``display_skipped_hosts = no`` option instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``gcdns_record`` module has been removed. Use ``google.cloud.gcp_dns_resource_record_set`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcdns_zone`` module has been removed. Use ``google.cloud.gcp_dns_managed_zone`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gce`` module has been removed. Use ``google.cloud.gcp_compute_instance`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_backend_service`` module has been removed. Use ``google.cloud.gcp_compute_backend_service`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_forwarding_rule`` module has been removed. Use ``google.cloud.gcp_compute_forwarding_rule`` or ``google.cloud.gcp_compute_global_forwarding_rule`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_healthcheck`` module has been removed. Use ``google.cloud.gcp_compute_health_check``, ``google.cloud.gcp_compute_http_health_check`` or ``google.cloud.gcp_compute_https_health_check`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_target_proxy`` module has been removed. Use ``google.cloud.gcp_compute_target_http_proxy`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcp_url_map`` module has been removed. Use ``google.cloud.gcp_compute_url_map`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``gcspanner`` module has been removed. Use ``google.cloud.gcp_spanner_database`` and/or ``google.cloud.gcp_spanner_instance`` instead (https://github.com/ansible-collections/community.general/pull/1370).
- The deprecated ``github_hooks`` module has been removed. Use ``community.general.github_webhook`` and ``community.general.github_webhook_info`` instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``katello`` module has been removed. Use the modules from the theforeman.foreman collection instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_aggregate`` module has been removed. Use netapp.ontap.na_ontap_aggregate instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_license`` module has been removed. Use netapp.ontap.na_ontap_license instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_lun`` module has been removed. Use netapp.ontap.na_ontap_lun instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_qtree`` module has been removed. Use netapp.ontap.na_ontap_qtree instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_svm`` module has been removed. Use netapp.ontap.na_ontap_svm instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_user_role`` module has been removed. Use netapp.ontap.na_ontap_user_role instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_user`` module has been removed. Use netapp.ontap.na_ontap_user instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``na_cdot_volume`` module has been removed. Use netapp.ontap.na_ontap_volume instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_account_manager`` module has been removed. Use netapp.elementsw.na_elementsw_account instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_check_connections`` module has been removed. Use netapp.elementsw.na_elementsw_check_connections instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_snapshot_schedule_manager`` module has been removed. Use netapp.elementsw.na_elementsw_snapshot_schedule instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_volume_access_group_manager`` module has been removed. Use netapp.elementsw.na_elementsw_access_group instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``sf_volume_manager`` module has been removed. Use netapp.elementsw.na_elementsw_volume instead (https://github.com/ansible-collections/community.general/pull/1347).
- The deprecated ``stderr`` callback plugin has been removed. Use the ``ansible.builtin.default`` callback plugin with ``display_failed_stderr = yes`` option instead (https://github.com/ansible-collections/community.general/pull/1347).
- The redirect of the ``conjur_variable`` lookup plugin to ``cyberark.conjur.conjur_variable`` collection was removed (https://github.com/ansible-collections/community.general/pull/1346).
- The redirect of the ``firewalld`` module and the ``firewalld`` module_utils to the ``ansible.posix`` collection was removed (https://github.com/ansible-collections/community.general/pull/1346).
- The redirect to the ``community.digitalocean`` collection was removed for: the ``digital_ocean`` doc fragment, the ``digital_ocean`` module_utils, and the following modules: ``digital_ocean``, ``digital_ocean_account_facts``, ``digital_ocean_account_info``, ``digital_ocean_block_storage``, ``digital_ocean_certificate``, ``digital_ocean_certificate_facts``, ``digital_ocean_certificate_info``, ``digital_ocean_domain``, ``digital_ocean_domain_facts``, ``digital_ocean_domain_info``, ``digital_ocean_droplet``, ``digital_ocean_firewall_facts``, ``digital_ocean_firewall_info``, ``digital_ocean_floating_ip``, ``digital_ocean_floating_ip_facts``, ``digital_ocean_floating_ip_info``, ``digital_ocean_image_facts``, ``digital_ocean_image_info``, ``digital_ocean_load_balancer_facts``, ``digital_ocean_load_balancer_info``, ``digital_ocean_region_facts``, ``digital_ocean_region_info``, ``digital_ocean_size_facts``, ``digital_ocean_size_info``, ``digital_ocean_snapshot_facts``, ``digital_ocean_snapshot_info``, ``digital_ocean_sshkey``, ``digital_ocean_sshkey_facts``, ``digital_ocean_sshkey_info``, ``digital_ocean_tag``, ``digital_ocean_tag_facts``, ``digital_ocean_tag_info``, ``digital_ocean_volume_facts``, ``digital_ocean_volume_info`` (https://github.com/ansible-collections/community.general/pull/1346).
- The redirect to the ``community.mysql`` collection was removed for: the ``mysql`` doc fragment, the ``mysql`` module_utils, and the following modules: ``mysql_db``, ``mysql_info``, ``mysql_query``, ``mysql_replication``, ``mysql_user``, ``mysql_variables`` (https://github.com/ansible-collections/community.general/pull/1346).
- The redirect to the ``community.proxysql`` collection was removed for: the ``proxysql`` doc fragment, and the following modules: ``proxysql_backend_servers``, ``proxysql_global_variables``, ``proxysql_manage_config``, ``proxysql_mysql_users``, ``proxysql_query_rules``, ``proxysql_replication_hostgroups``, ``proxysql_scheduler`` (https://github.com/ansible-collections/community.general/pull/1346).
- The redirect to the ``infinidat.infinibox`` collection was removed for: the ``infinibox`` doc fragment, the ``infinibox`` module_utils, and the following modules: ``infini_export``, ``infini_export_client``, ``infini_fs``, ``infini_host``, ``infini_pool``, ``infini_vol`` (https://github.com/ansible-collections/community.general/pull/1346).
- conjur_variable lookup - has been moved to the ``cyberark.conjur`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/570).
- digital_ocean_* - all DigitalOcean modules have been moved to the ``community.digitalocean`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/622).
- infini_* - all infinidat modules have been moved to the ``infinidat.infinibox`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/607).
- iptables_state - the ``ANSIBLE_ASYNC_DIR`` environment is no longer supported, use the ``async_dir`` shell option instead (https://github.com/ansible-collections/community.general/pull/1371).
- logicmonitor - the module has been removed in 1.0.0 since it is unmaintained and the API used by the module has been turned off in 2017 (https://github.com/ansible-collections/community.general/issues/539, https://github.com/ansible-collections/community.general/pull/541).
- logicmonitor_facts - the module has been removed in 1.0.0 since it is unmaintained and the API used by the module has been turned off in 2017 (https://github.com/ansible-collections/community.general/issues/539, https://github.com/ansible-collections/community.general/pull/541).
- memcached cache plugin - do not import ``CacheModule``s directly. Use ``ansible.plugins.loader.cache_loader`` instead (https://github.com/ansible-collections/community.general/pull/1371).
- mysql_* - all MySQL modules have been moved to the ``community.mysql`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/633).
- proxysql_* - all ProxySQL modules have been moved to the ``community.proxysql`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/624).
- redis cache plugin - do not import ``CacheModule``s directly. Use ``ansible.plugins.loader.cache_loader`` instead (https://github.com/ansible-collections/community.general/pull/1371).
- xml - when ``content=attribute``, the ``attribute`` option is ignored (https://github.com/ansible-collections/community.general/pull/1371).
Security Fixes
--------------
- dnsmadeeasy - mark the ``account_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- gitlab_runner - mark the ``registration_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- hwc_ecs_instance - mark the ``admin_pass`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- ibm_sa_host - mark the ``iscsi_chap_secret`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- java_cert - remove password from ``run_command`` arguments (https://github.com/ansible-collections/community.general/pull/2008).
- java_keystore - pass secret to keytool through an environment variable to not expose it as a commandline argument (https://github.com/ansible-collections/community.general/issues/1668).
- keycloak_* modules - mark the ``auth_client_secret`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- keycloak_client - mark the ``registration_access_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- librato_annotation - mark the ``api_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- logentries_msg - mark the ``token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- module_utils/_netapp, na_ontap_gather_facts - enabled ``no_log`` for the options ``api_key`` and ``secret_key`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725).
- module_utils/identity/keycloak, keycloak_client, keycloak_clienttemplate, keycloak_group - enabled ``no_log`` for the option ``auth_client_secret`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725).
- nios_nsgroup - mark the ``tsig_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- oneandone_firewall_policy, oneandone_load_balancer, oneandone_monitoring_policy, oneandone_private_network, oneandone_public_ip - mark the ``auth_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- ovirt - mark the ``instance_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- ovirt - mark the ``instance_rootpw`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- pagerduty_alert - mark the ``api_key``, ``service_key`` and ``integration_key`` parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- pagerduty_change - mark the ``integration_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- pingdom - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- pulp_repo - mark the ``feed_client_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- rax_clb_ssl - mark the ``private_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- redfish_command - mark the ``update_creds.password`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- rollbar_deployment - mark the ``token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- spotinst_aws_elastigroup - mark the ``multai_token`` and ``token`` parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- stackdriver - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736).
- utm_proxy_auth_profile - enabled ``no_log`` for the option ``frontend_cookie_secret`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725).
- utm_proxy_auth_profile - mark the ``frontend_cookie_secret`` parameter as ``no_log`` to avoid leakage of secrets. This causes the ``utm_proxy_auth_profile`` return value to no longer containing the correct value, but a placeholder (https://github.com/ansible-collections/community.general/pull/1736).
- bitbucket_pipeline_variable - **CVE-2021-20180** - hide user sensitive information which are marked as ``secured`` from logging into the console (https://github.com/ansible-collections/community.general/pull/1635).
- snmp_facts - **CVE-2021-20178** - hide user sensitive information such as ``privkey`` and ``authkey`` from logging into the console (https://github.com/ansible-collections/community.general/pull/1621).
Bugfixes
--------
- Mark various module options with ``no_log=False`` which have a name that potentially could leak secrets, but which do not (https://github.com/ansible-collections/community.general/pull/2001).
- aerospike_migration - fix typo that caused ``migrate_tx_key`` instead of ``migrate_rx_key`` being used (https://github.com/ansible-collections/community.general/pull/1739).
- alternatives - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- beadm - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- bigpanda - actually use the ``deployment_message`` option (https://github.com/ansible-collections/community.general/pull/1928).
- chef_databag lookup plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- cloudforms inventory - fixed issue that non-existing (archived) VMs were synced (https://github.com/ansible-collections/community.general/pull/720).
- cobbler_sync, cobbler_system - fix SSL/TLS certificate check when ``validate_certs`` set to ``false`` (https://github.com/ansible-collections/community.general/pull/1880).
- consul_io inventory script - kv_groups - fix byte chain decoding for Python 3 (https://github.com/ansible-collections/community.general/pull/620).
- cronvar - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- dconf - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- deploy_helper - allow ``state=clean`` to be used without defining a ``release`` (https://github.com/ansible-collections/community.general/issues/1852).
- dimensiondata_network - bug when formatting message, instead of % a simple comma was used (https://github.com/ansible-collections/community.general/pull/2139).
- diy callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- elasticsearch_plugin - ``state`` parameter choices must use ``list()`` in python3 (https://github.com/ansible-collections/community.general/pull/1830).
- filesystem - do not fail when ``resizefs=yes`` and ``fstype=xfs`` if there is nothing to do, even if the filesystem is not mounted. This only covers systems supporting access to unmounted XFS filesystems. Others will still fail (https://github.com/ansible-collections/community.general/issues/1457, https://github.com/ansible-collections/community.general/pull/1478).
- filesystem - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- filesystem - remove ``swap`` from list of FS supported by ``resizefs=yes`` (https://github.com/ansible-collections/community.general/issues/790).
- funcd connection plugin - can now load (https://github.com/ansible-collections/community.general/pull/2235).
- git_config - fixed scope ``file`` behaviour and added integraton test for it (https://github.com/ansible-collections/community.general/issues/2117).
- git_config - prevent ``run_command`` from expanding values (https://github.com/ansible-collections/community.general/issues/1776).
- github_repo - PyGithub bug does not allow explicit port in ``base_url``. Specifying port is not required (https://github.com/PyGithub/PyGithub/issues/1913).
- gitlab_runner - parameter ``registration_token`` was required but is used only when ``state`` is ``present`` (https://github.com/ansible-collections/community.general/issues/1714).
- gitlab_user - make updates to the ``isadmin``, ``password`` and ``confirm`` options of an already existing GitLab user work (https://github.com/ansible-collections/community.general/pull/1724).
- haproxy - fix a bug preventing haproxy from properly entering ``DRAIN`` mode (https://github.com/ansible-collections/community.general/issues/1913).
- hiera lookup plugin - converts the return type of plugin to unicode string (https://github.com/ansible-collections/community.general/pull/2329).
- hipchat - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- idrac_redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- idrac_redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- idrac_redfish_info - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- imc_rest - explicitly logging out instead of registering the call in ```atexit``` (https://github.com/ansible-collections/community.general/issues/1735).
- influxdb_retention_policy - ensure idempotent module execution with different duration and shard duration parameter values (https://github.com/ansible-collections/community.general/issues/2281).
- infoblox inventory script - make sure that the script also works with Ansible 2.9, and returns a more helpful error when community.general is not installed as part of Ansible 2.10/3 (https://github.com/ansible-collections/community.general/pull/1871).
- ini_file - allows an empty string as a value for an option (https://github.com/ansible-collections/community.general/pull/1972).
- interfaces_file - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- ipa_user - allow ``sshpubkey`` to permit multiple word comments (https://github.com/ansible-collections/community.general/pull/2159).
- iso_extract - use proper alias deprecation mechanism for ``thirsty`` alias of ``force`` (https://github.com/ansible-collections/community.general/pull/1830).
- java_cert - allow setting ``state: absent`` by providing just the ``cert_alias`` (https://github.com/ansible/ansible/issues/27982).
- java_cert - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- java_cert - properly handle proxy arguments when the scheme is provided (https://github.com/ansible/ansible/issues/54481).
- java_keystore - improve error handling and return ``cmd`` as documented. Force ``LANG``, ``LC_ALL`` and ``LC_MESSAGES`` environment variables to ``C`` to rely on ``keytool`` output parsing. Fix pylint's ``unused-variable`` and ``no-else-return`` hints (https://github.com/ansible-collections/community.general/pull/2183).
- java_keystore - use tempfile lib to create temporary files with randomized names, and remove the temporary PKCS#12 keystore as well as other materials (https://github.com/ansible-collections/community.general/issues/1667).
- jenkins_plugin - fixes Python 2 compatibility issue (https://github.com/ansible-collections/community.general/pull/2340).
- jira - fixed calling of ``isinstance`` (https://github.com/ansible-collections/community.general/issues/2234).
- jira - fixed error when loading base64-encoded content as attachment (https://github.com/ansible-collections/community.general/pull/2349).
- jira - fixed fields' update in ticket transitions (https://github.com/ansible-collections/community.general/issues/818).
- kibana_plugin - ``state`` parameter choices must use ``list()`` in python3 (https://github.com/ansible-collections/community.general/pull/1830).
- kibana_plugin - added missing parameters to ``remove_plugin`` when using ``state=present force=true``, and fix potential quoting errors when invoking ``kibana`` (https://github.com/ansible-collections/community.general/pull/2143).
- logstash_plugin - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` setting (https://github.com/ansible-collections/community.general/pull/1830).
- lvg - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- lvol - fixed sizing calculation rounding to match the underlying tools (https://github.com/ansible-collections/community.general/issues/1988).
- lvol - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- lxc - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- lxc_container - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- lxc_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- lxd_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- manageiq_provider - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` setting (https://github.com/ansible-collections/community.general/pull/1970).
- memcached cache plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- meta/runtime.yml - improve deprecation messages (https://github.com/ansible-collections/community.general/pull/1918).
- module_helper module utils - actually ignoring formatting of parameters with value ``None`` (https://github.com/ansible-collections/community.general/pull/2024).
- module_helper module utils - fixed decorator ``cause_changes`` (https://github.com/ansible-collections/community.general/pull/2203).
- module_helper module utils - handling ``ModuleHelperException`` now properly calls ``fail_json()`` (https://github.com/ansible-collections/community.general/pull/2024).
- module_helper module utils - use the command name as-is in ``CmdMixin`` if it fails ``get_bin_path()`` - allowing full path names to be passed (https://github.com/ansible-collections/community.general/pull/2024).
- net_tools.nios.api module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- nios* modules - fix modules to work with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/2057).
- nios_host_record - allow DNS Bypass for views other than default (https://github.com/ansible-collections/community.general/issues/1786).
- nmap inventory plugin - fix cache and constructed group support (https://github.com/ansible-collections/community.general/issues/2242).
- nmcli - add ``method4`` and ``method6`` options (https://github.com/ansible-collections/community.general/pull/1894).
- nmcli - ensure the ``slave-type`` option is passed to ``nmcli`` for type ``bond-slave`` (https://github.com/ansible-collections/community.general/pull/1882).
- nomad_job_info - fix module failure when nomad client returns no jobs (https://github.com/ansible-collections/community.general/pull/1721).
- nsot inventory script - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- oci_vcn - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- oneandone_monitoring_policy - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- packet_volume_attachment - removed extraneous ``print`` call - old debug? (https://github.com/ansible-collections/community.general/pull/1970).
- parted - change the regex that decodes the partition size to better support different formats that parted uses. Change the regex that validates parted's version string (https://github.com/ansible-collections/community.general/pull/1695).
- parted - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- pkgutil - fixed calls to ``list.extend()`` (https://github.com/ansible-collections/community.general/pull/2161).
- proxmox - removed requirement that root password is provided when containter state is ``present`` (https://github.com/ansible-collections/community.general/pull/1999).
- proxmox inventory - added handling of commas in KVM agent configuration string (https://github.com/ansible-collections/community.general/pull/2245).
- proxmox inventory - added handling of extra trailing slashes in the URL (https://github.com/ansible-collections/community.general/pull/1914).
- proxmox inventory - exclude qemu templates from inclusion to the inventory via pools (https://github.com/ansible-collections/community.general/issues/1986, https://github.com/ansible-collections/community.general/pull/1991).
- proxmox inventory plugin - allowed proxomox tag string to contain commas when returned as fact (https://github.com/ansible-collections/community.general/pull/1949).
- proxmox inventory plugin - support network interfaces without IP addresses, multiple network interfaces and unsupported/commanddisabled guest error (https://github.com/ansible-collections/community.general/pull/2263).
- proxmox lxc - only add the features flag when module parameter ``features`` is set. Before an empty string was send to proxmox in case the parameter was not used, which required to use ``root@pam`` for module execution (https://github.com/ansible-collections/community.general/pull/1763).
- proxmox* modules - refactored some parameter validation code into use of ``env_fallback``, ``required_if``, ``required_together``, ``required_one_of`` (https://github.com/ansible-collections/community.general/pull/1765).
- proxmox_kvm - do not add ``args`` if ``proxmox_default_behavior`` is set to no_defaults (https://github.com/ansible-collections/community.general/issues/1641).
- proxmox_kvm - fix parameter ``vmid`` passed twice to ``exit_json`` while creating a virtual machine without cloning (https://github.com/ansible-collections/community.general/issues/1875, https://github.com/ansible-collections/community.general/pull/1895).
- proxmox_kvm - fix undefined local variable ``status`` when the parameter ``state`` is either ``stopped``, ``started``, ``restarted`` or ``absent`` (https://github.com/ansible-collections/community.general/pull/1847).
- proxmox_kvm - stop implicitly adding ``force`` equal to ``false``. Proxmox API requires not implemented parameters otherwise, and assumes ``force`` to be ``false`` by default anyways (https://github.com/ansible-collections/community.general/pull/1783).
- redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- redfish_config module, redfish_utils module utils - fix IndexError in ``SetManagerNic`` command (https://github.com/ansible-collections/community.general/issues/1692).
- redfish_info module, redfish_utils module utils - add ``Name`` and ``Id`` properties to output of Redfish inventory commands (https://github.com/ansible-collections/community.general/issues/1650).
- redhat_subscription - ``mutually_exclusive`` was referring to parameter alias instead of name (https://github.com/ansible-collections/community.general/pull/1795).
- redhat_subscription - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- redis cache plugin - wrapped usages of ``keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- riak - parameters ``wait_for_handoffs`` and ``wait_for_ring`` are ``int`` but the default value was ``false`` (https://github.com/ansible-collections/community.general/pull/1830).
- rundeck_acl_policy - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- runit - removed unused code, and passing command as ``list`` instead of ``str`` to ``run_command()`` (https://github.com/ansible-collections/community.general/pull/1830).
- scaleway inventory plugin - fix pagination on scaleway inventory plugin (https://github.com/ansible-collections/community.general/pull/2036).
- selective callback plugin - adjust import so that the plugin also works with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/1807).
- selective callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- sensu-silence module - fix json parsing of sensu API responses on Python 3.5 (https://github.com/ansible-collections/community.general/pull/1703).
- sensu_check - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- spotinst_aws_elastigroup - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- stacki_host - replaced ``default`` to environment variables with ``fallback`` to them (https://github.com/ansible-collections/community.general/pull/2072).
- statusio_maintenance - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- terraform - fix issue that cause the destroy to fail because from Terraform 0.15 on, the ``terraform destroy -force`` option is replaced with ``terraform destroy -auto-approve`` (https://github.com/ansible-collections/community.general/issues/2247).
- terraform - fix issue that cause the execution fail because from Terraform 0.15 on, the ``-var`` and ``-var-file`` options are no longer available on ``terraform validate`` (https://github.com/ansible-collections/community.general/pull/2246).
- terraform - remove uses of ``use_unsafe_shell=True`` (https://github.com/ansible-collections/community.general/pull/2246).
- timezone - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).
- udm_dns_record - fixed default value of parameter ``data`` to match its type (https://github.com/ansible-collections/community.general/pull/2268).
- utm_utils module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- vdo - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861).
- vmadm - correct type of list elements in ``resolvers`` parameter (https://github.com/ansible-collections/community.general/issues/2135).
- xfconf - module was not honoring check mode when ``state`` was ``absent`` (https://github.com/ansible-collections/community.general/pull/2185).
- xfs_quota - the feedback for initializing project quota using xfs_quota binary from ``xfsprogs`` has changed since the version it was written for (https://github.com/ansible-collections/community.general/pull/1596).
- zfs - some ZFS properties could be passed when the dataset/volume did not exist, but would fail if the dataset already existed, even if the property matched what was specified in the ansible task (https://github.com/ansible-collections/community.general/issues/868, https://github.com/ansible-collections/community.general/pull/1833).
- zfs_delegate_admin - the elements of ``users``, ``groups`` and ``permissions`` are now enforced to be strings (https://github.com/ansible-collections/community.general/pull/1766).
- zypper, zypper_repository - respect ``PATH`` environment variable when resolving zypper executable path (https://github.com/ansible-collections/community.general/pull/2094).
New Plugins
-----------
Become
~~~~~~
- sudosu - Run tasks using sudo su -
Callback
~~~~~~~~
- loganalytics - Posts task results to Azure Log Analytics
Filter
~~~~~~
- dict - The ``dict`` function as a filter: converts a list of tuples to a dictionary
- from_csv - Converts CSV text input into list of dicts
- hashids_decode - Decodes a sequence of numbers from a YouTube-like hash
- hashids_encode - Encodes YouTube-like hashes from a sequence of integers
- path_join - Redirects to ansible.builtin.path_join for ansible-base 2.10 or newer, and provides a compatible implementation for Ansible 2.9
- version_sort - Sort a list according to version order instead of pure alphabetical one
Inventory
~~~~~~~~~
- lxd - Returns Ansible inventory from lxd host
- aerospike_migrations - handle exception when unstable-cluster is returned (https://github.com/ansible-collections/community.general/pull/900).
- aix_filesystem - fix issues with ismount module_util pathing for Ansible 2.9 (https://github.com/ansible-collections/community.general/pull/567).
- apache2_module - amend existing module identifier workaround to also apply to updated Shibboleth modules (https://github.com/ansible-collections/community.general/issues/1379).
- beadm - fixed issue "list object has no attribute split" (https://github.com/ansible-collections/community.general/issues/791).
- bigpanda - removed the dynamic default for ``host`` param (https://github.com/ansible-collections/community.general/pull/1423).
- bitbucket_pipeline_variable - change pagination logic for pipeline variable get API (https://github.com/ansible-collections/community.general/issues/1425).
- capabilities - fix for a newer version of libcap release (https://github.com/ansible-collections/community.general/pull/1061).
- cobbler inventory plugin - ``name`` needed FQCN (https://github.com/ansible-collections/community.general/pull/722).
- cobbler inventory script - add Python 3 support (https://github.com/ansible-collections/community.general/issues/638).
- composer - fix bug in command idempotence with composer v2 (https://github.com/ansible-collections/community.general/issues/1179).
- consul_kv lookup - fix ``ANSIBLE_CONSUL_URL`` environment variable handling (https://github.com/ansible/ansible/issues/51960).
- consul_kv lookup - fix arguments handling (https://github.com/ansible-collections/community.general/pull/303).
- digital_ocean_tag_info - fix crash when querying for an individual tag (https://github.com/ansible-collections/community.general/pull/615).
- django_manage - fix idempotence for ``createcachetable`` (https://github.com/ansible-collections/community.general/pull/699).
- dnsmadeeasy - fix HTTP 400 errors when creating a TXT record (https://github.com/ansible-collections/community.general/issues/1237).
- doas become plugin - address a bug with the parameters handling that was breaking the plugin in community.general when ``become_flags`` and ``become_user`` were not explicitly specified (https://github.com/ansible-collections/community.general/pull/704).
- dsv lookup - use correct dict usage (https://github.com/ansible-collections/community.general/pull/743).
- dzdo become plugin - address a bug with the parameters handling that was breaking the plugin in community.general when ``become_user`` was not explicitly specified (https://github.com/ansible-collections/community.general/pull/708).
- filesystem - add option ``state`` with default ``present``. When set to ``absent``, filesystem signatures are removed (https://github.com/ansible-collections/community.general/issues/355).
- filesystem - resizefs of xfs filesystems is fixed. Filesystem needs to be mounted.
- flatpak - use of the ``--non-interactive`` argument instead of ``-y`` when possible (https://github.com/ansible-collections/community.general/pull/1246).
- gcp_storage_files lookup plugin - make sure that plugin errors out on initialization if the required library is not found, and not on load-time (https://github.com/ansible-collections/community.general/pull/1297).
- gem - fix get_installed_versions: correctly parse ``default`` version (https://github.com/ansible-collections/community.general/pull/783).
- git_config - now raises an error for non-existent repository paths (https://github.com/ansible-collections/community.general/issues/630).
- git_config - using list instead of string as first parameter in the ``run_command()`` call (https://github.com/ansible-collections/community.general/issues/1021).
- gitlab_group - added description parameter to ``createGroup()`` call (https://github.com/ansible-collections/community.general/issues/138).
- gitlab_group_variable - support for GitLab pagination limitation by iterating over GitLab variable pages (https://github.com/ansible-collections/community.general/pull/968).
- gitlab_project_variable - support for GitLab pagination limitation by iterating over GitLab variable pages (https://github.com/ansible-collections/community.general/pull/968).
- gitlab_runner - fix compatiblity with some versions of python-gitlab (https://github.com/ansible-collections/community.general/pull/1491).
- homebrew - add default search path for ``brew`` on Apple silicon hardware (https://github.com/ansible-collections/community.general/pull/1679).
- homebrew - fix package name validation for packages containing hypen ``-`` (https://github.com/ansible-collections/community.general/issues/1037).
- homebrew_cask - add default search path for ``brew`` on Apple silicon hardware (https://github.com/ansible-collections/community.general/pull/1679).
- homebrew_cask - fix package name validation for casks containing hypen ``-`` (https://github.com/ansible-collections/community.general/issues/1037).
- homebrew_cask - fixed issue where a cask with ``@`` in the name is incorrectly reported as invalid (https://github.com/ansible-collections/community.general/issues/733).
- homebrew_tap - add default search path for ``brew`` on Apple silicon hardware (https://github.com/ansible-collections/community.general/pull/1679).
- icinga2_host - fix returning error codes (https://github.com/ansible-collections/community.general/pull/335).
- influxdb - fix usage of path for older version of python-influxdb (https://github.com/ansible-collections/community.general/issues/997).
- ini_file - check for parameter ``value`` if ``state`` is ``present`` and ``allow_no_value`` is ``false`` (https://github.com/ansible-collections/community.general/issues/479).
- interfaces_file - escape regular expression characters in old value (https://github.com/ansible-collections/community.general/issues/777).
- inventory plugins - allow FQCN in ``plugin`` option (https://github.com/ansible-collections/community.general/pull/722).
- ipa_hostgroup - fix an issue with load-balanced ipa and cookie handling with Python 3 (https://github.com/ansible-collections/community.general/issues/737).
- iptables_state - fix race condition between module and its action plugin (https://github.com/ansible-collections/community.general/issues/1136).
- jenkins_plugin - replace MD5 checksum verification with SHA1 due to MD5 being disabled on systems with FIPS-only algorithms enabled (https://github.com/ansible/ansible/issues/34304).
- jira - ``fetch`` and ``search`` no longer indicate that something changed (https://github.com/ansible-collections/community.general/pull/1536).
- jira - ensured parameter ``issue`` is mandatory for operation ``transition`` (https://github.com/ansible-collections/community.general/pull/1536).
- jira - improve error message handling (https://github.com/ansible-collections/community.general/pull/311).
- jira - improve error message handling with multiple errors (https://github.com/ansible-collections/community.general/pull/707).
- jira - module no longer incorrectly reports change for information gathering operations (https://github.com/ansible-collections/community.general/pull/1536).
- jira - provide error message raised from exception (https://github.com/ansible-collections/community.general/issues/1504).
- jira - replaced custom parameter validation with ``required_if`` (https://github.com/ansible-collections/community.general/pull/1536).
- json_query - handle ``AnsibleUnicode`` and ``AnsibleUnsafeText`` (https://github.com/ansible-collections/community.general/issues/320).
- keycloak module_utils - provide meaningful error message to user when auth URL does not start with http or https (https://github.com/ansible-collections/community.general/issues/331).
- launchd - fix for user-level services (https://github.com/ansible-collections/community.general/issues/896).
- launchd - handle deprecated APIs like ``readPlist`` and ``writePlist`` in ``plistlib`` (https://github.com/ansible-collections/community.general/issues/1552).
- ldap modules - add ``sasl_class`` parameter to support passwordless SASL authentication via GSSAPI (kerberos), next to external (https://github.com/ansible-collections/community.general/issues/1523).
- ldap_entry - improvements in documentation, simplifications and replaced code with better ``AnsibleModule`` arguments (https://github.com/ansible-collections/community.general/pull/1516).
- ldap_search - ignore returned referrals (https://github.com/ansible-collections/community.general/issues/1067).
- ldap_search - the module no longer incorrectly reports a change (https://github.com/ansible-collections/community.general/issues/1040).
- linode inventory plugin - make sure that plugin errors out on initialization if the required library is not found, and not on load-time (https://github.com/ansible-collections/community.general/pull/1297).
- lldp - use ``get_bin_path`` to locate the ``lldpctl`` executable (https://github.com/ansible-collections/community.general/pull/1643).
- lxc_container - fix the type of the ``container_config`` parameter. It is now processed as a list and not a string (https://github.com/ansible-collections/community.general/pull/216).
- macports - fix failure to install a package whose name is contained within an already installed package's name or variant (https://github.com/ansible-collections/community.general/issues/1307).
- make - fixed ``make`` parameter used for check mode when running a non-GNU ``make`` (https://github.com/ansible-collections/community.general/pull/1574).
- mas - fix ``invalid literal`` when no app can be found (https://github.com/ansible-collections/community.general/pull/1436).
- maven_artifact - handle timestamped snapshot version strings properly (https://github.com/ansible-collections/community.general/issues/709).
- memcached cache plugin - make sure that plugin errors out on initialization if the required library is not found, and not on load-time (https://github.com/ansible-collections/community.general/pull/1297).
- monit - add support for all monit service checks (https://github.com/ansible-collections/community.general/pull/1532).
- monit - fix modules ability to determine the current state of the monitored process (https://github.com/ansible-collections/community.general/pull/1107).
- nios_fixed_address, nios_host_record, nios_zone - removed redundant parameter aliases causing warning messages to incorrectly appear in task output (https://github.com/ansible-collections/community.general/issues/852).
- nios_host_record - fix to remove ``aliases`` (CNAMES) for configuration comparison (https://github.com/ansible-collections/community.general/issues/1335).
- nios_member - fix Python 3 compatibility with nios api ``member_normalize`` function (https://github.com/ansible-collections/community.general/issues/1526).
- nmcli - cannot modify ``ifname`` after connection creation (https://github.com/ansible-collections/community.general/issues/1089).
- nmcli - fix idempotetency when modifying an existing connection (https://github.com/ansible-collections/community.general/issues/481).
- nmcli - remove ``bridge-slave`` from list of IP based connections ((https://github.com/ansible-collections/community.general/issues/1500).
- nmcli - set ``C`` locale when executing ``nmcli`` (https://github.com/ansible-collections/community.general/issues/989).
- nmcli - use consistent autoconnect parameters (https://github.com/ansible-collections/community.general/issues/459).
- npm - handle json decode exception while parsing command line output (https://github.com/ansible-collections/community.general/issues/1614).
- oc connection plugin - ``transport`` needed FQCN (https://github.com/ansible-collections/community.general/pull/722).
- omapi_host - fix compatibility with Python 3 (https://github.com/ansible-collections/community.general/issues/787).
- onepassword lookup plugin - updated to support password items, which place the password field directly in the payload's ``details`` attribute (https://github.com/ansible-collections/community.general/pull/1610).
- osx_defaults - fix handling negative integers (https://github.com/ansible-collections/community.general/issues/134).
- osx_defaults - unquote values and unescape double quotes when reading array values (https://github.com/ansible-collections/community.general/pull/358).
- packet_net.py inventory script - fixed failure w.r.t. operating system retrieval by changing array subscription back to attribute access (https://github.com/ansible-collections/community.general/pull/891).
- pacman - treat package names containing .zst as package files during installation (https://www.archlinux.org/news/now-using-zstandard-instead-of-xz-for-package-compression/, https://github.com/ansible-collections/community.general/pull/650).
- pamd - added logic to retain the comment line (https://github.com/ansible-collections/community.general/issues/1394).
- parted - fix creating partition when label is changed (https://github.com/ansible-collections/community.general/issues/522).
- passwordstore lookup plugin - always use explicit ``show`` command to retrieve password. This ensures compatibility with ``gopass`` and avoids problems when password names equal ``pass`` commands (https://github.com/ansible-collections/community.general/pull/1493).
- passwordstore lookup plugin - fix compatibility with gopass when used with ``create=true``. While pass returns 1 on a non-existent password, gopass returns 10, or 11, depending on whether a similar named password was stored. We now just check standard output and that the return code is not zero (https://github.com/ansible-collections/community.general/pull/1589).
- pbrun become plugin - address a bug with the parameters handling that was breaking the plugin in community.general when ``become_user`` was not explicitly specified (https://github.com/ansible-collections/community.general/pull/708).
- pkg5 - now works when Python 3 is used on the target (https://github.com/ansible-collections/community.general/pull/789).
- profitbricks_nic - removed the dynamic default for ``name`` param (https://github.com/ansible-collections/community.general/pull/1423).
- profitbricks_nic - replaced code with ``required`` and ``required_if`` (https://github.com/ansible-collections/community.general/pull/1423).
- proxmox_kvm - defer error-checking for non-existent VMs in order to fix idempotency of tasks using ``state=absent`` and properly recognize a success (https://github.com/ansible-collections/community.general/pull/811).
- proxmox_kvm - fix issue causing linked clones not being create by allowing ``format=unspecified`` (https://github.com/ansible-collections/community.general/issues/1027).
- proxmox_kvm - ignore unsupported ``pool`` parameter on update (https://github.com/ansible-collections/community.general/pull/1258).
- proxmox_kvm - improve handling of long-running tasks by creating a dedicated function (https://github.com/ansible-collections/community.general/pull/831).
- redfish_info module, redfish_utils module utils - correct ``PartNumber`` property name in Redfish ``GetMemoryInventory`` command (https://github.com/ansible-collections/community.general/issues/1483).
- redfish_info, redfish_config, redfish_command - Fix Redfish response payload decode on Python 3.5 (https://github.com/ansible-collections/community.general/issues/686)
- redis - fixes parsing of config values which should not be converted to bytes (https://github.com/ansible-collections/community.general/pull/1079).
- redis cache plugin - make sure that plugin errors out on initialization if the required library is not found, and not on load-time (https://github.com/ansible-collections/community.general/pull/1297).
- rhn_channel - Python 2.7.5 fails if the certificate should not be validated. Fixed this by creating the correct ``ssl_context`` (https://github.com/ansible-collections/community.general/pull/470).
- saltstack connection plugin - use ``hashutil.base64_decodefile`` to ensure that the file checksum is preserved (https://github.com/ansible-collections/community.general/pull/1472).
- selective - mark task failed correctly (https://github.com/ansible/ansible/issues/63767).
- sendgrid - update documentation and warn user about sendgrid Python library version (https://github.com/ansible-collections/community.general/issues/1553).
- slack - avoid trying to update existing message when sending messages that contain the string "ts" (https://github.com/ansible-collections/community.general/issues/1097).
- slack - fix ``xox[abp]`` token identification to capture everything after ``xox[abp]``, as the token is the only thing that should be in this argument (https://github.com/ansible-collections/community.general/issues/862).
- snmp_facts - skip ``EndOfMibView`` values (https://github.com/ansible/ansible/issues/49044).
- solaris_zone - fixed issue trying to configure zone in Python 3 (https://github.com/ansible-collections/community.general/issues/1081).
- syslogger - update ``syslog.openlog`` API call for older Python versions, and improve error handling (https://github.com/ansible-collections/community.general/issues/953).
- syspatch - fix bug where not setting ``apply=true`` would result in error (https://github.com/ansible-collections/community.general/pull/360).
- terraform - fix ``init_reconfigure`` option for proper CLI args (https://github.com/ansible-collections/community.general/pull/1620).
- terraform - fix incorrectly reporting a status of unchanged when number of resources added or destroyed are multiples of 10 (https://github.com/ansible-collections/community.general/issues/561).
- terraform - improve result code checking when executing terraform commands (https://github.com/ansible-collections/community.general/pull/1632).
- timezone - support Python3 on macos/darwin (https://github.com/ansible-collections/community.general/pull/945).
- udm_user - removed the dynamic default for ``userexpiry`` param (https://github.com/ansible-collections/community.general/pull/1423).
- utm_network_interface_address - changed param type from invalid 'boolean' to valid 'bool' (https://github.com/ansible-collections/community.general/pull/1423).
- utm_proxy_exception - four parameters had elements types set as 'string' (invalid), changed to 'str' (https://github.com/ansible-collections/community.general/pull/1399).
- vmadm - simplification of code (https://github.com/ansible-collections/community.general/pull/1415).
- xfconf - add in missing return values that are specified in the documentation (https://github.com/ansible-collections/community.general/issues/1418).
- xfconf - make it work in non-english locales (https://github.com/ansible-collections/community.general/pull/744).
- xfconf - parameter ``value`` no longer required for state ``absent`` (https://github.com/ansible-collections/community.general/issues/1329).
- xfconf - xfconf no longer passing the command args as a string, but rather as a list (https://github.com/ansible-collections/community.general/issues/1328).
- yaml callback plugin - do not remove non-ASCII Unicode characters from multiline string output (https://github.com/ansible-collections/community.general/issues/1519).
- yarn - fixed an index out of range error when no outdated packages where returned by yarn executable (see https://github.com/ansible-collections/community.general/pull/474).
- yarn - fixed an too many values to unpack error when scoped packages are installed (see https://github.com/ansible-collections/community.general/pull/474).
- zfs - fixed ``invalid character '@' in pool name"`` error when working with snapshots on a root zvol (https://github.com/ansible-collections/community.general/issues/932).
- zypper - force ``LANG=C`` to as zypper is looking in XML output where attribute could be translated (https://github.com/ansible-collections/community.general/issues/1175).
New Modules
-----------
@@ -501,17 +443,7 @@ Cloud
misc
^^^^
- proxmox_storage_info - Retrieve information about one or more Proxmox VE storages
opennebula
^^^^^^^^^^
- one_template - Manages OpenNebula templates
Files
~~~~~
- filesize - Create a file with a given size, or resize it if it exists
- proxmox_snap - Snapshot management of instances in Proxmox VE cluster
Identity
~~~~~~~~
@@ -519,55 +451,28 @@ Identity
ipa
^^^
- ipa_otpconfig - Manage FreeIPA OTP Configuration Settings
- ipa_otptoken - Manage FreeIPA OTPs
keycloak
^^^^^^^^
- keycloak_realm - Allows administration of Keycloak realm via Keycloak API
- ipa_pwpolicy - Manage FreeIPA password policies
Monitoring
~~~~~~~~~~
- spectrum_model_attrs - Enforce a model's attributes in CA Spectrum.
- statsd - Send metrics to StatsD
Net Tools
~~~~~~~~~
- gandi_livedns - Manage Gandi LiveDNS records
pritunl
datadog
^^^^^^^
- pritunl_org - Manages Pritunl Organizations using the Pritunl API
- pritunl_org_info - List Pritunl Organizations using the Pritunl API
- pritunl_user - Manage Pritunl Users using the Pritunl API
- pritunl_user_info - List Pritunl Users using the Pritunl API
- datadog_downtime - Manages Datadog downtimes
Remote Management
~~~~~~~~~~~~~~~~~
Packaging
~~~~~~~~~
lenovoxcc
^^^^^^^^^
os
^^
- xcc_redfish_command - Manages Lenovo Out-Of-Band controllers using Redfish APIs
- copr - Manage one of the Copr repositories
- rpm_ostree_pkg - Install or uninstall overlay additional packages
- yum_versionlock - Locks / unlocks a installed package(s) from being updated by yum package manager
Source Control
~~~~~~~~~~~~~~
System
~~~~~~
github
^^^^^^
- github_repo - Manage your repositories on Github
gitlab
^^^^^^
- gitlab_project_members - Manage project members on GitLab Server
Web Infrastructure
~~~~~~~~~~~~~~~~~~
- jenkins_build - Manage jenkins builds
- ssh_config - Manage SSH config for user
- sysrc - Manage FreeBSD using sysrc

View File

@@ -1,17 +1,15 @@
# Community General Collection
[![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)
[![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)
[![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.
You can find [documentation for this collection on the Ansible docs site](https://docs.ansible.com/ansible/latest/collections/community/general/).
Please note that this collection does **not** support Windows targets. Only connection plugins included in this collection might support Windows targets, and will explicitly mention that in their documentation if they do so.
## Tested with Ansible
Tested with the current Ansible 2.9, ansible-base 2.10 and ansible-core 2.11 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
Tested with the current Ansible 2.9 and 2.10 releases and the current development version of Ansible. Ansible versions before 2.9.10 are not supported.
## External requirements
@@ -78,7 +76,7 @@ Basic instructions without release branches:
## Release notes
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-3/CHANGELOG.rst).
See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-2/CHANGELOG.rst).
## Roadmap

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +0,0 @@
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: 3.0.2
version: 2.0.0
readme: README.md
authors:
- Ansible (https://github.com/ansible)

View File

@@ -39,9 +39,9 @@ plugin_routing:
redirect: community.hashi_vault.hashi_vault
modules:
ali_instance_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.ali_instance_info instead.
warning_text: see plugin documentation for details
docker_compose:
redirect: community.docker.docker_compose
docker_config:
@@ -159,7 +159,8 @@ plugin_routing:
gcpubsub_info:
redirect: community.google.gcpubsub_info
gcpubsub_facts:
tombstone:
redirect: community.google.gcpubsub_info
deprecation:
removal_version: 3.0.0
warning_text: Use community.google.gcpubsub_info instead.
gcspanner:
@@ -170,23 +171,22 @@ plugin_routing:
tombstone:
removal_version: 2.0.0
warning_text: Use community.general.github_webhook and community.general.github_webhook_info 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.
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.
hetzner_failover_ip:
redirect: community.hrobot.failover_ip
hetzner_failover_ip_info:
@@ -196,21 +196,17 @@ plugin_routing:
hetzner_firewall_info:
redirect: community.hrobot.firewall_info
hpilo_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.hpilo_info instead.
idrac_firmware:
redirect: dellemc.openmanage.idrac_firmware
warning_text: see plugin documentation for details
idrac_redfish_facts:
tombstone:
deprecation:
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
warning_text: see plugin documentation for details
jenkins_job_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.jenkins_job_info instead.
warning_text: see plugin documentation for details
katello:
tombstone:
removal_version: 2.0.0
@@ -228,9 +224,9 @@ plugin_routing:
kubevirt_vm:
redirect: community.kubevirt.kubevirt_vm
ldap_attr:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.ldap_attrs instead.
warning_text: see plugin documentation for details
logicmonitor:
tombstone:
removal_version: 1.0.0
@@ -240,13 +236,13 @@ 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:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.memset_memstore_info instead.
warning_text: see plugin documentation for details
memset_server_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.memset_server_info instead.
warning_text: see plugin documentation for details
na_cdot_aggregate:
tombstone:
removal_version: 2.0.0
@@ -280,163 +276,161 @@ plugin_routing:
removal_version: 2.0.0
warning_text: Use netapp.ontap.na_ontap_volume instead.
na_ontap_gather_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use netapp.ontap.na_ontap_info instead.
warning_text: see plugin documentation for details
nginx_status_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.nginx_status_info instead.
ome_device_info:
redirect: dellemc.openmanage.ome_device_info
warning_text: see plugin documentation for details
one_image_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.one_image_info instead.
warning_text: see plugin documentation for details
onepassword_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.onepassword_info instead.
warning_text: see plugin documentation for details
oneview_datacenter_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_datacenter_info instead.
warning_text: see plugin documentation for details
oneview_enclosure_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_enclosure_info instead.
warning_text: see plugin documentation for details
oneview_ethernet_network_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_ethernet_network_info instead.
warning_text: see plugin documentation for details
oneview_fc_network_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_fc_network_info instead.
warning_text: see plugin documentation for details
oneview_fcoe_network_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_fcoe_network_info instead.
warning_text: see plugin documentation for details
oneview_logical_interconnect_group_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_logical_interconnect_group_info instead.
warning_text: see plugin documentation for details
oneview_network_set_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_network_set_info instead.
warning_text: see plugin documentation for details
oneview_san_manager_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.oneview_san_manager_info instead.
warning_text: see plugin documentation for details
online_server_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.online_server_info instead.
warning_text: see plugin documentation for details
online_user_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.online_user_info instead.
warning_text: see plugin documentation for details
ovirt:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_vm instead.
warning_text: see plugin documentation for details
ovirt_affinity_label_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_affinity_label_info instead.
warning_text: see plugin documentation for details
ovirt_api_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_api_info instead.
warning_text: see plugin documentation for details
ovirt_cluster_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_cluster_info instead.
warning_text: see plugin documentation for details
ovirt_datacenter_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_datacenter_info instead.
warning_text: see plugin documentation for details
ovirt_disk_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_disk_info instead.
warning_text: see plugin documentation for details
ovirt_event_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_event_info instead.
warning_text: see plugin documentation for details
ovirt_external_provider_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_external_provider_info instead.
warning_text: see plugin documentation for details
ovirt_group_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_group_info instead.
warning_text: see plugin documentation for details
ovirt_host_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_host_info instead.
warning_text: see plugin documentation for details
ovirt_host_storage_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_host_storage_info instead.
warning_text: see plugin documentation for details
ovirt_network_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_network_info instead.
warning_text: see plugin documentation for details
ovirt_nic_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_nic_info instead.
warning_text: see plugin documentation for details
ovirt_permission_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_permission_info instead.
warning_text: see plugin documentation for details
ovirt_quota_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_quota_info instead.
warning_text: see plugin documentation for details
ovirt_scheduling_policy_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_scheduling_policy_info instead.
warning_text: see plugin documentation for details
ovirt_snapshot_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_snapshot_info instead.
warning_text: see plugin documentation for details
ovirt_storage_domain_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_storage_domain_info instead.
warning_text: see plugin documentation for details
ovirt_storage_template_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_storage_template_info instead.
warning_text: see plugin documentation for details
ovirt_storage_vm_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_storage_vm_info instead.
warning_text: see plugin documentation for details
ovirt_tag_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_tag_info instead.
warning_text: see plugin documentation for details
ovirt_template_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_template_info instead.
warning_text: see plugin documentation for details
ovirt_user_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_user_info instead.
warning_text: see plugin documentation for details
ovirt_vm_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_vm_info instead.
warning_text: see plugin documentation for details
ovirt_vmpool_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use ovirt.ovirt.ovirt_vmpool_info instead.
warning_text: see plugin documentation for details
postgresql_copy:
redirect: community.postgresql.postgresql_copy
postgresql_db:
@@ -482,49 +476,49 @@ plugin_routing:
postgresql_user:
redirect: community.postgresql.postgresql_user
purefa_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use purestorage.flasharray.purefa_info instead.
warning_text: see plugin documentation for details
purefb_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use purestorage.flashblade.purefb_info instead.
warning_text: see plugin documentation for details
python_requirements_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.python_requirements_info instead.
warning_text: see plugin documentation for details
redfish_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.redfish_info instead.
warning_text: see plugin documentation for details
scaleway_image_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_image_info instead.
warning_text: see plugin documentation for details
scaleway_ip_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_ip_info instead.
warning_text: see plugin documentation for details
scaleway_organization_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_organization_info instead.
warning_text: see plugin documentation for details
scaleway_security_group_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_security_group_info instead.
warning_text: see plugin documentation for details
scaleway_server_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_server_info instead.
warning_text: see plugin documentation for details
scaleway_snapshot_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_snapshot_info instead.
warning_text: see plugin documentation for details
scaleway_volume_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.scaleway_volume_info instead.
warning_text: see plugin documentation for details
sf_account_manager:
tombstone:
removal_version: 2.0.0
@@ -546,17 +540,17 @@ plugin_routing:
removal_version: 2.0.0
warning_text: Use netapp.elementsw.na_elementsw_volume instead.
smartos_image_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.smartos_image_info instead.
warning_text: see plugin documentation for details
vertica_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.vertica_info instead.
warning_text: see plugin documentation for details
xenserver_guest_facts:
tombstone:
deprecation:
removal_version: 3.0.0
warning_text: Use community.general.xenserver_guest_info instead.
warning_text: see plugin documentation for details
doc_fragments:
_gcp:
redirect: community.google._gcp
@@ -571,8 +565,6 @@ 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:
@@ -587,8 +579,6 @@ 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:
@@ -611,10 +601,3 @@ 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

View File

@@ -1,91 +0,0 @@
# -*- 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

@@ -162,7 +162,7 @@ class CacheModuleKeys(MutableSet):
self._cache.set(self.PREFIX, self._keyset)
def remove_by_timerange(self, s_min, s_max):
for k in list(self._keyset.keys()):
for k in self._keyset.keys():
t = self._keyset[k]
if s_min < t < s_max:
del self._keyset[k]

View File

@@ -217,12 +217,14 @@ class CacheModule(BaseCacheModule):
self._db.zrem(self._keys_set, key)
def flush(self):
for key in list(self.keys()):
for key in self.keys():
self.delete(key)
def copy(self):
# TODO: there is probably a better way to do this in redis
ret = dict([(k, self.get(k)) for k in self.keys()])
ret = dict()
for key in self.keys():
ret[key] = self.get(key)
return ret
def __getstate__(self):

View File

@@ -1013,7 +1013,7 @@ class CallbackModule(Default):
for attr in _stats_attributes:
_ret[self.DIY_NS]['stats'].update({attr: _get_value(obj=stats, attr=attr)})
_ret[self.DIY_NS].update({'top_level_var_names': list(_ret.keys())})
_ret[self.DIY_NS].update({'top_level_var_names': _ret.keys()})
return _ret

View File

@@ -173,7 +173,8 @@ class CallbackModule(CallbackBase):
# Displays info about playbook being started by a person on an
# inventory, as well as Tags, Skip Tags and Limits
if not self.printed_playbook:
self.playbook_name, dummy = os.path.splitext(os.path.basename(self.play.playbook.filename))
self.playbook_name, _ = os.path.splitext(
os.path.basename(self.play.playbook.filename))
host_list = self.play.playbook.inventory.host_list
inventory = os.path.basename(os.path.realpath(host_list))
self.send_msg("%s: Playbook initiated by %s against %s" %

View File

@@ -1,234 +0,0 @@
# 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

@@ -41,16 +41,7 @@ import difflib
from ansible import constants as C
from ansible.plugins.callback import CallbackBase
from ansible.module_utils._text import to_text
try:
codeCodes = C.COLOR_CODES
except AttributeError:
# This constant was moved to ansible.constants in
# https://github.com/ansible/ansible/commit/1202dd000f10b0e8959019484f1c3b3f9628fc67
# (will be included in ansible-core 2.11.0). For older Ansible/ansible-base versions,
# we include from the original location.
from ansible.utils.color import codeCodes
from ansible.utils.color import codeCodes
DONT_COLORIZE = False
COLORS = {
@@ -67,7 +58,7 @@ COLORS = {
def dict_diff(prv, nxt):
"""Return a dict of keys that differ with another config object."""
keys = set(list(prv.keys()) + list(nxt.keys()))
keys = set(prv.keys() + nxt.keys())
result = {}
for k in keys:
if prv.get(k) != nxt.get(k):

View File

@@ -37,13 +37,12 @@ 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(ConnectionBase):
class Connection(object):
''' Func-based connections '''
has_pipelining = False

View File

@@ -86,7 +86,7 @@ class Connection(ConnectionBase):
write_fds = []
while len(read_fds) > 0 or len(write_fds) > 0:
try:
ready_reads, ready_writes, dummy = select.select(read_fds, write_fds, [])
ready_reads, ready_writes, _ = select.select(read_fds, write_fds, [])
except select.error as e:
if e.args[0] == errno.EINTR:
continue

View File

@@ -30,6 +30,7 @@ options:
description:
- Keycloak realm name to authenticate to for API access.
type: str
required: true
auth_client_secret:
description:
@@ -40,6 +41,7 @@ options:
description:
- Username to authenticate for API access with.
type: str
required: true
aliases:
- username
@@ -47,15 +49,10 @@ 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,32 +13,12 @@ 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
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
type: path
requirements:
- python >= 2.7.9

View File

@@ -47,7 +47,7 @@ class ModuleDocFragment(object):
OCI_USER_KEY_FILE variable, if any, is used. This option is required if the private key is
not specified through a configuration file (See C(config_file_location)). If the key is encrypted
with a pass-phrase, the C(api_user_key_pass_phrase) option must also be provided.
type: path
type: str
api_user_key_pass_phrase:
description:
- Passphrase used by the key referenced in C(api_user_key_file), if it is encrypted. If not set, then

View File

@@ -20,5 +20,4 @@ class ModuleDocFragment(object):
identify an instance of the resource. By default, all the attributes of a resource except
I(freeform_tags) are used to uniquely identify a resource.
type: list
elements: str
"""

View File

@@ -0,0 +1,59 @@
# -*- 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

@@ -1,43 +0,0 @@
# -*- 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.
"""

View File

@@ -1,24 +0,0 @@
# -*- 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

@@ -1,49 +0,0 @@
# -*- 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
}

View File

@@ -1,97 +0,0 @@
# -*- 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

@@ -1,28 +0,0 @@
# -*- 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,
}

View File

@@ -1,21 +0,0 @@
# Copyright (C) 2021 Eric Lavarde <elavarde@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
from distutils.version import LooseVersion
def version_sort(value, reverse=False):
'''Sort a list according to loose versions so that e.g. 2.9 is smaller than 2.10'''
return sorted(value, key=LooseVersion, reverse=reverse)
class FilterModule(object):
''' Version sort filter '''
def filters(self):
return {
'version_sort': version_sort
}

View File

@@ -34,15 +34,18 @@ DOCUMENTATION = r'''
description: Populate inventory with instances in this region.
default: []
type: list
required: false
tags:
description: Populate inventory only with instances which have at least one of the tags listed here.
default: []
type: list
reqired: false
version_added: 2.0.0
types:
description: Populate inventory with instances with this type.
default: []
type: list
required: false
strict:
version_added: 2.0.0
compose:

View File

@@ -1,950 +0,0 @@
# -*- 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,25 +71,6 @@ 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
@@ -101,7 +82,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return valid
def parse(self, inventory, loader, path, cache=True):
def parse(self, inventory, loader, path, cache=False):
try:
self._nmap = get_bin_path('nmap')
@@ -112,101 +93,75 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._read_config_data(path)
cache_key = self.get_cache_key(path)
# setup command
cmd = [self._nmap]
if not self._options['ports']:
cmd.append('-sP')
# 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['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')
# 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 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 = []
if attempt_to_read_cache:
try:
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
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 cache_needs_update:
# setup command
cmd = [self._nmap]
if not self._options['ports']:
cmd.append('-sP')
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 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 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['exclude']:
cmd.append('--exclude')
cmd.append(','.join(self._options['exclude']))
# 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)
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)))
if host is not None:
# update inventory
self.inventory.add_host(host)
self.inventory.set_variable(host, 'ip', ip)
ports = []
continue
# parse results
host = None
ip = None
ports = []
results = []
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
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))
# TODO: parse more data, OS?
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
# if any leftovers
if host and ports:
self.inventory.set_variable(host, 'ports', ports)
# 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)
except Exception as e:
raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))

View File

@@ -19,7 +19,6 @@ 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:
@@ -70,21 +69,6 @@ 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 = '''
@@ -94,15 +78,6 @@ 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
@@ -111,7 +86,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, Constructable, Cacheable
from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable
from ansible.module_utils.six.moves.urllib.parse import urlencode
# 3rd party imports
@@ -124,7 +99,7 @@ except ImportError:
HAS_REQUESTS = False
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
class InventoryModule(BaseInventoryPlugin, Cacheable):
''' Host inventory parser for ansible using Proxmox as source. '''
NAME = 'community.general.proxmox'
@@ -231,45 +206,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, 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)
@@ -278,10 +217,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, 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()))
@@ -291,22 +226,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, 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
if isinstance(value, int) or ',' not in value:
value = value
# split off strings with commas to a dict
else:
# skip over any keys that cannot be processed
try:
value = dict(key.split("=") for key in value.split(","))
@@ -333,12 +256,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, 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()
@@ -370,9 +287,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self.inventory.add_child(nodes_group, node['node'])
# get node IP address
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)
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()))
@@ -394,8 +310,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, 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)
@@ -418,8 +332,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, 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'):
@@ -429,8 +341,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
for member in self._get_members_per_pool(pool['poolid']):
if member.get('name'):
if not member.get('template'):
self.inventory.add_child(pool_group, member['name'])
self.inventory.add_child(pool_group, member['name'])
def parse(self, inventory, loader, path, cache=True):
if not HAS_REQUESTS:
@@ -443,7 +354,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._read_config_data(path)
# get connection host
self.proxmox_url = self.get_option('url').rstrip('/')
self.proxmox_url = self.get_option('url')
self.proxmox_user = self.get_option('user')
self.proxmox_password = self.get_option('password')
self.cache_key = self.get_cache_key(path)

View File

@@ -102,13 +102,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
raise AnsibleError("plugin doesn't match this plugin")
try:
client_id = config['client_id']
if len(client_id) != 32:
if client_id != 32:
raise AnsibleError("client_id must be 32 characters long")
except KeyError:
raise AnsibleError("config missing client_id, a required option")
try:
client_secret = config['client_secret']
if len(client_secret) != 64:
if client_secret != 64:
raise AnsibleError("client_secret must be 64 characters long")
except KeyError:
raise AnsibleError("config missing client_id, a required option")

View File

@@ -216,7 +216,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
yield host not in v
yield True
return all(find_host(host, inventory))
return all([found_host for found_host in find_host(host, inventory)])
def verify_file(self, path):

View File

@@ -81,7 +81,7 @@ class LookupModule(LookupBase):
)
if args:
raise AnsibleError(
"unrecognized arguments to with_sequence: %r" % list(args.keys())
"unrecognized arguments to with_sequence: %r" % args.keys()
)
def run(self, terms, variables=None, **kwargs):

View File

@@ -31,9 +31,7 @@ EXAMPLES = r"""
- name: Template files (explicitly skip directories in order to use the 'src' attribute)
ansible.builtin.template:
src: '{{ item.src }}'
# 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 }}
dest: /web/{{ item.path }}
mode: '{{ item.mode }}'
with_community.general.filetree: web/
when: item.state == 'file'
@@ -43,7 +41,6 @@ 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,7 +63,6 @@ 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')
@@ -79,11 +78,13 @@ class Hiera(object):
rc, output, err = run_cmd("{0} -c {1} {2}".format(
ANSIBLE_HIERA_BIN, ANSIBLE_HIERA_CFG, hiera_key[0]))
return to_text(output.strip())
return output.strip()
class LookupModule(LookupBase):
def run(self, terms, variables=''):
hiera = Hiera()
ret = [hiera.get(terms)]
ret = []
ret.append(hiera.get(terms))
return ret

View File

@@ -82,27 +82,6 @@ EXAMPLES = r"""
| items2dict(key_name='slug',
value_name='itemValue'))['password']
}}
- hosts: localhost
vars:
secret: >-
{{
lookup(
'community.general.tss',
102,
base_url='https://secretserver.domain.com/SecretServer/',
username='user.name',
password='password'
)
}}
tasks:
- ansible.builtin.debug:
msg: >
the password is {{
(secret['items']
| items2dict(key_name='slug',
value_name='itemValue'))['password']
}}
"""
from ansible.errors import AnsibleError, AnsibleOptionsError

View File

@@ -142,8 +142,8 @@ def aws_cvs_host_argument_spec():
return dict(
api_url=dict(required=True, type='str'),
validate_certs=dict(required=False, type='bool', default=True),
api_key=dict(required=True, type='str', no_log=True),
secret_key=dict(required=True, type='str', no_log=True)
api_key=dict(required=True, type='str'),
secret_key=dict(required=True, type='str')
)

View File

@@ -0,0 +1,871 @@
# -*- 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

@@ -130,7 +130,7 @@ class CloudRetry(object):
try:
return f(*args, **kwargs)
except Exception as e:
if isinstance(e, cls.base_class): # pylint: disable=isinstance-second-argument-not-valid-type
if isinstance(e, cls.base_class):
response_code = cls.status_code_from_exception(e)
if cls.found(response_code, catch_extra_error_codes):
msg = "{0}: Retrying in {1} seconds...".format(str(e), delay)

View File

@@ -1,67 +0,0 @@
# -*- 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

@@ -1,234 +0,0 @@
# -*- 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,16 +30,12 @@ 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"
@@ -59,14 +55,13 @@ def keycloak_argument_spec():
:return: argument_spec dict
"""
return dict(
auth_keycloak_url=dict(type='str', aliases=['url'], required=True, no_log=False),
auth_keycloak_url=dict(type='str', aliases=['url'], required=True),
auth_client_id=dict(type='str', default='admin-cli'),
auth_realm=dict(type='str'),
auth_client_secret=dict(type='str', default=None, no_log=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),
auth_realm=dict(type='str', required=True),
auth_client_secret=dict(type='str', default=None),
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)
)
@@ -78,58 +73,41 @@ class KeycloakError(Exception):
pass
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')
def get_token(base_url, validate_certs, auth_realm, client_id,
auth_username, auth_password, client_secret):
if not base_url.lower().startswith(('http', 'https')):
raise KeycloakError("auth_url '%s' should either start with 'http' or 'https'." % base_url)
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,
}
# 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'
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'
}
except KeyError:
raise KeycloakError(
'Could not obtain access token from %s' % auth_url)
class KeycloakAPI(object):
@@ -142,75 +120,6 @@ 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', 'otpconfig_show'):
if method in ('ping', 'config_show'):
data['params'] = [[], {}]
elif method in ('config_mod', 'otpconfig_mod'):
elif method == 'config_mod':
data['params'] = [[], item]
else:
data['params'] = [[name], item]

View File

@@ -87,12 +87,11 @@ 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 = [
user_host_file,
"/etc/ssh/ssh_known_hosts",
"/etc/ssh/ssh_known_hosts2",
"/etc/openssh/ssh_known_hosts",
]
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")
hfiles_not_found = 0
for hf in host_file_list:

View File

@@ -10,7 +10,6 @@ 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):
@@ -25,12 +24,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)
super(ModuleHelperException, self).__init__(*args, **kwargs)
class ArgFormat(object):
"""
Argument formatter for use as a command line parameter. Used in CmdMixin.
Argument formatter
"""
BOOLEAN = 0
PRINTF = 1
@@ -51,8 +50,7 @@ class ArgFormat(object):
def __init__(self, name, fmt=None, style=FORMAT, stars=0):
"""
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.
Creates a new formatter
: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.
@@ -95,33 +93,22 @@ 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(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 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 module_fails_on_exception(func):
@@ -134,12 +121,10 @@ 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:
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)
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)
return wrapper
@@ -153,7 +138,7 @@ class DependencyCtxMgr(object):
self.exc_tb = None
def __enter__(self):
return self
pass
def __exit__(self, exc_type, exc_val, exc_tb):
self.has_it = exc_type is None
@@ -167,157 +152,32 @@ class DependencyCtxMgr(object):
return self.msg or str(self.exc_val)
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
module = {}
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):
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
return self[item]
def __init__(self, module=None):
self.vars = ModuleHelper.VarDict()
self.vars = ModuleHelper.AttrDict()
self.output_dict = dict()
self.facts_dict = dict()
self._changed = False
if module:
self.module = module
if not isinstance(self.module, AnsibleModule):
if isinstance(self.module, dict):
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.update_vars(meta={"output": True}, **kwargs)
self.output_dict.update(kwargs)
def update_facts(self, **kwargs):
self.update_vars(meta={"fact": True}, **kwargs)
self.facts_dict.update(kwargs)
def __init_module__(self):
pass
@@ -328,9 +188,6 @@ 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
@@ -339,25 +196,12 @@ 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.output())
result = dict(self.vars)
result.update(self.output_dict)
if self.facts_name:
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]
result['ansible_facts'] = {self.facts_name: self.facts_dict}
return result
@module_fails_on_exception
@@ -366,7 +210,7 @@ class ModuleHelper(object):
self.__init_module__()
self.__run__()
self.__quit_module__()
self.module.exit_json(changed=self.has_changed(), **self.output)
self.module.exit_json(changed=self.changed, **self.output_dict)
@classmethod
def dependency(cls, name, msg):
@@ -377,9 +221,9 @@ class ModuleHelper(object):
for d in self._dependencies:
if not d.has_it:
self.module.fail_json(changed=False,
exception="\n".join(traceback.format_exception(d.exc_type, d.exc_val, d.exc_tb)),
exception=d.exc_val.__traceback__.format_exc(),
msg=d.text,
**self.output)
**self.output_dict)
class StateMixin(object):
@@ -440,7 +284,7 @@ class CmdMixin(object):
def _calculate_args(self, extra_params=None, params=None):
def add_arg_formatted_param(_cmd_args, arg_format, _value):
args = list(arg_format.to_text(_value))
args = [x for x in arg_format.to_text(_value)]
return _cmd_args + args
def find_format(_param):
@@ -448,35 +292,21 @@ class CmdMixin(object):
extra_params = extra_params or dict()
cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command)
try:
cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True)
except ValueError:
pass
cmd_args[0] = self.module.get_bin_path(cmd_args[0])
param_list = params if params else self.module.params.keys()
for param in param_list:
if isinstance(param, dict):
if len(param) != 1:
raise ModuleHelperException("run_command parameter as a dict must "
"contain only one key: {0}".format(param))
_param = list(param.keys())[0]
fmt = find_format(_param)
value = param[_param]
elif isinstance(param, str):
if param in self.module.argument_spec:
fmt = find_format(param)
value = self.module.params[param]
elif param in extra_params:
fmt = find_format(param)
value = extra_params[param]
else:
self.module.deprecate("Cannot determine value for parameter: {0}. "
"From version 4.0.0 onwards this will generate an exception".format(param),
version="4.0.0", collection_name="community.general")
if param in self.module.argument_spec:
if param not in self.module.params:
continue
fmt = find_format(param)
value = self.module.params[param]
else:
raise ModuleHelperException("run_command parameter must be either a str or a dict: {0}".format(param))
if param not in extra_params:
continue
fmt = find_format(param)
value = extra_params[param]
self.cmd_args = cmd_args
cmd_args = add_arg_formatted_param(cmd_args, fmt, value)
return cmd_args
@@ -485,7 +315,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)
@@ -494,7 +324,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,7 +18,6 @@ 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
@@ -261,10 +260,13 @@ class WapiModule(WapiBase):
else:
proposed_object[key] = self.module.params[key]
# If configure_by_dns is set to False and view is 'default', then delete the default dns
# If configure_by_dns is set to False, then delete the default dns set in the param else throw exception
if not proposed_object.get('configure_for_dns') and proposed_object.get('view') == 'default'\
and ib_obj_type == NIOS_HOST_RECORD:
del proposed_object['view']
elif not proposed_object.get('configure_for_dns') and proposed_object.get('view') != 'default'\
and ib_obj_type == NIOS_HOST_RECORD:
self.module.fail_json(msg='DNS Bypass is not allowed if DNS view is set other than \'default\'')
if ib_obj_ref:
if len(ib_obj_ref) > 1:
@@ -400,11 +402,11 @@ class WapiModule(WapiBase):
if 'ipv4addrs' in proposed_object:
if 'nios_next_ip' in proposed_object['ipv4addrs'][0]['ipv4addr']:
ip_range = check_type_dict(proposed_object['ipv4addrs'][0]['ipv4addr'])['nios_next_ip']
ip_range = self.module._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 = check_type_dict(proposed_object['ipv4addr'])['nios_next_ip']
ip_range = self.module._check_type_dict(proposed_object['ipv4addr'])['nios_next_ip']
proposed_object['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range
return proposed_object
@@ -486,7 +488,7 @@ class WapiModule(WapiBase):
if ('name' in obj_filter):
# gets and returns the current object based on name/old_name passed
try:
name_obj = check_type_dict(obj_filter['name'])
name_obj = self.module._check_type_dict(obj_filter['name'])
old_name = name_obj['old_name']
new_name = name_obj['new_name']
except TypeError:
@@ -500,12 +502,12 @@ class WapiModule(WapiBase):
else:
test_obj_filter = dict([('name', old_name)])
# get the object reference
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=ib_spec.keys())
if ib_obj:
obj_filter['name'] = new_name
else:
test_obj_filter['name'] = new_name
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=ib_spec.keys())
update = True
return ib_obj, update, new_name
if (ib_obj_type == NIOS_HOST_RECORD):
@@ -522,7 +524,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 = check_type_dict(obj_filter['ipv4addr'])
ipaddr_obj = self.module._check_type_dict(obj_filter['ipv4addr'])
ipaddr = ipaddr_obj['old_ipv4addr']
except TypeError:
ipaddr = obj_filter['ipv4addr']
@@ -531,7 +533,7 @@ class WapiModule(WapiBase):
# resolves issue where multiple txt_records with same name and different text
test_obj_filter = obj_filter
try:
text_obj = check_type_dict(obj_filter['text'])
text_obj = self.module._check_type_dict(obj_filter['text'])
txt = text_obj['old_text']
except TypeError:
txt = obj_filter['text']
@@ -539,32 +541,32 @@ class WapiModule(WapiBase):
# check if test_obj_filter is empty copy passed obj_filter
else:
test_obj_filter = obj_filter
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys())
elif (ib_obj_type == NIOS_A_RECORD):
# resolves issue where multiple a_records with same name and different IP address
test_obj_filter = obj_filter
try:
ipaddr_obj = check_type_dict(obj_filter['ipv4addr'])
ipaddr_obj = self.module._check_type_dict(obj_filter['ipv4addr'])
ipaddr = ipaddr_obj['old_ipv4addr']
except TypeError:
ipaddr = obj_filter['ipv4addr']
test_obj_filter['ipv4addr'] = ipaddr
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys())
elif (ib_obj_type == NIOS_TXT_RECORD):
# resolves issue where multiple txt_records with same name and different text
test_obj_filter = obj_filter
try:
text_obj = check_type_dict(obj_filter['text'])
text_obj = self.module._check_type_dict(obj_filter['text'])
txt = text_obj['old_text']
except TypeError:
txt = obj_filter['text']
test_obj_filter['text'] = txt
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys())
elif (ib_obj_type == NIOS_ZONE):
# del key 'restart_if_needed' as nios_zone get_object fails with the key present
temp = ib_spec['restart_if_needed']
del ib_spec['restart_if_needed']
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())
# reinstate restart_if_needed if ib_obj is none, meaning there's no existing nios_zone ref
if not ib_obj:
ib_spec['restart_if_needed'] = temp
@@ -572,12 +574,12 @@ class WapiModule(WapiBase):
# del key 'create_token' as nios_member get_object fails with the key present
temp = ib_spec['create_token']
del ib_spec['create_token']
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())
if temp:
# reinstate 'create_token' key
ib_spec['create_token'] = temp
else:
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=list(ib_spec.keys()))
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())
return ib_obj, update, new_name
def on_update(self, proposed_object, ib_spec):

View File

@@ -1,370 +0,0 @@
# -*- 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,16 +39,14 @@ class OpenNebulaModule:
wait_timeout=dict(type='int', default=300),
)
def __init__(self, argument_spec, supports_check_mode=False, mutually_exclusive=None, required_one_of=None, required_if=None):
def __init__(self, argument_spec, supports_check_mode=False, mutually_exclusive=None):
module_args = OpenNebulaModule.common_args.copy()
module_args = OpenNebulaModule.common_args
module_args.update(argument_spec)
self.module = AnsibleModule(argument_spec=module_args,
supports_check_mode=supports_check_mode,
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of,
required_if=required_if)
mutually_exclusive=mutually_exclusive)
self.result = dict(changed=False,
original_message='',
message='')

View File

@@ -90,7 +90,7 @@ def get_common_arg_spec(supports_create=False, supports_wait=False):
config_profile_name=dict(type="str", default="DEFAULT"),
api_user=dict(type="str"),
api_user_fingerprint=dict(type="str", no_log=True),
api_user_key_file=dict(type="path"),
api_user_key_file=dict(type="str"),
api_user_key_pass_phrase=dict(type="str", no_log=True),
auth_type=dict(
type="str",
@@ -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", no_log=False),
key_by=dict(type="list"),
force_create=dict(type="bool", default=False),
)

View File

@@ -19,10 +19,11 @@ PATCH_HEADERS = {'content-type': 'application/json', 'accept': 'application/json
'OData-Version': '4.0'}
DELETE_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'}
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.'
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'
class RedfishUtils(object):
@@ -38,34 +39,13 @@ 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=req_headers,
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
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,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
data = json.loads(to_native(resp.read()))
@@ -86,16 +66,14 @@ 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=req_headers, method="POST",
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
headers=POST_HEADERS, method="POST",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, 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,
@@ -109,10 +87,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, 'headers': headers, 'resp': resp}
return {'ret': True, 'resp': resp}
def patch_request(self, uri, pyld):
req_headers = dict(PATCH_HEADERS)
headers = PATCH_HEADERS
r = self.get_request(uri)
if r['ret']:
# Get etag from etag header or @odata.etag property
@@ -120,13 +98,15 @@ class RedfishUtils(object):
if not etag:
etag = r['data'].get('@odata.etag')
if etag:
req_headers['If-Match'] = etag
username, password, basic_auth = self._auth_params(req_headers)
# Make copy of headers and add If-Match header
headers = dict(headers)
headers['If-Match'] = etag
try:
resp = open_url(uri, data=json.dumps(pyld),
headers=req_headers, method="PATCH",
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
headers=headers, method="PATCH",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, validate_certs=False,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
except HTTPError as e:
@@ -145,14 +125,13 @@ 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=req_headers, method="DELETE",
url_username=username, url_password=password,
force_basic_auth=basic_auth, validate_certs=False,
headers=DELETE_HEADERS, method="DELETE",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
force_basic_auth=True, validate_certs=False,
follow_redirects='all',
use_proxy=True, timeout=self.timeout)
except HTTPError as e:
@@ -266,7 +245,8 @@ class RedfishUtils(object):
'ret': False,
'msg': "System resource %s not found" % self.resource_id}
elif len(self.systems_uris) > 1:
self.module.fail_json(msg=FAIL_MSG % {'resource': 'System'})
self.module.deprecate(DEPRECATE_MSG % {'resource': 'System'},
version='3.0.0', collection_name='community.general') # was Ansible 2.14
return {'ret': True}
def _find_updateservice_resource(self):
@@ -316,7 +296,8 @@ class RedfishUtils(object):
'ret': False,
'msg': "Chassis resource %s not found" % self.resource_id}
elif len(self.chassis_uris) > 1:
self.module.fail_json(msg=FAIL_MSG % {'resource': 'Chassis'})
self.module.deprecate(DEPRECATE_MSG % {'resource': 'Chassis'},
version='3.0.0', collection_name='community.general') # was Ansible 2.14
return {'ret': True}
def _find_managers_resource(self):
@@ -345,7 +326,8 @@ class RedfishUtils(object):
'ret': False,
'msg': "Manager resource %s not found" % self.resource_id}
elif len(self.manager_uris) > 1:
self.module.fail_json(msg=FAIL_MSG % {'resource': 'Manager'})
self.module.deprecate(DEPRECATE_MSG % {'resource': 'Manager'},
version='3.0.0', collection_name='community.general') # was Ansible 2.14
return {'ret': True}
def _get_all_action_info_values(self, action):
@@ -487,7 +469,7 @@ class RedfishUtils(object):
controller_results = []
# Get these entries, but does not fail if not found
properties = ['CacheSummary', 'FirmwareVersion', 'Identifiers',
'Location', 'Manufacturer', 'Model', 'Name', 'Id',
'Location', 'Manufacturer', 'Model', 'Name',
'PartNumber', 'SerialNumber', 'SpeedGbps', 'Status']
key = "StorageControllers"
@@ -1214,54 +1196,6 @@ 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)
@@ -1766,7 +1700,7 @@ class RedfishUtils(object):
chassis_results = []
# Get these entries, but does not fail if not found
properties = ['Name', 'Id', 'ChassisType', 'PartNumber', 'AssetTag',
properties = ['ChassisType', 'PartNumber', 'AssetTag',
'Manufacturer', 'IndicatorLED', 'SerialNumber', 'Model']
# Go through list
@@ -1790,7 +1724,7 @@ class RedfishUtils(object):
fan_results = []
key = "Thermal"
# Get these entries, but does not fail if not found
properties = ['Name', 'FanName', 'Reading', 'ReadingUnits', 'Status']
properties = ['FanName', 'Reading', 'ReadingUnits', 'Status']
# Go through list
for chassis_uri in self.chassis_uris:
@@ -1902,8 +1836,8 @@ class RedfishUtils(object):
cpu_results = []
key = "Processors"
# Get these entries, but does not fail if not found
properties = ['Id', 'Name', 'Manufacturer', 'Model', 'MaxSpeedMHz',
'TotalCores', 'TotalThreads', 'Status']
properties = ['Id', 'Manufacturer', 'Model', 'MaxSpeedMHz', 'TotalCores',
'TotalThreads', 'Status']
# Search for 'key' entry and extract URI from it
response = self.get_request(self.root_uri + systems_uri)
@@ -1952,7 +1886,7 @@ class RedfishUtils(object):
memory_results = []
key = "Memory"
# Get these entries, but does not fail if not found
properties = ['Id', 'SerialNumber', 'MemoryDeviceType', 'PartNumber',
properties = ['SerialNumber', 'MemoryDeviceType', 'PartNumber',
'MemoryLocation', 'RankCount', 'CapacityMiB', 'OperatingMemoryModes', 'Status', 'Manufacturer', 'Name']
# Search for 'key' entry and extract URI from it
@@ -2009,7 +1943,7 @@ class RedfishUtils(object):
nic_results = []
key = "EthernetInterfaces"
# Get these entries, but does not fail if not found
properties = ['Name', 'Id', 'Description', 'FQDN', 'IPv4Addresses', 'IPv6Addresses',
properties = ['Description', 'FQDN', 'IPv4Addresses', 'IPv6Addresses',
'NameServers', 'MACAddress', 'PermanentMACAddress',
'SpeedMbps', 'MTUSize', 'AutoNeg', 'Status']
@@ -2434,7 +2368,7 @@ class RedfishUtils(object):
properties = ['Status', 'HostName', 'PowerState', 'Model', 'Manufacturer',
'PartNumber', 'SystemType', 'AssetTag', 'ServiceTag',
'SerialNumber', 'SKU', 'BiosVersion', 'MemorySummary',
'ProcessorSummary', 'TrustedModules', 'Name', 'Id']
'ProcessorSummary', 'TrustedModules']
response = self.get_request(self.root_uri + systems_uri)
if response['ret'] is False:
@@ -2698,7 +2632,7 @@ class RedfishUtils(object):
if response['ret'] is False:
return response
data = response['data']
if '"' + nic_addr.lower() + '"' in str(data).lower() or "'" + nic_addr.lower() + "'" in str(data).lower():
if '"' + nic_addr + '"' in str(data) or "'" + nic_addr + "'" in str(data):
target_ethernet_uri = uri
target_ethernet_current_setting = data
break
@@ -2742,10 +2676,6 @@ 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

@@ -0,0 +1,56 @@
# -*- 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

@@ -0,0 +1,163 @@
# -*- 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

@@ -84,7 +84,7 @@ class UTM:
raise UTMModuleConfigurationError(
"The keys " + to_native(
self.change_relevant_keys) + " to check are not in the modules keys:\n" + to_native(
list(module.params.keys())))
module.params.keys()))
def execute(self):
try:

View File

@@ -20,6 +20,7 @@ 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

@@ -0,0 +1 @@
cloud/alicloud/ali_instance_facts.py

View File

@@ -0,0 +1 @@
ali_instance_info.py

View File

@@ -383,6 +383,9 @@ 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,8 +102,7 @@ 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 []
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]
args = ['atomic', '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)
@@ -113,8 +112,7 @@ def do_install(module, mode, rootfs, container, image, values_list, backend):
def do_update(module, container, image, values_list):
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'containers', 'update', "--rebase=%s" % image] + values_list + [container]
args = ['atomic', '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)
@@ -124,8 +122,7 @@ def do_update(module, container, image, values_list):
def do_uninstall(module, name, backend):
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'uninstall', "--storage=%s" % backend, name]
args = ['atomic', '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)
@@ -133,8 +130,7 @@ def do_uninstall(module, name, backend):
def do_rollback(module, name):
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'containers', 'rollback', name]
args = ['atomic', 'containers', 'rollback', name]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
@@ -152,12 +148,14 @@ 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_bin, 'containers', 'list', '--no-trunc', '-n', '--all', '-f', 'backend=%s' % backend, '-f', 'container=%s' % name]
args = ['atomic', '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)
@@ -196,7 +194,9 @@ def main():
module.fail_json(msg="values is supported only with user or system mode")
# Verify that the platform supports atomic command
dummy = module.get_bin_path('atomic', required=True)
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)
try:
core(module)

View File

@@ -57,14 +57,18 @@ from ansible.module_utils._text import to_native
def core(module):
revision = module.params['revision']
atomic_bin = module.get_bin_path('atomic', required=True)
args = []
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
if revision == 'latest':
args = [atomic_bin, 'host', 'upgrade']
args = ['atomic', 'host', 'upgrade']
else:
args = [atomic_bin, 'host', 'deploy', revision]
args = ['atomic', 'host', 'deploy', revision]
out = {}
err = {}
rc = 0
rc, out, err = module.run_command(args, check_rc=False)

View File

@@ -73,8 +73,7 @@ from ansible.module_utils._text import to_native
def do_upgrade(module, image):
atomic_bin = module.get_bin_path('atomic')
args = [atomic_bin, 'update', '--force', image]
args = ['atomic', '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)
@@ -92,21 +91,20 @@ 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_bin, 'pull', "--storage=%s" % backend, image]
args = ['atomic', '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_bin, 'run', "--storage=%s" % backend, image]
args = ['atomic', '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)
@@ -114,7 +112,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_bin, 'images', 'delete', "--storage=%s" % backend, image]
args = ['atomic', '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)
@@ -128,11 +126,11 @@ def core(module):
is_upgraded = do_upgrade(module, image)
if started:
args = [atomic_bin, 'run', image]
args = ['atomic', 'run', image]
else:
args = [atomic_bin, 'install', image]
args = ['atomic', 'install', image]
elif state == 'absent':
args = [atomic_bin, 'uninstall', image]
args = ['atomic', 'uninstall', image]
rc, out, err = module.run_command(args, check_rc=False)
@@ -157,7 +155,9 @@ def main():
)
# Verify that the platform supports atomic command
dummy = module.get_bin_path('atomic', required=True)
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)
try:
core(module)

View File

@@ -30,6 +30,10 @@ 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
@@ -181,6 +185,7 @@ 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

@@ -32,7 +32,6 @@ options:
- A list of recipient email ids to notify the alert.
This is required for state 'present'
type: list
elements: str
metric:
description:
- The metric on which to measure the condition that will trigger the alert.
@@ -221,7 +220,7 @@ class ClcAlertPolicy:
name=dict(),
id=dict(),
alias=dict(required=True),
alert_recipients=dict(type='list', elements='str'),
alert_recipients=dict(type='list'),
metric=dict(
choices=[
'cpu',

View File

@@ -18,7 +18,6 @@ options:
- A list of server Ids to deploy the blue print package.
type: list
required: True
elements: str
package_id:
description:
- The package id of the blue print.
@@ -165,7 +164,7 @@ class ClcBlueprintPackage:
:return: the package dictionary object
"""
argument_spec = dict(
server_ids=dict(type='list', elements='str', required=True),
server_ids=dict(type='list', required=True),
package_id=dict(required=True),
package_params=dict(type='dict', default={}),
wait=dict(default=True), # @FIXME should be bool?

View File

@@ -29,20 +29,17 @@ options:
- The list of source addresses for traffic on the originating firewall.
This is required when state is 'present'
type: list
elements: str
destination:
description:
- The list of destination addresses for traffic on the terminating firewall.
This is required when state is 'present'
type: list
elements: str
ports:
description:
- The list of ports associated with the policy.
TCP and UDP can take in single ports or port ranges.
- "Example: C(['any', 'icmp', 'TCP/123', 'UDP/123', 'TCP/123-456', 'UDP/123-456'])."
type: list
elements: str
firewall_policy_id:
description:
- Id of the firewall policy. This is required to update or delete an existing firewall policy
@@ -220,9 +217,9 @@ class ClcFirewallPolicy:
source_account_alias=dict(required=True),
destination_account_alias=dict(),
firewall_policy_id=dict(),
ports=dict(type='list', elements='str'),
source=dict(type='list', elements='str'),
destination=dict(type='list', elements='str'),
ports=dict(type='list'),
source=dict(type='list'),
destination=dict(type='list'),
wait=dict(default=True), # @FIXME type=bool
state=dict(default='present', choices=['present', 'absent']),
enabled=dict(default=True, choices=[True, False])

View File

@@ -53,7 +53,6 @@ options:
- A list of nodes that needs to be added to the load balancer pool
type: list
default: []
elements: dict
status:
description:
- The status of the loadbalancer
@@ -870,7 +869,7 @@ class ClcLoadBalancer:
port=dict(choices=[80, 443]),
method=dict(choices=['leastConnection', 'roundRobin']),
persistence=dict(choices=['standard', 'sticky']),
nodes=dict(type='list', default=[], elements='dict'),
nodes=dict(type='list', default=[]),
status=dict(default='enabled', choices=['enabled', 'disabled']),
state=dict(
default='present',

View File

@@ -18,7 +18,6 @@ options:
- A list of server Ids to modify.
type: list
required: True
elements: str
cpu:
description:
- How many CPUs to update on the server
@@ -397,7 +396,7 @@ class ClcModifyServer:
:return: argument spec dictionary
"""
argument_spec = dict(
server_ids=dict(type='list', required=True, elements='str'),
server_ids=dict(type='list', required=True),
state=dict(default='present', choices=['present', 'absent']),
cpu=dict(),
memory=dict(),

View File

@@ -23,13 +23,11 @@ options:
description:
- A list of ports to expose. This is required when state is 'present'
type: list
elements: int
server_ids:
description:
- A list of servers to create public ips on.
type: list
required: True
elements: str
state:
description:
- Determine whether to create or delete public IPs. If present module will not create a second public ip if one
@@ -195,9 +193,9 @@ class ClcPublicIp(object):
:return: argument spec dictionary
"""
argument_spec = dict(
server_ids=dict(type='list', required=True, elements='str'),
server_ids=dict(type='list', required=True),
protocol=dict(default='TCP', choices=['TCP', 'UDP', 'ICMP']),
ports=dict(type='list', elements='int'),
ports=dict(type='list'),
wait=dict(type='bool', default=True),
state=dict(default='present', choices=['present', 'absent']),
)

View File

@@ -17,7 +17,6 @@ options:
description:
- The list of additional disks for the server
type: list
elements: dict
default: []
add_public_ip:
description:
@@ -67,7 +66,6 @@ options:
- The list of custom fields to set on the server.
type: list
default: []
elements: dict
description:
description:
- The description to set for the server.
@@ -113,7 +111,6 @@ options:
description:
- The list of blue print packages to run on the server after its created.
type: list
elements: dict
default: []
password:
description:
@@ -133,7 +130,6 @@ options:
description:
- A list of ports to allow on the firewall to the servers public ip, if add_public_ip is set to True.
type: list
elements: dict
default: []
secondary_dns:
description:
@@ -145,7 +141,6 @@ options:
A list of server Ids to insure are started, stopped, or absent.
type: list
default: []
elements: str
source_server_password:
description:
- The password for the source server if a clone is specified.
@@ -580,8 +575,8 @@ class ClcServer:
type=dict(default='standard', choices=['standard', 'hyperscale', 'bareMetal']),
primary_dns=dict(default=None),
secondary_dns=dict(default=None),
additional_disks=dict(type='list', default=[], elements='dict'),
custom_fields=dict(type='list', default=[], elements='dict'),
additional_disks=dict(type='list', default=[]),
custom_fields=dict(type='list', default=[]),
ttl=dict(default=None),
managed_os=dict(type='bool', default=False),
description=dict(default=None),
@@ -591,7 +586,7 @@ class ClcServer:
anti_affinity_policy_name=dict(default=None),
alert_policy_id=dict(default=None),
alert_policy_name=dict(default=None),
packages=dict(type='list', default=[], elements='dict'),
packages=dict(type='list', default=[]),
state=dict(
default='present',
choices=[
@@ -602,7 +597,7 @@ class ClcServer:
count=dict(type='int', default=1),
exact_count=dict(type='int', default=None),
count_group=dict(),
server_ids=dict(type='list', default=[], elements='str'),
server_ids=dict(type='list', default=[]),
add_public_ip=dict(type='bool', default=False),
public_ip_protocol=dict(
default='TCP',
@@ -610,7 +605,7 @@ class ClcServer:
'TCP',
'UDP',
'ICMP']),
public_ip_ports=dict(type='list', default=[], elements='dict'),
public_ip_ports=dict(type='list', default=[]),
configuration_id=dict(default=None),
os_type=dict(default=None,
choices=[

View File

@@ -18,7 +18,6 @@ options:
- The list of CLC server Ids.
type: list
required: True
elements: str
expiration_days:
description:
- The number of days to keep the server snapshot before it expires.
@@ -331,7 +330,7 @@ class ClcSnapshot:
:return: the package dictionary object
"""
argument_spec = dict(
server_ids=dict(type='list', required=True, elements='str'),
server_ids=dict(type='list', required=True),
expiration_days=dict(default=7, type='int'),
wait=dict(default=True),
state=dict(

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

@@ -26,7 +26,6 @@ options:
- Heroku API key
apps:
type: list
elements: str
description:
- List of Heroku App names
required: true
@@ -110,7 +109,7 @@ def main():
argument_spec = HerokuHelper.heroku_argument_spec()
argument_spec.update(
user=dict(required=True, type='str'),
apps=dict(required=True, type='list', elements='str'),
apps=dict(required=True, type='list'),
suppress_invitation=dict(default=False, type='bool'),
state=dict(default='present', type='str', choices=['present', 'absent']),
)

View File

@@ -543,7 +543,7 @@ def build_module():
snapshot_id=dict(type='str')
)),
vpc_id=dict(type='str', required=True),
admin_pass=dict(type='str', no_log=True),
admin_pass=dict(type='str'),
data_volumes=dict(type='list', elements='dict', options=dict(
volume_id=dict(type='str', required=True),
device=dict(type='str')

View File

@@ -28,6 +28,7 @@ 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:
@@ -35,12 +36,14 @@ 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
@@ -53,30 +56,25 @@ options:
group labelling is deprecated but still supported. The encouraged
method for marking instances is to use tags.
type: str
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
required: false
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:
description:
- 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:
description:
- The desired instance state.
@@ -208,8 +206,9 @@ 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-collections/community.general/issues'
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'
)
@@ -241,16 +240,15 @@ def initialise_module():
no_log=True,
fallback=(env_fallback, ['LINODE_ACCESS_TOKEN']),
),
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'),
authorized_keys=dict(type='list', 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', required=False),
type=dict(type='str', required=False),
stackscript_id=dict(type='int', required=False),
stackscript_data=dict(type='dict', required=False),
),
supports_check_mode=False,
required_one_of=(
@@ -290,7 +288,6 @@ 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

@@ -730,7 +730,7 @@ class LxcContainerManagement(object):
for option_line in container_config:
# Look for key in config
if keyre.match(option_line):
dummy, _value = option_line.split('=', 1)
_, _value = option_line.split('=', 1)
config_value = ' '.join(_value.split())
line_index = container_config.index(option_line)
# If the sanitized values don't match replace them
@@ -953,7 +953,7 @@ class LxcContainerManagement(object):
"""
self.container = self.get_container_bind()
for dummy in xrange(timeout):
for _ in xrange(timeout):
if self._get_state() != 'running':
self.container.start()
self.state_change = True
@@ -1006,7 +1006,7 @@ class LxcContainerManagement(object):
:type timeout: ``int``
"""
for dummy in xrange(timeout):
for _ in xrange(timeout):
if not self._container_exists(container_name=self.container_name, lxc_path=self.lxc_path):
break
@@ -1662,7 +1662,7 @@ def main():
),
backing_store=dict(
type='str',
choices=list(LXC_BACKING_STORE.keys()),
choices=LXC_BACKING_STORE.keys(),
default='dir'
),
template_options=dict(
@@ -1699,7 +1699,7 @@ def main():
type='path'
),
state=dict(
choices=list(LXC_ANSIBLE_STATES.keys()),
choices=LXC_ANSIBLE_STATES.keys(),
default='started'
),
container_command=dict(
@@ -1733,7 +1733,7 @@ def main():
type='path',
),
archive_compression=dict(
choices=list(LXC_COMPRESSION_MAP.keys()),
choices=LXC_COMPRESSION_MAP.keys(),
default='gzip'
)
),

View File

@@ -45,7 +45,6 @@ options:
description:
- Profile to be used by the container
type: list
elements: str
devices:
description:
- 'The devices for the container
@@ -133,14 +132,14 @@ options:
- If not specified, it defaults to C(${HOME}/.config/lxc/client.key).
required: false
aliases: [ key_file ]
type: path
type: str
client_cert:
description:
- The client certificate file path.
- If not specified, it defaults to C(${HOME}/.config/lxc/client.crt).
required: false
aliases: [ cert_file ]
type: path
type: str
trust_password:
description:
- The client trusted password.
@@ -659,13 +658,12 @@ def main():
),
profiles=dict(
type='list',
elements='str',
),
source=dict(
type='dict',
),
state=dict(
choices=list(LXD_ANSIBLE_STATES.keys()),
choices=LXD_ANSIBLE_STATES.keys(),
default='started'
),
target=dict(
@@ -692,11 +690,11 @@ def main():
default='unix:/var/snap/lxd/common/lxd/unix.socket'
),
client_key=dict(
type='path',
type='str',
aliases=['key_file']
),
client_cert=dict(
type='path',
type='str',
aliases=['cert_file']
),
trust_password=dict(type='str', no_log=True)

View File

@@ -2,12 +2,12 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2016, Hiroaki Nakamura <hnakamur@gmail.com>
# Copyright: (c) 2020, 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 = '''
---
module: lxd_profile
@@ -52,14 +52,6 @@ options:
See U(https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-11)
required: false
type: str
merge_profile:
description:
- Merge the configuration of the present profile with the new desired configuration,
instead of replacing it.
required: false
default: false
type: bool
version_added: 2.1.0
state:
choices:
- present
@@ -87,14 +79,14 @@ options:
- If not specified, it defaults to C($HOME/.config/lxc/client.key).
required: false
aliases: [ key_file ]
type: path
type: str
client_cert:
description:
- The client certificate file path.
- If not specified, it defaults to C($HOME/.config/lxc/client.crt).
required: false
aliases: [ cert_file ]
type: path
type: str
trust_password:
description:
- The client trusted password.
@@ -150,23 +142,6 @@ EXAMPLES = '''
parent: br0
type: nic
# An example for modify/merge a profile
- hosts: localhost
connection: local
tasks:
- name: Merge a profile
community.general.lxd_profile:
merge_profile: true
name: macvlan
state: present
config: {}
description: my macvlan profile
devices:
eth0:
nictype: macvlan
parent: br0
type: nic
# An example for deleting a profile
- hosts: localhost
connection: local
@@ -206,6 +181,7 @@ actions:
'''
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException
@@ -290,7 +266,7 @@ class LXDProfileManagement(object):
self._create_profile()
else:
self.module.fail_json(
msg='new_name must not be set when the profile does not exist and the state is present',
msg='new_name must not be set when the profile does not exist and the specified state is present',
changed=False)
else:
if self.new_name is not None and self.new_name != self.name:
@@ -331,96 +307,10 @@ class LXDProfileManagement(object):
self._needs_to_change_profile_config('devices')
)
def _merge_dicts(self, source, destination):
"""Merge Dictionarys
Get a list of filehandle numbers from logger to be handed to
DaemonContext.files_preserve
Args:
dict(source): source dict
dict(destination): destination dict
Kwargs:
None
Raises:
None
Returns:
dict(destination): merged dict"""
for key, value in source.items():
if isinstance(value, dict):
# get node or create one
node = destination.setdefault(key, {})
self._merge_dicts(value, node)
else:
destination[key] = value
return destination
def _merge_config(self, config):
""" merge profile
Merge Configuration of the present profile and the new desired configitems
Args:
dict(config): Dict with the old config in 'metadata' and new config in 'config'
Kwargs:
None
Raises:
None
Returns:
dict(config): new config"""
# merge or copy the sections from the existing profile to 'config'
for item in ['config', 'description', 'devices', 'name', 'used_by']:
if item in config:
config[item] = self._merge_dicts(config['metadata'][item], config[item])
else:
config[item] = config['metadata'][item]
# merge or copy the sections from the ansible-task to 'config'
return self._merge_dicts(self.config, config)
def _generate_new_config(self, config):
""" rebuild profile
Rebuild the Profile by the configuration provided in the play.
Existing configurations are discarded.
This ist the default behavior.
Args:
dict(config): Dict with the old config in 'metadata' and new config in 'config'
Kwargs:
None
Raises:
None
Returns:
dict(config): new config"""
def _apply_profile_configs(self):
config = self.old_profile_json.copy()
for k, v in self.config.items():
config[k] = v
return config
def _apply_profile_configs(self):
""" Selection of the procedure: rebuild or merge
The standard behavior is that all information not contained
in the play is discarded.
If "merge_profile" is provides in the play and "True", then existing
configurations from the profile and new ones defined are merged.
Args:
None
Kwargs:
None
Raises:
None
Returns:
None"""
config = self.old_profile_json.copy()
if self.module.params['merge_profile']:
config = self._merge_config(config)
else:
config = self._generate_new_config(config)
# upload config to lxd
self.client.do('PUT', '/1.0/profiles/{0}'.format(self.name), config)
self.actions.append('apply_profile_configs')
@@ -481,10 +371,6 @@ def main():
devices=dict(
type='dict',
),
merge_profile=dict(
type='bool',
default=False
),
state=dict(
choices=PROFILES_STATES,
default='present'
@@ -498,11 +384,11 @@ def main():
default='unix:/var/snap/lxd/common/lxd/unix.socket'
),
client_key=dict(
type='path',
type='str',
aliases=['key_file']
),
client_cert=dict(
type='path',
type='str',
aliases=['cert_file']
),
trust_password=dict(type='str', no_log=True)

View File

@@ -0,0 +1 @@
memset_memstore_info.py

View File

@@ -151,6 +151,9 @@ 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

@@ -0,0 +1 @@
memset_server_info.py

View File

@@ -276,6 +276,9 @@ 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

@@ -0,0 +1,216 @@
#!/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

@@ -0,0 +1,503 @@
#!/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']),
instance_key=dict(type='str', aliases=['key']),
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,6 +17,7 @@ options:
password:
description:
- the instance root password
- required only for C(state=present)
type: str
hostname:
description:
@@ -123,15 +124,6 @@ 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
@@ -353,6 +345,7 @@ EXAMPLES = r'''
state: absent
'''
import os
import time
import traceback
from distutils.version import LooseVersion
@@ -363,7 +356,7 @@ try:
except ImportError:
HAS_PROXMOXER = False
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
@@ -488,7 +481,7 @@ def main():
module = AnsibleModule(
argument_spec=dict(
api_host=dict(required=True),
api_password=dict(no_log=True, fallback=(env_fallback, ['PROXMOX_PASSWORD'])),
api_password=dict(no_log=True),
api_token_id=dict(no_log=True),
api_token_secret=dict(no_log=True),
api_user=dict(required=True),
@@ -515,17 +508,13 @@ 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),
description=dict(type='str'),
hookscript=dict(type='str'),
proxmox_default_behavior=dict(type='str', choices=['compatibility', 'no_defaults']),
),
required_if=[('state', 'present', ['node', 'hostname', 'ostemplate'])],
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
)
)
if not HAS_PROXMOXER:
@@ -572,7 +561,13 @@ def main():
module.params[param] = value
auth_args = {'user': api_user}
if not api_token_id:
if not (api_token_id and api_token_secret):
# If password not set get it from PROXMOX_PASSWORD env
if not api_password:
try:
api_password = os.environ['PROXMOX_PASSWORD']
except KeyError as e:
module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable')
auth_args['password'] = api_password
else:
auth_args['token_name'] = api_token_id
@@ -604,6 +599,8 @@ def main():
# If no vmid was passed, there cannot be another VM named 'hostname'
if not module.params['vmid'] and get_vmid(proxmox, hostname) and not module.params['force']:
module.exit_json(changed=False, msg="VM with hostname %s already exists and has ID number %s" % (hostname, get_vmid(proxmox, hostname)[0]))
elif not (node, module.params['hostname'] and module.params['password'] and module.params['ostemplate']):
module.fail_json(msg='node, hostname, password and ostemplate are mandatory for creating vm')
elif not node_check(proxmox, node):
module.fail_json(msg="node '%s' not exists in cluster" % node)
elif not content_check(proxmox, node, module.params['ostemplate'], template_store):
@@ -625,7 +622,7 @@ def main():
searchdomain=module.params['searchdomain'],
force=int(module.params['force']),
pubkey=module.params['pubkey'],
features=",".join(module.params['features']) if module.params['features'] is not None else None,
features=",".join(module.params['features'] or []),
unprivileged=int(module.params['unprivileged']),
description=module.params['description'],
hookscript=module.params['hookscript'])
@@ -696,13 +693,7 @@ 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)
delete_params = {}
if module.params['purge']:
delete_params['purge'] = 1
taskid = getattr(proxmox.nodes(vm[0]['node']), VZ_TYPE).delete(vmid, **delete_params)
taskid = getattr(proxmox.nodes(vm[0]['node']), VZ_TYPE).delete(vmid)
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 (@tleguern) <tleguern at bouledef.eu>
# Copyright: Tristan Le Guern (@Aversiste) <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 (@tleguern)
author: Tristan Le Guern (@Aversiste)
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 (@tleguern)
author: Tristan Le Guern (@Aversiste)
extends_documentation_fragment: community.general.proxmox.documentation
'''

View File

@@ -31,9 +31,6 @@ options:
description:
- Pass arbitrary arguments to kvm.
- This option is for experts only!
- If I(proxmox_default_behavior) is set to C(compatiblity) (the default value), this
option has a default of C(-serial unix:/var/run/qemu-server/<vmid>.serial,server,nowait).
Note that the default value of I(proxmox_default_behavior) changes in community.general 4.0.0.
type: str
autostart:
description:
@@ -425,14 +422,6 @@ 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.
@@ -741,23 +730,46 @@ EXAMPLES = '''
'''
RETURN = '''
devices:
description: The list of devices created or used.
returned: success
type: dict
sample: '
{
"ide0": "VMS_LVM:vm-115-disk-1",
"ide1": "VMs:115/vm-115-disk-3.raw",
"virtio0": "VMS_LVM:vm-115-disk-2",
"virtio1": "VMs:115/vm-115-disk-1.qcow2",
"virtio2": "VMs:115/vm-115-disk-2.raw"
}'
mac:
description: List of mac address created and net[n] attached. Useful when you want to use provision systems like Foreman via PXE.
returned: success
type: dict
sample: '
{
"net0": "3E:6E:97:D2:31:9F",
"net1": "B6:A1:FC:EF:78:A4"
}'
vmid:
description: The VM vmid.
returned: success
type: int
sample: 115
description: The VM vmid.
returned: success
type: int
sample: 115
status:
description: The current virtual machine status.
returned: success, not clone, not absent, not update
type: str
sample: running
msg:
description: A short message
returned: always
type: str
sample: "VM kropta with vmid = 110 is running"
description:
- The current virtual machine status.
- Returned only when C(state=current)
returned: success
type: dict
sample: '{
"changed": false,
"msg": "VM kropta with vmid = 110 is running",
"status": "running"
}'
'''
import os
import re
import time
import traceback
@@ -770,7 +782,7 @@ try:
except ImportError:
HAS_PROXMOXER = False
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
@@ -866,7 +878,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', 'tags']
only_v6 = ['ciuser', 'cipassword', 'sshkeys', 'ipconfig']
# valide clone parameters
valid_clone_params = ['format', 'full', 'pool', 'snapname', 'storage', 'target']
@@ -936,16 +948,9 @@ 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
# -args and skiplock require root@pam user
if module.params['api_user'] == "root@pam" and module.params['args'] is None:
if not update and module.params['proxmox_default_behavior'] == 'compatibility':
if not update:
kwargs['args'] = vm_args
elif module.params['api_user'] == "root@pam" and module.params['args'] is not None:
kwargs['args'] = module.params['args']
@@ -1010,7 +1015,7 @@ def main():
agent=dict(type='bool'),
args=dict(type='str'),
api_host=dict(required=True),
api_password=dict(no_log=True, fallback=(env_fallback, ['PROXMOX_PASSWORD'])),
api_password=dict(no_log=True),
api_token_id=dict(no_log=True),
api_token_secret=dict(no_log=True),
api_user=dict(required=True),
@@ -1023,12 +1028,12 @@ def main():
cipassword=dict(type='str', no_log=True),
citype=dict(type='str', choices=['nocloud', 'configdrive2']),
ciuser=dict(type='str'),
clone=dict(type='str'),
clone=dict(type='str', default=None),
cores=dict(type='int'),
cpu=dict(type='str'),
cpulimit=dict(type='int'),
cpuunits=dict(type='int'),
delete=dict(type='str'),
delete=dict(type='str', default=None),
description=dict(type='str'),
digest=dict(type='str'),
force=dict(type='bool'),
@@ -1051,7 +1056,7 @@ def main():
name=dict(type='str'),
nameservers=dict(type='list', elements='str'),
net=dict(type='dict'),
newid=dict(type='int'),
newid=dict(type='int', default=None),
node=dict(),
numa=dict(type='dict'),
numa_enabled=dict(type='bool'),
@@ -1072,13 +1077,12 @@ def main():
smbios=dict(type='str'),
snapname=dict(type='str'),
sockets=dict(type='int'),
sshkeys=dict(type='str', no_log=False),
sshkeys=dict(type='str'),
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'),
@@ -1088,14 +1092,13 @@ def main():
vcpus=dict(type='int'),
vga=dict(choices=['std', 'cirrus', 'vmware', 'qxl', 'serial0', 'serial1', 'serial2', 'serial3', 'qxl2', 'qxl3', 'qxl4']),
virtio=dict(type='dict'),
vmid=dict(type='int'),
vmid=dict(type='int', default=None),
watchdog=dict(),
proxmox_default_behavior=dict(type='str', choices=['compatibility', 'no_defaults']),
),
mutually_exclusive=[('delete', 'revert'), ('delete', 'update'), ('revert', 'update'), ('clone', 'update'), ('clone', 'delete'), ('clone', 'revert')],
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')],
required_if=[('state', 'present', ['node'])],
required_one_of=[('name', 'vmid',)],
required_if=[('state', 'present', ['node'])]
)
if not HAS_PROXMOXER:
@@ -1137,6 +1140,7 @@ def main():
cores=1,
cpu='kvm64',
cpuunits=1000,
force=False,
format='qcow2',
kvm=True,
memory=512,
@@ -1155,6 +1159,12 @@ def main():
auth_args = {'user': api_user}
if not (api_token_id and api_token_secret):
# If password not set get it from PROXMOX_PASSWORD env
if not api_password:
try:
api_password = os.environ['PROXMOX_PASSWORD']
except KeyError:
module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable')
auth_args['password'] = api_password
else:
auth_args['token_name'] = api_token_id
@@ -1197,36 +1207,36 @@ def main():
# Ensure source VM id exists when cloning
if not get_vm(proxmox, vmid):
module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid)
module.fail_json(msg='VM with vmid = %s does not exist in cluster' % vmid)
# Ensure the choosen VM name doesn't already exist when cloning
if get_vmid(proxmox, name):
module.exit_json(changed=False, vmid=vmid, msg="VM with name <%s> already exists" % name)
module.exit_json(changed=False, msg="VM with name <%s> already exists" % name)
# Ensure the choosen VM id doesn't already exist when cloning
if get_vm(proxmox, newid):
module.exit_json(changed=False, vmid=vmid, msg="vmid %s with VM name %s already exists" % (newid, name))
module.exit_json(changed=False, msg="vmid %s with VM name %s already exists" % (newid, name))
if delete is not None:
try:
settings(module, proxmox, vmid, node, name, delete=delete)
module.exit_json(changed=True, vmid=vmid, msg="Settings has deleted on VM {0} with vmid {1}".format(name, vmid))
module.exit_json(changed=True, msg="Settings has deleted on VM {0} with vmid {1}".format(name, vmid))
except Exception as e:
module.fail_json(vmid=vmid, msg='Unable to delete settings on VM {0} with vmid {1}: '.format(name, vmid) + str(e))
module.fail_json(msg='Unable to delete settings on VM {0} with vmid {1}: '.format(name, vmid) + str(e))
if revert is not None:
try:
settings(module, proxmox, vmid, node, name, revert=revert)
module.exit_json(changed=True, vmid=vmid, msg="Settings has reverted on VM {0} with vmid {1}".format(name, vmid))
module.exit_json(changed=True, msg="Settings has reverted on VM {0} with vmid {1}".format(name, vmid))
except Exception as e:
module.fail_json(vmid=vmid, msg='Unable to revert settings on VM {0} with vmid {1}: Maybe is not a pending task... '.format(name, vmid) + str(e))
module.fail_json(msg='Unable to revert settings on VM {0} with vmid {1}: Maybe is not a pending task... '.format(name, vmid) + str(e))
if state == 'present':
try:
if get_vm(proxmox, vmid) and not (update or clone):
module.exit_json(changed=False, vmid=vmid, msg="VM with vmid <%s> already exists" % vmid)
module.exit_json(changed=False, msg="VM with vmid <%s> already exists" % vmid)
elif get_vmid(proxmox, name) and not (update or clone):
module.exit_json(changed=False, vmid=vmid, msg="VM with name <%s> already exists" % name)
module.exit_json(changed=False, msg="VM with name <%s> already exists" % name)
elif not (node, name):
module.fail_json(msg='node, name is mandatory for creating/updating vm')
elif not node_check(proxmox, node):
@@ -1283,7 +1293,6 @@ 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'],
@@ -1300,93 +1309,85 @@ def main():
scsi=module.params['scsi'],
virtio=module.params['virtio'])
if update:
module.exit_json(changed=True, vmid=vmid, msg="VM %s with vmid %s updated" % (name, vmid))
module.exit_json(changed=True, msg="VM %s with vmid %s updated" % (name, vmid))
elif clone is not None:
module.exit_json(changed=True, vmid=vmid, msg="VM %s with newid %s cloned from vm with vmid %s" % (name, newid, vmid))
module.exit_json(changed=True, msg="VM %s with newid %s cloned from vm with vmid %s" % (name, newid, vmid))
else:
module.exit_json(changed=True, msg="VM %s with vmid %s deployed" % (name, vmid), **results)
except Exception as e:
if update:
module.fail_json(vmid=vmid, msg="Unable to update vm {0} with vmid {1}=".format(name, vmid) + str(e))
module.fail_json(msg="Unable to update vm {0} with vmid {1}=".format(name, vmid) + str(e))
elif clone is not None:
module.fail_json(vmid=vmid, msg="Unable to clone vm {0} from vmid {1}=".format(name, vmid) + str(e))
module.fail_json(msg="Unable to clone vm {0} from vmid {1}=".format(name, vmid) + str(e))
else:
module.fail_json(vmid=vmid, msg="creation of qemu VM %s with vmid %s failed with exception=%s" % (name, vmid, e))
module.fail_json(msg="creation of qemu VM %s with vmid %s failed with exception=%s" % (name, vmid, e))
elif state == 'started':
status = {}
try:
if -1 == vmid:
module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
vm = get_vm(proxmox, vmid)
if not vm:
module.fail_json(vmid=vmid, msg='VM with vmid <%s> does not exist in cluster' % vmid)
status['status'] = vm[0]['status']
module.fail_json(msg='VM with vmid <%s> does not exist in cluster' % vmid)
if vm[0]['status'] == 'running':
module.exit_json(changed=False, vmid=vmid, msg="VM %s is already running" % vmid, **status)
module.exit_json(changed=False, msg="VM %s is already running" % vmid)
if start_vm(module, proxmox, vm):
module.exit_json(changed=True, vmid=vmid, msg="VM %s started" % vmid, **status)
module.exit_json(changed=True, msg="VM %s started" % vmid)
except Exception as e:
module.fail_json(vmid=vmid, msg="starting of VM %s failed with exception: %s" % (vmid, e), **status)
module.fail_json(msg="starting of VM %s failed with exception: %s" % (vmid, e))
elif state == 'stopped':
status = {}
try:
if -1 == vmid:
module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
vm = get_vm(proxmox, vmid)
if not vm:
module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid)
module.fail_json(msg='VM with vmid = %s does not exist in cluster' % vmid)
status['status'] = vm[0]['status']
if vm[0]['status'] == 'stopped':
module.exit_json(changed=False, vmid=vmid, msg="VM %s is already stopped" % vmid, **status)
module.exit_json(changed=False, msg="VM %s is already stopped" % vmid)
if stop_vm(module, proxmox, vm, force=module.params['force']):
module.exit_json(changed=True, vmid=vmid, msg="VM %s is shutting down" % vmid, **status)
module.exit_json(changed=True, msg="VM %s is shutting down" % vmid)
except Exception as e:
module.fail_json(vmid=vmid, msg="stopping of VM %s failed with exception: %s" % (vmid, e), **status)
module.fail_json(msg="stopping of VM %s failed with exception: %s" % (vmid, e))
elif state == 'restarted':
status = {}
try:
if -1 == vmid:
module.fail_json(msg='VM with name = %s does not exist in cluster' % name)
vm = get_vm(proxmox, vmid)
if not vm:
module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid)
status['status'] = vm[0]['status']
module.fail_json(msg='VM with vmid = %s does not exist in cluster' % vmid)
if vm[0]['status'] == 'stopped':
module.exit_json(changed=False, vmid=vmid, msg="VM %s is not running" % vmid, **status)
module.exit_json(changed=False, msg="VM %s is not running" % vmid)
if stop_vm(module, proxmox, vm, force=module.params['force']) and start_vm(module, proxmox, vm):
module.exit_json(changed=True, vmid=vmid, msg="VM %s is restarted" % vmid, **status)
module.exit_json(changed=True, msg="VM %s is restarted" % vmid)
except Exception as e:
module.fail_json(vmid=vmid, msg="restarting of VM %s failed with exception: %s" % (vmid, e), **status)
module.fail_json(msg="restarting of VM %s failed with exception: %s" % (vmid, e))
elif state == 'absent':
status = {}
try:
vm = get_vm(proxmox, vmid)
if not vm:
module.exit_json(changed=False, vmid=vmid)
module.exit_json(changed=False)
proxmox_node = proxmox.nodes(vm[0]['node'])
status['status'] = vm[0]['status']
if vm[0]['status'] == 'running':
if module.params['force']:
stop_vm(module, proxmox, vm, True)
else:
module.exit_json(changed=False, vmid=vmid, msg="VM %s is running. Stop it before deletion or use force=yes." % vmid)
module.exit_json(changed=False, msg="VM %s is running. Stop it before deletion or use force=yes." % vmid)
taskid = proxmox_node.qemu.delete(vmid)
if not wait_for_task(module, proxmox, vm[0]['node'], taskid):
module.fail_json(msg='Reached timeout while waiting for removing VM. Last line in task before timeout: %s' %
proxmox_node.tasks(taskid).log.get()[:1])
else:
module.exit_json(changed=True, vmid=vmid, msg="VM %s removed" % vmid)
module.exit_json(changed=True, msg="VM %s removed" % vmid)
except Exception as e:
module.fail_json(msg="deletion of VM %s failed with exception: %s" % (vmid, e))
@@ -1397,12 +1398,10 @@ def main():
vm = get_vm(proxmox, vmid)
if not vm:
module.fail_json(msg='VM with vmid = %s does not exist in cluster' % vmid)
if not name:
name = vm[0]['name']
current = proxmox.nodes(vm[0]['node']).qemu(vmid).status.current.get()['status']
status['status'] = current
if status:
module.exit_json(changed=False, vmid=vmid, msg="VM %s with vmid = %s is %s" % (name, vmid, current), **status)
module.exit_json(changed=False, msg="VM %s with vmid = %s is %s" % (name, vmid, current), **status)
if __name__ == '__main__':

View File

@@ -31,7 +31,6 @@ options:
- The password to authenticate with.
- You can use PROXMOX_PASSWORD environment variable.
type: str
required: yes
hostname:
description:
- The instance name.
@@ -107,6 +106,7 @@ EXAMPLES = r'''
RETURN = r'''#'''
import os
import time
import traceback
@@ -118,7 +118,7 @@ except ImportError:
PROXMOXER_IMP_ERR = traceback.format_exc()
HAS_PROXMOXER = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
@@ -182,7 +182,7 @@ def main():
argument_spec=dict(
api_host=dict(required=True),
api_user=dict(required=True),
api_password=dict(no_log=True, required=True, fallback=(env_fallback, ['PROXMOX_PASSWORD'])),
api_password=dict(no_log=True),
vmid=dict(required=False),
validate_certs=dict(type='bool', default='no'),
hostname=dict(),
@@ -213,6 +213,13 @@ def main():
force = module.params['force']
vmstate = module.params['vmstate']
# If password not set get it from PROXMOX_PASSWORD env
if not api_password:
try:
api_password = os.environ['PROXMOX_PASSWORD']
except KeyError as e:
module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable' % to_native(e))
try:
proxmox = setup_api(api_host, api_user, api_password, validate_certs)

View File

@@ -1,190 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# 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
__metaclass__ = type
DOCUMENTATION = '''
---
module: proxmox_storage_info
short_description: Retrieve information about one or more Proxmox VE storages
version_added: 2.2.0
description:
- Retrieve information about one or more Proxmox VE storages.
options:
storage:
description:
- Only return informations on a specific storage.
aliases: ['name']
type: str
type:
description:
- Filter on a specifc storage type.
type: str
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).
'''
EXAMPLES = '''
- name: List existing storages
community.general.proxmox_storage_info:
api_host: helldorado
api_user: root@pam
api_password: "{{ password | default(omit) }}"
api_token_id: "{{ token_id | default(omit) }}"
api_token_secret: "{{ token_secret | default(omit) }}"
register: proxmox_storages
- name: List NFS storages only
community.general.proxmox_storage_info:
api_host: helldorado
api_user: root@pam
api_password: "{{ password | default(omit) }}"
api_token_id: "{{ token_id | default(omit) }}"
api_token_secret: "{{ token_secret | default(omit) }}"
type: nfs
register: proxmox_storages_nfs
- name: Retrieve information about the lvm2 storage
community.general.proxmox_storage_info:
api_host: helldorado
api_user: root@pam
api_password: "{{ password | default(omit) }}"
api_token_id: "{{ token_id | default(omit) }}"
api_token_secret: "{{ token_secret | default(omit) }}"
storage: lvm2
register: proxmox_storage_lvm
'''
RETURN = '''
proxmox_storages:
description: List of storage pools.
returned: on success
type: list
elements: dict
contains:
content:
description: Proxmox content types available in this storage
returned: on success
type: list
elements: str
digest:
description: Storage's digest
returned: on success
type: str
nodes:
description: List of nodes associated to this storage
returned: on success, if storage is not local
type: list
elements: str
path:
description: Physical path to this storage
returned: on success
type: str
prune-backups:
description: Backup retention options
returned: on success
type: list
elements: dict
shared:
description: Is this storage shared
returned: on success
type: bool
storage:
description: Storage name
returned: on success
type: str
type:
description: Storage type
returned: on success
type: str
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.community.general.plugins.module_utils.proxmox import (
proxmox_auth_argument_spec, ProxmoxAnsible, HAS_PROXMOXER, PROXMOXER_IMP_ERR, proxmox_to_ansible_bool)
class ProxmoxStorageInfoAnsible(ProxmoxAnsible):
def get_storage(self, storage):
try:
storage = self.proxmox_api.storage.get(storage)
except Exception:
self.module.fail_json(msg="Storage '%s' does not exist" % storage)
return ProxmoxStorage(storage)
def get_storages(self, type=None):
storages = self.proxmox_api.storage.get(type=type)
storages = [ProxmoxStorage(storage) for storage in storages]
return storages
class ProxmoxStorage:
def __init__(self, storage):
self.storage = storage
# Convert proxmox representation of lists, dicts and boolean for easier
# manipulation within ansible.
if 'shared' in self.storage:
self.storage['shared'] = proxmox_to_ansible_bool(self.storage['shared'])
if 'content' in self.storage:
self.storage['content'] = self.storage['content'].split(',')
if 'nodes' in self.storage:
self.storage['nodes'] = self.storage['nodes'].split(',')
if 'prune-backups' in storage:
options = storage['prune-backups'].split(',')
self.storage['prune-backups'] = dict()
for option in options:
k, v = option.split('=')
self.storage['prune-backups'][k] = v
def proxmox_storage_info_argument_spec():
return dict(
storage=dict(type='str', aliases=['name']),
type=dict(type='str'),
)
def main():
module_args = proxmox_auth_argument_spec()
storage_info_args = proxmox_storage_info_argument_spec()
module_args.update(storage_info_args)
module = AnsibleModule(
argument_spec=module_args,
required_one_of=[('api_password', 'api_token_id')],
required_together=[('api_token_id', 'api_token_secret')],
mutually_exclusive=[('storage', 'type')],
supports_check_mode=True
)
result = dict(
changed=False
)
if not HAS_PROXMOXER:
module.fail_json(msg=missing_required_lib('proxmoxer'), exception=PROXMOXER_IMP_ERR)
proxmox = ProxmoxStorageInfoAnsible(module)
storage = module.params['storage']
storagetype = module.params['type']
if storage:
storages = [proxmox.get_storage(storage)]
else:
storages = proxmox.get_storages(type=storagetype)
result['proxmox_storages'] = [storage.storage for storage in storages]
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -122,7 +122,7 @@ try:
except ImportError:
HAS_PROXMOXER = False
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.basic import AnsibleModule
def get_template(proxmox, node, storage, content_type, template):
@@ -175,7 +175,7 @@ def main():
module = AnsibleModule(
argument_spec=dict(
api_host=dict(required=True),
api_password=dict(no_log=True, fallback=(env_fallback, ['PROXMOX_PASSWORD'])),
api_password=dict(no_log=True),
api_token_id=dict(no_log=True),
api_token_secret=dict(no_log=True),
api_user=dict(required=True),
@@ -188,10 +188,7 @@ def main():
timeout=dict(type='int', default=30),
force=dict(type='bool', default=False),
state=dict(default='present', choices=['present', 'absent']),
),
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
required_if=[('state', 'absent', ['template'])]
)
)
if not HAS_PROXMOXER:
@@ -210,6 +207,12 @@ def main():
auth_args = {'user': api_user}
if not (api_token_id and api_token_secret):
# If password not set get it from PROXMOX_PASSWORD env
if not api_password:
try:
api_password = os.environ['PROXMOX_PASSWORD']
except KeyError as e:
module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable')
auth_args['password'] = api_password
else:
auth_args['token_name'] = api_token_id
@@ -258,7 +261,9 @@ def main():
content_type = module.params['content_type']
template = module.params['template']
if not get_template(proxmox, node, storage, content_type, template):
if not template:
module.fail_json(msg='template param is mandatory')
elif not get_template(proxmox, node, storage, content_type, template):
module.exit_json(changed=False, msg='template with volid=%s:%s/%s is already deleted' % (storage, content_type, template))
if delete_template(module, proxmox, node, storage, content_type, template, timeout):

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 (@tleguern)
author: Tristan Le Guern (@Aversiste)
extends_documentation_fragment: community.general.proxmox.documentation
'''

View File

@@ -1229,6 +1229,24 @@ 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)

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