diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml index 52eaf65069..fe31f4d650 100644 --- a/.azure-pipelines/azure-pipelines.yml +++ b/.azure-pipelines/azure-pipelines.yml @@ -29,6 +29,7 @@ schedules: always: true branches: include: + - stable-6 - stable-5 - cron: 0 11 * * 0 displayName: Weekly (old stable branches) @@ -111,19 +112,6 @@ stages: - test: 2 - test: 3 - test: 4 - - 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 ### Units - stage: Units_devel displayName: Units devel @@ -175,19 +163,26 @@ stages: targets: - test: 2.6 - test: 3.8 - - stage: Units_2_11 - displayName: Units 2.11 + +## Remote + - stage: Remote_devel_extra_vms + displayName: Remote devel extra VMs dependsOn: [] jobs: - template: templates/matrix.yml parameters: - nameFormat: Python {0} - testFormat: 2.11/units/{0}/1 + testFormat: devel/{0} targets: - - test: 2.7 - - test: 3.5 - -## Remote + - name: Alpine 3.17 + test: alpine/3.17 + # - name: Fedora 37 + # test: fedora/37 + # - name: Ubuntu 20.04 + # test: ubuntu/20.04 + - name: Ubuntu 22.04 + test: ubuntu/22.04 + groups: + - vm - stage: Remote_devel displayName: Remote devel dependsOn: [] @@ -196,16 +191,16 @@ stages: parameters: testFormat: devel/{0} targets: - - name: macOS 12.0 - test: macos/12.0 + - name: macOS 13.2 + test: macos/13.2 - name: RHEL 7.9 test: rhel/7.9 - - name: RHEL 9.0 - test: rhel/9.0 - - name: FreeBSD 12.3 - test: freebsd/12.3 + - name: RHEL 9.1 + test: rhel/9.1 - name: FreeBSD 13.1 test: freebsd/13.1 + - name: FreeBSD 12.4 + test: freebsd/12.4 groups: - 1 - 2 @@ -220,8 +215,8 @@ stages: targets: - name: RHEL 9.0 test: rhel/9.0 - - name: FreeBSD 13.1 - test: freebsd/13.1 + - name: FreeBSD 12.3 + test: freebsd/12.3 groups: - 1 - 2 @@ -260,22 +255,6 @@ stages: - 1 - 2 - 3 - - stage: Remote_2_11 - displayName: Remote 2.11 - dependsOn: [] - jobs: - - template: templates/matrix.yml - parameters: - testFormat: 2.11/{0} - targets: - - name: RHEL 7.9 - test: rhel/7.9 - - name: RHEL 8.3 - test: rhel/8.3 - groups: - - 1 - - 2 - - 3 ### Docker - stage: Docker_devel @@ -288,8 +267,8 @@ stages: targets: - name: CentOS 7 test: centos7 - - name: Fedora 36 - test: fedora36 + - name: Fedora 37 + test: fedora37 - name: openSUSE 15 test: opensuse15 - name: Ubuntu 20.04 @@ -310,8 +289,8 @@ stages: parameters: testFormat: 2.14/linux/{0} targets: - - name: Ubuntu 20.04 - test: ubuntu2004 + - name: Fedora 36 + test: fedora36 groups: - 1 - 2 @@ -352,24 +331,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: Fedora 32 - test: fedora32 - - name: Fedora 33 - test: fedora33 - - name: Alpine 3 - test: alpine3 - groups: - - 1 - - 2 - - 3 ### Community Docker - stage: Docker_community_devel @@ -385,7 +346,7 @@ stages: - name: ArchLinux test: archlinux/3.10 - name: CentOS Stream 8 - test: centos-stream8/3.8 + test: centos-stream8/3.9 groups: - 1 - 2 @@ -433,46 +394,32 @@ stages: testFormat: 2.12/generic/{0}/1 targets: - test: 3.8 - - stage: Generic_2_11 - displayName: Generic 2.11 - dependsOn: [] - jobs: - - template: templates/matrix.yml - parameters: - nameFormat: Python {0} - testFormat: 2.11/generic/{0}/1 - targets: - - test: 2.7 - - test: 3.5 - stage: Summary condition: succeededOrFailed() dependsOn: - Sanity_devel - - Sanity_2_11 - Sanity_2_12 - Sanity_2_13 - Sanity_2_14 - Units_devel - - Units_2_11 - Units_2_12 - Units_2_13 - Units_2_14 + - Remote_devel_extra_vms - Remote_devel - - Remote_2_11 - Remote_2_12 - Remote_2_13 - Remote_2_14 - Docker_devel - - Docker_2_11 - Docker_2_12 - Docker_2_13 - Docker_2_14 - Docker_community_devel - - Generic_devel - - Generic_2_11 - - Generic_2_12 - - Generic_2_13 - - Generic_2_14 +# Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled. +# - Generic_devel +# - Generic_2_12 +# - Generic_2_13 +# - Generic_2_14 jobs: - template: templates/coverage.yml diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index dfacfbecfb..d8e96880c7 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -241,6 +241,8 @@ files: $lookups/manifold.py: labels: manifold maintainers: galanoff + $lookups/merge_variables.py: + maintainers: rlenferink m-a-r-k-e $lookups/onepass: labels: onepassword maintainers: samdoran @@ -265,6 +267,8 @@ files: maintainers: delineaKrehl tylerezimmerman $module_utils/: labels: module_utils + $module_utils/deps.py: + maintainers: russoz $module_utils/gconftool2.py: labels: gconftool2 maintainers: russoz @@ -279,15 +283,19 @@ files: maintainers: $team_huawei $module_utils/identity/keycloak/keycloak.py: maintainers: $team_keycloak + $module_utils/identity/keycloak/keycloak_clientsecret.py: + maintainers: $team_keycloak fynncfchen johncant $module_utils/ipa.py: labels: ipa maintainers: $team_ipa + $module_utils/jenkins.py: + labels: jenkins + maintainers: russoz $module_utils/manageiq.py: labels: manageiq maintainers: $team_manageiq $module_utils/memset.py: labels: cloud memset - maintainers: glitchcrab $module_utils/mh/: labels: module_helper maintainers: russoz @@ -302,6 +310,9 @@ files: $module_utils/pipx.py: labels: pipx maintainers: russoz + $module_utils/puppet.py: + labels: puppet + maintainers: russoz $module_utils/pure.py: labels: pure pure_storage maintainers: $team_purestorage @@ -313,6 +324,8 @@ files: $module_utils/scaleway.py: labels: cloud scaleway maintainers: $team_scaleway + $module_utils/ssh.py: + maintainers: russoz $module_utils/storage/hpe3par/hpe3par.py: maintainers: farhan7500 gautamphegde $module_utils/utm_utils.py: @@ -428,7 +441,7 @@ files: labels: datadog_event maintainers: n0ts $modules/datadog_monitor.py: - maintainers: skornehl + ignore: skornehl $modules/dconf.py: maintainers: azaghal $modules/deploy_helper.py: @@ -655,6 +668,8 @@ files: ignore: DWSR labels: jira maintainers: Slezhuk tarka pertoft + $modules/kdeconfig.py: + maintainers: smeso $modules/kernel_blacklist.py: maintainers: matze $modules/keycloak_: @@ -665,6 +680,10 @@ files: maintainers: Gaetan2907 $modules/keycloak_clientscope.py: maintainers: Gaetan2907 + $modules/keycloak_clientsecret_info.py: + maintainers: fynncfchen johncant + $modules/keycloak_clientsecret_regenerate.py: + maintainers: fynncfchen johncant $modules/keycloak_group.py: maintainers: adamgoossens $modules/keycloak_identity_provider.py: @@ -769,7 +788,7 @@ files: labels: maven_artifact maintainers: tumbl3w33d turb $modules/memset_: - maintainers: glitchcrab + ignore: glitchcrab $modules/mksysb.py: labels: aix mksysb maintainers: $team_aix @@ -814,6 +833,10 @@ files: maintainers: shane-walker xcambar $modules/nsupdate.py: maintainers: nerzhul + $modules/ocapi_command.py: + maintainers: $team_wdc + $modules/ocapi_info.py: + maintainers: $team_wdc $modules/oci_vcn.py: maintainers: $team_oracle rohitChaware $modules/odbc.py: @@ -822,7 +845,8 @@ files: maintainers: marc-sensenich $modules/ohai.py: labels: ohai - maintainers: $team_ansible_core mpdehaan + maintainers: $team_ansible_core + ignore: mpdehaan $modules/omapi_host.py: maintainers: amasolov nerzhul $modules/one_: @@ -1018,7 +1042,7 @@ files: maintainers: dagwieers $modules/redfish_: ignore: jose-delarosa - maintainers: $team_redfish + maintainers: $team_redfish TSKushal $modules/redhat_subscription.py: labels: redhat_subscription maintainers: barnabycourt alikins kahowell @@ -1072,7 +1096,8 @@ files: $modules/sapcar_extract.py: maintainers: RainerLeber $modules/say.py: - maintainers: $team_ansible_core mpdehaan + maintainers: $team_ansible_core + ignore: mpdehaan $modules/scaleway_: maintainers: $team_scaleway $modules/scaleway_compute_private_network.py: @@ -1298,7 +1323,7 @@ files: labels: m:xml xml maintainers: dagwieers magnus919 tbielawa cmprescott sm4rk0 $modules/yarn.py: - maintainers: chrishoffman verkaufer + ignore: chrishoffman verkaufer $modules/yum_versionlock.py: maintainers: gyptazy aminvakil $modules/zfs: @@ -1372,7 +1397,7 @@ macros: team_opennebula: ilicmilan meerkampdvv rsmontero xorel nilsding team_oracle: manojmeda mross22 nalsaber team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16 - team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 + team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt team_rhn: FlossWare alikins barnabycourt vritant team_scaleway: remyleone abarbare team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l diff --git a/.github/workflows/ansible-test.yml b/.github/workflows/ansible-test.yml new file mode 100644 index 0000000000..67bdaeedba --- /dev/null +++ b/.github/workflows/ansible-test.yml @@ -0,0 +1,193 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# For the comprehensive list of the inputs supported by the ansible-community/ansible-test-gh-action GitHub Action, see +# https://github.com/marketplace/actions/ansible-test + +name: EOL CI +on: + # Run EOL CI against all pushes (direct commits, also merged PRs), Pull Requests + push: + branches: + - main + - stable-* + pull_request: + # Run EOL CI once per day (at 10:00 UTC) + schedule: + - cron: '0 10 * * *' + +concurrency: + # Make sure there is at most one active run per PR, but do not cancel any non-PR runs + group: ${{ github.workflow }}-${{ (github.head_ref && github.event.number) || github.run_id }} + cancel-in-progress: true + +jobs: + sanity: + name: EOL Sanity (Ⓐ${{ matrix.ansible }}) + strategy: + matrix: + ansible: + - '2.11' + # Ansible-test on various stable branches does not yet work well with cgroups v2. + # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 + # image for these stable branches. The list of branches where this is necessary will + # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 + # for the latest list. + runs-on: >- + ${{ contains(fromJson( + '["2.9", "2.10", "2.11"]' + ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} + steps: + - name: Perform sanity testing + uses: felixfontein/ansible-test-gh-action@main + with: + ansible-core-github-repository-slug: felixfontein/ansible + ansible-core-version: stable-${{ matrix.ansible }} + coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }} + pull-request-change-detection: 'true' + testing-type: sanity + + units: + # Ansible-test on various stable branches does not yet work well with cgroups v2. + # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 + # image for these stable branches. The list of branches where this is necessary will + # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 + # for the latest list. + runs-on: >- + ${{ contains(fromJson( + '["2.9", "2.10", "2.11"]' + ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} + name: EOL Units (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }}) + strategy: + # As soon as the first unit test fails, cancel the others to free up the CI queue + fail-fast: true + matrix: + ansible: + - '' + python: + - '' + exclude: + - ansible: '' + include: + - ansible: '2.11' + python: '2.7' + - ansible: '2.11' + python: '3.5' + + steps: + - name: >- + Perform unit testing against + Ansible version ${{ matrix.ansible }} + uses: felixfontein/ansible-test-gh-action@main + with: + ansible-core-github-repository-slug: felixfontein/ansible + ansible-core-version: stable-${{ matrix.ansible }} + coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }} + pre-test-cmd: >- + mkdir -p ../../ansible + ; + git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools + pull-request-change-detection: 'true' + target-python-version: ${{ matrix.python }} + testing-type: units + + integration: + # Ansible-test on various stable branches does not yet work well with cgroups v2. + # Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04 + # image for these stable branches. The list of branches where this is necessary will + # shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28 + # for the latest list. + runs-on: >- + ${{ contains(fromJson( + '["2.9", "2.10", "2.11"]' + ), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }} + name: EOL I (Ⓐ${{ matrix.ansible }}+${{ matrix.docker }}+py${{ matrix.python }}:${{ matrix.target }}) + strategy: + fail-fast: false + matrix: + ansible: + - '' + docker: + - '' + python: + - '' + target: + - '' + exclude: + - ansible: '' + include: + # 2.11 + - ansible: '2.11' + docker: fedora32 + python: '' + target: azp/posix/1/ + - ansible: '2.11' + docker: fedora32 + python: '' + target: azp/posix/2/ + - ansible: '2.11' + docker: fedora32 + python: '' + target: azp/posix/3/ + - ansible: '2.11' + docker: fedora33 + python: '' + target: azp/posix/1/ + - ansible: '2.11' + docker: fedora33 + python: '' + target: azp/posix/2/ + - ansible: '2.11' + docker: fedora33 + python: '' + target: azp/posix/3/ + - ansible: '2.11' + docker: alpine3 + python: '' + target: azp/posix/1/ + - ansible: '2.11' + docker: alpine3 + python: '' + target: azp/posix/2/ + - ansible: '2.11' + docker: alpine3 + python: '' + target: azp/posix/3/ + # Right now all generic tests are disabled. Uncomment when at least one of them is re-enabled. + # - ansible: '2.11' + # docker: default + # python: '2.7' + # target: azp/generic/1/ + # - ansible: '2.11' + # docker: default + # python: '3.5' + # target: azp/generic/2/ + + steps: + - name: >- + Perform integration testing against + Ansible version ${{ matrix.ansible }} + under Python ${{ matrix.python }} + uses: felixfontein/ansible-test-gh-action@main + with: + ansible-core-github-repository-slug: felixfontein/ansible + ansible-core-version: stable-${{ matrix.ansible }} + coverage: ${{ github.event_name == 'schedule' && 'always' || 'never' }} + docker-image: ${{ matrix.docker }} + integration-continue-on-error: 'false' + integration-diff: 'false' + integration-retry-on-error: 'true' + pre-test-cmd: >- + mkdir -p ../../ansible + ; + git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.posix.git ../../ansible/posix + ; + git clone --depth=1 --single-branch https://github.com/ansible-collections/community.crypto.git ../../community/crypto + ; + git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ../../community/internal_test_tools + pull-request-change-detection: 'true' + target: ${{ matrix.target }} + target-python-version: ${{ matrix.python }} + testing-type: integration diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml deleted file mode 100644 index 04a50dc201..0000000000 --- a/.github/workflows/docs-pr.yml +++ /dev/null @@ -1,93 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -name: Collection Docs -concurrency: - group: docs-${{ github.head_ref }} - cancel-in-progress: true -on: - pull_request_target: - types: [opened, synchronize, reopened, closed] - paths-ignore: - - '.azure-pipelines/**' - - 'changelogs/**' - - 'meta/**' - - 'tests/**' - -jobs: - build-docs: - permissions: - contents: read - name: Build Ansible Docs - uses: ansible-community/github-docs-build/.github/workflows/_shared-docs-build-pr.yml@main - with: - init-fail-on-error: true - provide-link-targets: | - ansible_collections.ansible.builtin.dict2items_filter - ansible_collections.ansible.builtin.items_lookup - ansible_collections.ansible.builtin.path_join_filter - ansible_collections.community.kubevirt.kubevirt_cdi_upload_module - ansible_collections.community.kubevirt.kubevirt_inventory - ansible_collections.community.kubevirt.kubevirt_preset_module - ansible_collections.community.kubevirt.kubevirt_pvc_module - ansible_collections.community.kubevirt.kubevirt_rs_module - ansible_collections.community.kubevirt.kubevirt_template_module - ansible_collections.community.kubevirt.kubevirt_vm_module - ansible_collections.infoblox.nios_modules.nios_a_record_module - ansible_collections.infoblox.nios_modules.nios_aaaa_record_module - ansible_collections.infoblox.nios_modules.nios_cname_record_module - ansible_collections.infoblox.nios_modules.nios_dns_view_module - ansible_collections.infoblox.nios_modules.nios_fixed_address_module - ansible_collections.infoblox.nios_modules.nios_host_record_module - ansible_collections.infoblox.nios_modules.nios_lookup_lookup - ansible_collections.infoblox.nios_modules.nios_member_module - ansible_collections.infoblox.nios_modules.nios_mx_record_module - ansible_collections.infoblox.nios_modules.nios_naptr_record_module - ansible_collections.infoblox.nios_modules.nios_network_module - ansible_collections.infoblox.nios_modules.nios_network_view_module - ansible_collections.infoblox.nios_modules.nios_next_ip_lookup - ansible_collections.infoblox.nios_modules.nios_next_network_lookup - ansible_collections.infoblox.nios_modules.nios_nsgroup_module - ansible_collections.infoblox.nios_modules.nios_ptr_record_module - ansible_collections.infoblox.nios_modules.nios_srv_record_module - ansible_collections.infoblox.nios_modules.nios_txt_record_module - ansible_collections.infoblox.nios_modules.nios_zone_module - - comment: - permissions: - pull-requests: write - runs-on: ubuntu-latest - needs: build-docs - name: PR comments - steps: - - name: PR comment - uses: ansible-community/github-docs-build/actions/ansible-docs-build-comment@main - with: - body-includes: '## Docs Build' - reactions: heart - action: ${{ needs.build-docs.outputs.changed != 'true' && 'remove' || '' }} - on-closed-body: | - ## Docs Build 📝 - - This PR is closed and any previously published docsite has been unpublished. - on-merged-body: | - ## Docs Build 📝 - - Thank you for contribution!✨ - - This PR has been merged and your docs changes will be incorporated when they are next published. - body: | - ## Docs Build 📝 - - Thank you for contribution!✨ - - The docsite for **this PR** is available for download as an artifact from this run: - ${{ needs.build-docs.outputs.artifact-url }} - - File changes: - - ${{ needs.build-docs.outputs.diff-files-rendered }} - - ${{ needs.build-docs.outputs.diff-rendered }} diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3b83faa551..a11b95b481 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,13 +6,336 @@ Community General Release Notes This changelog describes changes after version 5.0.0. -v6.0.0-a1 -========= +v6.5.0 +====== Release Summary --------------- -This is a pre-release for the upcoming 6.0.0 major release. The main objective of this pre-release is to make it possible to test the large stuctural changes by flattening the directory structure. See the corresponding entry in the changelog for details. +Feature and bugfix release. + +Minor Changes +------------- + +- apt_rpm - adds ``clean``, ``dist_upgrade`` and ``update_kernel`` parameters for clear caches, complete upgrade system, and upgrade kernel packages (https://github.com/ansible-collections/community.general/pull/5867). +- dconf - parse GVariants for equality comparison when the Python module ``gi.repository`` is available (https://github.com/ansible-collections/community.general/pull/6049). +- gitlab_runner - allow to register group runner (https://github.com/ansible-collections/community.general/pull/3935). +- jira - add worklog functionality (https://github.com/ansible-collections/community.general/issues/6209, https://github.com/ansible-collections/community.general/pull/6210). +- ldap modules - add ``ca_path`` option (https://github.com/ansible-collections/community.general/pull/6185). +- make - add ``command`` return value to the module output (https://github.com/ansible-collections/community.general/pull/6160). +- nmap inventory plugin - add new option ``open`` for only returning open ports (https://github.com/ansible-collections/community.general/pull/6200). +- nmap inventory plugin - add new option ``port`` for port specific scan (https://github.com/ansible-collections/community.general/pull/6165). +- nmcli - add ``default`` and ``default-or-eui64`` to the list of valid choices for ``addr_gen_mode6`` parameter (https://github.com/ansible-collections/community.general/pull/5974). +- nmcli - add support for ``team.runner-fast-rate`` parameter for ``team`` connections (https://github.com/ansible-collections/community.general/issues/6065). +- openbsd_pkg - set ``TERM`` to ``'dumb'`` in ``execute_command()`` to make module less dependant on the ``TERM`` environment variable set on the Ansible controller (https://github.com/ansible-collections/community.general/pull/6149). +- pipx - optional ``install_apps`` parameter added to install applications from injected packages (https://github.com/ansible-collections/community.general/pull/6198). +- proxmox_kvm - add new ``archive`` parameter. This is needed to create a VM from an archive (backup) (https://github.com/ansible-collections/community.general/pull/6159). +- redfish_info - adds commands to retrieve the HPE ThermalConfiguration and FanPercentMinimum settings from iLO (https://github.com/ansible-collections/community.general/pull/6208). +- redhat_subscription - credentials (``username``, ``activationkey``, and so on) are required now only if a system needs to be registered, or ``force_register`` is specified (https://github.com/ansible-collections/community.general/pull/5664). +- redhat_subscription - the registration is done using the D-Bus ``rhsm`` service instead of spawning a ``subscription-manager register`` command, if possible; this avoids passing plain-text credentials as arguments to ``subscription-manager register``, which can be seen while that command runs (https://github.com/ansible-collections/community.general/pull/6122). +- ssh_config - add ``proxyjump`` option (https://github.com/ansible-collections/community.general/pull/5970). +- ssh_config - vendored StormSSH's config parser to avoid having to install StormSSH to use the module (https://github.com/ansible-collections/community.general/pull/6117). +- znode module - optional ``use_tls`` parameter added for encrypted communication (https://github.com/ansible-collections/community.general/issues/6154). + +Bugfixes +-------- + +- archive - avoid deprecated exception class on Python 3 (https://github.com/ansible-collections/community.general/pull/6180). +- gitlab_runner - fix ``KeyError`` on runner creation and update (https://github.com/ansible-collections/community.general/issues/6112). +- influxdb_user - fix running in check mode when the user does not exist yet (https://github.com/ansible-collections/community.general/pull/6111). +- interfaces_file - fix reading options in lines not starting with a space (https://github.com/ansible-collections/community.general/issues/6120). +- jail connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/6118). +- memset - fix memset urlerror handling (https://github.com/ansible-collections/community.general/pull/6114). +- nmcli - fixed idempotency issue for bridge connections. Module forced default value of ``bridge.priority`` to nmcli if not set; if ``bridge.stp`` is disabled nmcli ignores it and keep default (https://github.com/ansible-collections/community.general/issues/3216, https://github.com/ansible-collections/community.general/issues/4683). +- nmcli - fixed idempotency issue when module params is set to ``may_fail4=false`` and ``method4=disabled``; in this case nmcli ignores change and keeps their own default value ``yes`` (https://github.com/ansible-collections/community.general/pull/6106). +- nmcli - implemented changing mtu value on vlan interfaces (https://github.com/ansible-collections/community.general/issues/4387). +- opkg - fixes bug when using ``update_cache=true`` (https://github.com/ansible-collections/community.general/issues/6004). +- redhat_subscription, rhsm_release, rhsm_repository - cleanly fail when not running as root, rather than hanging on an interactive ``console-helper`` prompt; they all interact with ``subscription-manager``, which already requires to be run as root (https://github.com/ansible-collections/community.general/issues/734, https://github.com/ansible-collections/community.general/pull/6211). +- xenorchestra inventory plugin - fix failure to receive objects from server due to not checking the id of the response (https://github.com/ansible-collections/community.general/pull/6227). +- yarn - fix ``global=true`` to not fail when `executable` wasn't specified (https://github.com/ansible-collections/community.general/pull/6132) +- yarn - fixes bug where yarn module tasks would fail when warnings were emitted from Yarn. The ``yarn.list`` method was not filtering out warnings (https://github.com/ansible-collections/community.general/issues/6127). + +New Plugins +----------- + +Lookup +~~~~~~ + +- merge_variables - merge variables with a certain suffix + +New Modules +----------- + +- kdeconfig - Manage KDE configuration files + +v6.4.0 +====== + +Release Summary +--------------- + +Regular feature and bugfix release. + +Minor Changes +------------- + +- dnsimple - set custom User-Agent for API requests to DNSimple (https://github.com/ansible-collections/community.general/pull/5927). +- flatpak_remote - add new boolean option ``enabled``. It controls, whether the remote is enabled or not (https://github.com/ansible-collections/community.general/pull/5926). +- gitlab_project - add ``releases_access_level``, ``environments_access_level``, ``feature_flags_access_level``, ``infrastructure_access_level``, ``monitor_access_level``, and ``security_and_compliance_access_level`` options (https://github.com/ansible-collections/community.general/pull/5986). +- jc filter plugin - added the ability to use parser plugins (https://github.com/ansible-collections/community.general/pull/6043). +- keycloak_group - add new optional module parameter ``parents`` to properly handle keycloak subgroups (https://github.com/ansible-collections/community.general/pull/5814). +- keycloak_user_federation - make ``org.keycloak.storage.ldap.mappers.LDAPStorageMapper`` the default value for mappers ``providerType`` (https://github.com/ansible-collections/community.general/pull/5863). +- ldap modules - add ``xorder_discovery`` option (https://github.com/ansible-collections/community.general/issues/6045, https://github.com/ansible-collections/community.general/pull/6109). +- lxd_container - add diff and check mode (https://github.com/ansible-collections/community.general/pull/5866). +- mattermost, rocketchat, slack - replace missing default favicon with docs.ansible.com favicon (https://github.com/ansible-collections/community.general/pull/5928). +- modprobe - add ``persistent`` option (https://github.com/ansible-collections/community.general/issues/4028, https://github.com/ansible-collections/community.general/pull/542). +- osx_defaults - include stderr in error messages (https://github.com/ansible-collections/community.general/pull/6011). +- proxmox - suppress urllib3 ``InsecureRequestWarnings`` when ``validate_certs`` option is ``false`` (https://github.com/ansible-collections/community.general/pull/5931). +- redfish_command - adding ``EnableSecureBoot`` functionality (https://github.com/ansible-collections/community.general/pull/5899). +- redfish_command - adding ``VerifyBiosAttributes`` functionality (https://github.com/ansible-collections/community.general/pull/5900). +- sefcontext - add support for path substitutions (https://github.com/ansible-collections/community.general/issues/1193). + +Deprecated Features +------------------- + +- gitlab_runner - the option ``access_level`` will lose its default value in community.general 8.0.0. From that version on, you have set this option to ``ref_protected`` explicitly, if you want to have a protected runner (https://github.com/ansible-collections/community.general/issues/5925). + +Bugfixes +-------- + +- cartesian and flattened lookup plugins - adjust to parameter deprecation in ansible-core 2.14's ``listify_lookup_plugin_terms`` helper function (https://github.com/ansible-collections/community.general/pull/6074). +- cloudflare_dns - fixed the idempotency for SRV DNS records (https://github.com/ansible-collections/community.general/pull/5972). +- cloudflare_dns - fixed the possiblity of setting a root-level SRV DNS record (https://github.com/ansible-collections/community.general/pull/5972). +- github_webhook - fix always changed state when no secret is provided (https://github.com/ansible-collections/community.general/pull/5994). +- jenkins_plugin - fix error due to undefined variable when updates file is not downloaded (https://github.com/ansible-collections/community.general/pull/6100). +- keycloak_client - fix accidental replacement of value for attribute ``saml.signing.private.key`` with ``no_log`` in wrong contexts (https://github.com/ansible-collections/community.general/pull/5934). +- lxd_* modules, lxd inventory plugin - fix TLS/SSL certificate validation problems by using the correct purpose when creating the TLS context (https://github.com/ansible-collections/community.general/issues/5616, https://github.com/ansible-collections/community.general/pull/6034). +- nmcli - fix change handling of values specified as an integer 0 (https://github.com/ansible-collections/community.general/pull/5431). +- nmcli - fix failure to handle WIFI settings when connection type not specified (https://github.com/ansible-collections/community.general/pull/5431). +- nmcli - fix improper detection of changes to ``wifi.wake-on-wlan`` (https://github.com/ansible-collections/community.general/pull/5431). +- nmcli - order is significant for lists of addresses (https://github.com/ansible-collections/community.general/pull/6048). +- onepassword lookup plugin - Changed to ignore errors from "op account get" calls. Previously, errors would prevent auto-signin code from executing (https://github.com/ansible-collections/community.general/pull/5942). +- terraform and timezone - slight refactoring to avoid linter reporting potentially undefined variables (https://github.com/ansible-collections/community.general/pull/5933). +- various plugins and modules - remove unnecessary imports (https://github.com/ansible-collections/community.general/pull/5940). +- yarn - fix ``global=true`` to check for the configured global folder instead of assuming the default (https://github.com/ansible-collections/community.general/pull/5829) +- yarn - fix ``state=absent`` not working with ``global=true`` when the package does not include a binary (https://github.com/ansible-collections/community.general/pull/5829) +- yarn - fix ``state=latest`` not working with ``global=true`` (https://github.com/ansible-collections/community.general/issues/5712). +- zfs_delegate_admin - zfs allow output can now be parsed when uids/gids are not known to the host system (https://github.com/ansible-collections/community.general/pull/5943). +- zypper - make package managing work on readonly filesystem of openSUSE MicroOS (https://github.com/ansible-collections/community.general/pull/5615). + +v6.3.0 +====== + +Release Summary +--------------- + +Regular bugfix and feature release. + +Minor Changes +------------- + +- apache2_module - add module argument ``warn_mpm_absent`` to control whether warning are raised in some edge cases (https://github.com/ansible-collections/community.general/pull/5793). +- bitwarden lookup plugin - can now retrieve secrets from custom fields (https://github.com/ansible-collections/community.general/pull/5694). +- bitwarden lookup plugin - implement filtering results by ``collection_id`` parameter (https://github.com/ansible-collections/community.general/issues/5849). +- dig lookup plugin - support CAA record type (https://github.com/ansible-collections/community.general/pull/5913). +- gitlab_project - add ``builds_access_level``, ``container_registry_access_level`` and ``forking_access_level`` options (https://github.com/ansible-collections/community.general/pull/5706). +- gitlab_runner - add new boolean option ``access_level_on_creation``. It controls, whether the value of ``access_level`` is used for runner registration or not. The option ``access_level`` has been ignored on registration so far and was only used on updates (https://github.com/ansible-collections/community.general/issues/5907, https://github.com/ansible-collections/community.general/pull/5908). +- ilo_redfish_utils module utils - change implementation of DNS Server IP and NTP Server IP update (https://github.com/ansible-collections/community.general/pull/5804). +- ipa_group - allow to add and remove external users with the ``external_user`` option (https://github.com/ansible-collections/community.general/pull/5897). +- iptables_state - minor refactoring within the module (https://github.com/ansible-collections/community.general/pull/5844). +- one_vm - add a new ``updateconf`` option which implements the ``one.vm.updateconf`` API call (https://github.com/ansible-collections/community.general/pull/5812). +- opkg - refactored module to use ``CmdRunner`` for executing ``opkg`` (https://github.com/ansible-collections/community.general/pull/5718). +- redhat_subscription - adds ``token`` parameter for subscription-manager authentication using Red Hat API token (https://github.com/ansible-collections/community.general/pull/5725). +- snap - minor refactor when executing module (https://github.com/ansible-collections/community.general/pull/5773). +- snap_alias - refactored module to use ``CmdRunner`` to execute ``snap`` (https://github.com/ansible-collections/community.general/pull/5486). +- sudoers - add ``setenv`` parameters to support passing environment variables via sudo. (https://github.com/ansible-collections/community.general/pull/5883) + +Breaking Changes / Porting Guide +-------------------------------- + +- ModuleHelper module utils - when the module sets output variables named ``msg``, ``exception``, ``output``, ``vars``, or ``changed``, the actual output will prefix those names with ``_`` (underscore symbol) only when they clash with output variables generated by ModuleHelper itself, which only occurs when handling exceptions. Please note that this breaking change does not require a new major release since before this release, it was not possible to add such variables to the output `due to a bug `__ (https://github.com/ansible-collections/community.general/pull/5765). + +Deprecated Features +------------------- + +- consul - deprecate using parameters unused for ``state=absent`` (https://github.com/ansible-collections/community.general/pull/5772). +- gitlab_runner - the default of the new option ``access_level_on_creation`` will change from ``false`` to ``true`` in community.general 7.0.0. This will cause ``access_level`` to be used during runner registration as well, and not only during updates (https://github.com/ansible-collections/community.general/pull/5908). + +Bugfixes +-------- + +- ModuleHelper - fix bug when adjusting the name of reserved output variables (https://github.com/ansible-collections/community.general/pull/5755). +- alternatives - support subcommands on Fedora 37, which uses ``follower`` instead of ``slave`` (https://github.com/ansible-collections/community.general/pull/5794). +- bitwarden lookup plugin - clarify what to do, if the bitwarden vault is not unlocked (https://github.com/ansible-collections/community.general/pull/5811). +- dig lookup plugin - correctly handle DNSKEY record type's ``algorithm`` field (https://github.com/ansible-collections/community.general/pull/5914). +- gem - fix force parameter not being passed to gem command when uninstalling (https://github.com/ansible-collections/community.general/pull/5822). +- gem - fix hang due to interactive prompt for confirmation on specific version uninstall (https://github.com/ansible-collections/community.general/pull/5751). +- gitlab_deploy_key - also update ``title`` and not just ``can_push`` (https://github.com/ansible-collections/community.general/pull/5888). +- keycloak_user_federation - fixes federation creation issue. When a new federation was created and at the same time a default / standard mapper was also changed / updated the creation process failed as a bad None set variable led to a bad malformed url request (https://github.com/ansible-collections/community.general/pull/5750). +- keycloak_user_federation - fixes idempotency detection issues. In some cases the module could fail to properly detect already existing user federations because of a buggy seemingly superflous extra query parameter (https://github.com/ansible-collections/community.general/pull/5732). +- loganalytics callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). +- logdna callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). +- logstash callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). +- nsupdate - fix zone lookup. The SOA record for an existing zone is returned as an answer RR and not as an authority RR (https://github.com/ansible-collections/community.general/issues/5817, https://github.com/ansible-collections/community.general/pull/5818). +- proxmox_disk - fixed issue with read timeout on import action (https://github.com/ansible-collections/community.general/pull/5803). +- redfish_utils - removed basic auth HTTP header when performing a GET on the service root resource and when performing a POST to the session collection (https://github.com/ansible-collections/community.general/issues/5886). +- splunk callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). +- sumologic callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). +- syslog_json callback plugin - adjust type of callback to ``notification``, it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). +- terraform - fix ``current`` workspace never getting appended to the ``all`` key in the ``workspace_ctf`` object (https://github.com/ansible-collections/community.general/pull/5735). +- terraform - fix ``terraform init`` failure when there are multiple workspaces on the remote backend and when ``default`` workspace is missing by setting ``TF_WORKSPACE`` environmental variable to the value of ``workspace`` when used (https://github.com/ansible-collections/community.general/pull/5735). +- terraform module - disable ANSI escape sequences during validation phase (https://github.com/ansible-collections/community.general/pull/5843). +- xml - fixed a bug where empty ``children`` list would not be set (https://github.com/ansible-collections/community.general/pull/5808). + +New Modules +----------- + +- ocapi_command - Manages Out-Of-Band controllers using Open Composable API (OCAPI) +- ocapi_info - Manages Out-Of-Band controllers using Open Composable API (OCAPI) + +v6.2.0 +====== + +Release Summary +--------------- + +Regular bugfix and feature release. + +Minor Changes +------------- + +- opkg - allow installing a package in a certain version (https://github.com/ansible-collections/community.general/pull/5688). +- proxmox - added new module parameter ``tags`` for use with PVE 7+ (https://github.com/ansible-collections/community.general/pull/5714). +- puppet - refactored module to use ``CmdRunner`` for executing ``puppet`` (https://github.com/ansible-collections/community.general/pull/5612). +- redhat_subscription - add a ``server_proxy_scheme`` parameter to configure the scheme for the proxy server (https://github.com/ansible-collections/community.general/pull/5662). +- ssh_config - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5720). +- sudoers - adds ``host`` parameter for setting hostname restrictions in sudoers rules (https://github.com/ansible-collections/community.general/issues/5702). + +Deprecated Features +------------------- + +- manageiq_policies - deprecate ``state=list`` in favour of using ``community.general.manageiq_policies_info`` (https://github.com/ansible-collections/community.general/pull/5721). +- rax - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_cbs - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_cbs_attachments - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_cdb - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_cdb_database - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_cdb_user - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_clb - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_clb_nodes - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_clb_ssl - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_dns - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_dns_record - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_facts - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_files - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_files_objects - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_identity - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_keypair - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_meta - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_mon_alarm - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_mon_check - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_mon_entity - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_mon_notification - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_mon_notification_plan - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_network - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_queue - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_scaling_group - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). +- rax_scaling_policy - module relies on deprecates library ``pyrax``. Unless maintainers step up to work on the module, it will be marked as deprecated in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + +Bugfixes +-------- + +- ansible_galaxy_install - set default to raise exception if command's return code is different from zero (https://github.com/ansible-collections/community.general/pull/5680). +- ansible_galaxy_install - try ``C.UTF-8`` and then fall back to ``en_US.UTF-8`` before failing (https://github.com/ansible-collections/community.general/pull/5680). +- gitlab_group_variables - fix dropping variables accidentally when GitLab introduced new properties (https://github.com/ansible-collections/community.general/pull/5667). +- gitlab_project_variables - fix dropping variables accidentally when GitLab introduced new properties (https://github.com/ansible-collections/community.general/pull/5667). +- lxc_container - fix the arguments of the lxc command which broke the creation and cloning of containers (https://github.com/ansible-collections/community.general/issues/5578). +- opkg - fix issue that ``force=reinstall`` would not reinstall an existing package (https://github.com/ansible-collections/community.general/pull/5705). +- proxmox_disk - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5672). +- proxmox_nic - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5672). +- unixy callback plugin - fix typo introduced when updating to use Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600). + +v6.1.0 +====== + +Release Summary +--------------- + +Regular bugfix and feature release. + +Minor Changes +------------- + +- cmd_runner module utils - ``cmd_runner_fmt.as_bool()`` can now take an extra parameter to format when value is false (https://github.com/ansible-collections/community.general/pull/5647). +- gconftool2 - refactor using ``ModuleHelper`` and ``CmdRunner`` (https://github.com/ansible-collections/community.general/pull/5545). +- java_certs - add more detailed error output when extracting certificate from PKCS12 fails (https://github.com/ansible-collections/community.general/pull/5550). +- jenkins_plugin - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5565). +- lxd_project - refactored code out to module utils to clear sanity check (https://github.com/ansible-collections/community.general/pull/5549). +- nmap inventory plugin - add new options ``udp_scan``, ``icmp_timestamp``, and ``dns_resolve`` for different types of scans (https://github.com/ansible-collections/community.general/pull/5566). +- rax_scaling_group - refactored out code to the ``rax`` module utils to clear the sanity check (https://github.com/ansible-collections/community.general/pull/5563). +- redfish_command - add ``PerformRequestedOperations`` command to perform any operations necessary to continue the update flow (https://github.com/ansible-collections/community.general/issues/4276). +- redfish_command - add ``update_apply_time`` to ``SimpleUpdate`` command (https://github.com/ansible-collections/community.general/issues/3910). +- redfish_command - add ``update_status`` to output of ``SimpleUpdate`` command to allow a user monitor the update in progress (https://github.com/ansible-collections/community.general/issues/4276). +- redfish_info - add ``GetUpdateStatus`` command to check the progress of a previous update request (https://github.com/ansible-collections/community.general/issues/4276). +- redfish_utils module utils - added PUT (``put_request()``) functionality (https://github.com/ansible-collections/community.general/pull/5490). +- slack - add option ``prepend_hash`` which allows to control whether a ``#`` is prepended to ``channel_id``. The current behavior (value ``auto``) is to prepend ``#`` unless some specific prefixes are found. That list of prefixes is incomplete, and there does not seem to exist a documented condition on when exactly ``#`` must not be prepended. We recommend to explicitly set ``prepend_hash=always`` or ``prepend_hash=never`` to avoid any ambiguity (https://github.com/ansible-collections/community.general/pull/5629). +- spotinst_aws_elastigroup - add ``elements`` attribute when missing in ``list`` parameters (https://github.com/ansible-collections/community.general/pull/5553). +- ssh_config - add ``host_key_algorithms`` option (https://github.com/ansible-collections/community.general/pull/5605). +- udm_share - added ``elements`` attribute to ``list`` type parameters (https://github.com/ansible-collections/community.general/pull/5557). +- udm_user - add ``elements`` attribute when missing in ``list`` parameters (https://github.com/ansible-collections/community.general/pull/5559). + +Deprecated Features +------------------- + +- The ``sap`` modules ``sapcar_extract``, ``sap_task_list_execute``, and ``hana_query``, will be removed from this collection in community.general 7.0.0 and replaced with redirects to ``community.sap_libs``. If you want to continue using these modules, make sure to also install ``community.sap_libs`` (it is part of the Ansible package) (https://github.com/ansible-collections/community.general/pull/5614). + +Bugfixes +-------- + +- chroot connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/5570). +- cmd_runner module utils - fixed bug when handling default cases in ``cmd_runner_fmt.as_map()`` (https://github.com/ansible-collections/community.general/pull/5538). +- cmd_runner module utils - formatting arguments ``cmd_runner_fmt.as_fixed()`` was expecting an non-existing argument (https://github.com/ansible-collections/community.general/pull/5538). +- keycloak_client_rolemapping - calculate ``proposed`` and ``after`` return values properly (https://github.com/ansible-collections/community.general/pull/5619). +- keycloak_client_rolemapping - remove only listed mappings with ``state=absent`` (https://github.com/ansible-collections/community.general/pull/5619). +- proxmox inventory plugin - fix bug while templating when using templates for the ``url``, ``user``, ``password``, ``token_id``, or ``token_secret`` options (https://github.com/ansible-collections/community.general/pull/5640). +- proxmox inventory plugin - handle tags delimited by semicolon instead of comma, which happens from Proxmox 7.3 on (https://github.com/ansible-collections/community.general/pull/5602). +- redhat_subscription - do not ignore ``consumer_name`` and other variables if ``activationkey`` is specified (https://github.com/ansible-collections/community.general/issues/3486, https://github.com/ansible-collections/community.general/pull/5627). +- redhat_subscription - do not pass arguments to ``subscription-manager register`` for things already configured; now a specified ``rhsm_baseurl`` is properly set for subscription-manager (https://github.com/ansible-collections/community.general/pull/5583). +- unixy callback plugin - fix plugin to work with ansible-core 2.14 by using Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600). +- vdo - now uses ``yaml.safe_load()`` to parse command output instead of the deprecated ``yaml.load()`` which is potentially unsafe. Using ``yaml.load()`` without explicitely setting a ``Loader=`` is also an error in pyYAML 6.0 (https://github.com/ansible-collections/community.general/pull/5632). +- vmadm - fix for index out of range error in ``get_vm_uuid`` (https://github.com/ansible-collections/community.general/pull/5628). + +New Modules +----------- + +- gitlab_project_badge - Manage project badges on GitLab Server +- keycloak_clientsecret_info - Retrieve client secret via Keycloak API +- keycloak_clientsecret_regenerate - Regenerate Keycloak client secret via Keycloak API + +v6.0.1 +====== + +Release Summary +--------------- + +Bugfix release for Ansible 7.0.0. + +Bugfixes +-------- + +- dependent lookup plugin - avoid warning on deprecated parameter for ``Templar.template()`` (https://github.com/ansible-collections/community.general/pull/5543). +- jenkins_build - fix the logical flaw when deleting a Jenkins build (https://github.com/ansible-collections/community.general/pull/5514). +- one_vm - avoid splitting labels that are ``None`` (https://github.com/ansible-collections/community.general/pull/5489). +- onepassword_raw - add missing parameter to plugin documentation (https://github.com/ansible-collections/community.general/issues/5506). +- proxmox_disk - avoid duplicate ``vmid`` reference (https://github.com/ansible-collections/community.general/issues/5492, https://github.com/ansible-collections/community.general/pull/5493). + +v6.0.0 +====== + +Release Summary +--------------- + +New major release of community.general with lots of bugfixes, new features, some removed deprecated features, and some other breaking changes. Please check the coresponding sections of the changelog for more details. Major Changes ------------- @@ -33,6 +356,7 @@ Minor Changes - alternatives - add ``state=absent`` to be able to remove an alternative (https://github.com/ansible-collections/community.general/pull/4654). - alternatives - add ``subcommands`` parameter (https://github.com/ansible-collections/community.general/pull/4654). - ansible_galaxy_install - minor refactoring using latest ``ModuleHelper`` updates (https://github.com/ansible-collections/community.general/pull/4752). +- ansible_galaxy_install - refactored module to use ``CmdRunner`` to execute ``ansible-galaxy`` (https://github.com/ansible-collections/community.general/pull/5477). - apk - add ``world`` parameter for supporting a custom world file (https://github.com/ansible-collections/community.general/pull/4976). - bitwarden lookup plugin - add option ``search`` to search for other attributes than name (https://github.com/ansible-collections/community.general/pull/5297). - cartesian lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440). @@ -41,6 +365,7 @@ Minor Changes - consul - adds ``ttl`` parameter for session (https://github.com/ansible-collections/community.general/pull/4996). - consul - minor refactoring (https://github.com/ansible-collections/community.general/pull/5367). - consul_session - adds ``token`` parameter for session (https://github.com/ansible-collections/community.general/pull/5193). +- cpanm - refactored module to use ``CmdRunner`` to execute ``cpanm`` (https://github.com/ansible-collections/community.general/pull/5485). - cpanm - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674). - credstash lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440). - dependent lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440). @@ -65,9 +390,11 @@ Minor Changes - gitlab_user - minor refactor when checking for installed dependency (https://github.com/ansible-collections/community.general/pull/5259). - hiera lookup plugin - start using Ansible's configuration manager to parse options. The Hiera executable and config file can now also be passed as lookup parameters (https://github.com/ansible-collections/community.general/pull/5440). - homebrew, homebrew_tap - added Homebrew on Linux path to defaults (https://github.com/ansible-collections/community.general/pull/5241). +- hponcfg - refactored module to use ``CmdRunner`` to execute ``hponcfg`` (https://github.com/ansible-collections/community.general/pull/5483). - keycloak_* modules - add ``http_agent`` parameter with default value ``Ansible`` (https://github.com/ansible-collections/community.general/issues/5023). - keyring lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440). - lastpass - use config manager for handling plugin options (https://github.com/ansible-collections/community.general/pull/5022). +- ldap_attrs - allow for DNs to have ``{x}`` prefix on first RDN (https://github.com/ansible-collections/community.general/issues/977, https://github.com/ansible-collections/community.general/pull/5450). - linode inventory plugin - simplify option handling (https://github.com/ansible-collections/community.general/pull/5438). - listen_ports_facts - add new ``include_non_listening`` option which adds ``-a`` option to ``netstat`` and ``ss``. This shows both listening and non-listening (for TCP this means established connections) sockets, and returns ``state`` and ``foreign_address`` (https://github.com/ansible-collections/community.general/issues/4762, https://github.com/ansible-collections/community.general/pull/4953). - lmdb_kv lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440). @@ -76,6 +403,7 @@ Minor Changes - machinectl become plugin - combine the success command when building the become command to be consistent with other become plugins (https://github.com/ansible-collections/community.general/pull/5287). - manifold lookup plugin - start using Ansible's configuration manager to parse options (https://github.com/ansible-collections/community.general/pull/5440). - maven_artifact - add a new ``unredirected_headers`` option that can be used with ansible-core 2.12 and above. The default value is to not use ``Authorization`` and ``Cookie`` headers on redirects for security reasons. With ansible-core 2.11, all headers are still passed on for redirects (https://github.com/ansible-collections/community.general/pull/4812). +- mksysb - refactored module to use ``CmdRunner`` to execute ``mksysb`` (https://github.com/ansible-collections/community.general/pull/5484). - mksysb - using ``do_raise()`` to raise exceptions in ``ModuleHelper`` derived modules (https://github.com/ansible-collections/community.general/pull/4674). - nagios - minor refactoring on parameter validation for different actions (https://github.com/ansible-collections/community.general/pull/5239). - netcup_dnsapi - add ``timeout`` parameter (https://github.com/ansible-collections/community.general/pull/5301). @@ -83,6 +411,7 @@ Minor Changes - nmcli - add bond option ``xmit_hash_policy`` to bond options (https://github.com/ansible-collections/community.general/issues/5148). - nmcli - adds ``vpn`` type and parameter for supporting VPN with service type L2TP and PPTP (https://github.com/ansible-collections/community.general/pull/4746). - nmcli - honor IP options for VPNs (https://github.com/ansible-collections/community.general/pull/5228). +- onepassword - support version 2 of the OnePassword CLI (https://github.com/ansible-collections/community.general/pull/4728) - opentelemetry callback plugin - allow configuring opentelementry callback via config file (https://github.com/ansible-collections/community.general/pull/4916). - opentelemetry callback plugin - send logs. This can be disabled by setting ``disable_logs=false`` (https://github.com/ansible-collections/community.general/pull/4175). - pacman - added parameters ``reason`` and ``reason_for`` to set/change the install reason of packages (https://github.com/ansible-collections/community.general/pull/4956). @@ -125,6 +454,7 @@ Breaking Changes / Porting Guide -------------------------------- - newrelic_deployment - ``revision`` is required for v2 API (https://github.com/ansible-collections/community.general/pull/5341). +- scaleway_container_registry_info - no longer replace ``secret_environment_variables`` in the output by ``SENSITIVE_VALUE`` (https://github.com/ansible-collections/community.general/pull/5497). Deprecated Features ------------------- @@ -179,8 +509,10 @@ Bugfixes - filesystem - improve error messages when output cannot be parsed by including newlines in escaped form (https://github.com/ansible-collections/community.general/pull/4700). - funcd connection plugin - fix signature of ``exec_command`` (https://github.com/ansible-collections/community.general/pull/5111). - ini_file - minor refactor fixing a python lint error (https://github.com/ansible-collections/community.general/pull/5307). +- iso_create - the module somtimes failed to add folders for Joliet and UDF formats (https://github.com/ansible-collections/community.general/issues/5275). - keycloak_realm - fix default groups and roles (https://github.com/ansible-collections/community.general/issues/4241). - keyring_info - fix the result from the keyring library never getting returned (https://github.com/ansible-collections/community.general/pull/4964). +- ldap_attrs - fix bug which caused a ``Bad search filter`` error. The error was occuring when the ldap attribute value contained special characters such as ``(`` or ``*`` (https://github.com/ansible-collections/community.general/issues/5434, https://github.com/ansible-collections/community.general/pull/5435). - ldap_attrs - fix ordering issue by ignoring the ``{x}`` prefix on attribute values (https://github.com/ansible-collections/community.general/issues/977, https://github.com/ansible-collections/community.general/pull/5385). - listen_ports_facts - removed leftover ``EnvironmentError`` . The ``else`` clause had a wrong indentation. The check is now handled in the ``split_pid_name`` function (https://github.com/ansible-collections/community.general/pull/5202). - locale_gen - fix support for Ubuntu (https://github.com/ansible-collections/community.general/issues/5281). @@ -220,6 +552,7 @@ Bugfixes - redis* modules - fix call to ``module.fail_json`` when failing because of missing Python libraries (https://github.com/ansible-collections/community.general/pull/4733). - slack - fix incorrect channel prefix ``#`` caused by incomplete pattern detection by adding ``G0`` and ``GF`` as channel ID patterns (https://github.com/ansible-collections/community.general/pull/5019). - slack - fix message update for channels which start with ``CP``. When ``message-id`` was passed it failed for channels which started with ``CP`` because the ``#`` symbol was added before the ``channel_id`` (https://github.com/ansible-collections/community.general/pull/5249). +- snap - allow values in the ``options`` parameter to contain whitespaces (https://github.com/ansible-collections/community.general/pull/5475). - sudoers - ensure sudoers config files are created with the permissions requested by sudoers (0440) (https://github.com/ansible-collections/community.general/pull/4814). - sudoers - fix incorrect handling of ``state: absent`` (https://github.com/ansible-collections/community.general/issues/4852). - tss lookup plugin - adding support for updated Delinea library (https://github.com/DelineaXPM/python-tss-sdk/issues/9, https://github.com/ansible-collections/community.general/pull/5151). @@ -229,8 +562,41 @@ Bugfixes - xfconf - fix setting of boolean values (https://github.com/ansible-collections/community.general/issues/4999, https://github.com/ansible-collections/community.general/pull/5007). - zfs - fix wrong quoting of properties (https://github.com/ansible-collections/community.general/issues/4707, https://github.com/ansible-collections/community.general/pull/4726). +New Plugins +----------- + +Filter +~~~~~~ + +- counter - Counts hashable elements in a sequence + +Lookup +~~~~~~ + +- bitwarden - Retrieve secrets from Bitwarden + New Modules ----------- +- gconftool2_info - Retrieve GConf configurations +- iso_customize - Add/remove/change files in ISO file +- keycloak_user_rolemapping - Allows administration of Keycloak user_rolemapping with the Keycloak API +- keyring - Set or delete a passphrase using the Operating System's native keyring +- keyring_info - Get a passphrase using the Operating System's native keyring +- manageiq_policies_info - Listing of resource policy_profiles in ManageIQ +- manageiq_tags_info - Retrieve resource tags in ManageIQ +- pipx_info - Rretrieves information about applications installed with pipx +- proxmox_disk - Management of a disk of a Qemu(KVM) VM in a Proxmox VE cluster. +- scaleway_compute_private_network - Scaleway compute - private network management +- scaleway_container - Scaleway Container management +- scaleway_container_info - Retrieve information on Scaleway Container +- scaleway_container_namespace - Scaleway Container namespace management +- scaleway_container_namespace_info - Retrieve information on Scaleway Container namespace +- scaleway_container_registry - Scaleway Container registry management module +- scaleway_container_registry_info - Scaleway Container registry info module +- scaleway_function - Scaleway Function management +- scaleway_function_info - Retrieve information on Scaleway Function - scaleway_function_namespace - Scaleway Function namespace management - scaleway_function_namespace_info - Retrieve information on Scaleway Function namespace +- wdc_redfish_command - Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs +- wdc_redfish_info - Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish APIs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4236778dc6..358daa5e91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ Also, consider taking up a valuable, reviewed, but abandoned pull request which * Try committing your changes with an informative but short commit message. * Do not squash your commits and force-push to your branch if not needed. Reviews of your pull request are much easier with individual commits to comprehend the pull request history. All commits of your pull request branch will be squashed into one commit by GitHub upon merge. * Do not add merge commits to your PR. The bot will complain and you will have to rebase ([instructions for rebasing](https://docs.ansible.com/ansible/latest/dev_guide/developing_rebasing.html)) to remove them before your PR can be merged. To avoid that git automatically does merges during pulls, you can configure it to do rebases instead by running `git config pull.rebase true` inside the repository checkout. -* Make sure your PR includes a [changelog fragment](https://docs.ansible.com/ansible/devel/community/development_process.html#changelogs-how-to). (You must not include a fragment for new modules or new plugins, except for test and filter plugins. Also you shouldn't include one for docs-only changes. If you're not sure, simply don't include one, we'll tell you whether one is needed or not :) ) +* Make sure your PR includes a [changelog fragment](https://docs.ansible.com/ansible/devel/community/development_process.html#creating-changelog-fragments). (You must not include a fragment for new modules or new plugins, except for test and filter plugins. Also you shouldn't include one for docs-only changes. If you're not sure, simply don't include one, we'll tell you whether one is needed or not :) ) * Avoid reformatting unrelated parts of the codebase in your PR. These types of changes will likely be requested for reversion, create additional work for reviewers, and may cause approval to be delayed. You can also read [our Quick-start development guide](https://github.com/ansible/community-docs/blob/main/create_pr_quick_start_guide.rst). diff --git a/README.md b/README.md index 94d5b506ba..dc5db29448 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ SPDX-License-Identifier: GPL-3.0-or-later # Community General Collection -[![Build Status](https://dev.azure.com/ansible/community.general/_apis/build/status/CI?branchName=main)](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-6)](https://dev.azure.com/ansible/community.general/_build?definitionId=31) +[![EOL CI](https://github.com/ansible-collections/community.general/workflows/EOL%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.general/actions) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.general)](https://codecov.io/gh/ansible-collections/community.general) This repository contains the `community.general` Ansible Collection. The collection is a part of the Ansible package and includes many modules and plugins supported by Ansible community which are not part of more specialized community collections. @@ -72,13 +73,13 @@ We are actively accepting new contributors. All types of contributions are very welcome. -You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/main/CONTRIBUTING.md)! +You don't know how to start? Refer to our [contribution guide](https://github.com/ansible-collections/community.general/blob/stable-6/CONTRIBUTING.md)! -The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/main/commit-rights.md#people) file. If you have questions or need help, feel free to mention them in the proposals. +The current maintainers are listed in the [commit-rights.md](https://github.com/ansible-collections/community.general/blob/stable-6/commit-rights.md#people) file. If you have questions or need help, feel free to mention them in the proposals. You can find more information in the [developer guide for collections](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#contributing-to-collections), and in the [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html). -Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/main/CONTRIBUTING.md). +Also for some notes specific to this collection see [our CONTRIBUTING documentation](https://github.com/ansible-collections/community.general/blob/stable-6/CONTRIBUTING.md). ### Running tests @@ -88,7 +89,7 @@ See [here](https://docs.ansible.com/ansible/devel/dev_guide/developing_collectio To learn how to maintain / become a maintainer of this collection, refer to: -* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/main/commit-rights.md). +* [Committer guidelines](https://github.com/ansible-collections/community.general/blob/stable-6/commit-rights.md). * [Maintainer guidelines](https://github.com/ansible/community-docs/blob/main/maintaining.rst). It is necessary for maintainers of this collection to be subscribed to: @@ -116,7 +117,7 @@ See the [Releasing guidelines](https://github.com/ansible/community-docs/blob/ma ## Release notes -See the [changelog](https://github.com/ansible-collections/community.general/blob/main/CHANGELOG.rst). +See the [changelog](https://github.com/ansible-collections/community.general/blob/stable-6/CHANGELOG.rst). ## Roadmap @@ -135,8 +136,8 @@ See [this issue](https://github.com/ansible-collections/community.general/issues This collection is primarily licensed and distributed as a whole under the GNU General Public License v3.0 or later. -See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/main/COPYING) for the full text. +See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/stable-6/COPYING) for the full text. -Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/main/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/main/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/main/LICENSES/PSF-2.0.txt). +Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.general/blob/stable-6/LICENSES/BSD-2-Clause.txt), the [MIT license](https://github.com/ansible-collections/community.general/blob/stable-6/LICENSES/MIT.txt), and the [PSF 2.0 license](https://github.com/ansible-collections/community.general/blob/stable-6/LICENSES/PSF-2.0.txt). All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/). diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index b5612b7ccc..bcdc1415b5 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -1,5 +1,62 @@ ancestor: 5.0.0 releases: + 6.0.0: + changes: + breaking_changes: + - scaleway_container_registry_info - no longer replace ``secret_environment_variables`` + in the output by ``SENSITIVE_VALUE`` (https://github.com/ansible-collections/community.general/pull/5497). + bugfixes: + - iso_create - the module somtimes failed to add folders for Joliet and UDF + formats (https://github.com/ansible-collections/community.general/issues/5275). + - ldap_attrs - fix bug which caused a ``Bad search filter`` error. The error + was occuring when the ldap attribute value contained special characters such + as ``(`` or ``*`` (https://github.com/ansible-collections/community.general/issues/5434, + https://github.com/ansible-collections/community.general/pull/5435). + - snap - allow values in the ``options`` parameter to contain whitespaces (https://github.com/ansible-collections/community.general/pull/5475). + minor_changes: + - ansible_galaxy_install - refactored module to use ``CmdRunner`` to execute + ``ansible-galaxy`` (https://github.com/ansible-collections/community.general/pull/5477). + - cpanm - refactored module to use ``CmdRunner`` to execute ``cpanm`` (https://github.com/ansible-collections/community.general/pull/5485). + - hponcfg - refactored module to use ``CmdRunner`` to execute ``hponcfg`` (https://github.com/ansible-collections/community.general/pull/5483). + - ldap_attrs - allow for DNs to have ``{x}`` prefix on first RDN (https://github.com/ansible-collections/community.general/issues/977, + https://github.com/ansible-collections/community.general/pull/5450). + - mksysb - refactored module to use ``CmdRunner`` to execute ``mksysb`` (https://github.com/ansible-collections/community.general/pull/5484). + - onepassword - support version 2 of the OnePassword CLI (https://github.com/ansible-collections/community.general/pull/4728) + release_summary: New major release of community.general with lots of bugfixes, + new features, some removed deprecated features, and some other breaking changes. + Please check the coresponding sections of the changelog for more details. + fragments: + - 4728-onepassword-v2.yml + - 5435-escape-ldap-param.yml + - 5450-allow-for-xordered-dns.yaml + - 5468-iso-create-not-add-folders.yml + - 5475-snap-option-value-whitespace.yml + - 5477-ansible-galaxy-install-cmd-runner.yml + - 5483-hponcfg-cmd-runner.yml + - 5484-mksysb-cmd-runner.yml + - 5485-cpanm-cmd-runner.yml + - 5497-scaleway-filtering.yml + - 6.0.0.yml + modules: + - description: Scaleway Container management + name: scaleway_container + namespace: '' + - description: Retrieve information on Scaleway Container + name: scaleway_container_info + namespace: '' + - description: Scaleway Container namespace management + name: scaleway_container_namespace + namespace: '' + - description: Retrieve information on Scaleway Container namespace + name: scaleway_container_namespace_info + namespace: '' + - description: Scaleway Function management + name: scaleway_function + namespace: '' + - description: Retrieve information on Scaleway Function + name: scaleway_function_info + namespace: '' + release_date: '2022-11-07' 6.0.0-a1: changes: breaking_changes: @@ -508,10 +565,675 @@ releases: - simplified-bsd-license.yml - unflatmap.yml modules: + - description: Retrieve GConf configurations + name: gconftool2_info + namespace: '' + - description: Add/remove/change files in ISO file + name: iso_customize + namespace: '' + - description: Allows administration of Keycloak user_rolemapping with the Keycloak + API + name: keycloak_user_rolemapping + namespace: '' + - description: Set or delete a passphrase using the Operating System's native + keyring + name: keyring + namespace: '' + - description: Get a passphrase using the Operating System's native keyring + name: keyring_info + namespace: '' + - description: Listing of resource policy_profiles in ManageIQ + name: manageiq_policies_info + namespace: '' + - description: Retrieve resource tags in ManageIQ + name: manageiq_tags_info + namespace: '' + - description: Rretrieves information about applications installed with pipx + name: pipx_info + namespace: '' + - description: Management of a disk of a Qemu(KVM) VM in a Proxmox VE cluster. + name: proxmox_disk + namespace: '' + - description: Scaleway compute - private network management + name: scaleway_compute_private_network + namespace: '' + - description: Scaleway Container registry management module + name: scaleway_container_registry + namespace: '' + - description: Scaleway Container registry info module + name: scaleway_container_registry_info + namespace: '' - description: Scaleway Function namespace management name: scaleway_function_namespace namespace: '' - description: Retrieve information on Scaleway Function namespace name: scaleway_function_namespace_info namespace: '' + - description: Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish + APIs + name: wdc_redfish_command + namespace: '' + - description: Manages WDC UltraStar Data102 Out-Of-Band controllers using Redfish + APIs + name: wdc_redfish_info + namespace: '' + plugins: + filter: + - description: Counts hashable elements in a sequence + name: counter + namespace: null + lookup: + - description: Retrieve secrets from Bitwarden + name: bitwarden + namespace: null release_date: '2022-11-02' + 6.0.1: + changes: + bugfixes: + - dependent lookup plugin - avoid warning on deprecated parameter for ``Templar.template()`` + (https://github.com/ansible-collections/community.general/pull/5543). + - jenkins_build - fix the logical flaw when deleting a Jenkins build (https://github.com/ansible-collections/community.general/pull/5514). + - one_vm - avoid splitting labels that are ``None`` (https://github.com/ansible-collections/community.general/pull/5489). + - onepassword_raw - add missing parameter to plugin documentation (https://github.com/ansible-collections/community.general/issues/5506). + - proxmox_disk - avoid duplicate ``vmid`` reference (https://github.com/ansible-collections/community.general/issues/5492, + https://github.com/ansible-collections/community.general/pull/5493). + release_summary: Bugfix release for Ansible 7.0.0. + fragments: + - 5489-nonetype-in-get-vm-by-label.yml + - 5493-proxmox.yml + - 5506-onepassword_raw-missing-param.yml + - 5514-fix-logical-flaw-when-deleting-jenkins-build.yml + - 5543-dependent-template.yml + - 6.0.1.yml + release_date: '2022-11-15' + 6.1.0: + changes: + bugfixes: + - chroot connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. + This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/5570). + - cmd_runner module utils - fixed bug when handling default cases in ``cmd_runner_fmt.as_map()`` + (https://github.com/ansible-collections/community.general/pull/5538). + - cmd_runner module utils - formatting arguments ``cmd_runner_fmt.as_fixed()`` + was expecting an non-existing argument (https://github.com/ansible-collections/community.general/pull/5538). + - keycloak_client_rolemapping - calculate ``proposed`` and ``after`` return + values properly (https://github.com/ansible-collections/community.general/pull/5619). + - keycloak_client_rolemapping - remove only listed mappings with ``state=absent`` + (https://github.com/ansible-collections/community.general/pull/5619). + - proxmox inventory plugin - fix bug while templating when using templates for + the ``url``, ``user``, ``password``, ``token_id``, or ``token_secret`` options + (https://github.com/ansible-collections/community.general/pull/5640). + - proxmox inventory plugin - handle tags delimited by semicolon instead of comma, + which happens from Proxmox 7.3 on (https://github.com/ansible-collections/community.general/pull/5602). + - redhat_subscription - do not ignore ``consumer_name`` and other variables + if ``activationkey`` is specified (https://github.com/ansible-collections/community.general/issues/3486, + https://github.com/ansible-collections/community.general/pull/5627). + - redhat_subscription - do not pass arguments to ``subscription-manager register`` + for things already configured; now a specified ``rhsm_baseurl`` is properly + set for subscription-manager (https://github.com/ansible-collections/community.general/pull/5583). + - unixy callback plugin - fix plugin to work with ansible-core 2.14 by using + Ansible's configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600). + - vdo - now uses ``yaml.safe_load()`` to parse command output instead of the + deprecated ``yaml.load()`` which is potentially unsafe. Using ``yaml.load()`` + without explicitely setting a ``Loader=`` is also an error in pyYAML 6.0 (https://github.com/ansible-collections/community.general/pull/5632). + - vmadm - fix for index out of range error in ``get_vm_uuid`` (https://github.com/ansible-collections/community.general/pull/5628). + deprecated_features: + - The ``sap`` modules ``sapcar_extract``, ``sap_task_list_execute``, and ``hana_query``, + will be removed from this collection in community.general 7.0.0 and replaced + with redirects to ``community.sap_libs``. If you want to continue using these + modules, make sure to also install ``community.sap_libs`` (it is part of the + Ansible package) (https://github.com/ansible-collections/community.general/pull/5614). + minor_changes: + - cmd_runner module utils - ``cmd_runner_fmt.as_bool()`` can now take an extra + parameter to format when value is false (https://github.com/ansible-collections/community.general/pull/5647). + - gconftool2 - refactor using ``ModuleHelper`` and ``CmdRunner`` (https://github.com/ansible-collections/community.general/pull/5545). + - java_certs - add more detailed error output when extracting certificate from + PKCS12 fails (https://github.com/ansible-collections/community.general/pull/5550). + - jenkins_plugin - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5565). + - lxd_project - refactored code out to module utils to clear sanity check (https://github.com/ansible-collections/community.general/pull/5549). + - nmap inventory plugin - add new options ``udp_scan``, ``icmp_timestamp``, + and ``dns_resolve`` for different types of scans (https://github.com/ansible-collections/community.general/pull/5566). + - rax_scaling_group - refactored out code to the ``rax`` module utils to clear + the sanity check (https://github.com/ansible-collections/community.general/pull/5563). + - redfish_command - add ``PerformRequestedOperations`` command to perform any + operations necessary to continue the update flow (https://github.com/ansible-collections/community.general/issues/4276). + - redfish_command - add ``update_apply_time`` to ``SimpleUpdate`` command (https://github.com/ansible-collections/community.general/issues/3910). + - redfish_command - add ``update_status`` to output of ``SimpleUpdate`` command + to allow a user monitor the update in progress (https://github.com/ansible-collections/community.general/issues/4276). + - redfish_info - add ``GetUpdateStatus`` command to check the progress of a + previous update request (https://github.com/ansible-collections/community.general/issues/4276). + - redfish_utils module utils - added PUT (``put_request()``) functionality (https://github.com/ansible-collections/community.general/pull/5490). + - slack - add option ``prepend_hash`` which allows to control whether a ``#`` + is prepended to ``channel_id``. The current behavior (value ``auto``) is to + prepend ``#`` unless some specific prefixes are found. That list of prefixes + is incomplete, and there does not seem to exist a documented condition on + when exactly ``#`` must not be prepended. We recommend to explicitly set ``prepend_hash=always`` + or ``prepend_hash=never`` to avoid any ambiguity (https://github.com/ansible-collections/community.general/pull/5629). + - spotinst_aws_elastigroup - add ``elements`` attribute when missing in ``list`` + parameters (https://github.com/ansible-collections/community.general/pull/5553). + - ssh_config - add ``host_key_algorithms`` option (https://github.com/ansible-collections/community.general/pull/5605). + - udm_share - added ``elements`` attribute to ``list`` type parameters (https://github.com/ansible-collections/community.general/pull/5557). + - udm_user - add ``elements`` attribute when missing in ``list`` parameters + (https://github.com/ansible-collections/community.general/pull/5559). + release_summary: Regular bugfix and feature release. + fragments: + - 3910-redfish-add-operation-apply-time-to-simple-update.yml + - 4276-redfish-command-updates-for-full-simple-update-workflow.yml + - 5490-adding-put-functionality.yml + - 5538-cmd-runner-as-fixed.yml + - 5545-gconftool-cmd-runner.yml + - 5549-lxd-project-sanity.yml + - 5550-java_certs-not-enough-info-on-error.yml + - 5553-spotinst-aws-elasticgroup-sanity.yml + - 5557-udm-share-sanity.yml + - 5559-udm-user-sanity.yml + - 5563-rax-scaling-group-sanity.yml + - 5565-jenkins-plugin-sanity.yml + - 5566-additional-flags-nmap.yml + - 5570-chroot-plugin-fix-default-inventory_hostname.yml + - 5583-redhat_subscription-subscribe-parameters.yaml + - 5601-unixy-callback-use-config-manager.yml + - 5602-proxmox-tags.yml + - 5605-ssh-config-add-host-key-algorithms.yaml + - 5619-keycloak-improvements.yml + - 5627-redhat_subscription-subscribe-parameters-2.yaml + - 5628-fix-vmadm-off-by-one.yml + - 5629-add-prepend-hash-option-for-channel-id.yml + - 5632-vdo-Use-yaml-safe-load-instead-of-yaml-load.yml + - 5640-fix-typo-proxmox-inventory.yml + - 5647-cmd-runner-as-bool-false.yml + - 6.1.0.yml + - sap-removal.yml + modules: + - description: Manage project badges on GitLab Server + name: gitlab_project_badge + namespace: '' + - description: Retrieve client secret via Keycloak API + name: keycloak_clientsecret_info + namespace: '' + - description: Regenerate Keycloak client secret via Keycloak API + name: keycloak_clientsecret_regenerate + namespace: '' + release_date: '2022-12-06' + 6.2.0: + changes: + bugfixes: + - ansible_galaxy_install - set default to raise exception if command's return + code is different from zero (https://github.com/ansible-collections/community.general/pull/5680). + - ansible_galaxy_install - try ``C.UTF-8`` and then fall back to ``en_US.UTF-8`` + before failing (https://github.com/ansible-collections/community.general/pull/5680). + - gitlab_group_variables - fix dropping variables accidentally when GitLab introduced + new properties (https://github.com/ansible-collections/community.general/pull/5667). + - gitlab_project_variables - fix dropping variables accidentally when GitLab + introduced new properties (https://github.com/ansible-collections/community.general/pull/5667). + - lxc_container - fix the arguments of the lxc command which broke the creation + and cloning of containers (https://github.com/ansible-collections/community.general/issues/5578). + - opkg - fix issue that ``force=reinstall`` would not reinstall an existing + package (https://github.com/ansible-collections/community.general/pull/5705). + - proxmox_disk - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, + https://github.com/ansible-collections/community.general/pull/5672). + - proxmox_nic - fixed possible issues with redundant ``vmid`` parameter (https://github.com/ansible-collections/community.general/issues/5492, + https://github.com/ansible-collections/community.general/pull/5672). + - unixy callback plugin - fix typo introduced when updating to use Ansible's + configuration manager for handling options (https://github.com/ansible-collections/community.general/issues/5600). + deprecated_features: + - manageiq_policies - deprecate ``state=list`` in favour of using ``community.general.manageiq_policies_info`` + (https://github.com/ansible-collections/community.general/pull/5721). + - rax - module relies on deprecates library ``pyrax``. Unless maintainers step + up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_cbs - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_cbs_attachments - module relies on deprecates library ``pyrax``. Unless + maintainers step up to work on the module, it will be marked as deprecated + in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_cdb - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_cdb_database - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_cdb_user - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_clb - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_clb_nodes - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_clb_ssl - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_dns - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_dns_record - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_facts - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_files - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_files_objects - module relies on deprecates library ``pyrax``. Unless + maintainers step up to work on the module, it will be marked as deprecated + in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_identity - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_keypair - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_meta - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_mon_alarm - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_mon_check - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_mon_entity - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_mon_notification - module relies on deprecates library ``pyrax``. Unless + maintainers step up to work on the module, it will be marked as deprecated + in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_mon_notification_plan - module relies on deprecates library ``pyrax``. + Unless maintainers step up to work on the module, it will be marked as deprecated + in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_network - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_queue - module relies on deprecates library ``pyrax``. Unless maintainers + step up to work on the module, it will be marked as deprecated in community.general + 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_scaling_group - module relies on deprecates library ``pyrax``. Unless + maintainers step up to work on the module, it will be marked as deprecated + in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + - rax_scaling_policy - module relies on deprecates library ``pyrax``. Unless + maintainers step up to work on the module, it will be marked as deprecated + in community.general 7.0.0 and removed in version 9.0.0 (https://github.com/ansible-collections/community.general/pull/5733). + minor_changes: + - opkg - allow installing a package in a certain version (https://github.com/ansible-collections/community.general/pull/5688). + - proxmox - added new module parameter ``tags`` for use with PVE 7+ (https://github.com/ansible-collections/community.general/pull/5714). + - puppet - refactored module to use ``CmdRunner`` for executing ``puppet`` (https://github.com/ansible-collections/community.general/pull/5612). + - redhat_subscription - add a ``server_proxy_scheme`` parameter to configure + the scheme for the proxy server (https://github.com/ansible-collections/community.general/pull/5662). + - ssh_config - refactor code to module util to fix sanity check (https://github.com/ansible-collections/community.general/pull/5720). + - sudoers - adds ``host`` parameter for setting hostname restrictions in sudoers + rules (https://github.com/ansible-collections/community.general/issues/5702). + release_summary: Regular bugfix and feature release. + fragments: + - 5612-puppet-cmd-runner.yml + - 5659-fix-lxc_container-command.yml + - 5662-redhat_subscription-server_proxy_scheme.yaml + - 5666-gitlab-variables.yml + - 5672-proxmox.yml + - 5680-ansible_galaxy_install-fx-locale.yaml + - 5688-opkg-module-install-certain-version.yml + - 5703-sudoers-host-support.yml + - 5705-opkg-fix-force-reinstall.yml + - 5714-proxmox-lxc-tag-support.yml + - 5720-ssh_config-plugin-sanity.yml + - 5721-manageiq-policies-deprecate-list-state.yaml + - 5733-rax-deprecation-notice.yml + - 5744-unixy-callback-fix-config-manager-typo.yml + - 6.2.0.yml + release_date: '2023-01-04' + 6.3.0: + changes: + breaking_changes: + - 'ModuleHelper module utils - when the module sets output variables named ``msg``, + ``exception``, ``output``, ``vars``, or ``changed``, the actual output will + prefix those names with ``_`` (underscore symbol) only when they clash with + output variables generated by ModuleHelper itself, which only occurs when + handling exceptions. Please note that this breaking change does not require + a new major release since before this release, it was not possible to add + such variables to the output `due to a bug `__ + (https://github.com/ansible-collections/community.general/pull/5765). + + ' + bugfixes: + - ModuleHelper - fix bug when adjusting the name of reserved output variables + (https://github.com/ansible-collections/community.general/pull/5755). + - alternatives - support subcommands on Fedora 37, which uses ``follower`` instead + of ``slave`` (https://github.com/ansible-collections/community.general/pull/5794). + - bitwarden lookup plugin - clarify what to do, if the bitwarden vault is not + unlocked (https://github.com/ansible-collections/community.general/pull/5811). + - dig lookup plugin - correctly handle DNSKEY record type's ``algorithm`` field + (https://github.com/ansible-collections/community.general/pull/5914). + - gem - fix force parameter not being passed to gem command when uninstalling + (https://github.com/ansible-collections/community.general/pull/5822). + - gem - fix hang due to interactive prompt for confirmation on specific version + uninstall (https://github.com/ansible-collections/community.general/pull/5751). + - gitlab_deploy_key - also update ``title`` and not just ``can_push`` (https://github.com/ansible-collections/community.general/pull/5888). + - keycloak_user_federation - fixes federation creation issue. When a new federation + was created and at the same time a default / standard mapper was also changed + / updated the creation process failed as a bad None set variable led to a + bad malformed url request (https://github.com/ansible-collections/community.general/pull/5750). + - 'keycloak_user_federation - fixes idempotency detection issues. In some cases + the module could fail to properly detect already existing user federations + because of a buggy seemingly superflous extra query parameter (https://github.com/ansible-collections/community.general/pull/5732). + + ' + - loganalytics callback plugin - adjust type of callback to ``notification``, + it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). + - logdna callback plugin - adjust type of callback to ``notification``, it was + incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). + - logstash callback plugin - adjust type of callback to ``notification``, it + was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). + - nsupdate - fix zone lookup. The SOA record for an existing zone is returned + as an answer RR and not as an authority RR (https://github.com/ansible-collections/community.general/issues/5817, + https://github.com/ansible-collections/community.general/pull/5818). + - proxmox_disk - fixed issue with read timeout on import action (https://github.com/ansible-collections/community.general/pull/5803). + - redfish_utils - removed basic auth HTTP header when performing a GET on the + service root resource and when performing a POST to the session collection + (https://github.com/ansible-collections/community.general/issues/5886). + - splunk callback plugin - adjust type of callback to ``notification``, it was + incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). + - sumologic callback plugin - adjust type of callback to ``notification``, it + was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). + - syslog_json callback plugin - adjust type of callback to ``notification``, + it was incorrectly classified as ``aggregate`` before (https://github.com/ansible-collections/community.general/pull/5761). + - terraform - fix ``current`` workspace never getting appended to the ``all`` + key in the ``workspace_ctf`` object (https://github.com/ansible-collections/community.general/pull/5735). + - terraform - fix ``terraform init`` failure when there are multiple workspaces + on the remote backend and when ``default`` workspace is missing by setting + ``TF_WORKSPACE`` environmental variable to the value of ``workspace`` when + used (https://github.com/ansible-collections/community.general/pull/5735). + - terraform module - disable ANSI escape sequences during validation phase (https://github.com/ansible-collections/community.general/pull/5843). + - xml - fixed a bug where empty ``children`` list would not be set (https://github.com/ansible-collections/community.general/pull/5808). + deprecated_features: + - consul - deprecate using parameters unused for ``state=absent`` (https://github.com/ansible-collections/community.general/pull/5772). + - gitlab_runner - the default of the new option ``access_level_on_creation`` + will change from ``false`` to ``true`` in community.general 7.0.0. This will + cause ``access_level`` to be used during runner registration as well, and + not only during updates (https://github.com/ansible-collections/community.general/pull/5908). + minor_changes: + - apache2_module - add module argument ``warn_mpm_absent`` to control whether + warning are raised in some edge cases (https://github.com/ansible-collections/community.general/pull/5793). + - bitwarden lookup plugin - can now retrieve secrets from custom fields (https://github.com/ansible-collections/community.general/pull/5694). + - bitwarden lookup plugin - implement filtering results by ``collection_id`` + parameter (https://github.com/ansible-collections/community.general/issues/5849). + - dig lookup plugin - support CAA record type (https://github.com/ansible-collections/community.general/pull/5913). + - gitlab_project - add ``builds_access_level``, ``container_registry_access_level`` + and ``forking_access_level`` options (https://github.com/ansible-collections/community.general/pull/5706). + - gitlab_runner - add new boolean option ``access_level_on_creation``. It controls, + whether the value of ``access_level`` is used for runner registration or not. + The option ``access_level`` has been ignored on registration so far and was + only used on updates (https://github.com/ansible-collections/community.general/issues/5907, + https://github.com/ansible-collections/community.general/pull/5908). + - ilo_redfish_utils module utils - change implementation of DNS Server IP and + NTP Server IP update (https://github.com/ansible-collections/community.general/pull/5804). + - ipa_group - allow to add and remove external users with the ``external_user`` + option (https://github.com/ansible-collections/community.general/pull/5897). + - iptables_state - minor refactoring within the module (https://github.com/ansible-collections/community.general/pull/5844). + - one_vm - add a new ``updateconf`` option which implements the ``one.vm.updateconf`` + API call (https://github.com/ansible-collections/community.general/pull/5812). + - opkg - refactored module to use ``CmdRunner`` for executing ``opkg`` (https://github.com/ansible-collections/community.general/pull/5718). + - redhat_subscription - adds ``token`` parameter for subscription-manager authentication + using Red Hat API token (https://github.com/ansible-collections/community.general/pull/5725). + - snap - minor refactor when executing module (https://github.com/ansible-collections/community.general/pull/5773). + - snap_alias - refactored module to use ``CmdRunner`` to execute ``snap`` (https://github.com/ansible-collections/community.general/pull/5486). + - sudoers - add ``setenv`` parameters to support passing environment variables + via sudo. (https://github.com/ansible-collections/community.general/pull/5883) + release_summary: Regular bugfix and feature release. + fragments: + - 5486-snap-alias-cmd-runner.yml + - 5694-add-custom-fields-to-bitwarden.yml + - 5706-add-builds-forks-container-registry.yml + - 5718-opkg-refactor.yaml + - 5725-redhat_subscription-add-red-hat-api-token.yml + - 5732-bugfix-keycloak-userfed-idempotency.yml + - 5735-terraform-init-fix-when-default-workspace-doesnt-exists.yaml + - 5750-bugfixing-keycloak-usrfed-fail-when-update-default-mapper-simultaneously.yml + - 5751-gem-fix-uninstall-hang.yml + - 5755-mh-fix-output-conflict.yml + - 5761-callback-types.yml + - 5765-mh-lax-output-conflict.yml + - 5772-consul-deprecate-params-when-absent.yml + - 5773-snap-mh-execute.yml + - 5793-apache2-module-npm-warnings.yml + - 5794-alternatives-fedora37.yml + - 5803-proxmox-read-timeout.yml + - 5804-minor-changes-to-hpe-ilo-collection.yml + - 5808-xml-children-parameter-does-not-exist.yml + - 5811-clarify-bitwarden-error.yml + - 5812-implement-updateconf-api-call.yml + - 5818-nsupdate-fix-zone-lookup.yml + - 5822-gem-uninstall-force.yml + - 5843-terraform-validate-no-color.yml + - 5844-iptables-state-refactor.yml + - 5851-lookup-bitwarden-add-filter-by-collection-id-parameter.yml + - 5883-sudoers-add-support-for-setenv-parameter.yml + - 5886-redfish-correct-basic-auth-usage-on-session-creation.yml + - 5888-update-key-title.yml + - 5897-ipa_group-add-external-users.yml + - 5907-fix-gitlab_runner-not-idempotent.yml + - 5913-dig-caa.yml + - 5914-dig-dnskey.yml + - 6.3.0.yml + modules: + - description: Manages Out-Of-Band controllers using Open Composable API (OCAPI) + name: ocapi_command + namespace: '' + - description: Manages Out-Of-Band controllers using Open Composable API (OCAPI) + name: ocapi_info + namespace: '' + release_date: '2023-01-31' + 6.4.0: + changes: + bugfixes: + - cartesian and flattened lookup plugins - adjust to parameter deprecation in + ansible-core 2.14's ``listify_lookup_plugin_terms`` helper function (https://github.com/ansible-collections/community.general/pull/6074). + - cloudflare_dns - fixed the idempotency for SRV DNS records (https://github.com/ansible-collections/community.general/pull/5972). + - cloudflare_dns - fixed the possiblity of setting a root-level SRV DNS record + (https://github.com/ansible-collections/community.general/pull/5972). + - github_webhook - fix always changed state when no secret is provided (https://github.com/ansible-collections/community.general/pull/5994). + - jenkins_plugin - fix error due to undefined variable when updates file is + not downloaded (https://github.com/ansible-collections/community.general/pull/6100). + - keycloak_client - fix accidental replacement of value for attribute ``saml.signing.private.key`` + with ``no_log`` in wrong contexts (https://github.com/ansible-collections/community.general/pull/5934). + - lxd_* modules, lxd inventory plugin - fix TLS/SSL certificate validation problems + by using the correct purpose when creating the TLS context (https://github.com/ansible-collections/community.general/issues/5616, + https://github.com/ansible-collections/community.general/pull/6034). + - nmcli - fix change handling of values specified as an integer 0 (https://github.com/ansible-collections/community.general/pull/5431). + - nmcli - fix failure to handle WIFI settings when connection type not specified + (https://github.com/ansible-collections/community.general/pull/5431). + - nmcli - fix improper detection of changes to ``wifi.wake-on-wlan`` (https://github.com/ansible-collections/community.general/pull/5431). + - nmcli - order is significant for lists of addresses (https://github.com/ansible-collections/community.general/pull/6048). + - onepassword lookup plugin - Changed to ignore errors from "op account get" + calls. Previously, errors would prevent auto-signin code from executing (https://github.com/ansible-collections/community.general/pull/5942). + - terraform and timezone - slight refactoring to avoid linter reporting potentially + undefined variables (https://github.com/ansible-collections/community.general/pull/5933). + - various plugins and modules - remove unnecessary imports (https://github.com/ansible-collections/community.general/pull/5940). + - yarn - fix ``global=true`` to check for the configured global folder instead + of assuming the default (https://github.com/ansible-collections/community.general/pull/5829) + - yarn - fix ``state=absent`` not working with ``global=true`` when the package + does not include a binary (https://github.com/ansible-collections/community.general/pull/5829) + - yarn - fix ``state=latest`` not working with ``global=true`` (https://github.com/ansible-collections/community.general/issues/5712). + - zfs_delegate_admin - zfs allow output can now be parsed when uids/gids are + not known to the host system (https://github.com/ansible-collections/community.general/pull/5943). + - zypper - make package managing work on readonly filesystem of openSUSE MicroOS + (https://github.com/ansible-collections/community.general/pull/5615). + deprecated_features: + - gitlab_runner - the option ``access_level`` will lose its default value in + community.general 8.0.0. From that version on, you have set this option to + ``ref_protected`` explicitly, if you want to have a protected runner (https://github.com/ansible-collections/community.general/issues/5925). + minor_changes: + - dnsimple - set custom User-Agent for API requests to DNSimple (https://github.com/ansible-collections/community.general/pull/5927). + - flatpak_remote - add new boolean option ``enabled``. It controls, whether + the remote is enabled or not (https://github.com/ansible-collections/community.general/pull/5926). + - gitlab_project - add ``releases_access_level``, ``environments_access_level``, + ``feature_flags_access_level``, ``infrastructure_access_level``, ``monitor_access_level``, + and ``security_and_compliance_access_level`` options (https://github.com/ansible-collections/community.general/pull/5986). + - jc filter plugin - added the ability to use parser plugins (https://github.com/ansible-collections/community.general/pull/6043). + - keycloak_group - add new optional module parameter ``parents`` to properly + handle keycloak subgroups (https://github.com/ansible-collections/community.general/pull/5814). + - keycloak_user_federation - make ``org.keycloak.storage.ldap.mappers.LDAPStorageMapper`` + the default value for mappers ``providerType`` (https://github.com/ansible-collections/community.general/pull/5863). + - ldap modules - add ``xorder_discovery`` option (https://github.com/ansible-collections/community.general/issues/6045, + https://github.com/ansible-collections/community.general/pull/6109). + - lxd_container - add diff and check mode (https://github.com/ansible-collections/community.general/pull/5866). + - mattermost, rocketchat, slack - replace missing default favicon with docs.ansible.com + favicon (https://github.com/ansible-collections/community.general/pull/5928). + - modprobe - add ``persistent`` option (https://github.com/ansible-collections/community.general/issues/4028, + https://github.com/ansible-collections/community.general/pull/542). + - osx_defaults - include stderr in error messages (https://github.com/ansible-collections/community.general/pull/6011). + - proxmox - suppress urllib3 ``InsecureRequestWarnings`` when ``validate_certs`` + option is ``false`` (https://github.com/ansible-collections/community.general/pull/5931). + - redfish_command - adding ``EnableSecureBoot`` functionality (https://github.com/ansible-collections/community.general/pull/5899). + - redfish_command - adding ``VerifyBiosAttributes`` functionality (https://github.com/ansible-collections/community.general/pull/5900). + - sefcontext - add support for path substitutions (https://github.com/ansible-collections/community.general/issues/1193). + release_summary: Regular feature and bugfix release. + fragments: + - 4028-modprobe-persistent-option.yml + - 5431-nmcli-wifi.yml + - 5615-zypper-transactional-update.yml + - 5814-support-keycloak-subgroups.yml + - 5829-fix-yarn-global.yml + - 5830-sefcontext-path-subs.yml + - 5863-providerType-defaulted-keycloak_userfed-mappers.yml + - 5866-lxd_container-diff-and-check-mode.yml + - 5899-adding-enablesecureboot-functionality-to-redfish-config.yml + - 5900-adding-verifybiosattribute-fucntionality-to-redfish-command.yml + - 5915-suppress-urllib3-insecure-request-warnings.yml + - 5925-align_gitlab_runner_access_level_default_with_gitlab.yml + - 5926-flatpak-remote-enabled.yml + - 5927-set-user-agent-dnsimple.yml + - 5928-fix-favicon-url.yml + - 5933-linting.yml + - 5934-fix-keycloak-sanitize_cr.yml + - 5942-onepassword-ignore-errors-from-op-account-get.yml + - 5943-zfs_delegate_admin-fix-zfs-allow-cannot-parse-unknown-uid-gid.yml + - 5972-cloudflare-dns-srv-record.yml + - 5985-add-new-gitlab-api-features.yml + - 5994-github-webhook-secret.yml + - 6.4.0.yml + - 6011-osx-defaults-errors.yml + - 6034-lxd-tls.yml + - 6043-jc_plugin_parser_support.yml + - 6045-xorder-discovery.yml + - 6048-nmcli-addres-order.yml + - 6074-loader_in_listify.yml.yml + - 6100-jenkins_plugin.yml + - remove-unneeded-imports.yml + release_date: '2023-02-27' + 6.5.0: + changes: + bugfixes: + - archive - avoid deprecated exception class on Python 3 (https://github.com/ansible-collections/community.general/pull/6180). + - gitlab_runner - fix ``KeyError`` on runner creation and update (https://github.com/ansible-collections/community.general/issues/6112). + - influxdb_user - fix running in check mode when the user does not exist yet + (https://github.com/ansible-collections/community.general/pull/6111). + - interfaces_file - fix reading options in lines not starting with a space (https://github.com/ansible-collections/community.general/issues/6120). + - jail connection plugin - add ``inventory_hostname`` to vars under ``remote_addr``. + This is needed for compatibility with ansible-core 2.13 (https://github.com/ansible-collections/community.general/pull/6118). + - memset - fix memset urlerror handling (https://github.com/ansible-collections/community.general/pull/6114). + - nmcli - fixed idempotency issue for bridge connections. Module forced default + value of ``bridge.priority`` to nmcli if not set; if ``bridge.stp`` is disabled + nmcli ignores it and keep default (https://github.com/ansible-collections/community.general/issues/3216, + https://github.com/ansible-collections/community.general/issues/4683). + - nmcli - fixed idempotency issue when module params is set to ``may_fail4=false`` + and ``method4=disabled``; in this case nmcli ignores change and keeps their + own default value ``yes`` (https://github.com/ansible-collections/community.general/pull/6106). + - nmcli - implemented changing mtu value on vlan interfaces (https://github.com/ansible-collections/community.general/issues/4387). + - opkg - fixes bug when using ``update_cache=true`` (https://github.com/ansible-collections/community.general/issues/6004). + - redhat_subscription, rhsm_release, rhsm_repository - cleanly fail when not + running as root, rather than hanging on an interactive ``console-helper`` + prompt; they all interact with ``subscription-manager``, which already requires + to be run as root (https://github.com/ansible-collections/community.general/issues/734, + https://github.com/ansible-collections/community.general/pull/6211). + - xenorchestra inventory plugin - fix failure to receive objects from server + due to not checking the id of the response (https://github.com/ansible-collections/community.general/pull/6227). + - yarn - fix ``global=true`` to not fail when `executable` wasn't specified + (https://github.com/ansible-collections/community.general/pull/6132) + - yarn - fixes bug where yarn module tasks would fail when warnings were emitted + from Yarn. The ``yarn.list`` method was not filtering out warnings (https://github.com/ansible-collections/community.general/issues/6127). + minor_changes: + - apt_rpm - adds ``clean``, ``dist_upgrade`` and ``update_kernel`` parameters + for clear caches, complete upgrade system, and upgrade kernel packages (https://github.com/ansible-collections/community.general/pull/5867). + - dconf - parse GVariants for equality comparison when the Python module ``gi.repository`` + is available (https://github.com/ansible-collections/community.general/pull/6049). + - gitlab_runner - allow to register group runner (https://github.com/ansible-collections/community.general/pull/3935). + - jira - add worklog functionality (https://github.com/ansible-collections/community.general/issues/6209, + https://github.com/ansible-collections/community.general/pull/6210). + - ldap modules - add ``ca_path`` option (https://github.com/ansible-collections/community.general/pull/6185). + - make - add ``command`` return value to the module output (https://github.com/ansible-collections/community.general/pull/6160). + - nmap inventory plugin - add new option ``open`` for only returning open ports + (https://github.com/ansible-collections/community.general/pull/6200). + - nmap inventory plugin - add new option ``port`` for port specific scan (https://github.com/ansible-collections/community.general/pull/6165). + - nmcli - add ``default`` and ``default-or-eui64`` to the list of valid choices + for ``addr_gen_mode6`` parameter (https://github.com/ansible-collections/community.general/pull/5974). + - nmcli - add support for ``team.runner-fast-rate`` parameter for ``team`` connections + (https://github.com/ansible-collections/community.general/issues/6065). + - openbsd_pkg - set ``TERM`` to ``'dumb'`` in ``execute_command()`` to make + module less dependant on the ``TERM`` environment variable set on the Ansible + controller (https://github.com/ansible-collections/community.general/pull/6149). + - pipx - optional ``install_apps`` parameter added to install applications from + injected packages (https://github.com/ansible-collections/community.general/pull/6198). + - proxmox_kvm - add new ``archive`` parameter. This is needed to create a VM + from an archive (backup) (https://github.com/ansible-collections/community.general/pull/6159). + - redfish_info - adds commands to retrieve the HPE ThermalConfiguration and + FanPercentMinimum settings from iLO (https://github.com/ansible-collections/community.general/pull/6208). + - redhat_subscription - credentials (``username``, ``activationkey``, and so + on) are required now only if a system needs to be registered, or ``force_register`` + is specified (https://github.com/ansible-collections/community.general/pull/5664). + - redhat_subscription - the registration is done using the D-Bus ``rhsm`` service + instead of spawning a ``subscription-manager register`` command, if possible; + this avoids passing plain-text credentials as arguments to ``subscription-manager + register``, which can be seen while that command runs (https://github.com/ansible-collections/community.general/pull/6122). + - ssh_config - add ``proxyjump`` option (https://github.com/ansible-collections/community.general/pull/5970). + - ssh_config - vendored StormSSH's config parser to avoid having to install + StormSSH to use the module (https://github.com/ansible-collections/community.general/pull/6117). + - znode module - optional ``use_tls`` parameter added for encrypted communication + (https://github.com/ansible-collections/community.general/issues/6154). + release_summary: Feature and bugfix release. + fragments: + - 3216-nmcli-bridge-idempotency-fix.yml + - 3935-add-gitlab-group-runner.yml + - 4387-nmcli-mtu-for-vlan-connection-fix.yml + - 5664-redhat_subscription-credentials-when-needed.yaml + - 5867-apt_rpm-add-clean-and-upgrade.yml + - 5970-add-proxyjump-option-to-ssh-config.yml + - 5974-nmcli_add_new_addr_gen_mode6_options.yml + - 6.5.0.yml + - 6049-dconf-strings.yml + - 6065-nmcli-add-runner-fast-rate-option.yml + - 6106-nmcli-ipv4-mayfail-idempotency-fix.yml + - 6111-influxdb_user-check-mode.yaml + - 6112-fix_key_error_in_gitlab_runner_creation_update.yml + - 6114-memset-add-url-error-handling.yml + - 6117-remove-stormssh-depend.yml + - 6118-jail-plugin-fix-default-inventory_hostname.yml + - 6119-opkg-update.yaml + - 6122-redhat_subscription-subscribe-via-dbus.yaml + - 6127-yarn-ignore-warnings.yml + - 6131-fix-interfaces_file-for-no-leading-spaces.yml + - 6138-fix-yarn-global.yml + - 6149-openbsd_pkg-term.yml + - 6154-znode-optional-tls.yml + - 6158-create-proxmox-vm-from-archive.yml + - 6160-add-command-make-output.yml + - 6165-nmap-port.yml + - 6180-replace-deprecated-badzipfile.yml + - 6198-pipx-inject-install-apps.yml + - 6200-adding-open-option-to-nmap.yml + - 6208-hpe-thermal-fan-percent.yaml + - 6210-add-worklog-functionality-to-jira.yml + - 6211-rhsm-require-root.yml + - 6227-xen-orchestra-check-response-id.yml + - xxxx-ldap-ca-cert-file.yml + modules: + - description: Manage KDE configuration files + name: kdeconfig + namespace: '' + plugins: + lookup: + - description: merge variables with a certain suffix + name: merge_variables + namespace: null + release_date: '2023-03-27' diff --git a/changelogs/fragments/4728-onepassword-v2.yml b/changelogs/fragments/4728-onepassword-v2.yml deleted file mode 100644 index fbec3aa60d..0000000000 --- a/changelogs/fragments/4728-onepassword-v2.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - onepassword - support version 2 of the OnePassword CLI (https://github.com/ansible-collections/community.general/pull/4728) diff --git a/changelogs/fragments/5435-escape-ldap-param.yml b/changelogs/fragments/5435-escape-ldap-param.yml deleted file mode 100644 index 3f22f61759..0000000000 --- a/changelogs/fragments/5435-escape-ldap-param.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - ldap_attrs - fix bug which caused a ``Bad search filter`` error. The error was occuring when the ldap attribute value contained special characters such as ``(`` or ``*`` (https://github.com/ansible-collections/community.general/issues/5434, https://github.com/ansible-collections/community.general/pull/5435). diff --git a/changelogs/fragments/5450-allow-for-xordered-dns.yaml b/changelogs/fragments/5450-allow-for-xordered-dns.yaml deleted file mode 100644 index 1bb1d9c761..0000000000 --- a/changelogs/fragments/5450-allow-for-xordered-dns.yaml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - ldap_attrs - allow for DNs to have ``{x}`` prefix on first RDN (https://github.com/ansible-collections/community.general/issues/977, https://github.com/ansible-collections/community.general/pull/5450). diff --git a/changelogs/fragments/5468-iso-create-not-add-folders.yml b/changelogs/fragments/5468-iso-create-not-add-folders.yml deleted file mode 100644 index 5bbe48f579..0000000000 --- a/changelogs/fragments/5468-iso-create-not-add-folders.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - iso_create - the module somtimes failed to add folders for Joliet and UDF formats (https://github.com/ansible-collections/community.general/issues/5275). diff --git a/changelogs/fragments/5475-snap-option-value-whitespace.yml b/changelogs/fragments/5475-snap-option-value-whitespace.yml deleted file mode 100644 index c41c70da38..0000000000 --- a/changelogs/fragments/5475-snap-option-value-whitespace.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - snap - allow values in the ``options`` parameter to contain whitespaces (https://github.com/ansible-collections/community.general/pull/5475). diff --git a/changelogs/fragments/5477-ansible-galaxy-install-cmd-runner.yml b/changelogs/fragments/5477-ansible-galaxy-install-cmd-runner.yml deleted file mode 100644 index f480456953..0000000000 --- a/changelogs/fragments/5477-ansible-galaxy-install-cmd-runner.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - ansible_galaxy_install - refactored module to use ``CmdRunner`` to execute ``ansible-galaxy`` (https://github.com/ansible-collections/community.general/pull/5477). diff --git a/changelogs/fragments/5483-hponcfg-cmd-runner.yml b/changelogs/fragments/5483-hponcfg-cmd-runner.yml deleted file mode 100644 index 9d6c0eb8a9..0000000000 --- a/changelogs/fragments/5483-hponcfg-cmd-runner.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - hponcfg - refactored module to use ``CmdRunner`` to execute ``hponcfg`` (https://github.com/ansible-collections/community.general/pull/5483). diff --git a/changelogs/fragments/5484-mksysb-cmd-runner.yml b/changelogs/fragments/5484-mksysb-cmd-runner.yml deleted file mode 100644 index 89f4d0dac8..0000000000 --- a/changelogs/fragments/5484-mksysb-cmd-runner.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - mksysb - refactored module to use ``CmdRunner`` to execute ``mksysb`` (https://github.com/ansible-collections/community.general/pull/5484). diff --git a/changelogs/fragments/5485-cpanm-cmd-runner.yml b/changelogs/fragments/5485-cpanm-cmd-runner.yml deleted file mode 100644 index 508f261762..0000000000 --- a/changelogs/fragments/5485-cpanm-cmd-runner.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - cpanm - refactored module to use ``CmdRunner`` to execute ``cpanm`` (https://github.com/ansible-collections/community.general/pull/5485). diff --git a/changelogs/fragments/6.0.0.yml b/changelogs/fragments/6.0.0.yml deleted file mode 100644 index 347d16861c..0000000000 --- a/changelogs/fragments/6.0.0.yml +++ /dev/null @@ -1,3 +0,0 @@ -release_summary: >- - New major release of community.general with lots of bugfixes, new features, some removed deprecated features, and some other breaking changes. - Please check the coresponding sections of the changelog for more details. diff --git a/galaxy.yml b/galaxy.yml index 0a3fe09b6e..11a63851a6 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -5,7 +5,7 @@ namespace: community name: general -version: 6.0.0 +version: 6.5.0 readme: README.md authors: - Ansible (https://github.com/ansible) diff --git a/plugins/cache/memcached.py b/plugins/cache/memcached.py index 77f1717e45..0bc5256b3f 100644 --- a/plugins/cache/memcached.py +++ b/plugins/cache/memcached.py @@ -52,11 +52,9 @@ import time from multiprocessing import Lock from itertools import chain -from ansible import constants as C from ansible.errors import AnsibleError from ansible.module_utils.common._collections_compat import MutableSet from ansible.plugins.cache import BaseCacheModule -from ansible.release import __version__ as ansible_base_version from ansible.utils.display import Display try: diff --git a/plugins/cache/redis.py b/plugins/cache/redis.py index 81e960cf18..8c06217176 100644 --- a/plugins/cache/redis.py +++ b/plugins/cache/redis.py @@ -67,12 +67,10 @@ import re import time import json -from ansible import constants as C from ansible.errors import AnsibleError from ansible.module_utils.common.text.converters import to_native from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder from ansible.plugins.cache import BaseCacheModule -from ansible.release import __version__ as ansible_base_version from ansible.utils.display import Display try: diff --git a/plugins/callback/cgroup_memory_recap.py b/plugins/callback/cgroup_memory_recap.py index eedacfeecb..ccdbcc9cf0 100644 --- a/plugins/callback/cgroup_memory_recap.py +++ b/plugins/callback/cgroup_memory_recap.py @@ -16,15 +16,15 @@ DOCUMENTATION = ''' - cgroups short_description: Profiles maximum memory usage of tasks and full execution using cgroups description: - - This is an ansible callback plugin that profiles maximum memory usage of ansible and individual tasks, and displays a recap at the end using cgroups + - This is an ansible callback plugin that profiles maximum memory usage of ansible and individual tasks, and displays a recap at the end using cgroups. notes: - - Requires ansible to be run from within a cgroup, such as with C(cgexec -g memory:ansible_profile ansible-playbook ...) - - This cgroup should only be used by ansible to get accurate results - - To create the cgroup, first use a command such as C(sudo cgcreate -a ec2-user:ec2-user -t ec2-user:ec2-user -g memory:ansible_profile) + - Requires ansible to be run from within a cgroup, such as with C(cgexec -g memory:ansible_profile ansible-playbook ...). + - This cgroup should only be used by ansible to get accurate results. + - To create the cgroup, first use a command such as C(sudo cgcreate -a ec2-user:ec2-user -t ec2-user:ec2-user -g memory:ansible_profile). options: max_mem_file: required: true - description: Path to cgroups C(memory.max_usage_in_bytes) file. Example C(/sys/fs/cgroup/memory/ansible_profile/memory.max_usage_in_bytes) + description: Path to cgroups C(memory.max_usage_in_bytes) file. Example C(/sys/fs/cgroup/memory/ansible_profile/memory.max_usage_in_bytes). env: - name: CGROUP_MAX_MEM_FILE ini: @@ -32,7 +32,7 @@ DOCUMENTATION = ''' key: max_mem_file cur_mem_file: required: true - description: Path to C(memory.usage_in_bytes) file. Example C(/sys/fs/cgroup/memory/ansible_profile/memory.usage_in_bytes) + description: Path to C(memory.usage_in_bytes) file. Example C(/sys/fs/cgroup/memory/ansible_profile/memory.usage_in_bytes). env: - name: CGROUP_CUR_MEM_FILE ini: diff --git a/plugins/callback/context_demo.py b/plugins/callback/context_demo.py index 9c3c9c5afc..b9558fc064 100644 --- a/plugins/callback/context_demo.py +++ b/plugins/callback/context_demo.py @@ -13,8 +13,8 @@ DOCUMENTATION = ''' type: aggregate short_description: demo callback that adds play/task context description: - - Displays some play and task context along with normal output - - This is mostly for demo purposes + - Displays some play and task context along with normal output. + - This is mostly for demo purposes. requirements: - whitelist in configuration ''' diff --git a/plugins/callback/counter_enabled.py b/plugins/callback/counter_enabled.py index e0e040c9d4..27adc97a6c 100644 --- a/plugins/callback/counter_enabled.py +++ b/plugins/callback/counter_enabled.py @@ -21,13 +21,12 @@ DOCUMENTATION = ''' extends_documentation_fragment: - default_callback requirements: - - set as stdout callback in ansible.cfg (stdout_callback = counter_enabled) + - set as stdout callback in C(ansible.cfg) (C(stdout_callback = counter_enabled)) ''' from ansible import constants as C from ansible.plugins.callback import CallbackBase from ansible.utils.color import colorize, hostcolor -from ansible.template import Templar from ansible.playbook.task_include import TaskInclude diff --git a/plugins/callback/dense.py b/plugins/callback/dense.py index 18e4f162ff..490705fd27 100644 --- a/plugins/callback/dense.py +++ b/plugins/callback/dense.py @@ -14,7 +14,7 @@ short_description: minimal stdout output extends_documentation_fragment: - default_callback description: -- When in verbose mode it will act the same as the default callback +- When in verbose mode it will act the same as the default callback. author: - Dag Wieers (@dagwieers) requirements: diff --git a/plugins/callback/diy.py b/plugins/callback/diy.py index 55a07725f2..75b3f4e24b 100644 --- a/plugins/callback/diy.py +++ b/plugins/callback/diy.py @@ -786,10 +786,6 @@ playbook.yml: > import sys from contextlib import contextmanager -from ansible import constants as C -from ansible.playbook.task_include import TaskInclude -from ansible.plugins.callback import CallbackBase -from ansible.utils.color import colorize, hostcolor from ansible.template import Templar from ansible.vars.manager import VariableManager from ansible.plugins.callback.default import CallbackModule as Default diff --git a/plugins/callback/jabber.py b/plugins/callback/jabber.py index 823ae20144..d2d00496d8 100644 --- a/plugins/callback/jabber.py +++ b/plugins/callback/jabber.py @@ -13,10 +13,10 @@ DOCUMENTATION = ''' type: notification short_description: post task events to a jabber server description: - - The chatty part of ChatOps with a Hipchat server as a target + - The chatty part of ChatOps with a Hipchat server as a target. - This callback plugin sends status updates to a HipChat channel during playbook execution. requirements: - - xmpp (python lib https://github.com/ArchipelProject/xmpppy) + - xmpp (Python library U(https://github.com/ArchipelProject/xmpppy)) options: server: description: connection info to jabber server diff --git a/plugins/callback/log_plays.py b/plugins/callback/log_plays.py index b1dc69364c..e99054e176 100644 --- a/plugins/callback/log_plays.py +++ b/plugins/callback/log_plays.py @@ -13,10 +13,10 @@ DOCUMENTATION = ''' type: notification short_description: write playbook output to log file description: - - This callback writes playbook output to a file per host in the C(/var/log/ansible/hosts) directory + - This callback writes playbook output to a file per host in the C(/var/log/ansible/hosts) directory. requirements: - Whitelist in configuration - - A writeable /var/log/ansible/hosts directory by the user executing Ansible on the controller + - A writeable C(/var/log/ansible/hosts) directory by the user executing Ansible on the controller options: log_folder: default: /var/log/ansible/hosts diff --git a/plugins/callback/loganalytics.py b/plugins/callback/loganalytics.py index 54acf846a3..fbcdc6f89f 100644 --- a/plugins/callback/loganalytics.py +++ b/plugins/callback/loganalytics.py @@ -8,7 +8,7 @@ __metaclass__ = type DOCUMENTATION = ''' name: loganalytics - type: aggregate + type: notification short_description: Posts task results to Azure Log Analytics author: "Cyrus Li (@zhcli) " description: @@ -54,7 +54,6 @@ examples: | import hashlib import hmac import base64 -import logging import json import uuid import socket @@ -155,7 +154,7 @@ class AzureLogAnalyticsSource(object): class CallbackModule(CallbackBase): CALLBACK_VERSION = 2.0 - CALLBACK_TYPE = 'aggregate' + CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'loganalytics' CALLBACK_NEEDS_WHITELIST = True diff --git a/plugins/callback/logdna.py b/plugins/callback/logdna.py index ee0a1eb022..fc9a81ac8a 100644 --- a/plugins/callback/logdna.py +++ b/plugins/callback/logdna.py @@ -9,17 +9,17 @@ __metaclass__ = type DOCUMENTATION = ''' author: Unknown (!UNKNOWN) name: logdna - type: aggregate + type: notification short_description: Sends playbook logs to LogDNA description: - - This callback will report logs from playbook actions, tasks, and events to LogDNA (https://app.logdna.com) + - This callback will report logs from playbook actions, tasks, and events to LogDNA (U(https://app.logdna.com)). requirements: - - LogDNA Python Library (https://github.com/logdna/python) + - LogDNA Python Library (U(https://github.com/logdna/python)) - whitelisting in configuration options: conf_key: required: true - description: LogDNA Ingestion Key + description: LogDNA Ingestion Key. type: string env: - name: LOGDNA_INGESTION_KEY @@ -28,7 +28,7 @@ DOCUMENTATION = ''' key: conf_key plugin_ignore_errors: required: false - description: Whether to ignore errors on failing or not + description: Whether to ignore errors on failing or not. type: boolean env: - name: ANSIBLE_IGNORE_ERRORS @@ -38,7 +38,7 @@ DOCUMENTATION = ''' default: false conf_hostname: required: false - description: Alternative Host Name; the current host name by default + description: Alternative Host Name; the current host name by default. type: string env: - name: LOGDNA_HOSTNAME @@ -47,7 +47,7 @@ DOCUMENTATION = ''' key: conf_hostname conf_tags: required: false - description: Tags + description: Tags. type: string env: - name: LOGDNA_TAGS @@ -111,7 +111,7 @@ def isJSONable(obj): class CallbackModule(CallbackBase): CALLBACK_VERSION = 0.1 - CALLBACK_TYPE = 'aggregate' + CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'community.general.logdna' CALLBACK_NEEDS_WHITELIST = True diff --git a/plugins/callback/logentries.py b/plugins/callback/logentries.py index d40939b0ab..22322a4df2 100644 --- a/plugins/callback/logentries.py +++ b/plugins/callback/logentries.py @@ -13,15 +13,15 @@ DOCUMENTATION = ''' short_description: Sends events to Logentries description: - This callback plugin will generate JSON objects and send them to Logentries via TCP for auditing/debugging purposes. - - Before 2.4, if you wanted to use an ini configuration, the file must be placed in the same directory as this plugin and named logentries.ini + - Before 2.4, if you wanted to use an ini configuration, the file must be placed in the same directory as this plugin and named C(logentries.ini). - In 2.4 and above you can just put it in the main Ansible configuration file. requirements: - whitelisting in configuration - - certifi (python library) - - flatdict (python library), if you want to use the 'flatten' option + - certifi (Python library) + - flatdict (Python library), if you want to use the 'flatten' option options: api: - description: URI to the Logentries API + description: URI to the Logentries API. env: - name: LOGENTRIES_API default: data.logentries.com @@ -29,7 +29,7 @@ DOCUMENTATION = ''' - section: callback_logentries key: api port: - description: HTTP port to use when connecting to the API + description: HTTP port to use when connecting to the API. env: - name: LOGENTRIES_PORT default: 80 @@ -37,7 +37,7 @@ DOCUMENTATION = ''' - section: callback_logentries key: port tls_port: - description: Port to use when connecting to the API when TLS is enabled + description: Port to use when connecting to the API when TLS is enabled. env: - name: LOGENTRIES_TLS_PORT default: 443 @@ -45,7 +45,7 @@ DOCUMENTATION = ''' - section: callback_logentries key: tls_port token: - description: The logentries "TCP token" + description: The logentries C(TCP token). env: - name: LOGENTRIES_ANSIBLE_TOKEN required: true @@ -54,7 +54,7 @@ DOCUMENTATION = ''' key: token use_tls: description: - - Toggle to decide whether to use TLS to encrypt the communications with the API server + - Toggle to decide whether to use TLS to encrypt the communications with the API server. env: - name: LOGENTRIES_USE_TLS default: false @@ -63,7 +63,7 @@ DOCUMENTATION = ''' - section: callback_logentries key: use_tls flatten: - description: flatten complex data structures into a single dictionary with complex keys + description: Flatten complex data structures into a single dictionary with complex keys. type: boolean default: false env: diff --git a/plugins/callback/logstash.py b/plugins/callback/logstash.py index 5d3c1e50b8..144e1f9915 100644 --- a/plugins/callback/logstash.py +++ b/plugins/callback/logstash.py @@ -13,13 +13,13 @@ DOCUMENTATION = r''' type: notification short_description: Sends events to Logstash description: - - This callback will report facts and task events to Logstash https://www.elastic.co/products/logstash + - This callback will report facts and task events to Logstash U(https://www.elastic.co/products/logstash). requirements: - whitelisting in configuration - - logstash (python library) + - logstash (Python library) options: server: - description: Address of the Logstash server + description: Address of the Logstash server. env: - name: LOGSTASH_SERVER ini: @@ -28,7 +28,7 @@ DOCUMENTATION = r''' version_added: 1.0.0 default: localhost port: - description: Port on which logstash is listening + description: Port on which logstash is listening. env: - name: LOGSTASH_PORT ini: @@ -37,7 +37,7 @@ DOCUMENTATION = r''' version_added: 1.0.0 default: 5000 type: - description: Message type + description: Message type. env: - name: LOGSTASH_TYPE ini: @@ -54,7 +54,7 @@ DOCUMENTATION = r''' env: - name: LOGSTASH_PRE_COMMAND format_version: - description: Logging format + description: Logging format. type: str version_added: 2.0.0 ini: @@ -113,7 +113,7 @@ from ansible.plugins.callback import CallbackBase class CallbackModule(CallbackBase): CALLBACK_VERSION = 2.0 - CALLBACK_TYPE = 'aggregate' + CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'community.general.logstash' CALLBACK_NEEDS_WHITELIST = True diff --git a/plugins/callback/mail.py b/plugins/callback/mail.py index a605d13eac..9e8314baf8 100644 --- a/plugins/callback/mail.py +++ b/plugins/callback/mail.py @@ -79,7 +79,6 @@ import re import email.utils import smtplib -from ansible.module_utils.six import string_types from ansible.module_utils.common.text.converters import to_bytes from ansible.parsing.ajson import AnsibleJSONEncoder from ansible.plugins.callback import CallbackBase diff --git a/plugins/callback/nrdp.py b/plugins/callback/nrdp.py index 8295bf9759..c16a3c7bec 100644 --- a/plugins/callback/nrdp.py +++ b/plugins/callback/nrdp.py @@ -67,9 +67,6 @@ DOCUMENTATION = ''' type: string ''' -import os -import json - from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible.module_utils.common.text.converters import to_bytes from ansible.module_utils.urls import open_url diff --git a/plugins/callback/null.py b/plugins/callback/null.py index 01f5f6ca06..f53a242945 100644 --- a/plugins/callback/null.py +++ b/plugins/callback/null.py @@ -15,7 +15,7 @@ DOCUMENTATION = ''' - set as main display callback short_description: Don't display stuff to screen description: - - This callback prevents outputing events to screen + - This callback prevents outputing events to screen. ''' from ansible.plugins.callback import CallbackBase diff --git a/plugins/callback/say.py b/plugins/callback/say.py index 03d7060352..005725a22b 100644 --- a/plugins/callback/say.py +++ b/plugins/callback/say.py @@ -14,12 +14,12 @@ DOCUMENTATION = ''' type: notification requirements: - whitelisting in configuration - - the '/usr/bin/say' command line program (standard on macOS) or 'espeak' command line program + - the C(/usr/bin/say) command line program (standard on macOS) or C(espeak) command line program short_description: notify using software speech synthesizer description: - - This plugin will use the 'say' or 'espeak' program to "speak" about play events. + - This plugin will use the C(say) or C(espeak) program to "speak" about play events. notes: - - In 2.8, this callback has been renamed from C(osx_say) into M(community.general.say). + - In Ansible 2.8, this callback has been renamed from C(osx_say) into M(community.general.say). ''' import platform diff --git a/plugins/callback/selective.py b/plugins/callback/selective.py index 6476f5ba53..526975bd2c 100644 --- a/plugins/callback/selective.py +++ b/plugins/callback/selective.py @@ -22,7 +22,7 @@ DOCUMENTATION = ''' options: nocolor: default: false - description: This setting allows suppressing colorizing output + description: This setting allows suppressing colorizing output. env: - name: ANSIBLE_NOCOLOR - name: ANSIBLE_SELECTIVE_DONT_COLORIZE diff --git a/plugins/callback/slack.py b/plugins/callback/slack.py index 6ca15b43f5..e9b84bbb38 100644 --- a/plugins/callback/slack.py +++ b/plugins/callback/slack.py @@ -18,11 +18,11 @@ DOCUMENTATION = ''' short_description: Sends play events to a Slack channel description: - This is an ansible callback plugin that sends status updates to a Slack channel during playbook execution. - - Before 2.4 only environment variables were available for configuring this plugin + - Before Ansible 2.4 only environment variables were available for configuring this plugin. options: webhook_url: required: true - description: Slack Webhook URL + description: Slack Webhook URL. env: - name: SLACK_WEBHOOK_URL ini: @@ -45,7 +45,7 @@ DOCUMENTATION = ''' - section: callback_slack key: username validate_certs: - description: validate the SSL certificate of the Slack server. (For HTTPS URLs) + description: Validate the SSL certificate of the Slack server for HTTPS URLs. env: - name: SLACK_VALIDATE_CERTS ini: diff --git a/plugins/callback/splunk.py b/plugins/callback/splunk.py index 701cbfdebd..67ad944d2e 100644 --- a/plugins/callback/splunk.py +++ b/plugins/callback/splunk.py @@ -8,27 +8,27 @@ __metaclass__ = type DOCUMENTATION = ''' name: splunk - type: aggregate + type: notification short_description: Sends task result events to Splunk HTTP Event Collector author: "Stuart Hirst (!UNKNOWN) " description: - This callback plugin will send task results as JSON formatted events to a Splunk HTTP collector. - - The companion Splunk Monitoring & Diagnostics App is available here "https://splunkbase.splunk.com/app/4023/" + - The companion Splunk Monitoring & Diagnostics App is available here U(https://splunkbase.splunk.com/app/4023/). - Credit to "Ryan Currah (@ryancurrah)" for original source upon which this is based. requirements: - Whitelisting this callback plugin - 'Create a HTTP Event Collector in Splunk' - - 'Define the url and token in ansible.cfg' + - 'Define the URL and token in C(ansible.cfg)' options: url: - description: URL to the Splunk HTTP collector source + description: URL to the Splunk HTTP collector source. env: - name: SPLUNK_URL ini: - section: callback_splunk key: url authtoken: - description: Token to authenticate the connection to the Splunk HTTP collector + description: Token to authenticate the connection to the Splunk HTTP collector. env: - name: SPLUNK_AUTHTOKEN ini: @@ -48,7 +48,7 @@ DOCUMENTATION = ''' version_added: '1.0.0' include_milliseconds: description: Whether to include milliseconds as part of the generated timestamp field in the event - sent to the Splunk HTTP collector + sent to the Splunk HTTP collector. env: - name: SPLUNK_INCLUDE_MILLISECONDS ini: @@ -165,7 +165,7 @@ class SplunkHTTPCollectorSource(object): class CallbackModule(CallbackBase): CALLBACK_VERSION = 2.0 - CALLBACK_TYPE = 'aggregate' + CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'community.general.splunk' CALLBACK_NEEDS_WHITELIST = True diff --git a/plugins/callback/sumologic.py b/plugins/callback/sumologic.py index 0b6c9b6fee..998081c35b 100644 --- a/plugins/callback/sumologic.py +++ b/plugins/callback/sumologic.py @@ -8,18 +8,18 @@ __metaclass__ = type DOCUMENTATION = ''' name: sumologic -type: aggregate +type: notification short_description: Sends task result events to Sumologic author: "Ryan Currah (@ryancurrah)" description: - - This callback plugin will send task results as JSON formatted events to a Sumologic HTTP collector source + - This callback plugin will send task results as JSON formatted events to a Sumologic HTTP collector source. requirements: - Whitelisting this callback plugin - 'Create a HTTP collector source in Sumologic and specify a custom timestamp format of C(yyyy-MM-dd HH:mm:ss ZZZZ) and a custom timestamp locator of C("timestamp": "(.*)")' options: url: - description: URL to the Sumologic HTTP collector source + description: URL to the Sumologic HTTP collector source. env: - name: SUMOLOGIC_URL ini: @@ -28,7 +28,7 @@ options: ''' EXAMPLES = ''' -examples: > +examples: | To enable, add this to your ansible.cfg file in the defaults block [defaults] callback_whitelist = community.general.sumologic @@ -111,7 +111,7 @@ class SumologicHTTPCollectorSource(object): class CallbackModule(CallbackBase): CALLBACK_VERSION = 2.0 - CALLBACK_TYPE = 'aggregate' + CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'community.general.sumologic' CALLBACK_NEEDS_WHITELIST = True diff --git a/plugins/callback/syslog_json.py b/plugins/callback/syslog_json.py index 7ca99a9edd..0f5ec4d0d9 100644 --- a/plugins/callback/syslog_json.py +++ b/plugins/callback/syslog_json.py @@ -15,11 +15,11 @@ DOCUMENTATION = ''' - whitelist in configuration short_description: sends JSON events to syslog description: - - This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format - - Before Ansible 2.9 only environment variables were available for configuration + - This plugin logs ansible-playbook and ansible runs to a syslog server in JSON format. + - Before Ansible 2.9 only environment variables were available for configuration. options: server: - description: syslog server that will receive the event + description: Syslog server that will receive the event. env: - name: SYSLOG_SERVER default: localhost @@ -27,7 +27,7 @@ DOCUMENTATION = ''' - section: callback_syslog_json key: syslog_server port: - description: port on which the syslog server is listening + description: Port on which the syslog server is listening. env: - name: SYSLOG_PORT default: 514 @@ -35,7 +35,7 @@ DOCUMENTATION = ''' - section: callback_syslog_json key: syslog_port facility: - description: syslog facility to log as + description: Syslog facility to log as. env: - name: SYSLOG_FACILITY default: user @@ -54,9 +54,6 @@ DOCUMENTATION = ''' version_added: 4.5.0 ''' -import os -import json - import logging import logging.handlers @@ -71,7 +68,7 @@ class CallbackModule(CallbackBase): """ CALLBACK_VERSION = 2.0 - CALLBACK_TYPE = 'aggregate' + CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'community.general.syslog_json' CALLBACK_NEEDS_WHITELIST = True diff --git a/plugins/callback/unixy.py b/plugins/callback/unixy.py index fa26be8238..02a2e46ba6 100644 --- a/plugins/callback/unixy.py +++ b/plugins/callback/unixy.py @@ -63,7 +63,7 @@ class CallbackModule(CallbackModule_default): def _preprocess_result(self, result): self.delegated_vars = result._result.get('_ansible_delegated_vars', None) - self._handle_exception(result._result, use_stderr=self.display_failed_stderr) + self._handle_exception(result._result, use_stderr=self.get_option('display_failed_stderr')) self._handle_warnings(result._result) def _process_result_output(self, result, msg): @@ -109,7 +109,7 @@ class CallbackModule(CallbackModule_default): self._display.display(msg) def v2_runner_on_skipped(self, result, ignore_errors=False): - if self.display_skipped_hosts: + if self.get_option('display_skipped_hosts'): self._preprocess_result(result) display_color = C.COLOR_SKIP msg = "skipped" @@ -128,7 +128,7 @@ class CallbackModule(CallbackModule_default): msg += " | item: %s" % (item_value,) task_result = self._process_result_output(result, msg) - self._display.display(" " + task_result, display_color, stderr=self.display_failed_stderr) + self._display.display(" " + task_result, display_color, stderr=self.get_option('display_failed_stderr')) def v2_runner_on_ok(self, result, msg="ok", display_color=C.COLOR_OK): self._preprocess_result(result) @@ -142,7 +142,7 @@ class CallbackModule(CallbackModule_default): display_color = C.COLOR_CHANGED task_result = self._process_result_output(result, msg) self._display.display(" " + task_result, display_color) - elif self.display_ok_hosts: + elif self.get_option('display_ok_hosts'): task_result = self._process_result_output(result, msg) self._display.display(" " + task_result, display_color) @@ -162,7 +162,7 @@ class CallbackModule(CallbackModule_default): display_color = C.COLOR_UNREACHABLE task_result = self._process_result_output(result, msg) - self._display.display(" " + task_result, display_color, stderr=self.display_failed_stderr) + self._display.display(" " + task_result, display_color, stderr=self.get_option('display_failed_stderr')) def v2_on_file_diff(self, result): if result._task.loop and 'results' in result._result: @@ -205,7 +205,7 @@ class CallbackModule(CallbackModule_default): colorize(u'ignored', t['ignored'], None)), log_only=True ) - if stats.custom and self.show_custom_stats: + if stats.custom and self.get_option('show_custom_stats'): self._display.banner("CUSTOM STATS: ") # per host # TODO: come up with 'pretty format' diff --git a/plugins/callback/yaml.py b/plugins/callback/yaml.py index 81d59e2e70..ae2c8f8810 100644 --- a/plugins/callback/yaml.py +++ b/plugins/callback/yaml.py @@ -11,7 +11,7 @@ DOCUMENTATION = ''' author: Unknown (!UNKNOWN) name: yaml type: stdout - short_description: yaml-ized Ansible screen output + short_description: YAML-ized Ansible screen output description: - Ansible output that can be quite a bit easier to read than the default JSON formatting. @@ -25,12 +25,10 @@ import yaml import json import re import string -import sys -from ansible.module_utils.common.text.converters import to_bytes, to_text -from ansible.module_utils.six import string_types +from ansible.module_utils.common.text.converters import to_text from ansible.parsing.yaml.dumper import AnsibleDumper -from ansible.plugins.callback import CallbackBase, strip_internal_keys, module_response_deepcopy +from ansible.plugins.callback import strip_internal_keys, module_response_deepcopy from ansible.plugins.callback.default import CallbackModule as Default diff --git a/plugins/connection/chroot.py b/plugins/connection/chroot.py index cbbf9612e9..ef6d5566d3 100644 --- a/plugins/connection/chroot.py +++ b/plugins/connection/chroot.py @@ -22,6 +22,7 @@ DOCUMENTATION = ''' - The path of the chroot you want to access. default: inventory_hostname vars: + - name: inventory_hostname - name: ansible_host executable: description: diff --git a/plugins/connection/jail.py b/plugins/connection/jail.py index d813780136..3a3edd4b18 100644 --- a/plugins/connection/jail.py +++ b/plugins/connection/jail.py @@ -22,6 +22,7 @@ DOCUMENTATION = ''' - Path to the jail default: inventory_hostname vars: + - name: inventory_hostname - name: ansible_host - name: ansible_jail_host remote_user: diff --git a/plugins/doc_fragments/attributes.py b/plugins/doc_fragments/attributes.py index da089dff21..9b8488e0a5 100644 --- a/plugins/doc_fragments/attributes.py +++ b/plugins/doc_fragments/attributes.py @@ -20,9 +20,13 @@ attributes: description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode. ''' -# platform: -# description: Target OS/families that can be operated against. -# support: N/A + PLATFORM = r''' +options: {} +attributes: + platform: + description: Target OS/families that can be operated against. + support: N/A +''' # Should be used together with the standard fragment INFO_MODULE = r''' diff --git a/plugins/doc_fragments/hpe3par.py b/plugins/doc_fragments/hpe3par.py index 96e53846e1..606a2502a6 100644 --- a/plugins/doc_fragments/hpe3par.py +++ b/plugins/doc_fragments/hpe3par.py @@ -29,8 +29,7 @@ options: required: true requirements: - - hpe3par_sdk >= 1.0.2. Install using 'pip install hpe3par_sdk' + - hpe3par_sdk >= 1.0.2. Install using C(pip install hpe3par_sdk). - WSAPI service should be enabled on the 3PAR storage array. notes: - - check_mode not supported ''' diff --git a/plugins/doc_fragments/ldap.py b/plugins/doc_fragments/ldap.py index c73e5080be..b321c75eb8 100644 --- a/plugins/doc_fragments/ldap.py +++ b/plugins/doc_fragments/ldap.py @@ -24,6 +24,11 @@ options: - The password to use with I(bind_dn). type: str default: '' + ca_path: + description: + - Set the path to PEM file with CA certs. + type: path + version_added: "6.5.0" dn: required: true description: @@ -60,9 +65,20 @@ options: sasl_class: description: - The class to use for SASL authentication. - - possible choices are C(external), C(gssapi). + - Possible choices are C(external), C(gssapi). type: str choices: ['external', 'gssapi'] default: external version_added: "2.0.0" + xorder_discovery: + description: + - Set the behavior on how to process Xordered DNs. + - C(enable) will perform a C(ONELEVEL) search below the superior RDN to find the matching DN. + - C(disable) will always use the DN unmodified (as passed by the I(dn) parameter). + - C(auto) will only perform a search if the first RDN does not contain an index number (C({x})). + - Possible choices are C(enable), C(auto), C(disable). + type: str + choices: ['enable', 'auto', 'disable'] + default: auto + version_added: "6.4.0" ''' diff --git a/plugins/filter/jc.py b/plugins/filter/jc.py index 879647a04d..3aa8d20a5f 100644 --- a/plugins/filter/jc.py +++ b/plugins/filter/jc.py @@ -26,6 +26,7 @@ DOCUMENTATION = ''' description: - The correct parser for the input data. - For example C(ifconfig). + - "Note: use underscores instead of dashes (if any) in the parser module name." - See U(https://github.com/kellyjonbrazil/jc#parsers) for the latest list of parsers. type: string required: true @@ -79,13 +80,13 @@ from ansible.errors import AnsibleError, AnsibleFilterError import importlib try: - import jc + import jc # noqa: F401, pylint: disable=unused-import HAS_LIB = True except ImportError: HAS_LIB = False -def jc(data, parser, quiet=True, raw=False): +def jc_filter(data, parser, quiet=True, raw=False): """Convert returned command output to JSON using the JC library Arguments: @@ -137,8 +138,14 @@ def jc(data, parser, quiet=True, raw=False): raise AnsibleError('You need to install "jc" as a Python library on the Ansible controller prior to running jc filter') try: - jc_parser = importlib.import_module('jc.parsers.' + parser) - return jc_parser.parse(data, quiet=quiet, raw=raw) + # new API (jc v1.18.0 and higher) allows use of plugin parsers + if hasattr(jc, 'parse'): + return jc.parse(parser, data, quiet=quiet, raw=raw) + + # old API (jc v1.17.7 and lower) + else: + jc_parser = importlib.import_module('jc.parsers.' + parser) + return jc_parser.parse(data, quiet=quiet, raw=raw) except Exception as e: raise AnsibleFilterError('Error in jc filter plugin: %s' % e) @@ -149,5 +156,5 @@ class FilterModule(object): def filters(self): return { - 'jc': jc + 'jc': jc_filter, } diff --git a/plugins/filter/lists_mergeby.py b/plugins/filter/lists_mergeby.py index a89039ed89..036dfe4d7c 100644 --- a/plugins/filter/lists_mergeby.py +++ b/plugins/filter/lists_mergeby.py @@ -102,8 +102,6 @@ from ansible.errors import AnsibleFilterError from ansible.module_utils.six import string_types from ansible.module_utils.common._collections_compat import Mapping, Sequence from ansible.utils.vars import merge_hash -from ansible.release import __version__ as ansible_version -from ansible_collections.community.general.plugins.module_utils.version import LooseVersion from collections import defaultdict from operator import itemgetter diff --git a/plugins/inventory/linode.py b/plugins/inventory/linode.py index ea87a9a58e..b28cfa27ba 100644 --- a/plugins/inventory/linode.py +++ b/plugins/inventory/linode.py @@ -121,10 +121,7 @@ compose: ansible_host: "ipv4 | community.general.json_query('[?public==`false`].address') | first" ''' -import os - -from ansible.errors import AnsibleError, AnsibleParserError -from ansible.module_utils.six import string_types +from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable diff --git a/plugins/inventory/lxd.py b/plugins/inventory/lxd.py index 291d12b037..bd0a6ce008 100644 --- a/plugins/inventory/lxd.py +++ b/plugins/inventory/lxd.py @@ -55,6 +55,11 @@ DOCUMENTATION = r''' type: str default: none choices: [ 'STOPPED', 'STARTING', 'RUNNING', 'none' ] + project: + description: Filter the instance according to the given project. + type: str + default: default + version_added: 6.2.0 type_filter: description: - Filter the instances by type C(virtual-machine), C(container) or C(both). @@ -140,19 +145,21 @@ groupby: vlan666: type: vlanid attribute: 666 + projectInternals: + type: project + attribute: internals ''' -import binascii import json import re import time import os -import socket from ansible.plugins.inventory import BaseInventoryPlugin from ansible.module_utils.common.text.converters import to_native, to_text from ansible.module_utils.common.dict_transformations import dict_merge from ansible.module_utils.six import raise_from from ansible.errors import AnsibleError, AnsibleParserError +from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException try: @@ -330,7 +337,15 @@ class InventoryModule(BaseInventoryPlugin): # "status_code": 200, # "type": "sync" # } - instances = self.socket.do('GET', '/1.0/instances') + url = '/1.0/instances' + if self.project: + url = url + '?{0}'.format(urlencode(dict(project=self.project))) + + instances = self.socket.do('GET', url) + + if self.project: + return [m.split('/')[3].split('?')[0] for m in instances['metadata']] + return [m.split('/')[3] for m in instances['metadata']] def _get_config(self, branch, name): @@ -351,9 +366,11 @@ class InventoryModule(BaseInventoryPlugin): dict(config): Config of the instance""" 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])))} + config[name] = {branch[1]: self.socket.do( + 'GET', '/1.0/{0}/{1}/{2}?{3}'.format(to_native(branch[0]), to_native(name), to_native(branch[1]), urlencode(dict(project=self.project))))} else: - config[name] = {branch: self.socket.do('GET', '/1.0/{0}/{1}'.format(to_native(branch), to_native(name)))} + config[name] = {branch: self.socket.do( + 'GET', '/1.0/{0}/{1}?{2}'.format(to_native(branch), to_native(name), urlencode(dict(project=self.project))))} return config def get_instance_data(self, names): @@ -583,6 +600,8 @@ class InventoryModule(BaseInventoryPlugin): self._set_data_entry(instance_name, 'network_interfaces', self.extract_network_information_from_instance_config(instance_name)) self._set_data_entry(instance_name, 'preferred_interface', self.get_prefered_instance_network_interface(instance_name)) self._set_data_entry(instance_name, 'vlan_ids', self.get_instance_vlans(instance_name)) + self._set_data_entry(instance_name, 'project', self._get_data_entry( + 'instances/{0}/instances/metadata/project'.format(instance_name))) def build_inventory_network(self, instance_name): """Add the network interfaces of the instance to the inventory @@ -686,6 +705,8 @@ class InventoryModule(BaseInventoryPlugin): # add VLAN_ID information if self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name)): self.inventory.set_variable(instance_name, 'ansible_lxd_vlan_ids', self._get_data_entry('inventory/{0}/vlan_ids'.format(instance_name))) + # add project + self.inventory.set_variable(instance_name, 'ansible_lxd_project', self._get_data_entry('inventory/{0}/project'.format(instance_name))) def build_inventory_groups_location(self, group_name): """create group by attribute: location @@ -761,6 +782,28 @@ class InventoryModule(BaseInventoryPlugin): # Ignore invalid IP addresses returned by lxd pass + def build_inventory_groups_project(self, group_name): + """create group by attribute: project + + 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_instances = [ + instance_name for instance_name in self.inventory.hosts + if 'ansible_lxd_project' in self.inventory.get_host(instance_name).get_vars()] + for instance_name in gen_instances: + if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_project'): + self.inventory.add_child(group_name, instance_name) + def build_inventory_groups_os(self, group_name): """create group by attribute: os @@ -899,6 +942,7 @@ class InventoryModule(BaseInventoryPlugin): * 'profile' * 'vlanid' * 'type' + * 'project' Args: str(group_name): Group name @@ -926,6 +970,8 @@ class InventoryModule(BaseInventoryPlugin): self.build_inventory_groups_vlanid(group_name) elif self.groupby[group_name].get('type') == 'type': self.build_inventory_groups_type(group_name) + elif self.groupby[group_name].get('type') == 'project': + self.build_inventory_groups_project(group_name) else: raise AnsibleParserError('Unknown group type: {0}'.format(to_native(group_name))) @@ -1032,6 +1078,7 @@ class InventoryModule(BaseInventoryPlugin): try: self.client_key = self.get_option('client_key') self.client_cert = self.get_option('client_cert') + self.project = self.get_option('project') self.debug = self.DEBUG self.data = {} # store for inventory-data self.groupby = self.get_option('groupby') diff --git a/plugins/inventory/nmap.py b/plugins/inventory/nmap.py index 01a5fa04ba..621f047577 100644 --- a/plugins/inventory/nmap.py +++ b/plugins/inventory/nmap.py @@ -34,8 +34,15 @@ DOCUMENTATION = ''' description: list of addresses to exclude type: list elements: string + port: + description: + - Only scan specific port or port range (C(-p)). + - For example, you could pass C(22) for a single port, C(1-65535) for a range of ports, + or C(U:53,137,T:21-25,139,8080,S:9) to check port 53 with UDP, ports 21-25 with TCP, port 9 with SCTP, and ports 137, 139, and 8080 with all. + type: string + version_added: 6.5.0 ports: - description: Enable/disable scanning for open ports + description: Enable/disable scanning ports. type: boolean default: true ipv4: @@ -46,6 +53,30 @@ DOCUMENTATION = ''' description: use IPv6 type addresses type: boolean default: true + udp_scan: + description: + - Scan via UDP. + - Depending on your system you might need I(sudo=true) for this to work. + type: boolean + default: false + version_added: 6.1.0 + icmp_timestamp: + description: + - Scan via ICMP Timestamp (C(-PP)). + - Depending on your system you might need I(sudo=true) for this to work. + type: boolean + default: false + version_added: 6.1.0 + open: + description: Only scan for open (or possibly open) ports. + type: boolean + default: false + version_added: 6.5.0 + dns_resolve: + description: Whether to always (C(true)) or never (C(false)) do DNS resolution. + type: boolean + default: false + version_added: 6.1.0 notes: - At least one of ipv4 or ipv6 is required to be True, both can be True, but they cannot both be False. - 'TODO: add OS fingerprinting' @@ -62,6 +93,14 @@ plugin: community.general.nmap sudo: true strict: false address: 192.168.0.0/24 + +# an nmap scan specifying ports and classifying results to an inventory group +plugin: community.general.nmap +address: 192.168.0.0/24 +exclude: 192.168.0.1, web.example.com +port: 22, 443 +groups: + web_servers: "ports | selectattr('port', 'equalto', '443')" ''' import os @@ -152,6 +191,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): if self._options['sudo']: cmd.insert(0, 'sudo') + if self._options['port']: + cmd.append('-p') + cmd.append(self._options['port']) + if not self._options['ports']: cmd.append('-sP') @@ -166,6 +209,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): cmd.append('--exclude') cmd.append(','.join(self._options['exclude'])) + if self._options['dns_resolve']: + cmd.append('-n') + + if self._options['udp_scan']: + cmd.append('-sU') + + if self._options['icmp_timestamp']: + cmd.append('-PP') + + if self._options['open']: + cmd.append('--open') + cmd.append(self._options['address']) try: # execute diff --git a/plugins/inventory/online.py b/plugins/inventory/online.py index 261548d8a2..3fccd58d2f 100644 --- a/plugins/inventory/online.py +++ b/plugins/inventory/online.py @@ -65,7 +65,7 @@ from sys import version as python_version from ansible.errors import AnsibleError from ansible.module_utils.urls import open_url from ansible.plugins.inventory import BaseInventoryPlugin -from ansible.module_utils.common.text.converters import to_native, to_text +from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.ansible_release import __version__ as ansible_version from ansible.module_utils.six.moves.urllib.parse import urljoin diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index b24bcacf25..dc2e1febca 100644 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -277,6 +277,11 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): credentials = urlencode({'username': self.proxmox_user, 'password': self.proxmox_password, }) a = self._get_session() + + if a.verify is False: + from requests.packages.urllib3 import disable_warnings + disable_warnings() + ret = a.post('%s/api2/json/access/ticket' % self.proxmox_url, data=credentials) json = ret.json() @@ -408,7 +413,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): stripped_value = value.strip() if stripped_value: parsed_key = key + "_parsed" - properties[parsed_key] = [tag.strip() for tag in stripped_value.split(",")] + properties[parsed_key] = [tag.strip() for tag in stripped_value.replace(',', ';').split(";")] # The first field in the agent string tells you whether the agent is enabled # the rest of the comma separated string is extra config for the agent. @@ -615,7 +620,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): for o in ('url', 'user', 'password', 'token_id', 'token_secret'): v = self.get_option(o) if self.templar.is_template(v): - v = self.templar.template(v, disable_looups=False) + v = self.templar.template(v, disable_lookups=False) setattr(self, 'proxmox_%s' % o, v) # some more cleanup and validation diff --git a/plugins/inventory/xen_orchestra.py b/plugins/inventory/xen_orchestra.py index 5a466a6ab0..ddbdd9bb04 100644 --- a/plugins/inventory/xen_orchestra.py +++ b/plugins/inventory/xen_orchestra.py @@ -78,6 +78,7 @@ compose: import json import ssl +from time import sleep from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable @@ -138,21 +139,42 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self.conn = create_connection( '{0}://{1}/api/'.format(proto, xoa_api_host), sslopt=sslopt) + CALL_TIMEOUT = 100 + """Number of 1/10ths of a second to wait before method call times out.""" + + def call(self, method, params): + """Calls a method on the XO server with the provided parameters.""" + id = self.pointer + self.conn.send(json.dumps({ + 'id': id, + 'jsonrpc': '2.0', + 'method': method, + 'params': params + })) + + waited = 0 + while waited < self.CALL_TIMEOUT: + response = json.loads(self.conn.recv()) + if 'id' in response and response['id'] == id: + return response + else: + sleep(0.1) + waited += 1 + + raise AnsibleError( + 'Method call {method} timed out after {timeout} seconds.'.format(method=method, timeout=self.CALL_TIMEOUT / 10)) + def login(self, user, password): - payload = {'id': self.pointer, 'jsonrpc': '2.0', 'method': 'session.signIn', 'params': { - 'username': user, 'password': password}} - self.conn.send(json.dumps(payload)) - result = json.loads(self.conn.recv()) + result = self.call('session.signIn', { + 'username': user, 'password': password + }) if 'error' in result: raise AnsibleError( 'Could not connect: {0}'.format(result['error'])) def get_object(self, name): - payload = {'id': self.pointer, 'jsonrpc': '2.0', - 'method': 'xo.getAllObjects', 'params': {'filter': {'type': name}}} - self.conn.send(json.dumps(payload)) - answer = json.loads(self.conn.recv()) + answer = self.call('xo.getAllObjects', {'filter': {'type': name}}) if 'error' in answer: raise AnsibleError( diff --git a/plugins/lookup/bitwarden.py b/plugins/lookup/bitwarden.py index 1cc2e44c74..389fa475bd 100644 --- a/plugins/lookup/bitwarden.py +++ b/plugins/lookup/bitwarden.py @@ -28,8 +28,12 @@ DOCUMENTATION = """ default: name version_added: 5.7.0 field: - description: Field to fetch; leave unset to fetch whole response. + description: Field to fetch. Leave unset to fetch whole response. type: str + collection_id: + description: Collection ID to filter results by collection. Leave unset to skip filtering. + type: str + version_added: 6.3.0 """ EXAMPLES = """ @@ -43,10 +47,20 @@ EXAMPLES = """ msg: >- {{ lookup('community.general.bitwarden', 'bafba515-af11-47e6-abe3-af1200cd18b2', search='id', field='password') }} +- name: "Get 'password' from Bitwarden record named 'a_test' from collection" + ansible.builtin.debug: + msg: >- + {{ lookup('community.general.bitwarden', 'a_test', field='password', collection_id='bafba515-af11-47e6-abe3-af1200cd18b2') }} + - name: "Get full Bitwarden record named 'a_test'" ansible.builtin.debug: msg: >- {{ lookup('community.general.bitwarden', 'a_test') }} + +- name: "Get custom field 'api_key' from Bitwarden record named 'a_test'" + ansible.builtin.debug: + msg: >- + {{ lookup('community.general.bitwarden', 'a_test', field='api_key') }} """ RETURN = """ @@ -78,7 +92,7 @@ class Bitwarden(object): return self._cli_path @property - def logged_in(self): + def unlocked(self): out, err = self._run(['status'], stdin="") decoded = AnsibleJSONDecoder().raw_decode(out)[0] return decoded['status'] == 'unlocked' @@ -91,10 +105,17 @@ class Bitwarden(object): raise BitwardenException(err) return to_text(out, errors='surrogate_or_strict'), to_text(err, errors='surrogate_or_strict') - def _get_matches(self, search_value, search_field): + def _get_matches(self, search_value, search_field, collection_id): """Return matching records whose search_field is equal to key. """ - out, err = self._run(['list', 'items', '--search', search_value]) + + # Prepare set of params for Bitwarden CLI + params = ['list', 'items', '--search', search_value] + + if collection_id: + params.extend(['--collectionid', collection_id]) + + out, err = self._run(params) # This includes things that matched in different fields. initial_matches = AnsibleJSONDecoder().raw_decode(out)[0] @@ -102,17 +123,27 @@ class Bitwarden(object): # Filter to only include results from the right field. return [item for item in initial_matches if item[search_field] == search_value] - def get_field(self, field, search_value, search_field="name"): - """Return a list of the specified field for records whose search_field match search_value. + def get_field(self, field, search_value, search_field="name", collection_id=None): + """Return a list of the specified field for records whose search_field match search_value + and filtered by collection if collection has been provided. If field is None, return the whole record for each match. """ - matches = self._get_matches(search_value, search_field) + matches = self._get_matches(search_value, search_field, collection_id) - if field: + if field in ['autofillOnPageLoad', 'password', 'passwordRevisionDate', 'totp', 'uris', 'username']: return [match['login'][field] for match in matches] - - return matches + elif not field: + return matches + else: + custom_field_matches = [] + for match in matches: + for custom_field in match['fields']: + if custom_field['name'] == field: + custom_field_matches.append(custom_field['value']) + if matches and not custom_field_matches: + raise AnsibleError("Custom field {field} does not exist in {search_value}".format(field=field, search_value=search_value)) + return custom_field_matches class LookupModule(LookupBase): @@ -121,10 +152,11 @@ class LookupModule(LookupBase): self.set_options(var_options=variables, direct=kwargs) field = self.get_option('field') search_field = self.get_option('search') - if not _bitwarden.logged_in: - raise AnsibleError("Not logged into Bitwarden. Run 'bw login'.") + collection_id = self.get_option('collection_id') + if not _bitwarden.unlocked: + raise AnsibleError("Bitwarden Vault locked. Run 'bw unlock'.") - return [_bitwarden.get_field(field, term, search_field) for term in terms] + return [_bitwarden.get_field(field, term, search_field, collection_id) for term in terms] _bitwarden = Bitwarden() diff --git a/plugins/lookup/cartesian.py b/plugins/lookup/cartesian.py index d76e8f532a..d63f3943b0 100644 --- a/plugins/lookup/cartesian.py +++ b/plugins/lookup/cartesian.py @@ -66,7 +66,12 @@ class LookupModule(LookupBase): """ results = [] for x in terms: - intermediate = listify_lookup_plugin_terms(x, templar=self._templar, loader=self._loader) + try: + intermediate = listify_lookup_plugin_terms(x, templar=self._templar) + except TypeError: + # The loader argument is deprecated in ansible-core 2.14+. Fall back to + # pre-2.14 behavior for older ansible-core versions. + intermediate = listify_lookup_plugin_terms(x, templar=self._templar, loader=self._loader) results.append(intermediate) return results diff --git a/plugins/lookup/consul_kv.py b/plugins/lookup/consul_kv.py index 2d4a202d94..f17f1b2694 100644 --- a/plugins/lookup/consul_kv.py +++ b/plugins/lookup/consul_kv.py @@ -105,7 +105,6 @@ RETURN = """ type: dict """ -import os from ansible.module_utils.six.moves.urllib.parse import urlparse from ansible.errors import AnsibleError, AnsibleAssertionError from ansible.plugins.lookup import LookupBase diff --git a/plugins/lookup/credstash.py b/plugins/lookup/credstash.py index d49d5b23cb..6a3f58595b 100644 --- a/plugins/lookup/credstash.py +++ b/plugins/lookup/credstash.py @@ -93,8 +93,6 @@ RETURN = """ type: str """ -import os - from ansible.errors import AnsibleError from ansible.plugins.lookup import LookupBase diff --git a/plugins/lookup/cyberarkpassword.py b/plugins/lookup/cyberarkpassword.py index 1e005e23e8..c3cc427df8 100644 --- a/plugins/lookup/cyberarkpassword.py +++ b/plugins/lookup/cyberarkpassword.py @@ -80,7 +80,6 @@ from subprocess import Popen from ansible.errors import AnsibleError from ansible.plugins.lookup import LookupBase -from ansible.parsing.splitter import parse_kv from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native from ansible.utils.display import Display diff --git a/plugins/lookup/dependent.py b/plugins/lookup/dependent.py index b44a9208af..54714344eb 100644 --- a/plugins/lookup/dependent.py +++ b/plugins/lookup/dependent.py @@ -125,8 +125,16 @@ from ansible.errors import AnsibleLookupError from ansible.module_utils.common._collections_compat import Mapping, Sequence from ansible.module_utils.six import string_types from ansible.plugins.lookup import LookupBase +from ansible.release import __version__ as ansible_version from ansible.template import Templar +from ansible_collections.community.general.plugins.module_utils.version import LooseVersion + + +# Whether Templar has a cache, which can be controlled by Templar.template()'s cache option. +# The cache was removed for ansible-core 2.14 (https://github.com/ansible/ansible/pull/78419) +_TEMPLAR_HAS_TEMPLATE_CACHE = LooseVersion(ansible_version) < LooseVersion('2.14.0') + class LookupModule(LookupBase): def __evaluate(self, expression, templar, variables): @@ -136,7 +144,10 @@ class LookupModule(LookupBase): ``variables`` are the variables to use. """ templar.available_variables = variables or {} - return templar.template("{0}{1}{2}".format("{{", expression, "}}"), cache=False) + expression = "{0}{1}{2}".format("{{", expression, "}}") + if _TEMPLAR_HAS_TEMPLATE_CACHE: + return templar.template(expression, cache=False) + return templar.template(expression) def __process(self, result, terms, index, current, templar, variables): """Fills ``result`` list with evaluated items. diff --git a/plugins/lookup/dig.py b/plugins/lookup/dig.py index ceaff15e9f..d0d94e988a 100644 --- a/plugins/lookup/dig.py +++ b/plugins/lookup/dig.py @@ -35,9 +35,10 @@ DOCUMENTATION = ''' description: - Record type to query. - C(DLV) has been removed in community.general 6.0.0. + - C(CAA) has been added in community.general 6.3.0. type: str default: 'A' - choices: [A, ALL, AAAA, CNAME, DNAME, DNSKEY, DS, HINFO, LOC, MX, NAPTR, NS, NSEC3PARAM, PTR, RP, RRSIG, SOA, SPF, SRV, SSHFP, TLSA, TXT] + choices: [A, ALL, AAAA, CAA, CNAME, DNAME, DNSKEY, DS, HINFO, LOC, MX, NAPTR, NS, NSEC3PARAM, PTR, RP, RRSIG, SOA, SPF, SRV, SSHFP, TLSA, TXT] flat: description: If 0 each record is returned as a dictionary, otherwise a string. type: int @@ -129,6 +130,12 @@ RETURN = """ AAAA: description: - address + CAA: + description: + - flags + - tag + - value + version_added: 6.3.0 CNAME: description: - target @@ -198,7 +205,7 @@ try: import dns.resolver import dns.reversename import dns.rdataclass - from dns.rdatatype import (A, AAAA, CNAME, DNAME, DNSKEY, DS, HINFO, LOC, + from dns.rdatatype import (A, AAAA, CAA, CNAME, DNAME, DNSKEY, DS, HINFO, LOC, MX, NAPTR, NS, NSEC3PARAM, PTR, RP, SOA, SPF, SRV, SSHFP, TLSA, TXT) HAVE_DNS = True except ImportError: @@ -218,6 +225,7 @@ def make_rdata_dict(rdata): supported_types = { A: ['address'], AAAA: ['address'], + CAA: ['flags', 'tag', 'value'], CNAME: ['target'], DNAME: ['target'], DNSKEY: ['flags', 'algorithm', 'protocol', 'key'], @@ -230,7 +238,7 @@ def make_rdata_dict(rdata): NSEC3PARAM: ['algorithm', 'flags', 'iterations', 'salt'], PTR: ['target'], RP: ['mbox', 'txt'], - # RRSIG: ['algorithm', 'labels', 'original_ttl', 'expiration', 'inception', 'signature'], + # RRSIG: ['type_covered', 'algorithm', 'labels', 'original_ttl', 'expiration', 'inception', 'key_tag', 'signer', 'signature'], SOA: ['mname', 'rname', 'serial', 'refresh', 'retry', 'expire', 'minimum'], SPF: ['strings'], SRV: ['priority', 'weight', 'port', 'target'], @@ -251,6 +259,8 @@ def make_rdata_dict(rdata): if rdata.rdtype == DS and f == 'digest': val = dns.rdata._hexify(rdata.digest).replace(' ', '') + if rdata.rdtype == DNSKEY and f == 'algorithm': + val = int(val) if rdata.rdtype == DNSKEY and f == 'key': val = dns.rdata._base64ify(rdata.key).replace(' ', '') if rdata.rdtype == NSEC3PARAM and f == 'salt': diff --git a/plugins/lookup/etcd3.py b/plugins/lookup/etcd3.py index df41d791e8..7f0a0cf90e 100644 --- a/plugins/lookup/etcd3.py +++ b/plugins/lookup/etcd3.py @@ -136,12 +136,11 @@ RETURN = ''' import re -from ansible.plugins.lookup import LookupBase -from ansible.utils.display import Display +from ansible.errors import AnsibleLookupError from ansible.module_utils.basic import missing_required_lib from ansible.module_utils.common.text.converters import to_native from ansible.plugins.lookup import LookupBase -from ansible.errors import AnsibleError, AnsibleLookupError +from ansible.utils.display import Display try: import etcd3 diff --git a/plugins/lookup/flattened.py b/plugins/lookup/flattened.py index 0f290e559d..e955b6478e 100644 --- a/plugins/lookup/flattened.py +++ b/plugins/lookup/flattened.py @@ -67,7 +67,12 @@ class LookupModule(LookupBase): if isinstance(term, string_types): # convert a variable to a list - term2 = listify_lookup_plugin_terms(term, templar=self._templar, loader=self._loader) + try: + term2 = listify_lookup_plugin_terms(term, templar=self._templar) + except TypeError: + # The loader argument is deprecated in ansible-core 2.14+. Fall back to + # pre-2.14 behavior for older ansible-core versions. + term2 = listify_lookup_plugin_terms(term, templar=self._templar, loader=self._loader) # but avoid converting a plain string to a list of one string if term2 != [term]: term = term2 diff --git a/plugins/lookup/hiera.py b/plugins/lookup/hiera.py index 1049e80b02..fa4d0a1999 100644 --- a/plugins/lookup/hiera.py +++ b/plugins/lookup/hiera.py @@ -61,8 +61,6 @@ RETURN = """ elements: str """ -import os - from ansible.plugins.lookup import LookupBase from ansible.utils.cmd_functions import run_cmd from ansible.module_utils.common.text.converters import to_text diff --git a/plugins/lookup/manifold.py b/plugins/lookup/manifold.py index 51064b9c2b..049d453e4f 100644 --- a/plugins/lookup/manifold.py +++ b/plugins/lookup/manifold.py @@ -69,7 +69,6 @@ from ansible.utils.display import Display from traceback import format_exception import json import sys -import os display = Display() diff --git a/plugins/lookup/merge_variables.py b/plugins/lookup/merge_variables.py new file mode 100644 index 0000000000..cd5fa5b7df --- /dev/null +++ b/plugins/lookup/merge_variables.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Thales Netherlands +# Copyright (c) 2021, Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = """ + author: + - Roy Lenferink (@rlenferink) + - Mark Ettema (@m-a-r-k-e) + name: merge_variables + short_description: merge variables with a certain suffix + description: + - This lookup returns the merged result of all variables in scope that match the given prefixes, suffixes, or + regular expressions, optionally. + version_added: 6.5.0 + options: + _terms: + description: + - Depending on the value of I(pattern_type), this is a list of prefixes, suffixes, or regular expressions + that will be used to match all variables that should be merged. + required: true + type: list + elements: str + pattern_type: + description: + - Change the way of searching for the specified pattern. + type: str + default: 'regex' + choices: + - prefix + - suffix + - regex + env: + - name: ANSIBLE_MERGE_VARIABLES_PATTERN_TYPE + ini: + - section: merge_variables_lookup + key: pattern_type + initial_value: + description: + - An initial value to start with. + type: raw + override: + description: + - Return an error, print a warning or ignore it when a key will be overwritten. + - The default behavior C(error) makes the plugin fail when a key would be overwritten. + - When C(warn) and C(ignore) are used, note that it is important to know that the variables + are sorted by name before being merged. Keys for later variables in this order will overwrite + keys of the same name for variables earlier in this order. To avoid potential confusion, + better use I(override=error) whenever possible. + type: str + default: 'error' + choices: + - error + - warn + - ignore + env: + - name: ANSIBLE_MERGE_VARIABLES_OVERRIDE + ini: + - section: merge_variables_lookup + key: override +""" + +EXAMPLES = """ +# Some example variables, they can be defined anywhere as long as they are in scope +test_init_list: + - "list init item 1" + - "list init item 2" + +testa__test_list: + - "test a item 1" + +testb__test_list: + - "test b item 1" + +testa__test_dict: + ports: + - 1 + +testb__test_dict: + ports: + - 3 + + +# Merge variables that end with '__test_dict' and store the result in a variable 'example_a' +example_a: "{{ lookup('community.general.merge_variables', '__test_dict', pattern_type='suffix') }}" + +# The variable example_a now contains: +# ports: +# - 1 +# - 3 + + +# Merge variables that match the '^.+__test_list$' regular expression, starting with an initial value and store the +# result in a variable 'example_b' +example_b: "{{ lookup('community.general.merge_variables', '^.+__test_list$', initial_value=test_init_list) }}" + +# The variable example_b now contains: +# - "list init item 1" +# - "list init item 2" +# - "test a item 1" +# - "test b item 1" +""" + +RETURN = """ + _raw: + description: In case the search matches list items, a list will be returned. In case the search matches dicts, a + dict will be returned. + type: raw + elements: raw +""" + +import re + +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase +from ansible.utils.display import Display + +display = Display() + + +def _verify_and_get_type(variable): + if isinstance(variable, list): + return "list" + elif isinstance(variable, dict): + return "dict" + else: + raise AnsibleError("Not supported type detected, variable must be a list or a dict") + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + self.set_options(direct=kwargs) + initial_value = self.get_option("initial_value", None) + self._override = self.get_option('override', 'error') + self._pattern_type = self.get_option('pattern_type', 'regex') + + ret = [] + for term in terms: + if not isinstance(term, str): + raise AnsibleError("Non-string type '{0}' passed, only 'str' types are allowed!".format(type(term))) + + ret.append(self._merge_vars(term, initial_value, variables)) + + return ret + + def _var_matches(self, key, search_pattern): + if self._pattern_type == "prefix": + return key.startswith(search_pattern) + elif self._pattern_type == "suffix": + return key.endswith(search_pattern) + elif self._pattern_type == "regex": + matcher = re.compile(search_pattern) + return matcher.search(key) + + return False + + def _merge_vars(self, search_pattern, initial_value, variables): + display.vvv("Merge variables with {0}: {1}".format(self._pattern_type, search_pattern)) + var_merge_names = sorted([key for key in variables.keys() if self._var_matches(key, search_pattern)]) + display.vvv("The following variables will be merged: {0}".format(var_merge_names)) + + prev_var_type = None + result = None + + if initial_value is not None: + prev_var_type = _verify_and_get_type(initial_value) + result = initial_value + + for var_name in var_merge_names: + var_value = self._templar.template(variables[var_name]) # Render jinja2 templates + var_type = _verify_and_get_type(var_value) + + if prev_var_type is None: + prev_var_type = var_type + elif prev_var_type != var_type: + raise AnsibleError("Unable to merge, not all variables are of the same type") + + if result is None: + result = var_value + continue + + if var_type == "dict": + result = self._merge_dict(var_value, result, [var_name]) + else: # var_type == "list" + result += var_value + + return result + + def _merge_dict(self, src, dest, path): + for key, value in src.items(): + if isinstance(value, dict): + node = dest.setdefault(key, {}) + self._merge_dict(value, node, path + [key]) + elif isinstance(value, list) and key in dest: + dest[key] += value + else: + if (key in dest) and dest[key] != value: + msg = "The key '{0}' with value '{1}' will be overwritten with value '{2}' from '{3}.{0}'".format( + key, dest[key], value, ".".join(path)) + + if self._override == "error": + raise AnsibleError(msg) + if self._override == "warn": + display.warning(msg) + + dest[key] = value + + return dest diff --git a/plugins/lookup/onepassword.py b/plugins/lookup/onepassword.py index bbd11d6645..0e78e4b1a0 100644 --- a/plugins/lookup/onepassword.py +++ b/plugins/lookup/onepassword.py @@ -32,7 +32,7 @@ DOCUMENTATION = ''' section: description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from any section. domain: - description: Domain of 1Password. Default is U(1password.com). + description: Domain of 1Password. version_added: 3.2.0 default: '1password.com' type: str @@ -488,7 +488,7 @@ class OnePassCLIv2(OnePassCLIBase): account = "{subdomain}.{domain}".format(subdomain=self.subdomain, domain=self.domain) args.extend(["--account", account]) - rc, out, err = self._run(args) + rc, out, err = self._run(args, ignore_errors=True) return not bool(rc) diff --git a/plugins/lookup/onepassword_raw.py b/plugins/lookup/onepassword_raw.py index c7b9332953..9b87a3f619 100644 --- a/plugins/lookup/onepassword_raw.py +++ b/plugins/lookup/onepassword_raw.py @@ -30,6 +30,11 @@ DOCUMENTATION = ''' description: Item section containing the field to retrieve (case-insensitive). If absent will return first match from any section. subdomain: description: The 1Password subdomain to authenticate against. + domain: + description: Domain of 1Password. + version_added: 6.0.0 + default: '1password.com' + type: str username: description: The username used to sign in. secret_key: diff --git a/plugins/lookup/redis.py b/plugins/lookup/redis.py index 490751a398..43b046a798 100644 --- a/plugins/lookup/redis.py +++ b/plugins/lookup/redis.py @@ -73,8 +73,6 @@ _raw: elements: str """ -import os - HAVE_REDIS = False try: import redis diff --git a/plugins/module_utils/_stormssh.py b/plugins/module_utils/_stormssh.py new file mode 100644 index 0000000000..ec364b83d4 --- /dev/null +++ b/plugins/module_utils/_stormssh.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is based on +# the config parser from here: https://github.com/emre/storm/blob/master/storm/parsers/ssh_config_parser.py +# Copyright (C) <2013> +# SPDX-License-Identifier: MIT + +from __future__ import (absolute_import, division, print_function) +import os +import re +import traceback +from operator import itemgetter + +__metaclass__ = type + +try: + from paramiko.config import SSHConfig +except ImportError: + SSHConfig = object + HAS_PARAMIKO = False + PARAMIKO_IMPORT_ERROR = traceback.format_exc() +else: + HAS_PARAMIKO = True + PARAMIKO_IMPORT_ERROR = None + + +class StormConfig(SSHConfig): + def parse(self, file_obj): + """ + Read an OpenSSH config from the given file object. + @param file_obj: a file-like object to read the config file from + @type file_obj: file + """ + order = 1 + host = {"host": ['*'], "config": {}, } + for line in file_obj: + line = line.rstrip('\n').lstrip() + if line == '': + self._config.append({ + 'type': 'empty_line', + 'value': line, + 'host': '', + 'order': order, + }) + order += 1 + continue + + if line.startswith('#'): + self._config.append({ + 'type': 'comment', + 'value': line, + 'host': '', + 'order': order, + }) + order += 1 + continue + + if '=' in line: + # Ensure ProxyCommand gets properly split + if line.lower().strip().startswith('proxycommand'): + proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I) + match = proxy_re.match(line) + key, value = match.group(1).lower(), match.group(2) + else: + key, value = line.split('=', 1) + key = key.strip().lower() + else: + # find first whitespace, and split there + i = 0 + while (i < len(line)) and not line[i].isspace(): + i += 1 + if i == len(line): + raise Exception('Unparsable line: %r' % line) + key = line[:i].lower() + value = line[i:].lstrip() + if key == 'host': + self._config.append(host) + value = value.split() + host = { + key: value, + 'config': {}, + 'type': 'entry', + 'order': order + } + order += 1 + elif key in ['identityfile', 'localforward', 'remoteforward']: + if key in host['config']: + host['config'][key].append(value) + else: + host['config'][key] = [value] + elif key not in host['config']: + host['config'].update({key: value}) + self._config.append(host) + + +class ConfigParser(object): + """ + Config parser for ~/.ssh/config files. + """ + + def __init__(self, ssh_config_file=None): + if not ssh_config_file: + ssh_config_file = self.get_default_ssh_config_file() + + self.defaults = {} + + self.ssh_config_file = ssh_config_file + + if not os.path.exists(self.ssh_config_file): + if not os.path.exists(os.path.dirname(self.ssh_config_file)): + os.makedirs(os.path.dirname(self.ssh_config_file)) + open(self.ssh_config_file, 'w+').close() + os.chmod(self.ssh_config_file, 0o600) + + self.config_data = [] + + def get_default_ssh_config_file(self): + return os.path.expanduser("~/.ssh/config") + + def load(self): + config = StormConfig() + + with open(self.ssh_config_file) as fd: + config.parse(fd) + + for entry in config.__dict__.get("_config"): + if entry.get("host") == ["*"]: + self.defaults.update(entry.get("config")) + + if entry.get("type") in ["comment", "empty_line"]: + self.config_data.append(entry) + continue + + host_item = { + 'host': entry["host"][0], + 'options': entry.get("config"), + 'type': 'entry', + 'order': entry.get("order", 0), + } + + if len(entry["host"]) > 1: + host_item.update({ + 'host': " ".join(entry["host"]), + }) + # minor bug in paramiko.SSHConfig that duplicates + # "Host *" entries. + if entry.get("config") and len(entry.get("config")) > 0: + self.config_data.append(host_item) + + return self.config_data + + def add_host(self, host, options): + self.config_data.append({ + 'host': host, + 'options': options, + 'order': self.get_last_index(), + }) + + return self + + def update_host(self, host, options, use_regex=False): + for index, host_entry in enumerate(self.config_data): + if host_entry.get("host") == host or \ + (use_regex and re.match(host, host_entry.get("host"))): + + if 'deleted_fields' in options: + deleted_fields = options.pop("deleted_fields") + for deleted_field in deleted_fields: + del self.config_data[index]["options"][deleted_field] + + self.config_data[index]["options"].update(options) + + return self + + def search_host(self, search_string): + results = [] + for host_entry in self.config_data: + if host_entry.get("type") != 'entry': + continue + if host_entry.get("host") == "*": + continue + + searchable_information = host_entry.get("host") + for key, value in host_entry.get("options").items(): + if isinstance(value, list): + value = " ".join(value) + if isinstance(value, int): + value = str(value) + + searchable_information += " " + value + + if search_string in searchable_information: + results.append(host_entry) + + return results + + def delete_host(self, host): + found = 0 + for index, host_entry in enumerate(self.config_data): + if host_entry.get("host") == host: + del self.config_data[index] + found += 1 + + if found == 0: + raise ValueError('No host found') + return self + + def delete_all_hosts(self): + self.config_data = [] + self.write_to_ssh_config() + + return self + + def dump(self): + if len(self.config_data) < 1: + return + + file_content = "" + self.config_data = sorted(self.config_data, key=itemgetter("order")) + + for host_item in self.config_data: + if host_item.get("type") in ['comment', 'empty_line']: + file_content += host_item.get("value") + "\n" + continue + host_item_content = "Host {0}\n".format(host_item.get("host")) + for key, value in host_item.get("options").items(): + if isinstance(value, list): + sub_content = "" + for value_ in value: + sub_content += " {0} {1}\n".format( + key, value_ + ) + host_item_content += sub_content + else: + host_item_content += " {0} {1}\n".format( + key, value + ) + file_content += host_item_content + + return file_content + + def write_to_ssh_config(self): + with open(self.ssh_config_file, 'w+') as f: + data = self.dump() + if data: + f.write(data) + return self + + def get_last_index(self): + last_index = 0 + indexes = [] + for item in self.config_data: + if item.get("order"): + indexes.append(item.get("order")) + if len(indexes) > 0: + last_index = max(indexes) + + return last_index diff --git a/plugins/module_utils/cmd_runner.py b/plugins/module_utils/cmd_runner.py index 141a6be9b2..21d61a6a5c 100644 --- a/plugins/module_utils/cmd_runner.py +++ b/plugins/module_utils/cmd_runner.py @@ -88,9 +88,10 @@ class FormatError(CmdRunnerException): class _ArgFormat(object): - def __init__(self, func, ignore_none=None): + def __init__(self, func, ignore_none=None, ignore_missing_value=False): self.func = func self.ignore_none = ignore_none + self.ignore_missing_value = ignore_missing_value def __call__(self, value, ctx_ignore_none): ignore_none = self.ignore_none if self.ignore_none is not None else ctx_ignore_none @@ -102,8 +103,13 @@ class _ArgFormat(object): class _Format(object): @staticmethod - def as_bool(args): - return _ArgFormat(lambda value: _ensure_list(args) if value else []) + def as_bool(args_true, args_false=None, ignore_none=None): + if args_false is not None: + if ignore_none is None: + ignore_none = False + else: + args_false = [] + return _ArgFormat(lambda value: _ensure_list(args_true) if value else _ensure_list(args_false), ignore_none=ignore_none) @staticmethod def as_bool_not(args): @@ -127,7 +133,7 @@ class _Format(object): @staticmethod def as_fixed(args): - return _ArgFormat(lambda value: _ensure_list(args), ignore_none=False) + return _ArgFormat(lambda value: _ensure_list(args), ignore_none=False, ignore_missing_value=True) @staticmethod def as_func(func, ignore_none=None): @@ -135,14 +141,15 @@ class _Format(object): @staticmethod def as_map(_map, default=None, ignore_none=None): + if default is None: + default = [] return _ArgFormat(lambda value: _ensure_list(_map.get(value, default)), ignore_none=ignore_none) @staticmethod def as_default_type(_type, arg="", ignore_none=None): fmt = _Format if _type == "dict": - return fmt.as_func(lambda d: ["--{0}={1}".format(*a) for a in iteritems(d)], - ignore_none=ignore_none) + return fmt.as_func(lambda d: ["--{0}={1}".format(*a) for a in iteritems(d)], ignore_none=ignore_none) if _type == "list": return fmt.as_func(lambda value: ["--{0}".format(x) for x in value], ignore_none=ignore_none) if _type == "bool": @@ -261,10 +268,13 @@ class _CmdRunnerContext(object): for arg_name in self.args_order: value = None try: - value = named_args[arg_name] + if arg_name in named_args: + value = named_args[arg_name] + elif not runner.arg_formats[arg_name].ignore_missing_value: + raise MissingArgumentValue(self.args_order, arg_name) self.cmd.extend(runner.arg_formats[arg_name](value, ctx_ignore_none=self.ignore_value_none)) - except KeyError: - raise MissingArgumentValue(self.args_order, arg_name) + except MissingArgumentValue: + raise except Exception as e: raise FormatError(arg_name, value, runner.arg_formats[arg_name], e) diff --git a/plugins/module_utils/deps.py b/plugins/module_utils/deps.py new file mode 100644 index 0000000000..bfb94cbc09 --- /dev/null +++ b/plugins/module_utils/deps.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# (c) 2022, Alexei Znamensky +# Copyright (c) 2022, Ansible Project +# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause) +# SPDX-License-Identifier: BSD-2-Clause + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +import traceback +from contextlib import contextmanager + +from ansible.module_utils.common.text.converters import to_native +from ansible.module_utils.basic import missing_required_lib + + +_deps = dict() + + +class _Dependency(object): + _states = ["pending", "failure", "success"] + + def __init__(self, name, reason=None, url=None, msg=None): + self.name = name + self.reason = reason + self.url = url + self.msg = msg + + self.state = 0 + self.trace = None + self.exc = None + + def succeed(self): + self.state = 2 + + def fail(self, exc, trace): + self.state = 1 + self.exc = exc + self.trace = trace + + @property + def message(self): + if self.msg: + return to_native(self.msg) + else: + return missing_required_lib(self.name, reason=self.reason, url=self.url) + + @property + def failed(self): + return self.state == 1 + + def verify(self, module): + if self.failed: + module.fail_json(msg=self.message, exception=self.trace) + + def __str__(self): + return "".format(self.name, self._states[self.state]) + + +@contextmanager +def declare(name, *args, **kwargs): + dep = _Dependency(name, *args, **kwargs) + try: + yield dep + except Exception as e: + dep.fail(e, traceback.format_exc()) + else: + dep.succeed() + finally: + _deps[name] = dep + + +def validate(module, spec=None): + dep_names = sorted(_deps) + + if spec is not None: + if spec.startswith("-"): + spec_split = spec[1:].split(":") + for d in spec_split: + dep_names.remove(d) + else: + spec_split = spec[1:].split(":") + dep_names = [] + for d in spec_split: + _deps[d] # ensure it exists + dep_names.append(d) + + for dep in dep_names: + _deps[dep].verify(module) diff --git a/plugins/module_utils/dimensiondata.py b/plugins/module_utils/dimensiondata.py index 308615bfe4..0300f6c1e9 100644 --- a/plugins/module_utils/dimensiondata.py +++ b/plugins/module_utils/dimensiondata.py @@ -19,15 +19,16 @@ import os import re import traceback -from ansible.module_utils.basic import AnsibleModule, missing_required_lib +# (TODO: remove AnsibleModule from next line!) +from ansible.module_utils.basic import AnsibleModule, missing_required_lib # noqa: F401, pylint: disable=unused-import from ansible.module_utils.six.moves import configparser from os.path import expanduser from uuid import UUID LIBCLOUD_IMP_ERR = None try: - from libcloud.common.dimensiondata import API_ENDPOINTS, DimensionDataAPIException, DimensionDataStatus - from libcloud.compute.base import Node, NodeLocation + from libcloud.common.dimensiondata import API_ENDPOINTS, DimensionDataAPIException, DimensionDataStatus # noqa: F401, pylint: disable=unused-import + from libcloud.compute.base import Node, NodeLocation # noqa: F401, pylint: disable=unused-import from libcloud.compute.providers import get_driver from libcloud.compute.types import Provider diff --git a/plugins/module_utils/gconftool2.py b/plugins/module_utils/gconftool2.py index cd9de57695..e90c3fb2cb 100644 --- a/plugins/module_utils/gconftool2.py +++ b/plugins/module_utils/gconftool2.py @@ -6,7 +6,14 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt +from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt + + +_state_map = { + "present": "--set", + "absent": "--unset", + "get": "--get", +} def gconftool2_runner(module, **kwargs): @@ -14,14 +21,12 @@ def gconftool2_runner(module, **kwargs): module, command='gconftool-2', arg_formats=dict( - key=fmt.as_list(), - value_type=fmt.as_opt_val("--type"), - value=fmt.as_list(), - direct=fmt.as_bool("--direct"), - config_source=fmt.as_opt_val("--config-source"), - get=fmt.as_bool("--get"), - set_arg=fmt.as_bool("--set"), - unset=fmt.as_bool("--unset"), + state=cmd_runner_fmt.as_map(_state_map), + key=cmd_runner_fmt.as_list(), + value_type=cmd_runner_fmt.as_opt_val("--type"), + value=cmd_runner_fmt.as_list(), + direct=cmd_runner_fmt.as_bool("--direct"), + config_source=cmd_runner_fmt.as_opt_val("--config-source"), ), **kwargs ) diff --git a/plugins/module_utils/gitlab.py b/plugins/module_utils/gitlab.py index 3ed338b401..7cb59e4c2c 100644 --- a/plugins/module_utils/gitlab.py +++ b/plugins/module_utils/gitlab.py @@ -110,3 +110,14 @@ def gitlab_authentication(module): GitLab remove Session API now that private tokens are removed from user API endpoints since version 10.2." % to_native(e)) return gitlab_instance + + +def filter_returned_variables(gitlab_variables): + # pop properties we don't know + existing_variables = [dict(x.attributes) for x in gitlab_variables] + KNOWN = ['key', 'value', 'masked', 'protected', 'variable_type', 'environment_scope'] + for item in existing_variables: + for key in list(item.keys()): + if key not in KNOWN: + item.pop(key) + return existing_variables diff --git a/plugins/module_utils/identity/keycloak/keycloak.py b/plugins/module_utils/identity/keycloak/keycloak.py index 078925ef71..15b665752d 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -42,6 +42,7 @@ URL_CLIENTTEMPLATE = "{url}/admin/realms/{realm}/client-templates/{id}" URL_CLIENTTEMPLATES = "{url}/admin/realms/{realm}/client-templates" URL_GROUPS = "{url}/admin/realms/{realm}/groups" URL_GROUP = "{url}/admin/realms/{realm}/groups/{groupid}" +URL_GROUP_CHILDREN = "{url}/admin/realms/{realm}/groups/{groupid}/children" URL_CLIENTSCOPES = "{url}/admin/realms/{realm}/client-scopes" URL_CLIENTSCOPE = "{url}/admin/realms/{realm}/client-scopes/{id}" @@ -58,6 +59,8 @@ URL_CLIENT_USER_ROLEMAPPINGS = "{url}/admin/realms/{realm}/users/{id}/role-mappi URL_CLIENT_USER_ROLEMAPPINGS_AVAILABLE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}/available" URL_CLIENT_USER_ROLEMAPPINGS_COMPOSITE = "{url}/admin/realms/{realm}/users/{id}/role-mappings/clients/{client}/composite" +URL_CLIENTSECRET = "{url}/admin/realms/{realm}/clients/{id}/client-secret" + URL_AUTHENTICATION_FLOWS = "{url}/admin/realms/{realm}/authentication/flows" URL_AUTHENTICATION_FLOW = "{url}/admin/realms/{realm}/authentication/flows/{id}" URL_AUTHENTICATION_FLOW_COPY = "{url}/admin/realms/{realm}/authentication/flows/{copyfrom}/copy" @@ -606,7 +609,7 @@ class KeycloakAPI(object): """ available_rolemappings_url = URL_CLIENT_GROUP_ROLEMAPPINGS.format(url=self.baseurl, realm=realm, id=gid, client=cid) try: - open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, + open_url(available_rolemappings_url, method="DELETE", http_agent=self.http_agent, headers=self.restheaders, data=json.dumps(role_rep), validate_certs=self.validate_certs, timeout=self.connection_timeout) except Exception as e: self.module.fail_json(msg="Could not delete available rolemappings for client %s in group %s, realm %s: %s" @@ -1160,6 +1163,52 @@ class KeycloakAPI(object): self.module.fail_json(msg='Could not update protocolmappers for clientscope %s in realm %s: %s' % (mapper_rep, realm, str(e))) + def create_clientsecret(self, id, realm="master"): + """ Generate a new client secret by id + + :param id: id (not clientId) of client to be queried + :param realm: client from this realm + :return: dict of credential representation + """ + clientsecret_url = URL_CLIENTSECRET.format(url=self.baseurl, realm=realm, id=id) + + try: + return json.loads(to_native(open_url(clientsecret_url, method='POST', headers=self.restheaders, timeout=self.connection_timeout, + 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 clientsecret of client %s for realm %s: %s' + % (id, realm, str(e))) + except Exception as e: + self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s' + % (id, realm, str(e))) + + def get_clientsecret(self, id, realm="master"): + """ Obtain client secret by id + + :param id: id (not clientId) of client to be queried + :param realm: client from this realm + :return: dict of credential representation + """ + clientsecret_url = URL_CLIENTSECRET.format(url=self.baseurl, realm=realm, id=id) + + try: + return json.loads(to_native(open_url(clientsecret_url, method='GET', headers=self.restheaders, timeout=self.connection_timeout, + 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 clientsecret of client %s for realm %s: %s' + % (id, realm, str(e))) + except Exception as e: + self.module.fail_json(msg='Could not obtain clientsecret of client %s for realm %s: %s' + % (id, realm, str(e))) + def get_groups(self, realm="master"): """ Fetch the name and ID of all groups on the Keycloak server. @@ -1201,7 +1250,7 @@ class KeycloakAPI(object): self.module.fail_json(msg="Could not fetch group %s in realm %s: %s" % (gid, realm, str(e))) - def get_group_by_name(self, name, realm="master"): + def get_group_by_name(self, name, realm="master", parents=None): """ Fetch a keycloak group within a realm based on its name. The Keycloak API does not allow filtering of the Groups resource by name. @@ -1211,10 +1260,19 @@ class KeycloakAPI(object): If the group does not exist, None is returned. :param name: Name of the group to fetch. :param realm: Realm in which the group resides; default 'master' + :param parents: Optional list of parents when group to look for is a subgroup """ groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm) try: - all_groups = self.get_groups(realm=realm) + if parents: + parent = self.get_subgroup_direct_parent(parents, realm) + + if not parent: + return None + + all_groups = parent['subGroups'] + else: + all_groups = self.get_groups(realm=realm) for group in all_groups: if group['name'] == name: @@ -1226,6 +1284,102 @@ class KeycloakAPI(object): self.module.fail_json(msg="Could not fetch group %s in realm %s: %s" % (name, realm, str(e))) + def _get_normed_group_parent(self, parent): + """ Converts parent dict information into a more easy to use form. + + :param parent: parent describing dict + """ + if parent['id']: + return (parent['id'], True) + + return (parent['name'], False) + + def get_subgroup_by_chain(self, name_chain, realm="master"): + """ Access a subgroup API object by walking down a given name/id chain. + + Groups can be given either as by name or by ID, the first element + must either be a toplvl group or given as ID, all parents must exist. + + If the group cannot be found, None is returned. + :param name_chain: Topdown ordered list of subgroup parent (ids or names) + its own name at the end + :param realm: Realm in which the group resides; default 'master' + """ + cp = name_chain[0] + + # for 1st parent in chain we must query the server + cp, is_id = self._get_normed_group_parent(cp) + + if is_id: + tmp = self.get_group_by_groupid(cp, realm=realm) + else: + # given as name, assume toplvl group + tmp = self.get_group_by_name(cp, realm=realm) + + if not tmp: + return None + + for p in name_chain[1:]: + for sg in tmp['subGroups']: + pv, is_id = self._get_normed_group_parent(p) + + if is_id: + cmpkey = "id" + else: + cmpkey = "name" + + if pv == sg[cmpkey]: + tmp = sg + break + + if not tmp: + return None + + return tmp + + def get_subgroup_direct_parent(self, parents, realm="master", children_to_resolve=None): + """ Get keycloak direct parent group API object for a given chain of parents. + + To succesfully work the API for subgroups we actually dont need + to "walk the whole tree" for nested groups but only need to know + the ID for the direct predecessor of current subgroup. This + method will guarantee us this information getting there with + as minimal work as possible. + + Note that given parent list can and might be incomplete at the + upper levels as long as it starts with an ID instead of a name + + If the group does not exist, None is returned. + :param parents: Topdown ordered list of subgroup parents + :param realm: Realm in which the group resides; default 'master' + """ + if children_to_resolve is None: + # start recursion by reversing parents (in optimal cases + # we dont need to walk the whole tree upwarts) + parents = list(reversed(parents)) + children_to_resolve = [] + + if not parents: + # walk complete parents list to the top, all names, no id's, + # try to resolve it assuming list is complete and 1st + # element is a toplvl group + return self.get_subgroup_by_chain(list(reversed(children_to_resolve)), realm=realm) + + cp = parents[0] + unused, is_id = self._get_normed_group_parent(cp) + + if is_id: + # current parent is given as ID, we can stop walking + # upwards searching for an entry point + return self.get_subgroup_by_chain([cp] + list(reversed(children_to_resolve)), realm=realm) + else: + # current parent is given as name, it must be resolved + # later, try next parent (recurse) + children_to_resolve.append(cp) + return self.get_subgroup_direct_parent( + parents[1:], + realm=realm, children_to_resolve=children_to_resolve + ) + def create_group(self, grouprep, realm="master"): """ Create a Keycloak group. @@ -1240,6 +1394,34 @@ class KeycloakAPI(object): self.module.fail_json(msg="Could not create group %s in realm %s: %s" % (grouprep['name'], realm, str(e))) + def create_subgroup(self, parents, grouprep, realm="master"): + """ Create a Keycloak subgroup. + + :param parents: list of one or more parent groups + :param grouprep: a GroupRepresentation of the group to be created. Must contain at minimum the field name. + :return: HTTPResponse object on success + """ + parent_id = "---UNDETERMINED---" + try: + parent_id = self.get_subgroup_direct_parent(parents, realm) + + if not parent_id: + raise Exception( + "Could not determine subgroup parent ID for given" + " parent chain {0}. Assure that all parents exist" + " already and the list is complete and properly" + " ordered, starts with an ID or starts at the" + " top level".format(parents) + ) + + parent_id = parent_id["id"] + url = URL_GROUP_CHILDREN.format(url=self.baseurl, realm=realm, groupid=parent_id) + return open_url(url, method='POST', http_agent=self.http_agent, headers=self.restheaders, timeout=self.connection_timeout, + data=json.dumps(grouprep), validate_certs=self.validate_certs) + except Exception as e: + self.module.fail_json(msg="Could not create subgroup %s for parent group %s in realm %s: %s" + % (grouprep['name'], parent_id, realm, str(e))) + def update_group(self, grouprep, realm="master"): """ Update an existing group. diff --git a/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py b/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py new file mode 100644 index 0000000000..85caa8e16b --- /dev/null +++ b/plugins/module_utils/identity/keycloak/keycloak_clientsecret.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2022, John Cant +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import \ + keycloak_argument_spec + + +def keycloak_clientsecret_module(): + """ + Returns an AnsibleModule definition for modules that interact with a client + secret. + + :return: argument_spec dict + """ + argument_spec = keycloak_argument_spec() + + meta_args = dict( + realm=dict(default='master'), + id=dict(type='str'), + client_id=dict(type='str', aliases=['clientId']), + ) + + argument_spec.update(meta_args) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=([['id', 'client_id'], + ['token', 'auth_realm', 'auth_username', 'auth_password']]), + required_together=([['auth_realm', 'auth_username', 'auth_password']]), + mutually_exclusive=[ + ['token', 'auth_realm'], + ['token', 'auth_username'], + ['token', 'auth_password'] + ]) + + return module + + +def keycloak_clientsecret_module_resolve_params(module, kc): + """ + Given an AnsibleModule definition for keycloak_clientsecret_*, and a + KeycloakAPI client, resolve the params needed to interact with the Keycloak + client secret, looking up the client by clientId if necessary via an API + call. + + :return: tuple of id, realm + """ + + realm = module.params.get('realm') + id = module.params.get('id') + client_id = module.params.get('client_id') + + # only lookup the client_id if id isn't provided. + # in the case that both are provided, prefer the ID, since it's one + # less lookup. + if id is None: + # Due to the required_one_of spec, client_id is guaranteed to not be None + client = kc.get_client_by_clientid(client_id, realm=realm) + + if client is None: + module.fail_json( + msg='Client does not exist {client_id}'.format(client_id=client_id) + ) + + id = client['id'] + + return id, realm diff --git a/plugins/module_utils/ilo_redfish_utils.py b/plugins/module_utils/ilo_redfish_utils.py index 2d4d999cef..a6ab42dba6 100644 --- a/plugins/module_utils/ilo_redfish_utils.py +++ b/plugins/module_utils/ilo_redfish_utils.py @@ -85,17 +85,16 @@ class iLORedfishUtils(RedfishUtils): datetime_uri = self.manager_uri + "DateTime" - response = self.get_request(self.root_uri + datetime_uri) - if not response['ret']: - return response + listofips = mgr_attributes['mgr_attr_value'].split(" ") + if len(listofips) > 2: + return {'ret': False, 'changed': False, 'msg': "More than 2 NTP Servers mentioned"} - data = response['data'] + ntp_list = [] + for ips in listofips: + ntp_list.append(ips) - ntp_list = data[setkey] - if len(ntp_list) == 2: - ntp_list.pop(0) - - ntp_list.append(mgr_attributes['mgr_attr_value']) + while len(ntp_list) < 2: + ntp_list.append("0.0.0.0") payload = {setkey: ntp_list} @@ -137,18 +136,16 @@ class iLORedfishUtils(RedfishUtils): nic_info = self.get_manager_ethernet_uri() uri = nic_info["nic_addr"] - response = self.get_request(self.root_uri + uri) - if not response['ret']: - return response + listofips = attr['mgr_attr_value'].split(" ") + if len(listofips) > 3: + return {'ret': False, 'changed': False, 'msg': "More than 3 DNS Servers mentioned"} - data = response['data'] + dns_list = [] + for ips in listofips: + dns_list.append(ips) - dns_list = data["Oem"]["Hpe"]["IPv4"][key] - - if len(dns_list) == 3: - dns_list.pop(0) - - dns_list.append(attr['mgr_attr_value']) + while len(dns_list) < 3: + dns_list.append("0.0.0.0") payload = { "Oem": { diff --git a/plugins/module_utils/influxdb.py b/plugins/module_utils/influxdb.py index 9a30e76428..580cabe7d5 100644 --- a/plugins/module_utils/influxdb.py +++ b/plugins/module_utils/influxdb.py @@ -15,7 +15,7 @@ from ansible_collections.community.general.plugins.module_utils.version import L REQUESTS_IMP_ERR = None try: - import requests.exceptions + import requests.exceptions # noqa: F401, pylint: disable=unused-import HAS_REQUESTS = True except ImportError: REQUESTS_IMP_ERR = traceback.format_exc() @@ -25,7 +25,7 @@ INFLUXDB_IMP_ERR = None try: from influxdb import InfluxDBClient from influxdb import __version__ as influxdb_version - from influxdb import exceptions + from influxdb import exceptions # noqa: F401, pylint: disable=unused-import HAS_INFLUXDB = True except ImportError: INFLUXDB_IMP_ERR = traceback.format_exc() diff --git a/plugins/module_utils/jenkins.py b/plugins/module_utils/jenkins.py new file mode 100644 index 0000000000..c742b364b7 --- /dev/null +++ b/plugins/module_utils/jenkins.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2022, Alexei Znamensky +# +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +import os +import time + + +def download_updates_file(updates_expiration): + updates_filename = 'jenkins-plugin-cache.json' + updates_dir = os.path.expanduser('~/.ansible/tmp') + updates_file = os.path.join(updates_dir, updates_filename) + download_updates = True + + # Make sure the destination directory exists + if not os.path.isdir(updates_dir): + os.makedirs(updates_dir, 0o700) + + # Check if we need to download new updates file + if os.path.isfile(updates_file): + # Get timestamp when the file was changed last time + ts_file = os.stat(updates_file).st_mtime + ts_now = time.time() + + if ts_now - ts_file < updates_expiration: + download_updates = False + + return updates_file, download_updates diff --git a/plugins/module_utils/ldap.py b/plugins/module_utils/ldap.py index 03acaa58c5..6553713210 100644 --- a/plugins/module_utils/ldap.py +++ b/plugins/module_utils/ldap.py @@ -10,6 +10,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +import re import traceback from ansible.module_utils.common.text.converters import to_native @@ -33,12 +34,14 @@ def gen_specs(**specs): specs.update({ 'bind_dn': dict(), 'bind_pw': dict(default='', no_log=True), + 'ca_path': dict(type='path'), 'dn': dict(required=True), 'referrals_chasing': dict(type='str', default='anonymous', choices=['disabled', 'anonymous']), 'server_uri': dict(default='ldapi:///'), 'start_tls': dict(default=False, type='bool'), 'validate_certs': dict(default=True, type='bool'), 'sasl_class': dict(choices=['external', 'gssapi'], default='external', type='str'), + 'xorder_discovery': dict(choices=['enable', 'auto', 'disable'], default='auto', type='str'), }) return specs @@ -50,17 +53,22 @@ class LdapGeneric(object): self.module = module self.bind_dn = self.module.params['bind_dn'] self.bind_pw = self.module.params['bind_pw'] + self.ca_path = self.module.params['ca_path'] self.referrals_chasing = self.module.params['referrals_chasing'] self.server_uri = self.module.params['server_uri'] self.start_tls = self.module.params['start_tls'] self.verify_cert = self.module.params['validate_certs'] self.sasl_class = self.module.params['sasl_class'] + self.xorder_discovery = self.module.params['xorder_discovery'] # Establish connection self.connection = self._connect_to_ldap() - # Try to find the X_ORDERed version of the DN - self.dn = self._find_dn() + if self.xorder_discovery == "enable" or (self.xorder_discovery == "auto" and not self._xorder_dn()): + # Try to find the X_ORDERed version of the DN + self.dn = self._find_dn() + else: + self.dn = self.module.params['dn'] def fail(self, msg, exn): self.module.fail_json( @@ -91,6 +99,9 @@ class LdapGeneric(object): if not self.verify_cert: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) + if self.ca_path: + ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, self.ca_path) + connection = ldap.initialize(self.server_uri) if self.referrals_chasing == 'disabled': @@ -113,3 +124,8 @@ class LdapGeneric(object): self.fail("Cannot bind to the server.", e) return connection + + def _xorder_dn(self): + # match X_ORDERed DNs + regex = r"\w+=\{\d+\}.+" + return re.match(regex, self.module.params['dn']) is not None diff --git a/plugins/module_utils/lxd.py b/plugins/module_utils/lxd.py index bdf026313a..7f5362532a 100644 --- a/plugins/module_utils/lxd.py +++ b/plugins/module_utils/lxd.py @@ -8,8 +8,10 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import os import socket import ssl +import json from ansible.module_utils.urls import generic_urlparse from ansible.module_utils.six.moves.urllib.parse import urlparse @@ -20,8 +22,6 @@ from ansible.module_utils.common.text.converters import to_text HTTPConnection = http_client.HTTPConnection HTTPSConnection = http_client.HTTPSConnection -import json - class UnixHTTPConnection(HTTPConnection): def __init__(self, path): @@ -60,7 +60,7 @@ class LXDClient(object): self.cert_file = cert_file self.key_file = key_file parts = generic_urlparse(urlparse(self.url)) - ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) ctx.load_cert_chain(cert_file, keyfile=key_file) self.connection = HTTPSConnection(parts.get('netloc'), context=ctx) elif url.startswith('unix:'): @@ -124,3 +124,11 @@ class LXDClient(object): if err is None: err = resp_json.get('error', None) return err + + +def default_key_file(): + return os.path.expanduser('~/.config/lxc/client.key') + + +def default_cert_file(): + return os.path.expanduser('~/.config/lxc/client.crt') diff --git a/plugins/module_utils/memset.py b/plugins/module_utils/memset.py index 671a8de308..374b40ff44 100644 --- a/plugins/module_utils/memset.py +++ b/plugins/module_utils/memset.py @@ -26,6 +26,7 @@ class Response(object): def __init__(self): self.content = None self.status_code = None + self.stderr = None def json(self): return json.loads(self.content) @@ -75,6 +76,10 @@ def memset_api_call(api_key, api_method, payload=None): msg = "Memset API returned a {0} response ({1}, {2})." . format(response.status_code, response.json()['error_type'], response.json()['error']) else: msg = "Memset API returned an error ({0}, {1})." . format(response.json()['error_type'], response.json()['error']) + except urllib_error.URLError as e: + has_failed = True + msg = "An URLError occured ({0})." . format(type(e)) + response.stderr = "{0}" . format(e) if msg is None: msg = response.json() diff --git a/plugins/module_utils/mh/deco.py b/plugins/module_utils/mh/deco.py index 3073e4e9e7..5138b212c7 100644 --- a/plugins/module_utils/mh/deco.py +++ b/plugins/module_utils/mh/deco.py @@ -37,8 +37,17 @@ def cause_changes(on_success=None, on_failure=None): def module_fails_on_exception(func): + conflict_list = ('msg', 'exception', 'output', 'vars', 'changed') + @wraps(func) def wrapper(self, *args, **kwargs): + def fix_var_conflicts(output): + result = dict([ + (k if k not in conflict_list else "_" + k, v) + for k, v in output.items() + ]) + return result + try: func(self, *args, **kwargs) except SystemExit: @@ -46,12 +55,16 @@ def module_fails_on_exception(func): except ModuleHelperException as e: if e.update_output: self.update_output(e.update_output) + # patchy solution to resolve conflict with output variables + output = fix_var_conflicts(self.output) self.module.fail_json(msg=e.msg, exception=traceback.format_exc(), - output=self.output, vars=self.vars.output(), **self.output) + output=self.output, vars=self.vars.output(), **output) except Exception as e: + # patchy solution to resolve conflict with output variables + output = fix_var_conflicts(self.output) 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) + output=self.output, vars=self.vars.output(), **output) return wrapper diff --git a/plugins/module_utils/mh/module_helper.py b/plugins/module_utils/mh/module_helper.py index c844acba50..c5973262d2 100644 --- a/plugins/module_utils/mh/module_helper.py +++ b/plugins/module_utils/mh/module_helper.py @@ -9,7 +9,8 @@ __metaclass__ = type from ansible.module_utils.common.dict_transformations import dict_merge -from ansible_collections.community.general.plugins.module_utils.mh.base import ModuleHelperBase, AnsibleModule +# (TODO: remove AnsibleModule!) pylint: disable-next=unused-import +from ansible_collections.community.general.plugins.module_utils.mh.base import ModuleHelperBase, AnsibleModule # noqa: F401 from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyMixin @@ -18,7 +19,6 @@ from ansible_collections.community.general.plugins.module_utils.mh.mixins.deprec class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelperBase): - _output_conflict_list = ('msg', 'exception', 'output', 'vars', 'changed') facts_name = None output_params = () diff_params = () @@ -60,10 +60,6 @@ class ModuleHelper(DeprecateAttrsMixin, VarsMixin, DependencyMixin, ModuleHelper vars_diff = self.vars.diff() or {} result['diff'] = dict_merge(dict(diff), vars_diff) - for varname in result: - if varname in self._output_conflict_list: - result["_" + varname] = result[varname] - del result[varname] return result diff --git a/plugins/module_utils/module_helper.py b/plugins/module_utils/module_helper.py index 4cda4175c7..8a51de6658 100644 --- a/plugins/module_utils/module_helper.py +++ b/plugins/module_utils/module_helper.py @@ -8,12 +8,13 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible_collections.community.general.plugins.module_utils.mh.module_helper import ( +from ansible_collections.community.general.plugins.module_utils.mh.module_helper import ( # noqa: F401, pylint: disable=unused-import ModuleHelper, StateModuleHelper, CmdModuleHelper, CmdStateModuleHelper, AnsibleModule ) -from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin, ArgFormat -from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin -from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyCtxMgr -from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException -from ansible_collections.community.general.plugins.module_utils.mh.deco import cause_changes, module_fails_on_exception -from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarMeta, VarDict +from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin, ArgFormat # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyCtxMgr # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException # noqa: F401, pylint: disable=unused-import +# pylint: disable-next=unused-import +from ansible_collections.community.general.plugins.module_utils.mh.deco import cause_changes, module_fails_on_exception # noqa: F401 +from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarMeta, VarDict # noqa: F401, pylint: disable=unused-import diff --git a/plugins/module_utils/ocapi_utils.py b/plugins/module_utils/ocapi_utils.py new file mode 100644 index 0000000000..acc2ceae49 --- /dev/null +++ b/plugins/module_utils/ocapi_utils.py @@ -0,0 +1,502 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022 Western Digital Corporation +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import json +import os +import uuid + +from ansible.module_utils.urls import open_url +from ansible.module_utils.common.text.converters import to_native +from ansible.module_utils.common.text.converters import to_text +from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError +from ansible.module_utils.six.moves.urllib.parse import urlparse + + +GET_HEADERS = {'accept': 'application/json'} +PUT_HEADERS = {'content-type': 'application/json', 'accept': 'application/json'} +POST_HEADERS = {'content-type': 'application/json', 'accept': 'application/json'} +DELETE_HEADERS = {'accept': 'application/json'} + +HEALTH_OK = 5 + + +class OcapiUtils(object): + + def __init__(self, creds, base_uri, proxy_slot_number, timeout, module): + self.root_uri = base_uri + self.proxy_slot_number = proxy_slot_number + self.creds = creds + self.timeout = timeout + self.module = module + + def _auth_params(self): + """ + Return tuple of required authentication params based on the username and password. + + :return: tuple of username, password + """ + username = self.creds['user'] + password = self.creds['pswd'] + force_basic_auth = True + return username, password, force_basic_auth + + def get_request(self, uri): + req_headers = dict(GET_HEADERS) + username, password, basic_auth = self._auth_params() + try: + resp = open_url(uri, method="GET", headers=req_headers, + url_username=username, url_password=password, + force_basic_auth=basic_auth, validate_certs=False, + follow_redirects='all', + use_proxy=True, timeout=self.timeout) + data = json.loads(to_native(resp.read())) + headers = dict((k.lower(), v) for (k, v) in resp.info().items()) + except HTTPError as e: + return {'ret': False, + 'msg': "HTTP Error %s on GET request to '%s'" + % (e.code, uri), + 'status': e.code} + except URLError as e: + return {'ret': False, 'msg': "URL Error on GET request to '%s': '%s'" + % (uri, e.reason)} + # Almost all errors should be caught above, but just in case + except Exception as e: + return {'ret': False, + 'msg': "Failed GET request to '%s': '%s'" % (uri, to_text(e))} + return {'ret': True, 'data': data, 'headers': headers} + + def delete_request(self, uri, etag=None): + req_headers = dict(DELETE_HEADERS) + if etag is not None: + req_headers['If-Match'] = etag + username, password, basic_auth = self._auth_params() + try: + resp = open_url(uri, method="DELETE", headers=req_headers, + url_username=username, url_password=password, + force_basic_auth=basic_auth, validate_certs=False, + follow_redirects='all', + use_proxy=True, timeout=self.timeout) + if resp.status != 204: + data = json.loads(to_native(resp.read())) + else: + data = "" + headers = dict((k.lower(), v) for (k, v) in resp.info().items()) + except HTTPError as e: + return {'ret': False, + 'msg': "HTTP Error %s on DELETE request to '%s'" + % (e.code, uri), + 'status': e.code} + except URLError as e: + return {'ret': False, 'msg': "URL Error on DELETE request to '%s': '%s'" + % (uri, e.reason)} + # Almost all errors should be caught above, but just in case + except Exception as e: + return {'ret': False, + 'msg': "Failed DELETE request to '%s': '%s'" % (uri, to_text(e))} + return {'ret': True, 'data': data, 'headers': headers} + + def put_request(self, uri, payload, etag=None): + req_headers = dict(PUT_HEADERS) + if etag is not None: + req_headers['If-Match'] = etag + username, password, basic_auth = self._auth_params() + try: + resp = open_url(uri, data=json.dumps(payload), + headers=req_headers, method="PUT", + url_username=username, url_password=password, + force_basic_auth=basic_auth, validate_certs=False, + follow_redirects='all', + use_proxy=True, timeout=self.timeout) + headers = dict((k.lower(), v) for (k, v) in resp.info().items()) + except HTTPError as e: + return {'ret': False, + 'msg': "HTTP Error %s on PUT request to '%s'" + % (e.code, uri), + 'status': e.code} + except URLError as e: + return {'ret': False, 'msg': "URL Error on PUT request to '%s': '%s'" + % (uri, e.reason)} + # Almost all errors should be caught above, but just in case + except Exception as e: + return {'ret': False, + 'msg': "Failed PUT request to '%s': '%s'" % (uri, to_text(e))} + return {'ret': True, 'headers': headers, 'resp': resp} + + def post_request(self, uri, payload, content_type="application/json", timeout=None): + req_headers = dict(POST_HEADERS) + if content_type != "application/json": + req_headers["content-type"] = content_type + username, password, basic_auth = self._auth_params() + if content_type == "application/json": + request_data = json.dumps(payload) + else: + request_data = payload + try: + resp = open_url(uri, data=request_data, + headers=req_headers, method="POST", + url_username=username, url_password=password, + force_basic_auth=basic_auth, validate_certs=False, + follow_redirects='all', + use_proxy=True, timeout=self.timeout if timeout is None else timeout) + headers = dict((k.lower(), v) for (k, v) in resp.info().items()) + except HTTPError as e: + return {'ret': False, + 'msg': "HTTP Error %s on POST request to '%s'" + % (e.code, uri), + 'status': e.code} + except URLError as e: + return {'ret': False, 'msg': "URL Error on POST request to '%s': '%s'" + % (uri, e.reason)} + # Almost all errors should be caught above, but just in case + 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} + + def get_uri_with_slot_number_query_param(self, uri): + """Return the URI with proxy slot number added as a query param, if there is one. + + If a proxy slot number is provided, to access it, we must append it as a query parameter. + This method returns the given URI with the slotnumber query param added, if there is one. + If there is not a proxy slot number, it just returns the URI as it was passed in. + """ + if self.proxy_slot_number is not None: + parsed_url = urlparse(uri) + return parsed_url._replace(query="slotnumber=" + str(self.proxy_slot_number)).geturl() + else: + return uri + + def manage_system_power(self, command): + """Process a command to manage the system power. + + :param str command: The Ansible command being processed. + """ + if command == "PowerGracefulRestart": + resource_uri = self.root_uri + resource_uri = self.get_uri_with_slot_number_query_param(resource_uri) + + # Get the resource so that we have the Etag + response = self.get_request(resource_uri) + if 'etag' not in response['headers']: + return {'ret': False, 'msg': 'Etag not found in response.'} + etag = response['headers']['etag'] + if response['ret'] is False: + return response + + # Issue the PUT to do the reboot (unless we are in check mode) + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + payload = {'Reboot': True} + response = self.put_request(resource_uri, payload, etag) + if response['ret'] is False: + return response + elif command.startswith("PowerMode"): + return self.manage_power_mode(command) + else: + return {'ret': False, 'msg': 'Invalid command: ' + command} + + return {'ret': True} + + def manage_chassis_indicator_led(self, command): + """Process a command to manage the chassis indicator LED. + + :param string command: The Ansible command being processed. + """ + return self.manage_indicator_led(command, self.root_uri) + + def manage_indicator_led(self, command, resource_uri=None): + """Process a command to manage an indicator LED. + + :param string command: The Ansible command being processed. + :param string resource_uri: URI of the resource whose indicator LED is being managed. + """ + key = "IndicatorLED" + if resource_uri is None: + resource_uri = self.root_uri + resource_uri = self.get_uri_with_slot_number_query_param(resource_uri) + + payloads = { + 'IndicatorLedOn': { + 'ID': 2 + }, + 'IndicatorLedOff': { + 'ID': 4 + } + } + + response = self.get_request(resource_uri) + if 'etag' not in response['headers']: + return {'ret': False, 'msg': 'Etag not found in response.'} + etag = response['headers']['etag'] + if response['ret'] is False: + return response + data = response['data'] + if key not in data: + return {'ret': False, 'msg': "Key %s not found" % key} + if 'ID' not in data[key]: + return {'ret': False, 'msg': 'IndicatorLED for resource has no ID.'} + + if command in payloads.keys(): + # See if the LED is already set as requested. + current_led_status = data[key]['ID'] + if current_led_status == payloads[command]['ID']: + return {'ret': True, 'changed': False} + + # Set the LED (unless we are in check mode) + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + payload = {'IndicatorLED': payloads[command]} + response = self.put_request(resource_uri, payload, etag) + if response['ret'] is False: + return response + else: + return {'ret': False, 'msg': 'Invalid command'} + + return {'ret': True} + + def manage_power_mode(self, command): + key = "PowerState" + resource_uri = self.get_uri_with_slot_number_query_param(self.root_uri) + + payloads = { + "PowerModeNormal": 2, + "PowerModeLow": 4 + } + + response = self.get_request(resource_uri) + if 'etag' not in response['headers']: + return {'ret': False, 'msg': 'Etag not found in response.'} + etag = response['headers']['etag'] + if response['ret'] is False: + return response + data = response['data'] + if key not in data: + return {'ret': False, 'msg': "Key %s not found" % key} + if 'ID' not in data[key]: + return {'ret': False, 'msg': 'PowerState for resource has no ID.'} + + if command in payloads.keys(): + # See if the PowerState is already set as requested. + current_power_state = data[key]['ID'] + if current_power_state == payloads[command]: + return {'ret': True, 'changed': False} + + # Set the Power State (unless we are in check mode) + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + payload = {'PowerState': {"ID": payloads[command]}} + response = self.put_request(resource_uri, payload, etag) + if response['ret'] is False: + return response + else: + return {'ret': False, 'msg': 'Invalid command: ' + command} + + return {'ret': True} + + def prepare_multipart_firmware_upload(self, filename): + """Prepare a multipart/form-data body for OCAPI firmware upload. + + :arg filename: The name of the file to upload. + :returns: tuple of (content_type, body) where ``content_type`` is + the ``multipart/form-data`` ``Content-Type`` header including + ``boundary`` and ``body`` is the prepared bytestring body + + Prepares the body to include "FirmwareFile" field with the contents of the file. + Because some OCAPI targets do not support Base-64 encoding for multipart/form-data, + this method sends the file as binary. + """ + boundary = str(uuid.uuid4()) # Generate a random boundary + body = "--" + boundary + '\r\n' + body += 'Content-Disposition: form-data; name="FirmwareFile"; filename="%s"\r\n' % to_native(os.path.basename(filename)) + body += 'Content-Type: application/octet-stream\r\n\r\n' + body_bytes = bytearray(body, 'utf-8') + with open(filename, 'rb') as f: + body_bytes += f.read() + body_bytes += bytearray("\r\n--%s--" % boundary, 'utf-8') + return ("multipart/form-data; boundary=%s" % boundary, + body_bytes) + + def upload_firmware_image(self, update_image_path): + """Perform Firmware Upload to the OCAPI storage device. + + :param str update_image_path: The path/filename of the firmware image, on the local filesystem. + """ + if not (os.path.exists(update_image_path) and os.path.isfile(update_image_path)): + return {'ret': False, 'msg': 'File does not exist.'} + url = self.root_uri + "OperatingSystem" + url = self.get_uri_with_slot_number_query_param(url) + content_type, b_form_data = self.prepare_multipart_firmware_upload(update_image_path) + + # Post the firmware (unless we are in check mode) + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + result = self.post_request(url, b_form_data, content_type=content_type, timeout=300) + if result['ret'] is False: + return result + return {'ret': True} + + def update_firmware_image(self): + """Perform a Firmware Update on the OCAPI storage device.""" + resource_uri = self.root_uri + resource_uri = self.get_uri_with_slot_number_query_param(resource_uri) + # We have to do a GET to obtain the Etag. It's required on the PUT. + response = self.get_request(resource_uri) + if response['ret'] is False: + return response + if 'etag' not in response['headers']: + return {'ret': False, 'msg': 'Etag not found in response.'} + etag = response['headers']['etag'] + + # Issue the PUT (unless we are in check mode) + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + payload = {'FirmwareUpdate': True} + response = self.put_request(resource_uri, payload, etag) + if response['ret'] is False: + return response + + return {'ret': True, 'jobUri': response["headers"]["location"]} + + def activate_firmware_image(self): + """Perform a Firmware Activate on the OCAPI storage device.""" + resource_uri = self.root_uri + resource_uri = self.get_uri_with_slot_number_query_param(resource_uri) + # We have to do a GET to obtain the Etag. It's required on the PUT. + response = self.get_request(resource_uri) + if 'etag' not in response['headers']: + return {'ret': False, 'msg': 'Etag not found in response.'} + etag = response['headers']['etag'] + if response['ret'] is False: + return response + + # Issue the PUT (unless we are in check mode) + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + payload = {'FirmwareActivate': True} + response = self.put_request(resource_uri, payload, etag) + if response['ret'] is False: + return response + + return {'ret': True, 'jobUri': response["headers"]["location"]} + + def get_job_status(self, job_uri): + """Get the status of a job. + + :param str job_uri: The URI of the job's status monitor. + """ + job_uri = self.get_uri_with_slot_number_query_param(job_uri) + response = self.get_request(job_uri) + if response['ret'] is False: + if response.get('status') == 404: + # Job not found -- assume 0% + return { + "ret": True, + "percentComplete": 0, + "operationStatus": "Not Available", + "operationStatusId": 1, + "operationHealth": None, + "operationHealthId": None, + "details": "Job does not exist.", + "jobExists": False + } + else: + return response + details = response["data"]["Status"].get("Details") + if type(details) is str: + details = [details] + health_list = response["data"]["Status"]["Health"] + return_value = { + "ret": True, + "percentComplete": response["data"]["PercentComplete"], + "operationStatus": response["data"]["Status"]["State"]["Name"], + "operationStatusId": response["data"]["Status"]["State"]["ID"], + "operationHealth": health_list[0]["Name"] if len(health_list) > 0 else None, + "operationHealthId": health_list[0]["ID"] if len(health_list) > 0 else None, + "details": details, + "jobExists": True + } + return return_value + + def delete_job(self, job_uri): + """Delete the OCAPI job referenced by the specified job_uri.""" + job_uri = self.get_uri_with_slot_number_query_param(job_uri) + # We have to do a GET to obtain the Etag. It's required on the DELETE. + response = self.get_request(job_uri) + + if response['ret'] is True: + if 'etag' not in response['headers']: + return {'ret': False, 'msg': 'Etag not found in response.'} + else: + etag = response['headers']['etag'] + + if response['data']['PercentComplete'] != 100: + return { + 'ret': False, + 'changed': False, + 'msg': 'Cannot delete job because it is in progress.' + } + + if response['ret'] is False: + if response['status'] == 404: + return { + 'ret': True, + 'changed': False, + 'msg': 'Job already deleted.' + } + return response + if self.module.check_mode: + return { + 'ret': True, + 'changed': True, + 'msg': 'Update not performed in check mode.' + } + + # Do the DELETE (unless we are in check mode) + response = self.delete_request(job_uri, etag) + if response['ret'] is False: + if response['status'] == 404: + return { + 'ret': True, + 'changed': False + } + elif response['status'] == 409: + return { + 'ret': False, + 'changed': False, + 'msg': 'Cannot delete job because it is in progress.' + } + return response + return { + 'ret': True, + 'changed': True + } diff --git a/plugins/module_utils/oneview.py b/plugins/module_utils/oneview.py index dfd00c514e..4315a462dc 100644 --- a/plugins/module_utils/oneview.py +++ b/plugins/module_utils/oneview.py @@ -16,7 +16,8 @@ __metaclass__ = type import abc import collections import json -import os +# (TODO: remove next line!) +import os # noqa: F401, pylint: disable=unused-import import traceback HPE_ONEVIEW_IMP_ERR = None diff --git a/plugins/module_utils/opennebula.py b/plugins/module_utils/opennebula.py index b27ec229bf..0fe649ba5c 100644 --- a/plugins/module_utils/opennebula.py +++ b/plugins/module_utils/opennebula.py @@ -26,6 +26,36 @@ except ImportError: HAS_PYONE = False +# A helper function to mitigate https://github.com/OpenNebula/one/issues/6064. +# It allows for easily handling lists like "NIC" or "DISK" in the JSON-like template representation. +# There are either lists of dictionaries (length > 1) or just dictionaries. +def flatten(to_flatten, extract=False): + """Flattens nested lists (with optional value extraction).""" + def recurse(to_flatten): + return sum(map(recurse, to_flatten), []) if isinstance(to_flatten, list) else [to_flatten] + value = recurse(to_flatten) + if extract and len(value) == 1: + return value[0] + return value + + +# A helper function to mitigate https://github.com/OpenNebula/one/issues/6064. +# It renders JSON-like template representation into OpenNebula's template syntax (string). +def render(to_render): + """Converts dictionary to OpenNebula template.""" + def recurse(to_render): + for key, value in sorted(to_render.items()): + if isinstance(value, dict): + yield '{0:}=[{1:}]'.format(key, ','.join(recurse(value))) + continue + if isinstance(value, list): + for item in value: + yield '{0:}=[{1:}]'.format(key, ','.join(recurse(item))) + continue + yield '{0:}="{1:}"'.format(key, value) + return '\n'.join(recurse(to_render)) + + class OpenNebulaModule: """ Base class for all OpenNebula Ansible Modules. diff --git a/plugins/module_utils/oracle/oci_utils.py b/plugins/module_utils/oracle/oci_utils.py index 76fb45324b..3d9c20f2ac 100644 --- a/plugins/module_utils/oracle/oci_utils.py +++ b/plugins/module_utils/oracle/oci_utils.py @@ -10,13 +10,14 @@ import logging import logging.config import os import tempfile -from datetime import datetime +# (TODO: remove next line!) +from datetime import datetime # noqa: F401, pylint: disable=unused-import from operator import eq import time try: - import yaml + import yaml # noqa: F401, pylint: disable=unused-import import oci from oci.constants import HEADER_NEXT_PAGE diff --git a/plugins/module_utils/pipx.py b/plugins/module_utils/pipx.py index 35804c329a..ed23645e39 100644 --- a/plugins/module_utils/pipx.py +++ b/plugins/module_utils/pipx.py @@ -32,6 +32,7 @@ def pipx_runner(module, command, **kwargs): state=fmt.as_map(_state_map), name=fmt.as_list(), name_source=fmt.as_func(fmt.unpack_args(lambda n, s: [s] if s else [n])), + install_apps=fmt.as_bool("--include-apps"), install_deps=fmt.as_bool("--include-deps"), inject_packages=fmt.as_list(), force=fmt.as_bool("--force"), diff --git a/plugins/module_utils/proxmox.py b/plugins/module_utils/proxmox.py index 96a96c8b3c..58287cec17 100644 --- a/plugins/module_utils/proxmox.py +++ b/plugins/module_utils/proxmox.py @@ -7,9 +7,12 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -import atexit -import time -import re +# (TODO: remove next line!) +import atexit # noqa: F401, pylint: disable=unused-import +# (TODO: remove next line!) +import time # noqa: F401, pylint: disable=unused-import +# (TODO: remove next line!) +import re # noqa: F401, pylint: disable=unused-import import traceback PROXMOXER_IMP_ERR = None @@ -22,7 +25,8 @@ except ImportError: from ansible.module_utils.basic import env_fallback, missing_required_lib -from ansible.module_utils.common.text.converters import to_native +# (TODO: remove next line!) +from ansible.module_utils.common.text.converters import to_native # noqa: F401, pylint: disable=unused-import from ansible_collections.community.general.plugins.module_utils.version import LooseVersion diff --git a/plugins/module_utils/puppet.py b/plugins/module_utils/puppet.py new file mode 100644 index 0000000000..06369882fb --- /dev/null +++ b/plugins/module_utils/puppet.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +import os + +from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt + + +_PUPPET_PATH_PREFIX = ["/opt/puppetlabs/bin"] + + +def get_facter_dir(): + if os.getuid() == 0: + return '/etc/facter/facts.d' + else: + return os.path.expanduser('~/.facter/facts.d') + + +def _puppet_cmd(module): + return module.get_bin_path("puppet", False, _PUPPET_PATH_PREFIX) + + +# If the `timeout` CLI command feature is removed, +# Then we could add this as a fixed param to `puppet_runner` +def ensure_agent_enabled(module): + runner = CmdRunner( + module, + command="puppet", + path_prefix=_PUPPET_PATH_PREFIX, + arg_formats=dict( + _agent_disabled=cmd_runner_fmt.as_fixed(['config', 'print', 'agent_disabled_lockfile']), + ), + check_rc=False, + ) + + rc, stdout, stderr = runner("_agent_disabled").run() + if os.path.exists(stdout.strip()): + module.fail_json( + msg="Puppet agent is administratively disabled.", + disabled=True) + elif rc != 0: + module.fail_json( + msg="Puppet agent state could not be determined.") + + +def puppet_runner(module): + + # Keeping backward compatibility, allow for running with the `timeout` CLI command. + # If this can be replaced with ansible `timeout` parameter in playbook, + # then this function could be removed. + def _prepare_base_cmd(): + _tout_cmd = module.get_bin_path("timeout", False) + if _tout_cmd: + cmd = ["timeout", "-s", "9", module.params["timeout"], _puppet_cmd(module)] + else: + cmd = ["puppet"] + return cmd + + def noop_func(v): + _noop = cmd_runner_fmt.as_map({ + True: "--noop", + False: "--no-noop", + }) + return _noop(module.check_mode or v) + + _logdest_map = { + "syslog": ["--logdest", "syslog"], + "all": ["--logdest", "syslog", "--logdest", "console"], + } + + @cmd_runner_fmt.unpack_args + def execute_func(execute, manifest): + if execute: + return ["--execute", execute] + else: + return [manifest] + + runner = CmdRunner( + module, + command=_prepare_base_cmd(), + path_prefix=_PUPPET_PATH_PREFIX, + arg_formats=dict( + _agent_fixed=cmd_runner_fmt.as_fixed([ + "agent", "--onetime", "--no-daemonize", "--no-usecacheonfailure", + "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", + ]), + _apply_fixed=cmd_runner_fmt.as_fixed(["apply", "--detailed-exitcodes"]), + puppetmaster=cmd_runner_fmt.as_opt_val("--server"), + show_diff=cmd_runner_fmt.as_bool("--show-diff"), + confdir=cmd_runner_fmt.as_opt_val("--confdir"), + environment=cmd_runner_fmt.as_opt_val("--environment"), + tags=cmd_runner_fmt.as_func(lambda v: ["--tags", ",".join(v)]), + certname=cmd_runner_fmt.as_opt_eq_val("--certname"), + noop=cmd_runner_fmt.as_func(noop_func), + use_srv_records=cmd_runner_fmt.as_map({ + True: "--usr_srv_records", + False: "--no-usr_srv_records", + }), + logdest=cmd_runner_fmt.as_map(_logdest_map, default=[]), + modulepath=cmd_runner_fmt.as_opt_eq_val("--modulepath"), + _execute=cmd_runner_fmt.as_func(execute_func), + summarize=cmd_runner_fmt.as_bool("--summarize"), + debug=cmd_runner_fmt.as_bool("--debug"), + verbose=cmd_runner_fmt.as_bool("--verbose"), + ), + check_rc=False, + ) + return runner diff --git a/plugins/module_utils/pure.py b/plugins/module_utils/pure.py index c9914c38b5..8210e28f4d 100644 --- a/plugins/module_utils/pure.py +++ b/plugins/module_utils/pure.py @@ -21,13 +21,15 @@ except ImportError: HAS_PURITY_FB = True try: - from purity_fb import PurityFb, FileSystem, FileSystemSnapshot, SnapshotSuffix, rest + from purity_fb import PurityFb, FileSystem, FileSystemSnapshot, SnapshotSuffix, rest # noqa: F401, pylint: disable=unused-import except ImportError: HAS_PURITY_FB = False -from functools import wraps +# (TODO: remove next line!) +from functools import wraps # noqa: F401, pylint: disable=unused-import from os import environ -from os import path +# (TODO: remove next line!) +from os import path # noqa: F401, pylint: disable=unused-import import platform VERSION = 1.2 diff --git a/plugins/module_utils/rax.py b/plugins/module_utils/rax.py index 2372088033..6331c0d1be 100644 --- a/plugins/module_utils/rax.py +++ b/plugins/module_utils/rax.py @@ -314,3 +314,21 @@ def setup_rax_module(module, rax_module, region_required=True): (region, ','.join(rax_module.regions))) return rax_module + + +def rax_scaling_group_personality_file(module, files): + if not files: + return [] + + results = [] + for rpath, lpath in files.items(): + lpath = os.path.expanduser(lpath) + try: + with open(lpath, 'r') as f: + results.append({ + 'path': rpath, + 'contents': f.read(), + }) + except Exception as e: + module.fail_json(msg='Failed to load %s: %s' % (lpath, str(e))) + return results diff --git a/plugins/module_utils/redfish_utils.py b/plugins/module_utils/redfish_utils.py index 3bd3d73676..9b64703027 100644 --- a/plugins/module_utils/redfish_utils.py +++ b/plugins/module_utils/redfish_utils.py @@ -19,6 +19,8 @@ POST_HEADERS = {'content-type': 'application/json', 'accept': 'application/json' 'OData-Version': '4.0'} PATCH_HEADERS = {'content-type': 'application/json', 'accept': 'application/json', 'OData-Version': '4.0'} +PUT_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 '\ @@ -36,6 +38,8 @@ class RedfishUtils(object): self.timeout = timeout self.module = module self.service_root = '/redfish/v1/' + self.session_service_uri = '/redfish/v1/SessionService' + self.sessions_uri = '/redfish/v1/SessionService/Sessions' self.resource_id = resource_id self.data_modification = data_modification self.strip_etag_quotes = strip_etag_quotes @@ -123,6 +127,10 @@ class RedfishUtils(object): req_headers = dict(GET_HEADERS) username, password, basic_auth = self._auth_params(req_headers) try: + # Service root is an unauthenticated resource; remove credentials + # in case the caller will be using sessions later. + if uri == (self.root_uri + self.service_root): + basic_auth = False resp = open_url(uri, method="GET", headers=req_headers, url_username=username, url_password=password, force_basic_auth=basic_auth, validate_certs=False, @@ -143,18 +151,28 @@ class RedfishUtils(object): except Exception as e: return {'ret': False, 'msg': "Failed GET request to '%s': '%s'" % (uri, to_text(e))} - return {'ret': True, 'data': data, 'headers': headers} + return {'ret': True, 'data': data, 'headers': headers, 'resp': resp} def post_request(self, uri, pyld): req_headers = dict(POST_HEADERS) username, password, basic_auth = self._auth_params(req_headers) try: + # When performing a POST to the session collection, credentials are + # provided in the request body. Do not provide the basic auth + # header since this can cause conflicts with some services + if self.sessions_uri is not None and uri == (self.root_uri + self.sessions_uri): + basic_auth = False 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, follow_redirects='all', use_proxy=True, timeout=self.timeout) + try: + data = json.loads(to_native(resp.read())) + except Exception as e: + # No response data; this is okay in many cases + data = None headers = dict((k.lower(), v) for (k, v) in resp.info().items()) except HTTPError as e: msg = self._get_extended_message(e) @@ -169,7 +187,7 @@ 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, 'data': data, 'headers': headers, 'resp': resp} def patch_request(self, uri, pyld, check_pyld=False): req_headers = dict(PATCH_HEADERS) @@ -219,6 +237,41 @@ class RedfishUtils(object): 'msg': "Failed PATCH request to '%s': '%s'" % (uri, to_text(e))} return {'ret': True, 'changed': True, 'resp': resp, 'msg': 'Modified %s' % uri} + def put_request(self, uri, pyld): + req_headers = dict(PUT_HEADERS) + r = self.get_request(uri) + if r['ret']: + # Get etag from etag header or @odata.etag property + etag = r['headers'].get('etag') + if not etag: + etag = r['data'].get('@odata.etag') + if etag: + if self.strip_etag_quotes: + etag = etag.strip('"') + req_headers['If-Match'] = etag + username, password, basic_auth = self._auth_params(req_headers) + try: + resp = open_url(uri, data=json.dumps(pyld), + headers=req_headers, method="PUT", + url_username=username, url_password=password, + force_basic_auth=basic_auth, validate_certs=False, + follow_redirects='all', + use_proxy=True, timeout=self.timeout) + except HTTPError as e: + msg = self._get_extended_message(e) + return {'ret': False, + 'msg': "HTTP Error %s on PUT request to '%s', extended message: '%s'" + % (e.code, uri, msg), + 'status': e.code} + except URLError as e: + return {'ret': False, 'msg': "URL Error on PUT request to '%s': '%s'" + % (uri, e.reason)} + # Almost all errors should be caught above, but just in case + except Exception as e: + return {'ret': False, + 'msg': "Failed PUT request to '%s': '%s'" % (uri, to_text(e))} + 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) @@ -321,23 +374,23 @@ class RedfishUtils(object): return {'ret': True} def _find_sessionservice_resource(self): + # Get the service root response = self.get_request(self.root_uri + self.service_root) if response['ret'] is False: return response data = response['data'] - if 'SessionService' not in data: + + # Check for the session service and session collection. Well-known + # defaults are provided in the constructor, but services that predate + # Redfish 1.6.0 might contain different values. + self.session_service_uri = data.get('SessionService', {}).get('@odata.id') + self.sessions_uri = data.get('Links', {}).get('Sessions', {}).get('@odata.id') + + # If one isn't found, return an error + if self.session_service_uri is None: return {'ret': False, 'msg': "SessionService resource not found"} - else: - session_service = data["SessionService"]["@odata.id"] - self.session_service_uri = session_service - response = self.get_request(self.root_uri + session_service) - if response['ret'] is False: - return response - data = response['data'] - sessions = data['Sessions']['@odata.id'] - if sessions[-1:] == '/': - sessions = sessions[:-1] - self.sessions_uri = sessions + if self.sessions_uri is None: + return {'ret': False, 'msg': "SessionCollection resource not found"} return {'ret': True} def _get_resource_uri_by_id(self, uris, id_prop): @@ -1384,11 +1437,82 @@ class RedfishUtils(object): else: return self._software_inventory(self.software_uri) + def _operation_results(self, response, data, handle=None): + """ + Builds the results for an operation from task, job, or action response. + + :param response: HTTP response object + :param data: HTTP response data + :param handle: The task or job handle that was last used + :return: dict containing operation results + """ + + operation_results = {'status': None, 'messages': [], 'handle': None, 'ret': True, + 'resets_requested': []} + + if response.status == 204: + # No content; successful, but nothing to return + # Use the Redfish "Completed" enum from TaskState for the operation status + operation_results['status'] = 'Completed' + else: + # Parse the response body for details + + # Determine the next handle, if any + operation_results['handle'] = handle + if response.status == 202: + # Task generated; get the task monitor URI + operation_results['handle'] = response.getheader('Location', handle) + + # Pull out the status and messages based on the body format + if data is not None: + response_type = data.get('@odata.type', '') + if response_type.startswith('#Task.') or response_type.startswith('#Job.'): + # Task and Job have similar enough structures to treat the same + operation_results['status'] = data.get('TaskState', data.get('JobState')) + operation_results['messages'] = data.get('Messages', []) + else: + # Error response body, which is a bit of a misnomer since it's used in successful action responses + operation_results['status'] = 'Completed' + if response.status >= 400: + operation_results['status'] = 'Exception' + operation_results['messages'] = data.get('error', {}).get('@Message.ExtendedInfo', []) + else: + # No response body (or malformed); build based on status code + operation_results['status'] = 'Completed' + if response.status == 202: + operation_results['status'] = 'New' + elif response.status >= 400: + operation_results['status'] = 'Exception' + + # Clear out the handle if the operation is complete + if operation_results['status'] in ['Completed', 'Cancelled', 'Exception', 'Killed']: + operation_results['handle'] = None + + # Scan the messages to see if next steps are needed + for message in operation_results['messages']: + message_id = message['MessageId'] + + if message_id.startswith('Update.1.') and message_id.endswith('.OperationTransitionedToJob'): + # Operation rerouted to a job; update the status and handle + operation_results['status'] = 'New' + operation_results['handle'] = message['MessageArgs'][0] + operation_results['resets_requested'] = [] + # No need to process other messages in this case + break + + if message_id.startswith('Base.1.') and message_id.endswith('.ResetRequired'): + # A reset to some device is needed to continue the update + reset = {'uri': message['MessageArgs'][0], 'type': message['MessageArgs'][1]} + operation_results['resets_requested'].append(reset) + + return operation_results + def simple_update(self, update_opts): image_uri = update_opts.get('update_image_uri') protocol = update_opts.get('update_protocol') targets = update_opts.get('update_targets') creds = update_opts.get('update_creds') + apply_time = update_opts.get('update_apply_time') if not image_uri: return {'ret': False, 'msg': @@ -1439,11 +1563,65 @@ class RedfishUtils(object): payload["Username"] = creds.get('username') if creds.get('password'): payload["Password"] = creds.get('password') + if apply_time: + payload["@Redfish.OperationApplyTime"] = apply_time response = self.post_request(self.root_uri + update_uri, payload) if response['ret'] is False: return response return {'ret': True, 'changed': True, - 'msg': "SimpleUpdate requested"} + 'msg': "SimpleUpdate requested", + 'update_status': self._operation_results(response['resp'], response['data'])} + + def get_update_status(self, update_handle): + """ + Gets the status of an update operation. + + :param handle: The task or job handle tracking the update + :return: dict containing the response of the update status + """ + + if not update_handle: + return {'ret': False, 'msg': 'Must provide a handle tracking the update.'} + + # Get the task or job tracking the update + response = self.get_request(self.root_uri + update_handle) + if response['ret'] is False: + return response + + # Inspect the response to build the update status + return self._operation_results(response['resp'], response['data'], update_handle) + + def perform_requested_update_operations(self, update_handle): + """ + Performs requested operations to allow the update to continue. + + :param handle: The task or job handle tracking the update + :return: dict containing the result of the operations + """ + + # Get the current update status + update_status = self.get_update_status(update_handle) + if update_status['ret'] is False: + return update_status + + changed = False + + # Perform any requested updates + for reset in update_status['resets_requested']: + resp = self.post_request(self.root_uri + reset['uri'], {'ResetType': reset['type']}) + if resp['ret'] is False: + # Override the 'changed' indicator since other resets may have + # been successful + resp['changed'] = changed + return resp + changed = True + + msg = 'No operations required for the update' + if changed: + # Will need to consider finetuning this message if the scope of the + # requested operations grow over time + msg = 'One or more components reset to continue the update' + return {'ret': True, 'changed': changed, 'msg': msg} def get_bios_attributes(self, systems_uri): result = {} @@ -2985,3 +3163,89 @@ class RedfishUtils(object): if resp['ret'] and resp['changed']: resp['msg'] = 'Modified session service' return resp + + def verify_bios_attributes(self, bios_attributes): + # This method verifies BIOS attributes against the provided input + server_bios = self.get_multi_bios_attributes() + if server_bios["ret"] is False: + return server_bios + + bios_dict = {} + wrong_param = {} + + # Verify bios_attributes with BIOS settings available in the server + for key, value in bios_attributes.items(): + if key in server_bios["entries"][0][1]: + if server_bios["entries"][0][1][key] != value: + bios_dict.update({key: value}) + else: + wrong_param.update({key: value}) + + if wrong_param: + return { + "ret": False, + "msg": "Wrong parameters are provided: %s" % wrong_param + } + + if bios_dict: + return { + "ret": False, + "msg": "BIOS parameters are not matching: %s" % bios_dict + } + + return { + "ret": True, + "changed": False, + "msg": "BIOS verification completed" + } + + def enable_secure_boot(self): + # This function enable Secure Boot on an OOB controller + + response = self.get_request(self.root_uri + self.systems_uri) + if response["ret"] is False: + return response + + server_details = response["data"] + secure_boot_url = server_details["SecureBoot"]["@odata.id"] + + response = self.get_request(self.root_uri + secure_boot_url) + if response["ret"] is False: + return response + + body = {} + body["SecureBootEnable"] = True + + return self.patch_request(self.root_uri + secure_boot_url, body, check_pyld=True) + + def get_hpe_thermal_config(self): + result = {} + key = "Thermal" + # Go through list + for chassis_uri in self.chassis_uri_list: + response = self.get_request(self.root_uri + chassis_uri) + if response['ret'] is False: + return response + result['ret'] = True + data = response['data'] + oem = data.get['Oem'] + hpe = oem.get['Hpe'] + thermal_config = hpe.get('ThermalConfiguration') + result["current_thermal_config"] = thermal_config + return result + + def get_hpe_fan_percent_min(self): + result = {} + key = "Thermal" + # Go through list + for chassis_uri in self.chassis_uri_list: + response = self.get_request(self.root_uri + chassis_uri) + if response['ret'] is False: + return response + result['ret'] = True + data = response['data'] + oem = data.get['Oem'] + hpe = oem.get['Hpe'] + fan_percent_min_config = hpe.get('FanPercentMinimum') + result["fan_percent_min"] = fan_percent_min_config + return result diff --git a/plugins/module_utils/scaleway.py b/plugins/module_utils/scaleway.py index a44c52aa78..43f2094800 100644 --- a/plugins/module_utils/scaleway.py +++ b/plugins/module_utils/scaleway.py @@ -84,6 +84,10 @@ def parse_pagination_link(header): def filter_sensitive_attributes(container, attributes): + ''' + WARNING: This function is effectively private, **do not use it**! + It will be removed or renamed once changing its name no longer triggers a pylint bug. + ''' for attr in attributes: container[attr] = "SENSITIVE_VALUE" diff --git a/plugins/module_utils/ssh.py b/plugins/module_utils/ssh.py new file mode 100644 index 0000000000..082839e26d --- /dev/null +++ b/plugins/module_utils/ssh.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Björn Andersson +# Copyright (c) 2021, Ansible Project +# Copyright (c) 2021, Abhijeet Kasurde +# Copyright (c) 2022, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +import os + + +def determine_config_file(user, config_file): + if user: + config_file = os.path.join(os.path.expanduser('~%s' % user), '.ssh', 'config') + elif config_file is None: + config_file = '/etc/ssh/ssh_config' + return config_file diff --git a/plugins/module_utils/version.py b/plugins/module_utils/version.py index b671e59628..3699881978 100644 --- a/plugins/module_utils/version.py +++ b/plugins/module_utils/version.py @@ -13,10 +13,10 @@ __metaclass__ = type from ansible.module_utils.six import raise_from try: - from ansible.module_utils.compat.version import LooseVersion + from ansible.module_utils.compat.version import LooseVersion # noqa: F401, pylint: disable=unused-import except ImportError: try: - from distutils.version import LooseVersion + from distutils.version import LooseVersion # noqa: F401, pylint: disable=unused-import except ImportError as exc: msg = 'To use this plugin or module with ansible-core 2.11, you need to use Python < 3.12 with distutils.version present' raise_from(ImportError(msg), exc) diff --git a/plugins/modules/aerospike_migrations.py b/plugins/modules/aerospike_migrations.py index 32ab06a853..1eee5b1a2f 100644 --- a/plugins/modules/aerospike_migrations.py +++ b/plugins/modules/aerospike_migrations.py @@ -19,6 +19,13 @@ description: - If waiting for migrations is not desired, simply just poll until port 3000 if available or asinfo -v status returns ok author: "Albert Autin (@Alb0t)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: host: description: diff --git a/plugins/modules/airbrake_deployment.py b/plugins/modules/airbrake_deployment.py index 2fb5b58737..42ac037e1e 100644 --- a/plugins/modules/airbrake_deployment.py +++ b/plugins/modules/airbrake_deployment.py @@ -18,6 +18,13 @@ author: short_description: Notify airbrake about app deployments description: - Notify airbrake about app deployments (see U(https://airbrake.io/docs/api/#deploys-v4)). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: project_id: description: diff --git a/plugins/modules/aix_devices.py b/plugins/modules/aix_devices.py index be23937baa..ef4ed49612 100644 --- a/plugins/modules/aix_devices.py +++ b/plugins/modules/aix_devices.py @@ -16,6 +16,13 @@ module: aix_devices short_description: Manages AIX devices description: - This module discovers, defines, removes and modifies attributes of AIX devices. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: attributes: description: @@ -101,7 +108,7 @@ EXAMPLES = r''' device: en1 attributes: mtu: 900 - arp: off + arp: 'off' state: available - name: Configure IP, netmask and set en1 up. diff --git a/plugins/modules/aix_filesystem.py b/plugins/modules/aix_filesystem.py index 77d065a59a..b1f363a937 100644 --- a/plugins/modules/aix_filesystem.py +++ b/plugins/modules/aix_filesystem.py @@ -19,6 +19,13 @@ description: - This module creates, removes, mount and unmount LVM and NFS file system for AIX using C(/etc/filesystems). - For LVM file systems is possible to resize a file system. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: account_subsystem: description: diff --git a/plugins/modules/aix_inittab.py b/plugins/modules/aix_inittab.py index 57ef4758cb..c2c9681895 100644 --- a/plugins/modules/aix_inittab.py +++ b/plugins/modules/aix_inittab.py @@ -11,11 +11,18 @@ __metaclass__ = type DOCUMENTATION = r''' --- author: -- Joris Weijters (@molekuul) + - Joris Weijters (@molekuul) module: aix_inittab short_description: Manages the inittab on AIX description: - Manages the inittab on AIX. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/aix_lvg.py b/plugins/modules/aix_lvg.py index 44ad631236..d89c43de4b 100644 --- a/plugins/modules/aix_lvg.py +++ b/plugins/modules/aix_lvg.py @@ -11,11 +11,18 @@ __metaclass__ = type DOCUMENTATION = r''' --- author: -- Kairo Araujo (@kairoaraujo) + - Kairo Araujo (@kairoaraujo) module: aix_lvg short_description: Manage LVM volume groups on AIX description: -- This module creates, removes or resize volume groups on AIX LVM. + - This module creates, removes or resize volume groups on AIX LVM. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: force: description: diff --git a/plugins/modules/aix_lvol.py b/plugins/modules/aix_lvol.py index 6219bdb8e3..0a4a6eff53 100644 --- a/plugins/modules/aix_lvol.py +++ b/plugins/modules/aix_lvol.py @@ -17,6 +17,13 @@ module: aix_lvol short_description: Configure AIX LVM logical volumes description: - This module creates, removes or resizes AIX logical volumes. Inspired by lvol module. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: vg: description: diff --git a/plugins/modules/alerta_customer.py b/plugins/modules/alerta_customer.py index b9bfd4b265..120d989328 100644 --- a/plugins/modules/alerta_customer.py +++ b/plugins/modules/alerta_customer.py @@ -20,6 +20,13 @@ seealso: - name: API documentation description: Documentation for Alerta API link: https://docs.alerta.io/api/reference.html#customers +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: customer: description: diff --git a/plugins/modules/ali_instance.py b/plugins/modules/ali_instance.py index 4acec0a109..232c21ee0f 100644 --- a/plugins/modules/ali_instance.py +++ b/plugins/modules/ali_instance.py @@ -27,10 +27,15 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ali_instance -short_description: Create, Start, Stop, Restart or Terminate an Instance in ECS. Add or Remove Instance to/from a Security Group. +short_description: Create, Start, Stop, Restart or Terminate an Instance in ECS; Add or Remove Instance to/from a Security Group description: - Create, start, stop, restart, modify or terminate ecs instances. - Add or remove ecs instances to/from security group. +attributes: + check_mode: + support: none + diff_mode: + support: none options: state: description: @@ -252,6 +257,7 @@ requirements: - "footmark >= 1.19.0" extends_documentation_fragment: - community.general.alicloud + - community.general.attributes ''' EXAMPLES = ''' diff --git a/plugins/modules/ali_instance_info.py b/plugins/modules/ali_instance_info.py index ea7bcc8d4a..e7ec7f3956 100644 --- a/plugins/modules/ali_instance_info.py +++ b/plugins/modules/ali_instance_info.py @@ -27,12 +27,17 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ali_instance_info -short_description: Gather information on instances of Alibaba Cloud ECS. +short_description: Gather information on instances of Alibaba Cloud ECS description: - This module fetches data from the Open API in Alicloud. The module must be called from within the ECS instance itself. - This module was called C(ali_instance_facts) before Ansible 2.9. The usage did not change. +attributes: + check_mode: + version_added: 3.3.0 + # This was backported to 2.5.4 and 1.3.11 as well, since this was a bugfix + options: name_prefix: description: diff --git a/plugins/modules/alternatives.py b/plugins/modules/alternatives.py index 4566144493..97d4f51fbb 100644 --- a/plugins/modules/alternatives.py +++ b/plugins/modules/alternatives.py @@ -22,6 +22,13 @@ author: - Marius Rieder (@jiuka) - David Wittman (@DavidWittman) - Gabe Mulley (@mulby) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: full options: name: description: @@ -60,6 +67,8 @@ options: description: - A list of subcommands. - Each subcommand needs a name, a link and a path parameter. + - Subcommands are also named 'slaves' or 'followers', depending on the version + of alternatives. type: list elements: dict aliases: ['slaves'] @@ -310,10 +319,10 @@ class AlternativesModule(object): current_mode_regex = re.compile(r'\s-\s(?:status\sis\s)?(\w*)(?:\smode|.)$', re.MULTILINE) current_path_regex = re.compile(r'^\s*link currently points to (.*)$', re.MULTILINE) current_link_regex = re.compile(r'^\s*link \w+ is (.*)$', re.MULTILINE) - subcmd_path_link_regex = re.compile(r'^\s*slave (\S+) is (.*)$', re.MULTILINE) + subcmd_path_link_regex = re.compile(r'^\s*(?:slave|follower) (\S+) is (.*)$', re.MULTILINE) - alternative_regex = re.compile(r'^(\/.*)\s-\s(?:family\s\S+\s)?priority\s(\d+)((?:\s+slave.*)*)', re.MULTILINE) - subcmd_regex = re.compile(r'^\s+slave (.*): (.*)$', re.MULTILINE) + alternative_regex = re.compile(r'^(\/.*)\s-\s(?:family\s\S+\s)?priority\s(\d+)((?:\s+(?:slave|follower).*)*)', re.MULTILINE) + subcmd_regex = re.compile(r'^\s+(?:slave|follower) (.*): (.*)$', re.MULTILINE) match = current_mode_regex.search(display_output) if not match: diff --git a/plugins/modules/ansible_galaxy_install.py b/plugins/modules/ansible_galaxy_install.py index 5e5ec54eb0..0f38eabdf6 100644 --- a/plugins/modules/ansible_galaxy_install.py +++ b/plugins/modules/ansible_galaxy_install.py @@ -20,8 +20,19 @@ notes: - > B(Ansible 2.9/2.10): The C(ansible-galaxy) command changed significantly between Ansible 2.9 and ansible-base 2.10 (later ansible-core 2.11). See comments in the parameters. + - > + The module will try and run using the C(C.UTF-8) locale. + If that fails, it will try C(en_US.UTF-8). + If that one also fails, the module will fail. requirements: - Ansible 2.9, ansible-base 2.10, or ansible-core 2.11 or newer +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: type: description: @@ -185,7 +196,7 @@ RETURN = """ import re from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt -from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper +from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper, ModuleHelperException class AnsibleGalaxyInstall(ModuleHelper): @@ -226,11 +237,17 @@ class AnsibleGalaxyInstall(ModuleHelper): version=fmt.as_bool("--version"), name=fmt.as_list(), ) - force_lang = "en_US.UTF-8" - check_rc = True + + def _make_runner(self, lang): + return CmdRunner(self.module, command=self.command, arg_formats=self.command_args_formats, force_lang=lang, check_rc=True) def _get_ansible_galaxy_version(self): + class UnsupportedLocale(ModuleHelperException): + pass + def process(rc, out, err): + if (rc != 0 and "unsupported locale setting" in err) or (rc == 0 and "cannot change locale" in err): + raise UnsupportedLocale(msg=err) line = out.splitlines()[0] match = self._RE_GALAXY_VERSION.match(line) if not match: @@ -239,12 +256,18 @@ class AnsibleGalaxyInstall(ModuleHelper): version = tuple(int(x) for x in version.split('.')[:3]) return version - with self.runner("version", check_rc=True, output_process=process) as ctx: - return ctx.run(version=True) + try: + runner = self._make_runner("C.UTF-8") + with runner("version", check_rc=False, output_process=process) as ctx: + return runner, ctx.run(version=True) + except UnsupportedLocale as e: + runner = self._make_runner("en_US.UTF-8") + with runner("version", check_rc=True, output_process=process) as ctx: + return runner, ctx.run(version=True) def __init_module__(self): - self.runner = CmdRunner(self.module, command=self.command, arg_formats=self.command_args_formats, force_lang=self.force_lang) - self.ansible_version = self._get_ansible_galaxy_version() + # self.runner = CmdRunner(self.module, command=self.command, arg_formats=self.command_args_formats, force_lang=self.force_lang) + self.runner, self.ansible_version = self._get_ansible_galaxy_version() if self.ansible_version < (2, 11) and not self.vars.ack_min_ansiblecore211: self.module.deprecate( "Support for Ansible 2.9 and ansible-base 2.10 is being deprecated. " @@ -339,11 +362,12 @@ class AnsibleGalaxyInstall(ModuleHelper): self._setup210plus() with self.runner("type galaxy_cmd force no_deps dest requirements_file name", output_process=process) as ctx: ctx.run(galaxy_cmd="install") + if self.verbosity > 2: + self.vars.set("run_info", ctx.run_info) def main(): - galaxy = AnsibleGalaxyInstall() - galaxy.run() + AnsibleGalaxyInstall.execute() if __name__ == '__main__': diff --git a/plugins/modules/apache2_mod_proxy.py b/plugins/modules/apache2_mod_proxy.py index 70ab5a42ed..8f561e8ae0 100644 --- a/plugins/modules/apache2_mod_proxy.py +++ b/plugins/modules/apache2_mod_proxy.py @@ -20,6 +20,13 @@ description: status page has to be enabled and accessible, as this module relies on parsing this page. This module supports ansible check_mode, and requires BeautifulSoup python module. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: balancer_url_suffix: type: str diff --git a/plugins/modules/apache2_module.py b/plugins/modules/apache2_module.py index 65d2f689bf..2e2456d741 100644 --- a/plugins/modules/apache2_module.py +++ b/plugins/modules/apache2_module.py @@ -16,9 +16,16 @@ author: - Christian Berendt (@berendt) - Ralf Hertel (@n0trax) - Robin Roth (@robinro) -short_description: Enables/disables a module of the Apache2 webserver. +short_description: Enables/disables a module of the Apache2 webserver description: - Enables or disables a specified module of the Apache2 webserver. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: type: str @@ -49,6 +56,12 @@ options: - Ignore configuration checks about inconsistent module configuration. Especially for mpm_* modules. type: bool default: false + warn_mpm_absent: + description: + - Control the behavior of the warning process for MPM modules. + type: bool + default: true + version_added: 6.3.0 requirements: ["a2enmod","a2dismod"] notes: - This does not work on RedHat-based distributions. It does work on Debian- and SuSE-based distributions. @@ -78,6 +91,18 @@ EXAMPLES = ''' name: mpm_worker ignore_configcheck: true +- name: Disable mpm_event, enable mpm_prefork and ignore warnings about missing mpm module + community.general.apache2_module: + name: "{{ item.module }}" + state: "{{ item.state }}" + warn_mpm_absent: false + ignore_configcheck: true + loop: + - module: mpm_event + state: absent + - module: mpm_prefork + state: present + - name: Enable dump_io module, which is identified as dumpio_module inside apache2 community.general.apache2_module: state: present @@ -140,10 +165,11 @@ def _module_is_enabled(module): error_msg = "Error executing %s: %s" % (control_binary, stderr) if module.params['ignore_configcheck']: if 'AH00534' in stderr and 'mpm_' in module.params['name']: - module.warnings.append( - "No MPM module loaded! apache2 reload AND other module actions" - " will fail if no MPM module is loaded immediately." - ) + if module.params['warn_mpm_absent']: + module.warnings.append( + "No MPM module loaded! apache2 reload AND other module actions" + " will fail if no MPM module is loaded immediately." + ) else: module.warnings.append(error_msg) return False @@ -249,6 +275,7 @@ def main(): force=dict(type='bool', default=False), state=dict(default='present', choices=['absent', 'present']), ignore_configcheck=dict(type='bool', default=False), + warn_mpm_absent=dict(type='bool', default=True), ), supports_check_mode=True, ) diff --git a/plugins/modules/apk.py b/plugins/modules/apk.py index 831ab60749..e56b2165dd 100644 --- a/plugins/modules/apk.py +++ b/plugins/modules/apk.py @@ -19,6 +19,13 @@ short_description: Manages apk packages description: - Manages I(apk) packages for Alpine Linux. author: "Kevin Brebanov (@kbrebanov)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: available: description: diff --git a/plugins/modules/apt_repo.py b/plugins/modules/apt_repo.py index 2e9c9b109c..5560390271 100644 --- a/plugins/modules/apt_repo.py +++ b/plugins/modules/apt_repo.py @@ -19,6 +19,13 @@ description: notes: - This module works on ALT based distros. - Does NOT support checkmode, due to a limitation in apt-repo tool. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: repo: description: diff --git a/plugins/modules/apt_rpm.py b/plugins/modules/apt_rpm.py index 2b6bf3e8a2..8749086bbf 100644 --- a/plugins/modules/apt_rpm.py +++ b/plugins/modules/apt_rpm.py @@ -14,14 +14,20 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: apt_rpm -short_description: apt_rpm package manager +short_description: APT-RPM package manager description: - Manages packages with I(apt-rpm). Both low-level (I(rpm)) and high-level (I(apt-get)) package manager binaries required. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: package: description: - - list of packages to install, upgrade or remove. - required: true + - List of packages to install, upgrade, or remove. aliases: [ name, pkg ] type: list elements: str @@ -33,9 +39,30 @@ options: type: str update_cache: description: - - update the package database first C(apt-get update). + - Run the equivalent of C(apt-get update) before the operation. Can be run as part of the package installation or as a separate step. + - Default is not to update the cache. type: bool default: false + clean: + description: + - Run the equivalent of C(apt-get clean) to clear out the local repository of retrieved package files. It removes everything but + the lock file from C(/var/cache/apt/archives/) and C(/var/cache/apt/archives/partial/). + - Can be run as part of the package installation (clean runs before install) or as a separate step. + type: bool + default: false + version_added: 6.5.0 + dist_upgrade: + description: + - If true performs an C(apt-get dist-upgrade) to upgrade system. + type: bool + default: false + version_added: 6.5.0 + update_kernel: + description: + - If true performs an C(update-kernel) to upgrade kernel packages. + type: bool + default: false + version_added: 6.5.0 author: - Evgenii Terechkov (@evgkrsk) ''' @@ -69,6 +96,16 @@ EXAMPLES = ''' name: bar state: present update_cache: true + +- name: Run the equivalent of "apt-get clean" as a separate step + community.general.apt_rpm: + clean: true + +- name: Perform cache update and complete system upgrade (includes kernel) + community.general.apt_rpm: + update_cache: true + dist_upgrade: true + update_kernel: true ''' import os @@ -77,6 +114,8 @@ from ansible.module_utils.basic import AnsibleModule APT_PATH = "/usr/bin/apt-get" RPM_PATH = "/usr/bin/rpm" +APT_GET_ZERO = "\n0 upgraded, 0 newly installed" +UPDATE_KERNEL_ZERO = "\nTry to install new kernel " def query_package(module, name): @@ -97,14 +136,39 @@ def query_package_provides(module, name): def update_package_db(module): - rc, out, err = module.run_command("%s update" % APT_PATH) + rc, update_out, err = module.run_command([APT_PATH, "update"], check_rc=True, environ_update={"LANG": "C"}) + return (False, update_out) - if rc != 0: - module.fail_json(msg="could not update package db: %s" % err) + +def dir_size(module, path): + total_size = 0 + for path, dirs, files in os.walk(path): + for f in files: + total_size += os.path.getsize(os.path.join(path, f)) + return total_size + + +def clean(module): + t = dir_size(module, "/var/cache/apt/archives") + rc, out, err = module.run_command([APT_PATH, "clean"], check_rc=True) + return (t != dir_size(module, "/var/cache/apt/archives"), out) + + +def dist_upgrade(module): + rc, out, err = module.run_command([APT_PATH, "-y", "dist-upgrade"], check_rc=True, environ_update={"LANG": "C"}) + return (APT_GET_ZERO not in out, out) + + +def update_kernel(module): + rc, out, err = module.run_command(["/usr/sbin/update-kernel", "-y"], check_rc=True, environ_update={"LANG": "C"}) + return (UPDATE_KERNEL_ZERO not in out, out) def remove_packages(module, packages): + if packages is None: + return (False, "Empty package list") + remove_c = 0 # Using a for loop in case of error, we can report the package that failed for package in packages: @@ -112,7 +176,7 @@ def remove_packages(module, packages): if not query_package(module, package): continue - rc, out, err = module.run_command("%s -y remove %s" % (APT_PATH, package)) + rc, out, err = module.run_command("%s -y remove %s" % (APT_PATH, package), environ_update={"LANG": "C"}) if rc != 0: module.fail_json(msg="failed to remove %s: %s" % (package, err)) @@ -120,13 +184,16 @@ def remove_packages(module, packages): remove_c += 1 if remove_c > 0: - module.exit_json(changed=True, msg="removed %s package(s)" % remove_c) + return (True, "removed %s package(s)" % remove_c) - module.exit_json(changed=False, msg="package(s) already absent") + return (False, "package(s) already absent") def install_packages(module, pkgspec): + if pkgspec is None: + return (False, "Empty package list") + packages = "" for package in pkgspec: if not query_package_provides(module, package): @@ -134,7 +201,7 @@ def install_packages(module, pkgspec): if len(packages) != 0: - rc, out, err = module.run_command("%s -y install %s" % (APT_PATH, packages)) + rc, out, err = module.run_command("%s -y install %s" % (APT_PATH, packages), environ_update={"LANG": "C"}) installed = True for packages in pkgspec: @@ -145,9 +212,9 @@ def install_packages(module, pkgspec): if rc or not installed: module.fail_json(msg="'apt-get -y install %s' failed: %s" % (packages, err)) else: - module.exit_json(changed=True, msg="%s present(s)" % packages) + return (True, "%s present(s)" % packages) else: - module.exit_json(changed=False) + return (False, "Nothing to install") def main(): @@ -155,7 +222,10 @@ def main(): argument_spec=dict( state=dict(type='str', default='present', choices=['absent', 'installed', 'present', 'removed']), update_cache=dict(type='bool', default=False), - package=dict(type='list', elements='str', required=True, aliases=['name', 'pkg']), + clean=dict(type='bool', default=False), + dist_upgrade=dict(type='bool', default=False), + update_kernel=dict(type='bool', default=False), + package=dict(type='list', elements='str', aliases=['name', 'pkg']), ), ) @@ -163,17 +233,39 @@ def main(): module.fail_json(msg="cannot find /usr/bin/apt-get and/or /usr/bin/rpm") p = module.params + modified = False + output = "" if p['update_cache']: update_package_db(module) + if p['clean']: + (m, out) = clean(module) + modified = modified or m + + if p['dist_upgrade']: + (m, out) = dist_upgrade(module) + modified = modified or m + output += out + + if p['update_kernel']: + (m, out) = update_kernel(module) + modified = modified or m + output += out + packages = p['package'] - if p['state'] in ['installed', 'present']: - install_packages(module, packages) + (m, out) = install_packages(module, packages) + modified = modified or m + output += out - elif p['state'] in ['absent', 'removed']: - remove_packages(module, packages) + if p['state'] in ['absent', 'removed']: + (m, out) = remove_packages(module, packages) + modified = modified or m + output += out + + # Return total modification status and output of all commands + module.exit_json(changed=modified, msg=output) if __name__ == '__main__': diff --git a/plugins/modules/archive.py b/plugins/modules/archive.py index 83eae34f56..bcead7b73c 100644 --- a/plugins/modules/archive.py +++ b/plugins/modules/archive.py @@ -14,11 +14,18 @@ DOCUMENTATION = r''' --- module: archive short_description: Creates a compressed archive of one or more files or trees -extends_documentation_fragment: files +extends_documentation_fragment: + - files + - community.general.attributes description: - Creates or extends an archive. - The source and archive are on the remote host, and the archive I(is not) copied to the local host. - Source files can be deleted after archival by specifying I(remove=True). +attributes: + check_mode: + support: full + diff_mode: + support: none options: path: description: @@ -191,6 +198,10 @@ from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.common.text.converters import to_bytes, to_native from ansible.module_utils import six +try: # python 3.2+ + from zipfile import BadZipFile # type: ignore[attr-defined] +except ImportError: # older python + from zipfile import BadZipfile as BadZipFile LZMA_IMP_ERR = None if six.PY3: @@ -527,7 +538,7 @@ class ZipArchive(Archive): archive = zipfile.ZipFile(_to_native_ascii(path), 'r') checksums = set((info.filename, info.CRC) for info in archive.infolist()) archive.close() - except zipfile.BadZipfile: + except BadZipFile: checksums = set() return checksums diff --git a/plugins/modules/atomic_container.py b/plugins/modules/atomic_container.py index c32e617a22..c265102960 100644 --- a/plugins/modules/atomic_container.py +++ b/plugins/modules/atomic_container.py @@ -22,6 +22,13 @@ notes: requirements: - atomic - "python >= 2.6" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: backend: description: diff --git a/plugins/modules/atomic_host.py b/plugins/modules/atomic_host.py index 5aa389e174..bb44c44896 100644 --- a/plugins/modules/atomic_host.py +++ b/plugins/modules/atomic_host.py @@ -22,6 +22,13 @@ notes: requirements: - atomic - python >= 2.6 +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: revision: description: diff --git a/plugins/modules/atomic_image.py b/plugins/modules/atomic_image.py index 2705304f01..65aec1e9d5 100644 --- a/plugins/modules/atomic_image.py +++ b/plugins/modules/atomic_image.py @@ -22,6 +22,13 @@ notes: requirements: - atomic - python >= 2.6 +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: backend: description: diff --git a/plugins/modules/awall.py b/plugins/modules/awall.py index dc13d46789..856cf782d4 100644 --- a/plugins/modules/awall.py +++ b/plugins/modules/awall.py @@ -18,6 +18,13 @@ description: - This modules allows for enable/disable/activate of I(awall) policies. - Alpine Wall (I(awall)) generates a firewall configuration from the enabled policy files and activates the configuration on the system. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/beadm.py b/plugins/modules/beadm.py index 8cb43c6cbb..8857fd8464 100644 --- a/plugins/modules/beadm.py +++ b/plugins/modules/beadm.py @@ -12,11 +12,18 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: beadm -short_description: Manage ZFS boot environments on FreeBSD/Solaris/illumos systems. +short_description: Manage ZFS boot environments on FreeBSD/Solaris/illumos systems description: - Create, delete or activate ZFS boot environments. - Mount and unmount ZFS boot environments. author: Adam Števko (@xen0l) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/bearychat.py b/plugins/modules/bearychat.py index 48d5c994fc..28f1f8fcd9 100644 --- a/plugins/modules/bearychat.py +++ b/plugins/modules/bearychat.py @@ -14,6 +14,13 @@ description: - The M(community.general.bearychat) module sends notifications to U(https://bearychat.com) via the Incoming Robot integration. author: "Jiangge Zhang (@tonyseek)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: url: type: str diff --git a/plugins/modules/bigpanda.py b/plugins/modules/bigpanda.py index 4e653aadb7..bab200bc44 100644 --- a/plugins/modules/bigpanda.py +++ b/plugins/modules/bigpanda.py @@ -15,6 +15,13 @@ author: "Hagai Kariti (@hkariti)" short_description: Notify BigPanda about deployments description: - Notify BigPanda when deployments start and end (successfully or not). Returns a deployment object containing all the parameters for future module calls. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: component: type: str diff --git a/plugins/modules/bitbucket_access_key.py b/plugins/modules/bitbucket_access_key.py index 0708777a0a..5ef199f7a4 100644 --- a/plugins/modules/bitbucket_access_key.py +++ b/plugins/modules/bitbucket_access_key.py @@ -18,6 +18,12 @@ author: - Evgeniy Krysanov (@catcombo) extends_documentation_fragment: - community.general.bitbucket + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: repository: description: diff --git a/plugins/modules/bitbucket_pipeline_key_pair.py b/plugins/modules/bitbucket_pipeline_key_pair.py index db4453d45c..d39c054b11 100644 --- a/plugins/modules/bitbucket_pipeline_key_pair.py +++ b/plugins/modules/bitbucket_pipeline_key_pair.py @@ -18,6 +18,12 @@ author: - Evgeniy Krysanov (@catcombo) extends_documentation_fragment: - community.general.bitbucket + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: repository: description: diff --git a/plugins/modules/bitbucket_pipeline_known_host.py b/plugins/modules/bitbucket_pipeline_known_host.py index e573719362..28ff487398 100644 --- a/plugins/modules/bitbucket_pipeline_known_host.py +++ b/plugins/modules/bitbucket_pipeline_known_host.py @@ -19,8 +19,14 @@ author: - Evgeniy Krysanov (@catcombo) extends_documentation_fragment: - community.general.bitbucket + - community.general.attributes requirements: - - paramiko + - paramiko +attributes: + check_mode: + support: full + diff_mode: + support: none options: repository: description: diff --git a/plugins/modules/bitbucket_pipeline_variable.py b/plugins/modules/bitbucket_pipeline_variable.py index 45661d8dee..eac0d18dda 100644 --- a/plugins/modules/bitbucket_pipeline_variable.py +++ b/plugins/modules/bitbucket_pipeline_variable.py @@ -18,6 +18,12 @@ author: - Evgeniy Krysanov (@catcombo) extends_documentation_fragment: - community.general.bitbucket + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: repository: description: diff --git a/plugins/modules/bower.py b/plugins/modules/bower.py index 8964bd0434..1824e68bb8 100644 --- a/plugins/modules/bower.py +++ b/plugins/modules/bower.py @@ -16,6 +16,13 @@ short_description: Manage bower packages with bower description: - Manage bower packages with bower author: "Michael Warkentin (@mwarkentin)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: name: type: str diff --git a/plugins/modules/bundler.py b/plugins/modules/bundler.py index 5811fa720c..682dd334ac 100644 --- a/plugins/modules/bundler.py +++ b/plugins/modules/bundler.py @@ -15,6 +15,13 @@ module: bundler short_description: Manage Ruby Gem dependencies with Bundler description: - Manage installation and Gem version dependencies for Ruby using the Bundler gem +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: executable: type: str diff --git a/plugins/modules/bzr.py b/plugins/modules/bzr.py index 7832183806..e7aca7c6bb 100644 --- a/plugins/modules/bzr.py +++ b/plugins/modules/bzr.py @@ -17,6 +17,13 @@ author: short_description: Deploy software (or files) from bzr branches description: - Manage I(bzr) branches to deploy files or software. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/campfire.py b/plugins/modules/campfire.py index dfc9af1ce1..1e0f1ecea4 100644 --- a/plugins/modules/campfire.py +++ b/plugins/modules/campfire.py @@ -16,6 +16,13 @@ short_description: Send a message to Campfire description: - Send a message to Campfire. - Messages with newlines will result in a "Paste" message being sent. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: subscription: type: str diff --git a/plugins/modules/capabilities.py b/plugins/modules/capabilities.py index 9309958eca..9b72ac6ea1 100644 --- a/plugins/modules/capabilities.py +++ b/plugins/modules/capabilities.py @@ -14,6 +14,13 @@ module: capabilities short_description: Manage Linux capabilities description: - This module manipulates files privileges using the Linux capabilities(7) system. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: path: description: diff --git a/plugins/modules/cargo.py b/plugins/modules/cargo.py index 787edbd256..24be43741b 100644 --- a/plugins/modules/cargo.py +++ b/plugins/modules/cargo.py @@ -17,6 +17,13 @@ version_added: 4.3.0 description: - Manage Rust packages with cargo. author: "Radek Sprta (@radek-sprta)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/catapult.py b/plugins/modules/catapult.py index 8b8c72974d..a3bbef6c42 100644 --- a/plugins/modules/catapult.py +++ b/plugins/modules/catapult.py @@ -16,7 +16,14 @@ DOCUMENTATION = ''' module: catapult short_description: Send a sms / mms using the catapult bandwidth api description: - - Allows notifications to be sent using sms / mms via the catapult bandwidth api. + - Allows notifications to be sent using sms / mms via the catapult bandwidth api. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: src: type: str diff --git a/plugins/modules/circonus_annotation.py b/plugins/modules/circonus_annotation.py index 6248fd2f55..9376107761 100644 --- a/plugins/modules/circonus_annotation.py +++ b/plugins/modules/circonus_annotation.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: circonus_annotation -short_description: create an annotation in circonus +short_description: Create an annotation in circonus description: - Create an annotation event with a given category, title and description. Optionally start, end or durations can be provided author: "Nick Harring (@NickatEpic)" @@ -20,6 +20,13 @@ requirements: - requests (either >= 2.0.0 for Python 3, or >= 1.0.0 for Python 2) notes: - Check mode isn't supported. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: api_key: type: str diff --git a/plugins/modules/cisco_webex.py b/plugins/modules/cisco_webex.py index 95fcccb7d6..2e5cb50ea0 100644 --- a/plugins/modules/cisco_webex.py +++ b/plugins/modules/cisco_webex.py @@ -20,6 +20,14 @@ notes: - The C(recipient_id) type must be valid for the supplied C(recipient_id). - Full API documentation can be found at U(https://developer.webex.com/docs/api/basics). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none + options: recipient_type: diff --git a/plugins/modules/clc_aa_policy.py b/plugins/modules/clc_aa_policy.py index d5d56b2a65..05135bd957 100644 --- a/plugins/modules/clc_aa_policy.py +++ b/plugins/modules/clc_aa_policy.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_aa_policy -short_description: Create or Delete Anti Affinity Policies at CenturyLink Cloud. +short_description: Create or Delete Anti Affinity Policies at CenturyLink Cloud description: - An Ansible module to Create or Delete Anti Affinity Policies at CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/clc_alert_policy.py b/plugins/modules/clc_alert_policy.py index c7f02c2ffa..b77c83e3b7 100644 --- a/plugins/modules/clc_alert_policy.py +++ b/plugins/modules/clc_alert_policy.py @@ -12,9 +12,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_alert_policy -short_description: Create or Delete Alert Policies at CenturyLink Cloud. +short_description: Create or Delete Alert Policies at CenturyLink Cloud description: - An Ansible module to Create or Delete Alert Policies at CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: alias: description: diff --git a/plugins/modules/clc_blueprint_package.py b/plugins/modules/clc_blueprint_package.py index 0dc29b0ce0..672e06780f 100644 --- a/plugins/modules/clc_blueprint_package.py +++ b/plugins/modules/clc_blueprint_package.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_blueprint_package -short_description: deploys a blue print package on a set of servers in CenturyLink Cloud. +short_description: Deploys a blue print package on a set of servers in CenturyLink Cloud description: - An Ansible module to deploy blue print package on a set of servers in CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: server_ids: description: diff --git a/plugins/modules/clc_firewall_policy.py b/plugins/modules/clc_firewall_policy.py index cc77238db9..c832571d33 100644 --- a/plugins/modules/clc_firewall_policy.py +++ b/plugins/modules/clc_firewall_policy.py @@ -14,6 +14,13 @@ module: clc_firewall_policy short_description: Create/delete/update firewall policies description: - Create or delete or update firewall policies on Centurylink Cloud +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: location: description: diff --git a/plugins/modules/clc_group.py b/plugins/modules/clc_group.py index 21e6d93d28..88aef2d63d 100644 --- a/plugins/modules/clc_group.py +++ b/plugins/modules/clc_group.py @@ -15,6 +15,13 @@ module: clc_group short_description: Create/delete Server Groups at Centurylink Cloud description: - Create or delete Server Groups at Centurylink Centurylink Cloud +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/clc_loadbalancer.py b/plugins/modules/clc_loadbalancer.py index d13c2d76ce..675cc1100e 100644 --- a/plugins/modules/clc_loadbalancer.py +++ b/plugins/modules/clc_loadbalancer.py @@ -12,9 +12,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_loadbalancer -short_description: Create, Delete shared loadbalancers in CenturyLink Cloud. +short_description: Create, Delete shared loadbalancers in CenturyLink Cloud description: - An Ansible module to Create, Delete shared loadbalancers in CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/clc_modify_server.py b/plugins/modules/clc_modify_server.py index ff0611e3f8..b375d9d47a 100644 --- a/plugins/modules/clc_modify_server.py +++ b/plugins/modules/clc_modify_server.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_modify_server -short_description: modify servers in CenturyLink Cloud. +short_description: Modify servers in CenturyLink Cloud description: - An Ansible module to modify servers in CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: server_ids: description: diff --git a/plugins/modules/clc_publicip.py b/plugins/modules/clc_publicip.py index 98d392adf9..c1bffcea04 100644 --- a/plugins/modules/clc_publicip.py +++ b/plugins/modules/clc_publicip.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_publicip -short_description: Add and Delete public ips on servers in CenturyLink Cloud. +short_description: Add and Delete public ips on servers in CenturyLink Cloud description: - An Ansible module to add or delete public ip addresses on an existing server or servers in CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: protocol: description: diff --git a/plugins/modules/clc_server.py b/plugins/modules/clc_server.py index 062c5ea411..d2d019ff0d 100644 --- a/plugins/modules/clc_server.py +++ b/plugins/modules/clc_server.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_server -short_description: Create, Delete, Start and Stop servers in CenturyLink Cloud. +short_description: Create, Delete, Start and Stop servers in CenturyLink Cloud description: - An Ansible module to Create, Delete, Start and Stop servers in CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: additional_disks: description: diff --git a/plugins/modules/clc_server_snapshot.py b/plugins/modules/clc_server_snapshot.py index 44f52ece64..82b2a99568 100644 --- a/plugins/modules/clc_server_snapshot.py +++ b/plugins/modules/clc_server_snapshot.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: clc_server_snapshot -short_description: Create, Delete and Restore server snapshots in CenturyLink Cloud. +short_description: Create, Delete and Restore server snapshots in CenturyLink Cloud description: - An Ansible module to Create, Delete and Restore server snapshots in CenturyLink Cloud. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: server_ids: description: diff --git a/plugins/modules/cloud_init_data_facts.py b/plugins/modules/cloud_init_data_facts.py index df2f77148e..d8209cc61a 100644 --- a/plugins/modules/cloud_init_data_facts.py +++ b/plugins/modules/cloud_init_data_facts.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: cloud_init_data_facts -short_description: Retrieve facts of cloud-init. +short_description: Retrieve facts of cloud-init description: - Gathers facts by reading the status.json and result.json of cloud-init. author: René Moser (@resmo) diff --git a/plugins/modules/cloudflare_dns.py b/plugins/modules/cloudflare_dns.py index 92132c0f6f..8f45fcef3b 100644 --- a/plugins/modules/cloudflare_dns.py +++ b/plugins/modules/cloudflare_dns.py @@ -18,6 +18,13 @@ requirements: short_description: Manage Cloudflare DNS records description: - "Manages dns records via the Cloudflare API, see the docs: U(https://api.cloudflare.com/)." +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: api_token: description: @@ -687,10 +694,11 @@ class CloudflareAPI(object): "port": params['port'], "weight": params['weight'], "priority": params['priority'], - "name": params['record'][:-len('.' + params['zone'])], + "name": params['record'], "proto": params['proto'], "service": params['service'] } + new_record = {"type": params['type'], "ttl": params['ttl'], 'data': srv_data} search_value = str(params['weight']) + '\t' + str(params['port']) + '\t' + params['value'] search_record = params['service'] + '.' + params['proto'] + '.' + params['record'] diff --git a/plugins/modules/cobbler_sync.py b/plugins/modules/cobbler_sync.py index 5e7082ddf5..d7acf4be6a 100644 --- a/plugins/modules/cobbler_sync.py +++ b/plugins/modules/cobbler_sync.py @@ -13,7 +13,14 @@ DOCUMENTATION = r''' module: cobbler_sync short_description: Sync Cobbler description: -- Sync Cobbler to commit changes. + - Sync Cobbler to commit changes. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: host: description: diff --git a/plugins/modules/cobbler_system.py b/plugins/modules/cobbler_system.py index 973478b627..c30b4f1c12 100644 --- a/plugins/modules/cobbler_system.py +++ b/plugins/modules/cobbler_system.py @@ -13,7 +13,14 @@ DOCUMENTATION = r''' module: cobbler_system short_description: Manage system objects in Cobbler description: -- Add, modify or remove systems in Cobbler + - Add, modify or remove systems in Cobbler +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: full options: host: description: diff --git a/plugins/modules/composer.py b/plugins/modules/composer.py index 34a15edda5..793abcda18 100644 --- a/plugins/modules/composer.py +++ b/plugins/modules/composer.py @@ -21,6 +21,13 @@ description: Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: command: type: str diff --git a/plugins/modules/consul.py b/plugins/modules/consul.py index 0d75bde2eb..cc599be36d 100644 --- a/plugins/modules/consul.py +++ b/plugins/modules/consul.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' module: consul -short_description: "Add, modify & delete services within a consul cluster." +short_description: Add, modify & delete services within a consul cluster description: - Registers services and checks for an agent with a consul cluster. A service is some process running on the agent node that should be advertised by @@ -23,7 +23,7 @@ description: by Consul from the Service name and id respectively by appending 'service:' Node level checks require a I(check_name) and optionally a I(check_id)." - Currently, there is no complete way to retrieve the script, interval or ttl - metadata for a registered check. Without this metadata it is not possible to + metadata for a registered check. Without this metadata it is not possible to tell if the data supplied with ansible represents a change to a check. As a result this does not attempt to determine changes and will always report a changed occurred. An API method is planned to supply this metadata so at that @@ -33,11 +33,18 @@ requirements: - python-consul - requests author: "Steve Gargan (@sgargan)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: state: type: str description: - - register or deregister the consul service, defaults to present + - Register or deregister the consul service, defaults to present. default: present choices: ['present', 'absent'] service_name: @@ -45,30 +52,30 @@ options: description: - Unique name for the service on a node, must be unique per node, required if registering a service. May be omitted if registering - a node level check + a node level check. service_id: type: str description: - - the ID for the service, must be unique per node. If I(state=absent), + - The ID for the service, must be unique per node. If I(state=absent), defaults to the service name if supplied. host: type: str description: - - host of the consul agent defaults to localhost + - Host of the consul agent defaults to localhost. default: localhost port: type: int description: - - the port on which the consul agent is running + - The port on which the consul agent is running. default: 8500 scheme: type: str description: - - the protocol scheme on which the consul agent is running + - The protocol scheme on which the consul agent is running. default: http validate_certs: description: - - whether to verify the TLS certificate of the consul agent + - Whether to verify the TLS certificate of the consul agent. type: bool default: true notes: @@ -78,12 +85,12 @@ options: service_port: type: int description: - - the port on which the service is listening. Can optionally be supplied for - registration of a service, i.e. if I(service_name) or I(service_id) is set + - The port on which the service is listening. Can optionally be supplied for + registration of a service, i.e. if I(service_name) or I(service_id) is set. service_address: type: str description: - - the address to advertise that the service will be listening on. + - The address to advertise that the service will be listening on. This value will be passed as the I(address) parameter to Consul's C(/v1/agent/service/register) API method, so refer to the Consul API documentation for further details. @@ -91,63 +98,68 @@ options: type: list elements: str description: - - tags that will be attached to the service registration. + - Tags that will be attached to the service registration. script: type: str description: - - the script/command that will be run periodically to check the health - of the service. Scripts require I(interval) and vice versa. + - The script/command that will be run periodically to check the health of the service. + - Requires I(interval) to be provided. interval: type: str description: - - the interval at which the service check will be run. This is a number - with a s or m suffix to signify the units of seconds or minutes e.g - C(15s) or C(1m). If no suffix is supplied, m will be used by default e.g. - C(1) will be C(1m). Required if the I(script) parameter is specified. + - The interval at which the service check will be run. + This is a number with a C(s) or C(m) suffix to signify the units of seconds or minutes e.g C(15s) or C(1m). + If no suffix is supplied C(s) will be used by default, e.g. C(10) will be C(10s). + - Required if one of the parameters I(script), I(http), or I(tcp) is specified. check_id: type: str description: - - an ID for the service check. If I(state=absent), defaults to + - An ID for the service check. If I(state=absent), defaults to I(check_name). Ignored if part of a service definition. check_name: type: str description: - - a name for the service check. Required if standalone, ignored if + - Name for the service check. Required if standalone, ignored if part of service definition. ttl: type: str description: - - checks can be registered with a ttl instead of a I(script) and I(interval) + - Checks can be registered with a ttl instead of a I(script) and I(interval) this means that the service will check in with the agent before the ttl expires. If it doesn't the check will be considered failed. Required if registering a check and the script an interval are missing - Similar to the interval this is a number with a s or m suffix to - signify the units of seconds or minutes e.g C(15s) or C(1m). If no suffix - is supplied, C(m) will be used by default e.g. C(1) will be C(1m) + Similar to the interval this is a number with a C(s) or C(m) suffix to + signify the units of seconds or minutes e.g C(15s) or C(1m). + If no suffix is supplied C(s) will be used by default, e.g. C(10) will be C(10s). tcp: type: str description: - Checks can be registered with a TCP port. This means that consul will check if the connection attempt to that port is successful (that is, the port is currently accepting connections). The format is C(host:port), for example C(localhost:80). - I(interval) must also be provided with this option. + - Requires I(interval) to be provided. version_added: '1.3.0' http: type: str description: - - checks can be registered with an HTTP endpoint. This means that consul + - Checks can be registered with an HTTP endpoint. This means that consul will check that the http endpoint returns a successful HTTP status. - I(interval) must also be provided with this option. + - Requires I(interval) to be provided. timeout: type: str description: - A custom HTTP check timeout. The consul default is 10 seconds. Similar to the interval this is a number with a C(s) or C(m) suffix to signify the units of seconds or minutes, e.g. C(15s) or C(1m). + If no suffix is supplied C(s) will be used by default, e.g. C(10) will be C(10s). token: type: str description: - - the token key identifying an ACL rule set. May be required to register services. + - The token key identifying an ACL rule set. May be required to register services. + ack_params_state_absent: + type: bool + description: + - Disable deprecation warning when using parameters incompatible with I(state=absent). ''' EXAMPLES = ''' @@ -583,7 +595,8 @@ def main(): http=dict(type='str'), timeout=dict(type='str'), tags=dict(type='list', elements='str'), - token=dict(no_log=True) + token=dict(no_log=True), + ack_params_state_absent=dict(type='bool'), ), required_if=[ ('state', 'present', ['service_name']), @@ -591,14 +604,29 @@ def main(): ], supports_check_mode=False, ) + p = module.params test_dependencies(module) + if p['state'] == 'absent' and any(p[x] for x in ['script', 'ttl', 'tcp', 'http', 'interval']) and not p['ack_params_state_absent']: + module.deprecate( + "The use of parameters 'script', 'ttl', 'tcp', 'http', 'interval' along with 'state=absent' is deprecated. " + "In community.general 8.0.0 their use will become an error. " + "To suppress this deprecation notice, set parameter ack_params_state_absent=true.", + version="8.0.0", + collection_name="community.general", + ) + # When reaching c.g 8.0.0: + # - Replace the deprecation with a fail_json(), remove the "ack_params_state_absent" condition from the "if" + # - Add mutually_exclusive for ('script', 'ttl', 'tcp', 'http'), then remove that validation from parse_check() + # - Add required_by {'script': 'interval', 'http': 'interval', 'tcp': 'interval'}, then remove checks for 'interval' in ConsulCheck.__init__() + # - Deprecate the parameter ack_params_state_absent try: register_with_consul(module) + except SystemExit: + raise except ConnectionError as e: - module.fail_json(msg='Could not connect to consul agent at %s:%s, error was %s' % ( - module.params['host'], module.params['port'], str(e))) + module.fail_json(msg='Could not connect to consul agent at %s:%s, error was %s' % (p['host'], p['port'], str(e))) except Exception as e: module.fail_json(msg=str(e)) diff --git a/plugins/modules/consul_acl.py b/plugins/modules/consul_acl.py index b9f14db164..91f955228a 100644 --- a/plugins/modules/consul_acl.py +++ b/plugins/modules/consul_acl.py @@ -19,6 +19,13 @@ description: author: - Steve Gargan (@sgargan) - Colin Nolan (@colin-nolan) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: mgmt_token: description: diff --git a/plugins/modules/consul_kv.py b/plugins/modules/consul_kv.py index 3419e3322b..a4457f2445 100644 --- a/plugins/modules/consul_kv.py +++ b/plugins/modules/consul_kv.py @@ -26,6 +26,13 @@ requirements: author: - Steve Gargan (@sgargan) - Colin Nolan (@colin-nolan) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: diff --git a/plugins/modules/consul_session.py b/plugins/modules/consul_session.py index 062eb3befe..246d13846f 100644 --- a/plugins/modules/consul_session.py +++ b/plugins/modules/consul_session.py @@ -20,7 +20,14 @@ requirements: - python-consul - requests author: -- Steve Gargan (@sgargan) + - Steve Gargan (@sgargan) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: id: description: diff --git a/plugins/modules/copr.py b/plugins/modules/copr.py index 68738a6c98..965c2a935d 100644 --- a/plugins/modules/copr.py +++ b/plugins/modules/copr.py @@ -20,6 +20,13 @@ requirements: - dnf-plugins-core notes: - Supports C(check_mode). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: host: description: The Copr host to work with. diff --git a/plugins/modules/cpanm.py b/plugins/modules/cpanm.py index 7ac8429bda..b3d63e1fb1 100644 --- a/plugins/modules/cpanm.py +++ b/plugins/modules/cpanm.py @@ -13,9 +13,16 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: cpanm -short_description: Manages Perl library dependencies. +short_description: Manages Perl library dependencies description: - Manage Perl library dependencies using cpanminus. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: name: type: str diff --git a/plugins/modules/cronvar.py b/plugins/modules/cronvar.py index 9299880537..7effed2ae1 100644 --- a/plugins/modules/cronvar.py +++ b/plugins/modules/cronvar.py @@ -24,6 +24,13 @@ short_description: Manage variables in crontabs description: - Use this module to manage crontab variables. - This module allows you to create, update, or delete cron variable definitions. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/crypttab.py b/plugins/modules/crypttab.py index a334e8ab3f..6aea362e74 100644 --- a/plugins/modules/crypttab.py +++ b/plugins/modules/crypttab.py @@ -14,6 +14,13 @@ module: crypttab short_description: Encrypted Linux block devices description: - Control Linux encrypted block devices that are set up during system boot in C(/etc/crypttab). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/datadog_downtime.py b/plugins/modules/datadog_downtime.py index 8a1acf7e83..fd8421431d 100644 --- a/plugins/modules/datadog_downtime.py +++ b/plugins/modules/datadog_downtime.py @@ -22,6 +22,13 @@ author: requirements: - datadog-api-client - Python 3.6+ +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: api_key: description: diff --git a/plugins/modules/datadog_event.py b/plugins/modules/datadog_event.py index 0a669c4a81..b8161eca64 100644 --- a/plugins/modules/datadog_event.py +++ b/plugins/modules/datadog_event.py @@ -19,11 +19,18 @@ DOCUMENTATION = ''' module: datadog_event short_description: Posts events to Datadog service description: -- "Allows to post events to Datadog (www.datadoghq.com) service." -- "Uses http://docs.datadoghq.com/api/#events API." + - "Allows to post events to Datadog (www.datadoghq.com) service." + - "Uses http://docs.datadoghq.com/api/#events API." author: -- "Artūras 'arturaz' Šlajus (@arturaz)" -- "Naoya Nakazawa (@n0ts)" + - "Artūras 'arturaz' Šlajus (@arturaz)" + - "Naoya Nakazawa (@n0ts)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: api_key: type: str diff --git a/plugins/modules/datadog_monitor.py b/plugins/modules/datadog_monitor.py index ef6aa84255..f58df358b7 100644 --- a/plugins/modules/datadog_monitor.py +++ b/plugins/modules/datadog_monitor.py @@ -19,6 +19,13 @@ description: - The type C(event-v2) was added in community.general 4.8.0. author: Sebastian Kornehl (@skornehl) requirements: [datadog] +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: api_key: description: diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 61bf6f0e3f..b5ece96ec1 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -21,11 +21,23 @@ description: - Since C(dconf) requires a running D-Bus session to change values, the module will try to detect an existing session and reuse it, or run the tool via C(dbus-run-session). +requirements: + - Optionally the C(gi.repository) Python library (usually included in the OS + on hosts which have C(dconf)); this will become a non-optional requirement + in a future major release of community.general. notes: - This module depends on C(psutil) Python library (version 4.0.0 and upwards), C(dconf), C(dbus-send), and C(dbus-run-session) binaries. Depending on distribution you are using, you may need to install additional packages to have these available. + - This module uses the C(gi.repository) Python library when available for + accurate comparison of values in C(dconf) to values specified in Ansible + code. C(gi.repository) is likely to be present on most systems which have + C(dconf) but may not be present everywhere. When it is missing, a simple + string comparison between values is used, and there may be false positives, + that is, Ansible may think that a value is being changed when it is not. + This fallback will be removed in a future version of this module, at which + point the module will stop working on hosts without C(gi.repository). - Detection of existing, running D-Bus session, required to change settings via C(dconf), is not 100% reliable due to implementation details of D-Bus daemon itself. This might lead to running applications not picking-up @@ -44,6 +56,13 @@ notes: key is by making the configuration change in application affected by the key, and then having a look at value set via commands C(dconf dump /path/to/dir/) or C(dconf read /path/to/key). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: key: type: str @@ -121,6 +140,12 @@ EXAMPLES = r""" import os import traceback +try: + from gi.repository.GLib import Variant, GError +except ImportError: + Variant = None + GError = AttributeError + PSUTIL_IMP_ERR = None try: import psutil @@ -251,6 +276,25 @@ class DconfPreference(object): # Check if dconf binary exists self.dconf_bin = self.module.get_bin_path('dconf', required=True) + @staticmethod + def variants_are_equal(canonical_value, user_value): + """Compare two string GVariant representations for equality. + + Assumes `canonical_value` is "canonical" in the sense that the type of + the variant is specified explicitly if it cannot be inferred; this is + true for textual representations of variants generated by the `dconf` + command. The type of `canonical_value` is used to parse `user_value`, + so the latter does not need to be explicitly typed. + + Returns True if the two values are equal. + """ + try: + variant1 = Variant.parse(None, canonical_value) + variant2 = Variant.parse(variant1.get_type(), user_value) + return variant1 == variant2 + except GError: + return canonical_value == user_value + def read(self, key): """ Retrieves current value associated with the dconf key. @@ -291,7 +335,7 @@ class DconfPreference(object): """ # If no change is needed (or won't be done due to check_mode), notify # caller straight away. - if value == self.read(key): + if self.variants_are_equal(self.read(key), value): return False elif self.check_mode: return True @@ -362,6 +406,12 @@ def main(): supports_check_mode=True ) + if Variant is None: + module.warn( + 'WARNING: The gi.repository Python library is not available; ' + 'using string comparison to check value equality. This fallback ' + 'will be deprecated in a future version of community.general.') + if not HAS_PSUTIL: module.fail_json(msg=missing_required_lib("psutil"), exception=PSUTIL_IMP_ERR) diff --git a/plugins/modules/deploy_helper.py b/plugins/modules/deploy_helper.py index 3d3fe08f28..f0246cae63 100644 --- a/plugins/modules/deploy_helper.py +++ b/plugins/modules/deploy_helper.py @@ -15,7 +15,7 @@ DOCUMENTATION = ''' --- module: deploy_helper author: "Ramon de la Fuente (@ramondelafuente)" -short_description: Manages some of the steps common in deploying projects. +short_description: Manages some of the steps common in deploying projects description: - The Deploy Helper manages some of the steps common in deploying software. It creates a folder structure, manages a symlink for the current release @@ -31,6 +31,12 @@ description: C(new_release), either the 'release' parameter or a generated timestamp, C(new_release_path), the path to the new release folder (not created by the module)." +attributes: + check_mode: + support: full + diff_mode: + support: none + options: path: type: path @@ -111,7 +117,9 @@ notes: - Because of the default behaviour of generating the I(new_release) fact, this module will not be idempotent unless you pass your own release name with I(release). Due to the nature of deploying software, this should not be much of a problem. -extends_documentation_fragment: files +extends_documentation_fragment: + - ansible.builtin.files + - community.general.attributes ''' EXAMPLES = ''' diff --git a/plugins/modules/dimensiondata_network.py b/plugins/modules/dimensiondata_network.py index d88b4a9339..8c1469063c 100644 --- a/plugins/modules/dimensiondata_network.py +++ b/plugins/modules/dimensiondata_network.py @@ -19,12 +19,18 @@ DOCUMENTATION = ''' module: dimensiondata_network short_description: Create, update, and delete MCP 1.0 & 2.0 networks extends_documentation_fragment: -- community.general.dimensiondata -- community.general.dimensiondata_wait + - community.general.dimensiondata + - community.general.dimensiondata_wait + - community.general.attributes description: - Create, update, and delete MCP 1.0 & 2.0 networks author: 'Aimon Bustardo (@aimonb)' +attributes: + check_mode: + support: none + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/dimensiondata_vlan.py b/plugins/modules/dimensiondata_vlan.py index ca25374dcb..7d83ddc696 100644 --- a/plugins/modules/dimensiondata_vlan.py +++ b/plugins/modules/dimensiondata_vlan.py @@ -13,14 +13,20 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: dimensiondata_vlan -short_description: Manage a VLAN in a Cloud Control network domain. +short_description: Manage a VLAN in a Cloud Control network domain extends_documentation_fragment: -- community.general.dimensiondata -- community.general.dimensiondata_wait + - community.general.dimensiondata + - community.general.dimensiondata_wait + - community.general.attributes description: - Manage VLANs in Cloud Control network domains. author: 'Adam Friedman (@tintoy)' +attributes: + check_mode: + support: none + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/discord.py b/plugins/modules/discord.py index 9df00ad7dd..8b5391d44b 100644 --- a/plugins/modules/discord.py +++ b/plugins/modules/discord.py @@ -20,6 +20,13 @@ seealso: - name: API documentation description: Documentation for Discord API link: https://discord.com/developers/docs/resources/webhook#execute-webhook +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: webhook_id: description: diff --git a/plugins/modules/django_manage.py b/plugins/modules/django_manage.py index 188ff2d3df..537cf0fa74 100644 --- a/plugins/modules/django_manage.py +++ b/plugins/modules/django_manage.py @@ -17,6 +17,13 @@ short_description: Manages a Django application description: - Manages a Django application using the C(manage.py) application frontend to C(django-admin). With the I(virtualenv) parameter, all management commands will be executed by the given C(virtualenv) installation. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: command: description: diff --git a/plugins/modules/dnf_versionlock.py b/plugins/modules/dnf_versionlock.py index a0a440b620..fac3ad78d4 100644 --- a/plugins/modules/dnf_versionlock.py +++ b/plugins/modules/dnf_versionlock.py @@ -18,6 +18,21 @@ description: excludes all other versions of those packages. This allows you to for example protect packages from being updated by newer versions. The state of the plugin that reflects locking of packages is the C(locklist). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: partial + details: + - The logics of the C(versionlock) plugin for corner cases could be + confusing, so please take in account that this module will do its best to + give a C(check_mode) prediction on what is going to happen. In case of + doubt, check the documentation of the plugin. + - Sometimes the module could predict changes in C(check_mode) that will not + be such because C(versionlock) concludes that there is already a entry in + C(locklist) that already matches. + diff_mode: + support: none options: name: description: @@ -54,19 +69,11 @@ options: type: str default: present notes: - - The logics of the C(versionlock) plugin for corner cases could be - confusing, so please take in account that this module will do its best to - give a C(check_mode) prediction on what is going to happen. In case of - doubt, check the documentation of the plugin. - - Sometimes the module could predict changes in C(check_mode) that will not - be such because C(versionlock) concludes that there is already a entry in - C(locklist) that already matches. - In an ideal world, the C(versionlock) plugin would have a dry-run option to know for sure what is going to happen. So far we have to work with a best guess as close as possible to the behaviour inferred from its code. - For most of cases where you want to lock and unlock specific versions of a package, this works fairly well. - - Supports C(check_mode). requirements: - dnf - dnf-plugin-versionlock diff --git a/plugins/modules/dnsimple.py b/plugins/modules/dnsimple.py index e96c22613f..df41f73a69 100644 --- a/plugins/modules/dnsimple.py +++ b/plugins/modules/dnsimple.py @@ -16,6 +16,13 @@ module: dnsimple short_description: Interface with dnsimple.com (a DNS hosting service) description: - "Manages domains and records via the DNSimple API, see the docs: U(http://developer.dnsimple.com/)." +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: account_email: description: @@ -168,7 +175,7 @@ class DNSimpleV2(): def dnsimple_client(self): """creates a dnsimple client object""" if self.account_email and self.account_api_token: - client = Client(sandbox=self.sandbox, email=self.account_email, access_token=self.account_api_token) + client = Client(sandbox=self.sandbox, email=self.account_email, access_token=self.account_api_token, user_agent="ansible/community.general") else: msg = "Option account_email or account_api_token not provided. " \ "Dnsimple authentiction with a .dnsimple config file is not " \ diff --git a/plugins/modules/dnsimple_info.py b/plugins/modules/dnsimple_info.py index 959bacbbe8..52fd53303f 100644 --- a/plugins/modules/dnsimple_info.py +++ b/plugins/modules/dnsimple_info.py @@ -230,18 +230,11 @@ dnsimple_record_info: type: str ''' -import traceback from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.basic import missing_required_lib +from ansible_collections.community.general.plugins.module_utils import deps -try: +with deps.declare("requests"): from requests import Request, Session -except ImportError: - HAS_REQUESTS = False - REQUESTS_IMPORT_ERROR = traceback.format_exc() -else: - HAS_REQUESTS = True - REQUESTS_IMPORT_ERROR = None def build_url(account, key, is_sandbox): @@ -310,10 +303,7 @@ def main(): params['api_key'], params['sandbox']) - if not HAS_REQUESTS: - module.exit_json( - msg=missing_required_lib('requests'), - exception=REQUESTS_IMPORT_ERROR) + deps.validate(module) # At minimum we need account and key if params['account_id'] and params['api_key']: diff --git a/plugins/modules/dnsmadeeasy.py b/plugins/modules/dnsmadeeasy.py index cb27a5a68a..44587ca39c 100644 --- a/plugins/modules/dnsmadeeasy.py +++ b/plugins/modules/dnsmadeeasy.py @@ -12,11 +12,18 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: dnsmadeeasy -short_description: Interface with dnsmadeeasy.com (a DNS hosting service). +short_description: Interface with dnsmadeeasy.com (a DNS hosting service) description: - > Manages DNS records via the v2 REST API of the DNS Made Easy service. It handles records only; there is no manipulation of domains or monitor/account support yet. See: U(https://www.dnsmadeeasy.com/integration/restapi/) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: account_key: description: diff --git a/plugins/modules/dpkg_divert.py b/plugins/modules/dpkg_divert.py index 52e6c33d0d..4a1651f517 100644 --- a/plugins/modules/dpkg_divert.py +++ b/plugins/modules/dpkg_divert.py @@ -27,6 +27,13 @@ description: C(dpkg-divert) commandline tool. It can either create or remove a diversion for a given file, but also update an existing diversion to modify its I(holder) and/or its I(divert) location. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: full options: path: description: @@ -79,8 +86,6 @@ options: - This parameter is ignored when I(rename=false). type: bool default: false -notes: - - This module supports I(check_mode) and I(diff). requirements: - dpkg-divert >= 1.15.0 (Debian family) ''' diff --git a/plugins/modules/easy_install.py b/plugins/modules/easy_install.py index 0a3158c926..564493180a 100644 --- a/plugins/modules/easy_install.py +++ b/plugins/modules/easy_install.py @@ -14,7 +14,14 @@ DOCUMENTATION = ''' module: easy_install short_description: Installs Python libraries description: - - Installs Python libraries, optionally in a I(virtualenv) + - Installs Python libraries, optionally in a I(virtualenv) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: type: str diff --git a/plugins/modules/ejabberd_user.py b/plugins/modules/ejabberd_user.py index f7189fa08e..397207ae61 100644 --- a/plugins/modules/ejabberd_user.py +++ b/plugins/modules/ejabberd_user.py @@ -18,6 +18,13 @@ requirements: - ejabberd with mod_admin_extra description: - This module provides user management for ejabberd servers +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: username: type: str diff --git a/plugins/modules/elasticsearch_plugin.py b/plugins/modules/elasticsearch_plugin.py index a68ff086c3..cd4bb45de7 100644 --- a/plugins/modules/elasticsearch_plugin.py +++ b/plugins/modules/elasticsearch_plugin.py @@ -18,6 +18,13 @@ description: author: - Mathew Davies (@ThePixelDeveloper) - Sam Doran (@samdoran) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/emc_vnx_sg_member.py b/plugins/modules/emc_vnx_sg_member.py index 39c0ab3a99..487b6feefa 100644 --- a/plugins/modules/emc_vnx_sg_member.py +++ b/plugins/modules/emc_vnx_sg_member.py @@ -23,8 +23,14 @@ description: - "This module manages the members of an existing storage group." extends_documentation_fragment: -- community.general.emc.emc_vnx + - community.general.emc.emc_vnx + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: diff --git a/plugins/modules/etcd3.py b/plugins/modules/etcd3.py index e67227cc19..9cd0274068 100644 --- a/plugins/modules/etcd3.py +++ b/plugins/modules/etcd3.py @@ -12,12 +12,19 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: etcd3 -short_description: "Set or delete key value pairs from an etcd3 cluster" +short_description: Set or delete key value pairs from an etcd3 cluster requirements: - etcd3 description: - - Sets or deletes values in etcd3 cluster using its v3 api. - - Needs python etcd3 lib to work + - Sets or deletes values in etcd3 cluster using its v3 api. + - Needs python etcd3 lib to work +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: key: type: str diff --git a/plugins/modules/facter.py b/plugins/modules/facter.py index be45e3ce14..e7cf52e203 100644 --- a/plugins/modules/facter.py +++ b/plugins/modules/facter.py @@ -16,6 +16,13 @@ description: - Runs the C(facter) discovery program (U(https://github.com/puppetlabs/facter)) on the remote system, returning JSON data that can be useful for inventory purposes. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: arguments: description: diff --git a/plugins/modules/filesize.py b/plugins/modules/filesize.py index a8c47d70fd..b3eb90d611 100644 --- a/plugins/modules/filesize.py +++ b/plugins/modules/filesize.py @@ -25,6 +25,12 @@ author: version_added: "3.0.0" +attributes: + check_mode: + support: full + diff_mode: + support: full + options: path: description: @@ -93,14 +99,12 @@ options: - This option is silently ignored. This module always modifies file size in-place. -notes: - - This module supports C(check_mode) and C(diff). - requirements: - dd (Data Duplicator) in PATH extends_documentation_fragment: - ansible.builtin.files + - community.general.attributes seealso: - name: dd(1) manpage for Linux diff --git a/plugins/modules/filesystem.py b/plugins/modules/filesystem.py index 4dcfddee23..0e6b815b4e 100644 --- a/plugins/modules/filesystem.py +++ b/plugins/modules/filesystem.py @@ -19,6 +19,13 @@ module: filesystem short_description: Makes a filesystem description: - This module creates a filesystem. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -87,7 +94,6 @@ notes: a C(blkid) command that is compatible with this module. However, these packages conflict with each other, and only the C(util-linux) package provides the command required to not fail when I(state=absent). - - This module supports I(check_mode). seealso: - module: community.general.filesize - module: ansible.posix.mount diff --git a/plugins/modules/flatpak.py b/plugins/modules/flatpak.py index d6264a50d3..feda6ed5b4 100644 --- a/plugins/modules/flatpak.py +++ b/plugins/modules/flatpak.py @@ -15,13 +15,20 @@ DOCUMENTATION = r''' module: flatpak short_description: Manage flatpaks description: -- Allows users to add or remove flatpaks. -- See the M(community.general.flatpak_remote) module for managing flatpak remotes. + - Allows users to add or remove flatpaks. + - See the M(community.general.flatpak_remote) module for managing flatpak remotes. author: -- John Kwiatkoski (@JayKayy) -- Alexander Bethke (@oolongbrothers) + - John Kwiatkoski (@JayKayy) + - Alexander Bethke (@oolongbrothers) requirements: -- flatpak + - flatpak +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: executable: description: diff --git a/plugins/modules/flatpak_remote.py b/plugins/modules/flatpak_remote.py index bc52d5461a..9c097c411f 100644 --- a/plugins/modules/flatpak_remote.py +++ b/plugins/modules/flatpak_remote.py @@ -15,17 +15,24 @@ DOCUMENTATION = r''' module: flatpak_remote short_description: Manage flatpak repository remotes description: -- Allows users to add or remove flatpak remotes. -- The flatpak remotes concept is comparable to what is called repositories in other packaging - formats. -- Currently, remote addition is only supported via I(flatpakrepo) file URLs. -- Existing remotes will not be updated. -- See the M(community.general.flatpak) module for managing flatpaks. + - Allows users to add or remove flatpak remotes. + - The flatpak remotes concept is comparable to what is called repositories in other packaging + formats. + - Currently, remote addition is only supported via I(flatpakrepo) file URLs. + - Existing remotes will not be updated. + - See the M(community.general.flatpak) module for managing flatpaks. author: -- John Kwiatkoski (@JayKayy) -- Alexander Bethke (@oolongbrothers) + - John Kwiatkoski (@JayKayy) + - Alexander Bethke (@oolongbrothers) requirements: -- flatpak + - flatpak +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: executable: description: @@ -63,6 +70,12 @@ options: type: str choices: [ absent, present ] default: present + enabled: + description: + - Indicates whether this remote is enabled. + type: bool + default: true + version_added: 6.4.0 ''' EXAMPLES = r''' @@ -89,6 +102,12 @@ EXAMPLES = r''' community.general.flatpak_remote: name: flathub state: absent + +- name: Disable the flathub remote in the system installation + community.general.flatpak_remote: + name: flathub + state: present + enabled: false ''' RETURN = r''' @@ -141,7 +160,7 @@ def remove_remote(module, binary, name, method): def remote_exists(module, binary, name, method): """Check if the remote exists.""" - command = [binary, "remote-list", "-d", "--{0}".format(method)] + command = [binary, "remote-list", "--show-disabled", "--{0}".format(method)] # The query operation for the remote needs to be run even in check mode output = _flatpak_command(module, False, command) for line in output.splitlines(): @@ -153,6 +172,36 @@ def remote_exists(module, binary, name, method): return False +def enable_remote(module, binary, name, method): + """Enable a remote.""" + global result # pylint: disable=global-variable-not-assigned + command = [binary, "remote-modify", "--enable", "--{0}".format(method), name] + _flatpak_command(module, module.check_mode, command) + result['changed'] = True + + +def disable_remote(module, binary, name, method): + """Disable a remote.""" + global result # pylint: disable=global-variable-not-assigned + command = [binary, "remote-modify", "--disable", "--{0}".format(method), name] + _flatpak_command(module, module.check_mode, command) + result['changed'] = True + + +def remote_enabled(module, binary, name, method): + """Check if the remote is enabled.""" + command = [binary, "remote-list", "--show-disabled", "--{0}".format(method)] + # The query operation for the remote needs to be run even in check mode + output = _flatpak_command(module, False, command) + for line in output.splitlines(): + listed_remote = line.split() + if len(listed_remote) == 0: + continue + if listed_remote[0] == to_native(name): + return len(listed_remote) == 1 or "disabled" not in listed_remote[1].split(",") + return False + + def _flatpak_command(module, noop, command): global result # pylint: disable=global-variable-not-assigned result['command'] = ' '.join(command) @@ -175,6 +224,7 @@ def main(): choices=['user', 'system']), state=dict(type='str', default="present", choices=['absent', 'present']), + enabled=dict(type='bool', default=True), executable=dict(type='str', default="flatpak") ), # This module supports check mode @@ -185,6 +235,7 @@ def main(): flatpakrepo_url = module.params['flatpakrepo_url'] method = module.params['method'] state = module.params['state'] + enabled = module.params['enabled'] executable = module.params['executable'] binary = module.get_bin_path(executable, None) @@ -207,6 +258,14 @@ def main(): elif state == 'absent' and remote_already_exists: remove_remote(module, binary, name, method) + if state == 'present': + remote_already_enabled = remote_enabled(module, binary, to_bytes(name), method) + + if enabled and not remote_already_enabled: + enable_remote(module, binary, name, method) + if not enabled and remote_already_enabled: + disable_remote(module, binary, name, method) + module.exit_json(**result) diff --git a/plugins/modules/flowdock.py b/plugins/modules/flowdock.py index 965ae62d7a..c78716ba40 100644 --- a/plugins/modules/flowdock.py +++ b/plugins/modules/flowdock.py @@ -15,7 +15,14 @@ module: flowdock author: "Matt Coddington (@mcodd)" short_description: Send a message to a flowdock description: - - Send a message to a flowdock team inbox or chat using the push API (see https://www.flowdock.com/api/team-inbox and https://www.flowdock.com/api/chat) + - Send a message to a flowdock team inbox or chat using the push API (see https://www.flowdock.com/api/team-inbox and https://www.flowdock.com/api/chat) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: token: type: str diff --git a/plugins/modules/gandi_livedns.py b/plugins/modules/gandi_livedns.py index 9566661f41..cc9dd630b4 100644 --- a/plugins/modules/gandi_livedns.py +++ b/plugins/modules/gandi_livedns.py @@ -12,11 +12,18 @@ DOCUMENTATION = r''' --- module: gandi_livedns author: -- Gregory Thiemonge (@gthiemonge) + - Gregory Thiemonge (@gthiemonge) version_added: "2.3.0" short_description: Manage Gandi LiveDNS records description: -- "Manages DNS records by the Gandi LiveDNS API, see the docs: U(https://doc.livedns.gandi.net/)." + - "Manages DNS records by the Gandi LiveDNS API, see the docs: U(https://doc.livedns.gandi.net/)." +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: api_key: description: @@ -55,8 +62,6 @@ options: - The name of the Domain to work with (for example, "example.com"). required: true type: str -notes: -- Supports C(check_mode). ''' EXAMPLES = r''' diff --git a/plugins/modules/gconftool2.py b/plugins/modules/gconftool2.py index 931b43d76c..949e92b306 100644 --- a/plugins/modules/gconftool2.py +++ b/plugins/modules/gconftool2.py @@ -12,11 +12,18 @@ __metaclass__ = type DOCUMENTATION = ''' module: gconftool2 author: - - Kenneth D. Evensen (@kevensen) + - Kenneth D. Evensen (@kevensen) short_description: Edit GNOME Configurations description: - This module allows for the manipulation of GNOME 2 Configuration via gconftool-2. Please see the gconftool-2(1) man pages for more details. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: key: type: str @@ -83,75 +90,17 @@ RETURN = ''' ... ''' -from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper +from ansible_collections.community.general.plugins.module_utils.gconftool2 import gconftool2_runner -class GConf2Preference(object): - def __init__(self, ansible, key, value_type, value, - direct=False, config_source=""): - self.ansible = ansible - self.key = key - self.value_type = value_type - self.value = value - self.config_source = config_source - self.direct = direct - - def value_already_set(self): - return False - - def call(self, call_type, fail_onerr=True): - """ Helper function to perform gconftool-2 operations """ - config_source = [] - direct = [] - changed = False - out = '' - - # If the configuration source is different from the default, create - # the argument - if self.config_source is not None and len(self.config_source) > 0: - config_source = ["--config-source", self.config_source] - - # If direct is true, create the argument - if self.direct: - direct = ["--direct"] - - # Execute the call - cmd = ["gconftool-2"] - try: - # If the call is "get", then we don't need as many parameters and - # we can ignore some - if call_type == 'get': - self.ansible.deprecate( - msg="State 'get' is deprecated. Please use the module community.general.gconftool2_info instead", - version="8.0.0", collection_name="community.general" - ) - cmd.extend(["--get", self.key]) - # Otherwise, we will use all relevant parameters - elif call_type == 'set': - cmd.extend(direct) - cmd.extend(config_source) - cmd.extend(["--type", self.value_type, "--{3}".format(call_type), self.key, self.value]) - elif call_type == 'unset': - cmd.extend(["--unset", self.key]) - - # Start external command - rc, out, err = self.ansible.run_command(cmd) - - if err and fail_onerr: - self.ansible.fail_json(msg='gconftool-2 failed with ' - 'error: %s' % (str(err))) - else: - changed = True - - except OSError as exception: - self.ansible.fail_json(msg='gconftool-2 failed with exception: ' - '%s' % exception) - return changed, out.rstrip() - - -def main(): - # Setup the Ansible module - module = AnsibleModule( +class GConftool(StateModuleHelper): + change_params = ('value', ) + diff_params = ('value', ) + output_params = ('key', 'value_type') + facts_params = ('key', 'value_type') + facts_name = 'gconftool2' + module = dict( argument_spec=dict( key=dict(type='str', required=True, no_log=False), value_type=dict(type='str', choices=['bool', 'float', 'int', 'string']), @@ -160,75 +109,54 @@ def main(): direct=dict(type='bool', default=False), config_source=dict(type='str'), ), - supports_check_mode=True + required_if=[ + ('state', 'present', ['value', 'value_type']), + ('state', 'absent', ['value']), + ('direct', True, ['config_source']), + ], + supports_check_mode=True, ) - state_values = {"present": "set", "absent": "unset", "get": "get"} + def __init_module__(self): + self.runner = gconftool2_runner(self.module, check_rc=True) + if self.vars.state != "get": + if not self.vars.direct and self.vars.config_source is not None: + self.module.fail_json(msg='If the "config_source" is specified then "direct" must be "true"') - # Assign module values to dictionary values - key = module.params['key'] - value_type = module.params['value_type'] - if module.params['value'].lower() == "true": - value = "true" - elif module.params['value'] == "false": - value = "false" - else: - value = module.params['value'] + self.vars.set('previous_value', self._get(), fact=True) + self.vars.set('value_type', self.vars.value_type) + self.vars.set_meta('value', initial_value=self.vars.previous_value) + self.vars.set('playbook_value', self.vars.value, fact=True) - state = state_values[module.params['state']] - direct = module.params['direct'] - config_source = module.params['config_source'] + def _make_process(self, fail_on_err): + def process(rc, out, err): + if err and fail_on_err: + self.ansible.fail_json(msg='gconftool-2 failed with error: %s' % (str(err))) + self.vars.value = out.rstrip() + return self.vars.value + return process - # Initialize some variables for later - change = False - new_value = '' + def _get(self): + return self.runner("state key", output_process=self._make_process(False)).run(state="get") - if state != "get": - if value is None or value == "": - module.fail_json(msg='State %s requires "value" to be set' - % str(state)) - elif value_type is None or value_type == "": - module.fail_json(msg='State %s requires "value_type" to be set' - % str(state)) + def state_get(self): + self.deprecate( + msg="State 'get' is deprecated. Please use the module community.general.gconftool2_info instead", + version="8.0.0", collection_name="community.general" + ) - if direct and config_source is None: - module.fail_json(msg='If "direct" is "true" then the ' + - '"config_source" must be specified') - elif not direct and config_source is not None: - module.fail_json(msg='If the "config_source" is specified ' + - 'then "direct" must be "true"') + def state_absent(self): + with self.runner("state key", output_process=self._make_process(False)) as ctx: + ctx.run() + self.vars.set('new_value', None, fact=True) - # Create a gconf2 preference - gconf_pref = GConf2Preference(module, key, value_type, - value, direct, config_source) - # Now we get the current value, if not found don't fail - dummy, current_value = gconf_pref.call("get", fail_onerr=False) + def state_present(self): + with self.runner("direct config_source value_type state key value", output_process=self._make_process(True)) as ctx: + self.vars.set('new_value', ctx.run(), fact=True) - # Check if the current value equals the value we want to set. If not, make - # a change - if current_value != value: - # If check mode, we know a change would have occurred. - if module.check_mode: - # So we will set the change to True - change = True - # And set the new_value to the value that would have been set - new_value = value - # If not check mode make the change. - else: - change, new_value = gconf_pref.call(state) - # If the value we want to set is the same as the current_value, we will - # set the new_value to the current_value for reporting - else: - new_value = current_value - facts = dict(gconftool2={'changed': change, - 'key': key, - 'value_type': value_type, - 'new_value': new_value, - 'previous_value': current_value, - 'playbook_value': module.params['value']}) - - module.exit_json(changed=change, ansible_facts=facts) +def main(): + GConftool.execute() if __name__ == '__main__': diff --git a/plugins/modules/gconftool2_info.py b/plugins/modules/gconftool2_info.py index f9231104d4..282065b95e 100644 --- a/plugins/modules/gconftool2_info.py +++ b/plugins/modules/gconftool2_info.py @@ -65,8 +65,8 @@ class GConftoolInfo(ModuleHelper): self.runner = gconftool2_runner(self.module, check_rc=True) def __run__(self): - with self.runner.context(args_order=["get", "key"]) as ctx: - rc, out, err = ctx.run(get=True) + with self.runner.context(args_order=["state", "key"]) as ctx: + rc, out, err = ctx.run(state="get") self.vars.value = None if err and not out else out.rstrip() diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 8d7f7dade0..4bc99d39ef 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -15,6 +15,13 @@ module: gem short_description: Manage Ruby gems description: - Manage installation and uninstallation of Ruby gems. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: type: str @@ -105,7 +112,7 @@ options: required: false force: description: - - Force gem to install, bypassing dependency checks. + - Force gem to (un-)install, bypassing dependency checks. required: false default: false type: bool @@ -234,7 +241,9 @@ def uninstall(module): cmd.extend(['--version', module.params['version']]) else: cmd.append('--all') - cmd.append('--executable') + cmd.append('--executable') + if module.params['force']: + cmd.append('--force') cmd.append(module.params['name']) module.run_command(cmd, environ_update=environ, check_rc=True) diff --git a/plugins/modules/git_config.py b/plugins/modules/git_config.py index 9191de0e87..d673121748 100644 --- a/plugins/modules/git_config.py +++ b/plugins/modules/git_config.py @@ -21,10 +21,17 @@ requirements: ['git'] short_description: Read and write git configuration description: - The C(git_config) module changes git configuration by invoking 'git config'. - This is needed if you don't want to use M(ansible.builtin.template) for the entire git - config file (e.g. because you need to change just C(user.email) in + This is needed if you do not want to use M(ansible.builtin.template) for the entire git + config file (for example because you need to change just C(user.email) in /etc/.git/config). Solutions involving M(ansible.builtin.command) are cumbersome or - don't work correctly in check mode. + do not work correctly in check mode. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: list_all: description: diff --git a/plugins/modules/github_deploy_key.py b/plugins/modules/github_deploy_key.py index 97e7a1ac7f..322650bf70 100644 --- a/plugins/modules/github_deploy_key.py +++ b/plugins/modules/github_deploy_key.py @@ -13,11 +13,18 @@ DOCUMENTATION = ''' --- module: github_deploy_key author: "Ali (@bincyber)" -short_description: Manages deploy keys for GitHub repositories. +short_description: Manages deploy keys for GitHub repositories description: - "Adds or removes deploy keys for GitHub repositories. Supports authentication using username and password, username and password and 2-factor authentication code (OTP), OAuth2 token, or personal access token. Admin rights on the repository are required." +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: github_url: description: diff --git a/plugins/modules/github_issue.py b/plugins/modules/github_issue.py index 4f8f2363cc..4e10e9f925 100644 --- a/plugins/modules/github_issue.py +++ b/plugins/modules/github_issue.py @@ -12,9 +12,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: github_issue -short_description: View GitHub issue. +short_description: View GitHub issue description: - - View GitHub issue for a given repository and organization. + - View GitHub issue for a given repository and organization. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: repo: description: diff --git a/plugins/modules/github_key.py b/plugins/modules/github_key.py index 5dfd694275..683a963a7f 100644 --- a/plugins/modules/github_key.py +++ b/plugins/modules/github_key.py @@ -11,9 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' module: github_key -short_description: Manage GitHub access keys. +short_description: Manage GitHub access keys description: - - Creates, removes, or updates GitHub access keys. + - Creates, removes, or updates GitHub access keys. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: token: description: diff --git a/plugins/modules/github_release.py b/plugins/modules/github_release.py index 84ce4ce4ec..3ddd6c8820 100644 --- a/plugins/modules/github_release.py +++ b/plugins/modules/github_release.py @@ -15,6 +15,13 @@ module: github_release short_description: Interact with GitHub Releases description: - Fetch metadata about GitHub Releases +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: token: description: @@ -108,17 +115,8 @@ EXAMPLES = ''' ''' RETURN = ''' -create_release: - description: - - Version of the created release - - "For Ansible version 2.5 and later, if specified release version already exists, then State is unchanged" - - "For Ansible versions prior to 2.5, if specified release version already exists, then State is skipped" - type: str - returned: success - sample: 1.1.0 - -latest_release: - description: Version of the latest release +tag: + description: Version of the created/latest release. type: str returned: success sample: 1.1.0 diff --git a/plugins/modules/github_repo.py b/plugins/modules/github_repo.py index d01312fcfa..97076c58af 100644 --- a/plugins/modules/github_repo.py +++ b/plugins/modules/github_repo.py @@ -14,8 +14,15 @@ module: github_repo short_description: Manage your repositories on Github version_added: 2.2.0 description: -- Manages Github repositories using PyGithub library. -- Authentication can be done with I(access_token) or with I(username) and I(password). + - Manages Github repositories using PyGithub library. + - Authentication can be done with I(access_token) or with I(username) and I(password). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: username: description: @@ -89,7 +96,6 @@ notes: - For Python 3, PyGithub>=1.54 should be used. - "For Python 3.5, PyGithub==1.54 should be used. More information: U(https://pygithub.readthedocs.io/en/latest/changes.html#version-1-54-november-30-2020)." - "For Python 2.7, PyGithub==1.45 should be used. More information: U(https://pygithub.readthedocs.io/en/latest/changes.html#version-1-45-december-29-2019)." -- Supports C(check_mode). author: - Álvaro Torres Cogollo (@atorrescogollo) ''' diff --git a/plugins/modules/github_webhook.py b/plugins/modules/github_webhook.py index b97087d221..d47b7a82fa 100644 --- a/plugins/modules/github_webhook.py +++ b/plugins/modules/github_webhook.py @@ -16,6 +16,13 @@ description: - "Create and delete GitHub webhooks" requirements: - "PyGithub >= 1.3.5" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: repository: description: @@ -154,13 +161,18 @@ from ansible.module_utils.common.text.converters import to_native def _create_hook_config(module): - return { + hook_config = { "url": module.params["url"], "content_type": module.params["content_type"], - "secret": module.params.get("secret"), "insecure_ssl": "1" if module.params["insecure_ssl"] else "0" } + secret = module.params.get("secret") + if secret: + hook_config["secret"] = secret + + return hook_config + def create_hook(repo, module): config = _create_hook_config(module) diff --git a/plugins/modules/gitlab_branch.py b/plugins/modules/gitlab_branch.py index e57ca4922f..d7eecb33fb 100644 --- a/plugins/modules/gitlab_branch.py +++ b/plugins/modules/gitlab_branch.py @@ -21,6 +21,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: none + diff_mode: + support: none options: state: diff --git a/plugins/modules/gitlab_deploy_key.py b/plugins/modules/gitlab_deploy_key.py index 1b77801ec4..27cb01f87e 100644 --- a/plugins/modules/gitlab_deploy_key.py +++ b/plugins/modules/gitlab_deploy_key.py @@ -13,7 +13,7 @@ __metaclass__ = type DOCUMENTATION = ''' module: gitlab_deploy_key -short_description: Manages GitLab project deploy keys. +short_description: Manages GitLab project deploy keys description: - Adds, updates and removes project deploy keys author: @@ -25,6 +25,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: project: @@ -151,6 +158,7 @@ class GitLabDeployKey(object): changed = True else: changed, deploy_key = self.update_deploy_key(self.deploy_key_object, { + 'title': key_title, 'can_push': options['can_push']}) self.deploy_key_object = deploy_key diff --git a/plugins/modules/gitlab_group.py b/plugins/modules/gitlab_group.py index d099a0c274..624028f298 100644 --- a/plugins/modules/gitlab_group.py +++ b/plugins/modules/gitlab_group.py @@ -25,6 +25,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: diff --git a/plugins/modules/gitlab_group_members.py b/plugins/modules/gitlab_group_members.py index 6edc8c983f..66298e882c 100644 --- a/plugins/modules/gitlab_group_members.py +++ b/plugins/modules/gitlab_group_members.py @@ -22,6 +22,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: gitlab_group: @@ -81,8 +88,6 @@ options: elements: str choices: ['guest', 'reporter', 'developer', 'maintainer', 'owner'] version_added: 3.6.0 -notes: - - Supports C(check_mode). ''' EXAMPLES = r''' diff --git a/plugins/modules/gitlab_group_variable.py b/plugins/modules/gitlab_group_variable.py index c273777ca3..c7befe123c 100644 --- a/plugins/modules/gitlab_group_variable.py +++ b/plugins/modules/gitlab_group_variable.py @@ -26,6 +26,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: @@ -99,8 +106,6 @@ options: - The scope for the variable. type: str default: '*' -notes: -- Supports I(check_mode). ''' @@ -165,7 +170,7 @@ from ansible.module_utils.six import string_types from ansible.module_utils.six import integer_types from ansible_collections.community.general.plugins.module_utils.gitlab import ( - auth_argument_spec, gitlab_authentication, ensure_gitlab_package + auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables ) @@ -296,11 +301,7 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module): before = [x.attributes for x in gitlab_keys] gitlab_keys = this_gitlab.list_all_group_variables() - existing_variables = [x.attributes for x in gitlab_keys] - - # preprocessing:filter out and enrich before compare - for item in existing_variables: - item.pop('group_id') + existing_variables = filter_returned_variables(gitlab_keys) for item in requested_variables: item['key'] = item.pop('name') @@ -331,9 +332,7 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module): if purge: # refetch and filter gitlab_keys = this_gitlab.list_all_group_variables() - existing_variables = [x.attributes for x in gitlab_keys] - for item in existing_variables: - item.pop('group_id') + existing_variables = filter_returned_variables(gitlab_keys) remove = [x for x in existing_variables if x not in requested_variables] for item in remove: diff --git a/plugins/modules/gitlab_hook.py b/plugins/modules/gitlab_hook.py index c10cb45324..adf90eb7bc 100644 --- a/plugins/modules/gitlab_hook.py +++ b/plugins/modules/gitlab_hook.py @@ -14,7 +14,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: gitlab_hook -short_description: Manages GitLab project hooks. +short_description: Manages GitLab project hooks description: - Adds, updates and removes project hook author: @@ -26,6 +26,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: project: diff --git a/plugins/modules/gitlab_project.py b/plugins/modules/gitlab_project.py index ca1f8a6c64..d58ba35975 100644 --- a/plugins/modules/gitlab_project.py +++ b/plugins/modules/gitlab_project.py @@ -26,6 +26,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: group: @@ -172,6 +179,78 @@ options: - This option is only used on creation, not for updates. This is also only used if I(initialize_with_readme=true). type: str version_added: "4.2.0" + builds_access_level: + description: + - C(private) means that repository CI/CD is allowed only to project members. + - C(disabled) means that repository CI/CD is disabled. + - C(enabled) means that repository CI/CD is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.2.0" + forking_access_level: + description: + - C(private) means that repository forks is allowed only to project members. + - C(disabled) means that repository forks are disabled. + - C(enabled) means that repository forks are enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.2.0" + container_registry_access_level: + description: + - C(private) means that container registry is allowed only to project members. + - C(disabled) means that container registry is disabled. + - C(enabled) means that container registry is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.2.0" + releases_access_level: + description: + - C(private) means that accessing release is allowed only to project members. + - C(disabled) means that accessing release is disabled. + - C(enabled) means that accessing release is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.4.0" + environments_access_level: + description: + - C(private) means that deployment to environment is allowed only to project members. + - C(disabled) means that deployment to environment is disabled. + - C(enabled) means that deployment to environment is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.4.0" + feature_flags_access_level: + description: + - C(private) means that feature rollout is allowed only to project members. + - C(disabled) means that feature rollout is disabled. + - C(enabled) means that feature rollout is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.4.0" + infrastructure_access_level: + description: + - C(private) means that configuring infrastructure is allowed only to project members. + - C(disabled) means that configuring infrastructure is disabled. + - C(enabled) means that configuring infrastructure is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.4.0" + monitor_access_level: + description: + - C(private) means that monitoring health is allowed only to project members. + - C(disabled) means that monitoring health is disabled. + - C(enabled) means that monitoring health is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.4.0" + security_and_compliance_access_level: + description: + - C(private) means that accessing security and complicance tab is allowed only to project members. + - C(disabled) means that accessing security and complicance tab is disabled. + - C(enabled) means that accessing security and complicance tab is enabled. + type: str + choices: ["private", "disabled", "enabled"] + version_added: "6.4.0" ''' EXAMPLES = r''' @@ -287,6 +366,15 @@ class GitLabProject(object): 'squash_option': options['squash_option'], 'ci_config_path': options['ci_config_path'], 'shared_runners_enabled': options['shared_runners_enabled'], + 'builds_access_level': options['builds_access_level'], + 'forking_access_level': options['forking_access_level'], + 'container_registry_access_level': options['container_registry_access_level'], + 'releases_access_level': options['releases_access_level'], + 'environments_access_level': options['environments_access_level'], + 'feature_flags_access_level': options['feature_flags_access_level'], + 'infrastructure_access_level': options['infrastructure_access_level'], + 'monitor_access_level': options['monitor_access_level'], + 'security_and_compliance_access_level': options['security_and_compliance_access_level'], } # Because we have already call userExists in main() if self.project_object is None: @@ -417,6 +505,15 @@ def main(): ci_config_path=dict(type='str'), shared_runners_enabled=dict(type='bool'), avatar_path=dict(type='path'), + builds_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + forking_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + container_registry_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + releases_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + environments_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + feature_flags_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + infrastructure_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + monitor_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), + security_and_compliance_access_level=dict(type='str', choices=['private', 'disabled', 'enabled']), )) module = AnsibleModule( @@ -464,6 +561,15 @@ def main(): shared_runners_enabled = module.params['shared_runners_enabled'] avatar_path = module.params['avatar_path'] default_branch = module.params['default_branch'] + builds_access_level = module.params['builds_access_level'] + forking_access_level = module.params['forking_access_level'] + container_registry_access_level = module.params['container_registry_access_level'] + releases_access_level = module.params['releases_access_level'] + environments_access_level = module.params['environments_access_level'] + feature_flags_access_level = module.params['feature_flags_access_level'] + infrastructure_access_level = module.params['infrastructure_access_level'] + monitor_access_level = module.params['monitor_access_level'] + security_and_compliance_access_level = module.params['security_and_compliance_access_level'] if default_branch and not initialize_with_readme: module.fail_json(msg="Param default_branch need param initialize_with_readme set to true") @@ -533,6 +639,15 @@ def main(): "ci_config_path": ci_config_path, "shared_runners_enabled": shared_runners_enabled, "avatar_path": avatar_path, + "builds_access_level": builds_access_level, + "forking_access_level": forking_access_level, + "container_registry_access_level": container_registry_access_level, + "releases_access_level": releases_access_level, + "environments_access_level": environments_access_level, + "feature_flags_access_level": feature_flags_access_level, + "infrastructure_access_level": infrastructure_access_level, + "monitor_access_level": monitor_access_level, + "security_and_compliance_access_level": security_and_compliance_access_level, }): module.exit_json(changed=True, msg="Successfully created or updated the project %s" % project_name, project=gitlab_project.project_object._attrs) diff --git a/plugins/modules/gitlab_project_badge.py b/plugins/modules/gitlab_project_badge.py new file mode 100644 index 0000000000..5b1a8d3f1c --- /dev/null +++ b/plugins/modules/gitlab_project_badge.py @@ -0,0 +1,216 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2022, Guillaume MARTINEZ (lunik@tiwabbit.fr) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: gitlab_project_badge +short_description: Manage project badges on GitLab Server +version_added: 6.1.0 +description: + - This module allows to add and remove badges to/from a project. +author: Guillaume MARTINEZ (@Lunik) +requirements: + - C(owner) or C(maintainer) rights to project on the GitLab server +extends_documentation_fragment: + - community.general.auth_basic + - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none + +options: + project: + description: + - The name (or full path) of the GitLab project the badge is added to/removed from. + required: true + type: str + + state: + description: + - State of the badge in the project. + - On C(present), it adds a badge to a GitLab project. + - On C(absent), it removes a badge from a GitLab project. + choices: ['present', 'absent'] + default: 'present' + type: str + + link_url: + description: + - The URL associated with the badge. + required: true + type: str + + image_url: + description: + - The image URL of the badge. + - A badge is identified by this URL. + required: true + type: str +''' + +EXAMPLES = r''' +- name: Add a badge to a GitLab Project + community.general.gitlab_project_badge: + api_url: 'https://example.gitlab.com' + api_token: 'Your-Private-Token' + project: projectname + state: present + link_url: 'https://example.gitlab.com/%{project_path}' + image_url: 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg' + +- name: Remove a badge from a GitLab Project + community.general.gitlab_project_badge: + api_url: 'https://example.gitlab.com' + api_token: 'Your-Private-Token' + project: projectname + state: absent + link_url: 'https://example.gitlab.com/%{project_path}' + image_url: 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg' +''' + +RETURN = ''' +badge: + description: The badge information. + returned: when I(state=present) + type: dict + sample: + id: 1 + link_url: 'http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}' + image_url: 'https://shields.io/my/badge' + rendered_link_url: 'http://example.com/ci_status.svg?project=example-org/example-project&ref=master' + rendered_image_url: 'https://shields.io/my/badge' + kind: project +''' + +from ansible.module_utils.api import basic_auth_argument_spec +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.general.plugins.module_utils.gitlab import ( + auth_argument_spec, gitlab_authentication, find_project, ensure_gitlab_package +) + + +def present_strategy(module, gl, project, wished_badge): + changed = False + + existing_badge = None + for badge in project.badges.list(iterator=True): + if badge.image_url == wished_badge["image_url"]: + existing_badge = badge + break + + if not existing_badge: + changed = True + if module.check_mode: + return changed, {"status": "A project badge would be created."} + + badge = project.badges.create(wished_badge) + return changed, badge.attributes + + if existing_badge.link_url != wished_badge["link_url"]: + changed = True + existing_badge.link_url = wished_badge["link_url"] + + if changed: + if module.check_mode: + return changed, {"status": "Project badge attributes would be changed."} + + existing_badge.save() + + return changed, existing_badge.attributes + + +def absent_strategy(module, gl, project, wished_badge): + changed = False + + existing_badge = None + for badge in project.badges.list(iterator=True): + if badge.image_url == wished_badge["image_url"]: + existing_badge = badge + break + + if not existing_badge: + return changed, None + + changed = True + if module.check_mode: + return changed, {"status": "Project badge would be destroyed."} + + existing_badge.delete() + + return changed, None + + +state_strategy = { + "present": present_strategy, + "absent": absent_strategy +} + + +def core(module): + ensure_gitlab_package(module) + + gitlab_project = module.params['project'] + state = module.params['state'] + + gl = gitlab_authentication(module) + + project = find_project(gl, gitlab_project) + # project doesn't exist + if not project: + module.fail_json(msg="project '%s' not found." % gitlab_project) + + wished_badge = { + "link_url": module.params["link_url"], + "image_url": module.params["image_url"], + } + + changed, summary = state_strategy[state](module=module, gl=gl, project=project, wished_badge=wished_badge) + + module.exit_json(changed=changed, badge=summary) + + +def main(): + argument_spec = basic_auth_argument_spec() + argument_spec.update(auth_argument_spec()) + argument_spec.update(dict( + project=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['present', 'absent']), + link_url=dict(type='str', required=True), + image_url=dict(type='str', required=True), + )) + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ['api_username', 'api_token'], + ['api_username', 'api_oauth_token'], + ['api_username', 'api_job_token'], + ['api_token', 'api_oauth_token'], + ['api_token', 'api_job_token'], + ], + required_together=[ + ['api_username', 'api_password'], + ], + required_one_of=[ + ['api_username', 'api_token', 'api_oauth_token', 'api_job_token'], + ], + supports_check_mode=True, + ) + + core(module) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/gitlab_project_members.py b/plugins/modules/gitlab_project_members.py index 811033f312..9053584431 100644 --- a/plugins/modules/gitlab_project_members.py +++ b/plugins/modules/gitlab_project_members.py @@ -25,6 +25,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: project: @@ -82,8 +89,6 @@ options: elements: str choices: ['guest', 'reporter', 'developer', 'maintainer'] version_added: 3.7.0 -notes: - - Supports C(check_mode). ''' EXAMPLES = r''' @@ -136,7 +141,7 @@ EXAMPLES = r''' project: projectname gitlab_user: username access_level: developer - pruge_users: developer + purge_users: developer state: present - name: Remove a list of Users with Dedicated Access Levels to A GitLab project diff --git a/plugins/modules/gitlab_project_variable.py b/plugins/modules/gitlab_project_variable.py index cdd6402ae8..63569dd789 100644 --- a/plugins/modules/gitlab_project_variable.py +++ b/plugins/modules/gitlab_project_variable.py @@ -23,6 +23,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: @@ -182,14 +189,14 @@ from ansible.module_utils.six import integer_types GITLAB_IMP_ERR = None try: - import gitlab + import gitlab # noqa: F401, pylint: disable=unused-import HAS_GITLAB_PACKAGE = True except Exception: GITLAB_IMP_ERR = traceback.format_exc() HAS_GITLAB_PACKAGE = False from ansible_collections.community.general.plugins.module_utils.gitlab import ( - auth_argument_spec, gitlab_authentication, ensure_gitlab_package + auth_argument_spec, gitlab_authentication, ensure_gitlab_package, filter_returned_variables ) @@ -255,9 +262,11 @@ class GitlabProjectVariables(object): return True var = { - "key": var_obj.get('key'), "value": var_obj.get('value'), - "masked": var_obj.get('masked'), "protected": var_obj.get('protected'), - "variable_type": var_obj.get('variable_type') + "key": var_obj.get('key'), + "value": var_obj.get('value'), + "masked": var_obj.get('masked'), + "protected": var_obj.get('protected'), + "variable_type": var_obj.get('variable_type'), } if var_obj.get('environment_scope') is not None: @@ -319,12 +328,9 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module): before = [x.attributes for x in gitlab_keys] gitlab_keys = this_gitlab.list_all_project_variables() - existing_variables = [x.attributes for x in gitlab_keys] - - # preprocessing:filter out and enrich before compare - for item in existing_variables: - item.pop('project_id') + existing_variables = filter_returned_variables(gitlab_keys) + # filter out and enrich before compare for item in requested_variables: item['key'] = item.pop('name') item['value'] = str(item.get('value')) @@ -354,9 +360,7 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module): if purge: # refetch and filter gitlab_keys = this_gitlab.list_all_project_variables() - existing_variables = [x.attributes for x in gitlab_keys] - for item in existing_variables: - item.pop('project_id') + existing_variables = filter_returned_variables(gitlab_keys) remove = [x for x in existing_variables if x not in requested_variables] for item in remove: @@ -409,7 +413,7 @@ def main(): masked=dict(type='bool', default=False), protected=dict(type='bool', default=False), environment_scope=dict(type='str', default='*'), - variable_type=dict(type='str', default='env_var', choices=["env_var", "file"]) + variable_type=dict(type='str', default='env_var', choices=["env_var", "file"]), )), state=dict(type='str', default="present", choices=["absent", "present"]), ) diff --git a/plugins/modules/gitlab_protected_branch.py b/plugins/modules/gitlab_protected_branch.py index bddf175f0f..fea374cbfd 100644 --- a/plugins/modules/gitlab_protected_branch.py +++ b/plugins/modules/gitlab_protected_branch.py @@ -9,7 +9,7 @@ __metaclass__ = type DOCUMENTATION = ''' module: gitlab_protected_branch -short_description: (un)Marking existing branches for protection +short_description: Manage protection of existing branches version_added: 3.4.0 description: - (un)Marking existing branches for protection. @@ -21,11 +21,18 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: - - Create or delete proteced branch. + - Create or delete protected branch. default: present type: str choices: ["present", "absent"] diff --git a/plugins/modules/gitlab_runner.py b/plugins/modules/gitlab_runner.py index 67d998f12f..a41b135fc3 100644 --- a/plugins/modules/gitlab_runner.py +++ b/plugins/modules/gitlab_runner.py @@ -13,7 +13,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: gitlab_runner -short_description: Create, modify and delete GitLab Runners. +short_description: Create, modify and delete GitLab Runners description: - Register, update and delete runners with the GitLab API. - All operations are performed using the GitLab API v4. @@ -35,12 +35,26 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: + group: + description: + - ID or full path of the group in the form group/subgroup. + - Mutually exclusive with I(owned) and I(project). + type: str + version_added: '6.5.0' project: description: - ID or full path of the project in the form of group/name. - Mutually exclusive with I(owned) since community.general 4.5.0. + - Mutually exclusive with I(group). type: str version_added: '3.7.0' description: @@ -66,6 +80,7 @@ options: description: - Searches only runners available to the user when searching for existing, when false admin token required. - Mutually exclusive with I(project) since community.general 4.5.0. + - Mutually exclusive with I(group). default: false type: bool version_added: 2.0.0 @@ -84,12 +99,25 @@ options: access_level: description: - Determines if a runner can pick up jobs only from protected branches. - - If set to C(ref_protected), runner can pick up jobs only from protected branches. + - If I(access_level_on_creation) is not explicitly set to C(true), this option is ignored on registration and + is only applied on updates. - If set to C(not_protected), runner can pick up jobs from both protected and unprotected branches. + - If set to C(ref_protected), runner can pick up jobs only from protected branches. + - The current default is C(ref_protected). This will change to no default in community.general 8.0.0. + From that version on, if this option is not specified explicitly, GitLab will use C(not_protected) + on creation, and the value set will not be changed on any updates. required: false - default: ref_protected - choices: ["ref_protected", "not_protected"] + choices: ["not_protected", "ref_protected"] type: str + access_level_on_creation: + description: + - Whether the runner should be registered with an access level or not. + - If set to C(true), the value of I(access_level) is used for runner registration. + - If set to C(false), GitLab registers the runner with the default access level. + - The current default of this option is C(false). This default is deprecated and will change to C(true) in commuinty.general 7.0.0. + required: false + type: bool + version_added: 6.3.0 maximum_timeout: description: - The maximum time that a runner has to complete a specific job. @@ -189,45 +217,55 @@ except NameError: class GitLabRunner(object): - def __init__(self, module, gitlab_instance, project=None): + def __init__(self, module, gitlab_instance, group=None, project=None): self._module = module self._gitlab = gitlab_instance + self.runner_object = None + # Whether to operate on GitLab-instance-wide or project-wide runners # See https://gitlab.com/gitlab-org/gitlab-ce/issues/60774 # for group runner token access if project: self._runners_endpoint = project.runners.list + elif group: + self._runners_endpoint = group.runners.list elif module.params['owned']: self._runners_endpoint = gitlab_instance.runners.list else: self._runners_endpoint = gitlab_instance.runners.all - self.runner_object = None - def create_or_update_runner(self, description, options): changed = False + arguments = { + 'active': options['active'], + 'locked': options['locked'], + 'run_untagged': options['run_untagged'], + 'maximum_timeout': options['maximum_timeout'], + 'tag_list': options['tag_list'], + } + if options.get('access_level') is not None: + arguments['access_level'] = options['access_level'] # Because we have already call userExists in main() if self.runner_object is None: - runner = self.create_runner({ - 'description': description, - 'active': options['active'], - 'token': options['registration_token'], - 'locked': options['locked'], - 'run_untagged': options['run_untagged'], - 'maximum_timeout': options['maximum_timeout'], - 'tag_list': options['tag_list'], - }) + arguments['description'] = description + arguments['token'] = options['registration_token'] + + access_level_on_creation = self._module.params['access_level_on_creation'] + if access_level_on_creation is None: + message = "The option 'access_level_on_creation' is unspecified, so 'false' is assumed. "\ + "That means any value of 'access_level' is ignored and GitLab registers the runner with its default value. "\ + "The option 'access_level_on_creation' will switch to 'true' in community.general 7.0.0" + self._module.deprecate(message, version='7.0.0', collection_name='community.general') + access_level_on_creation = False + + if not access_level_on_creation: + arguments.pop('access_level', None) + + runner = self.create_runner(arguments) changed = True else: - changed, runner = self.update_runner(self.runner_object, { - 'active': options['active'], - 'locked': options['locked'], - 'run_untagged': options['run_untagged'], - 'maximum_timeout': options['maximum_timeout'], - 'access_level': options['access_level'], - 'tag_list': options['tag_list'], - }) + changed, runner = self.update_runner(self.runner_object, arguments) self.runner_object = runner if changed: @@ -327,10 +365,12 @@ def main(): tag_list=dict(type='list', elements='str', default=[]), run_untagged=dict(type='bool', default=True), locked=dict(type='bool', default=False), - access_level=dict(type='str', default='ref_protected', choices=["not_protected", "ref_protected"]), + access_level=dict(type='str', choices=["not_protected", "ref_protected"]), + access_level_on_creation=dict(type='bool'), maximum_timeout=dict(type='int', default=3600), registration_token=dict(type='str', no_log=True), project=dict(type='str'), + group=dict(type='str'), state=dict(type='str', default="present", choices=["absent", "present"]), )) @@ -343,6 +383,8 @@ def main(): ['api_token', 'api_oauth_token'], ['api_token', 'api_job_token'], ['project', 'owned'], + ['group', 'owned'], + ['project', 'group'], ], required_together=[ ['api_username', 'api_password'], @@ -367,16 +409,33 @@ def main(): maximum_timeout = module.params['maximum_timeout'] registration_token = module.params['registration_token'] project = module.params['project'] + group = module.params['group'] + + if access_level is None: + message = "The option 'access_level' is unspecified, so 'ref_protected' is assumed. "\ + "In order to align the module with GitLab's runner API, this option will lose "\ + "its default value in community.general 8.0.0. From that version on, you must set "\ + "this option to 'ref_protected' explicitly, if you want to have a protected runner, "\ + "otherwise GitLab's default access level gets applied, which is 'not_protected'" + module.deprecate(message, version='8.0.0', collection_name='community.general') + access_level = 'ref_protected' gitlab_instance = gitlab_authentication(module) gitlab_project = None + gitlab_group = None + if project: try: gitlab_project = gitlab_instance.projects.get(project) except gitlab.exceptions.GitlabGetError as e: module.fail_json(msg='No such a project %s' % project, exception=to_native(e)) + elif group: + try: + gitlab_group = gitlab_instance.groups.get(group) + except gitlab.exceptions.GitlabGetError as e: + module.fail_json(msg='No such a group %s' % group, exception=to_native(e)) - gitlab_runner = GitLabRunner(module, gitlab_instance, gitlab_project) + gitlab_runner = GitLabRunner(module, gitlab_instance, gitlab_group, gitlab_project) runner_exists = gitlab_runner.exists_runner(runner_description) if state == 'absent': diff --git a/plugins/modules/gitlab_user.py b/plugins/modules/gitlab_user.py index 4824f7301f..94f3713160 100644 --- a/plugins/modules/gitlab_user.py +++ b/plugins/modules/gitlab_user.py @@ -33,6 +33,13 @@ requirements: extends_documentation_fragment: - community.general.auth_basic - community.general.gitlab + - community.general.attributes + +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: diff --git a/plugins/modules/grove.py b/plugins/modules/grove.py index d3ad6bb019..b3e0508ffa 100644 --- a/plugins/modules/grove.py +++ b/plugins/modules/grove.py @@ -16,6 +16,13 @@ short_description: Sends a notification to a grove.io channel description: - The C(grove) module sends a message for a service to a Grove.io channel. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: channel_token: type: str diff --git a/plugins/modules/gunicorn.py b/plugins/modules/gunicorn.py index 9ed903dfd5..2b2abcf8e6 100644 --- a/plugins/modules/gunicorn.py +++ b/plugins/modules/gunicorn.py @@ -12,16 +12,23 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: gunicorn -short_description: Run gunicorn with various settings. +short_description: Run gunicorn with various settings description: - - Starts gunicorn with the parameters specified. Common settings for gunicorn - configuration are supported. For additional configuration use a config file - See U(https://gunicorn-docs.readthedocs.io/en/latest/settings.html) for more - options. It's recommended to always use the chdir option to avoid problems - with the location of the app. + - Starts gunicorn with the parameters specified. Common settings for gunicorn + configuration are supported. For additional configuration use a config file + See U(https://gunicorn-docs.readthedocs.io/en/latest/settings.html) for more + options. It's recommended to always use the chdir option to avoid problems + with the location of the app. requirements: [gunicorn] author: - "Alejandro Gomez (@agmezr)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: app: type: str diff --git a/plugins/modules/hana_query.py b/plugins/modules/hana_query.py index 746b2a3f44..0b12e9935c 100644 --- a/plugins/modules/hana_query.py +++ b/plugins/modules/hana_query.py @@ -13,6 +13,13 @@ module: hana_query short_description: Execute SQL on HANA version_added: 3.2.0 description: This module executes SQL statements on HANA with hdbsql. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: sid: description: The system ID. @@ -65,8 +72,6 @@ options: It is better to supply a one-element list instead to avoid mangled input. type: list elements: str -notes: - - Does not support C(check_mode). author: - Rainer Leber (@rainerleber) ''' diff --git a/plugins/modules/haproxy.py b/plugins/modules/haproxy.py index 38033bc81a..56f987d80f 100644 --- a/plugins/modules/haproxy.py +++ b/plugins/modules/haproxy.py @@ -13,7 +13,7 @@ DOCUMENTATION = r''' module: haproxy short_description: Enable, disable, and set weights for HAProxy backend servers using socket commands author: -- Ravi Bhure (@ravibhure) + - Ravi Bhure (@ravibhure) description: - Enable, disable, drain and set weights for HAProxy backend servers using socket commands. notes: @@ -23,6 +23,13 @@ notes: haproxy.cfg. See U(http://haproxy.1wt.eu/download/1.5/doc/configuration.txt). - Depends on netcat (C(nc)) being available; you need to install the appropriate package for your operating system before this module can be used. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: backend: description: diff --git a/plugins/modules/heroku_collaborator.py b/plugins/modules/heroku_collaborator.py index e29439ca2f..e7b0de3f99 100644 --- a/plugins/modules/heroku_collaborator.py +++ b/plugins/modules/heroku_collaborator.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: heroku_collaborator -short_description: "Add or delete app collaborators on Heroku" +short_description: Add or delete app collaborators on Heroku description: - Manages collaborators for Heroku apps. - If set to C(present) and heroku user is already collaborator, then do nothing. @@ -21,6 +21,13 @@ author: - Marcel Arns (@marns93) requirements: - heroku3 +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: api_key: type: str diff --git a/plugins/modules/hg.py b/plugins/modules/hg.py index 777feaa059..dbbd504b4e 100644 --- a/plugins/modules/hg.py +++ b/plugins/modules/hg.py @@ -16,6 +16,13 @@ short_description: Manages Mercurial (hg) repositories description: - Manages Mercurial (hg) repositories. Supports SSH, HTTP/S and local address. author: "Yeukhon Wong (@yeukhon)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: repo: description: diff --git a/plugins/modules/hipchat.py b/plugins/modules/hipchat.py index 1871bd23ae..11b5fb7358 100644 --- a/plugins/modules/hipchat.py +++ b/plugins/modules/hipchat.py @@ -12,9 +12,16 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: hipchat -short_description: Send a message to Hipchat. +short_description: Send a message to Hipchat description: - Send a message to a Hipchat room, with options to control the formatting. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: token: type: str diff --git a/plugins/modules/homebrew.py b/plugins/modules/homebrew.py index a72d929a81..7592f95a41 100644 --- a/plugins/modules/homebrew.py +++ b/plugins/modules/homebrew.py @@ -22,11 +22,17 @@ author: - "Daniel Jaouen (@danieljaouen)" - "Andrew Dunham (@andrew-d)" requirements: - - "python >= 2.6" - - homebrew must already be installed on the target system + - homebrew must already be installed on the target system short_description: Package manager for Homebrew description: - Manages Homebrew packages +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/homebrew_cask.py b/plugins/modules/homebrew_cask.py index 5937831962..162c184612 100644 --- a/plugins/modules/homebrew_cask.py +++ b/plugins/modules/homebrew_cask.py @@ -15,14 +15,19 @@ DOCUMENTATION = ''' --- module: homebrew_cask author: -- "Indrajit Raychaudhuri (@indrajitr)" -- "Daniel Jaouen (@danieljaouen)" -- "Enric Lluelles (@enriclluelles)" -requirements: -- "python >= 2.6" + - "Indrajit Raychaudhuri (@indrajitr)" + - "Daniel Jaouen (@danieljaouen)" + - "Enric Lluelles (@enriclluelles)" short_description: Install and uninstall homebrew casks description: -- Manages Homebrew casks. + - Manages Homebrew casks. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/homebrew_tap.py b/plugins/modules/homebrew_tap.py index 0cc5b23ce8..b230dbb34c 100644 --- a/plugins/modules/homebrew_tap.py +++ b/plugins/modules/homebrew_tap.py @@ -19,9 +19,16 @@ module: homebrew_tap author: - "Indrajit Raychaudhuri (@indrajitr)" - "Daniel Jaouen (@danieljaouen)" -short_description: Tap a Homebrew repository. +short_description: Tap a Homebrew repository description: - Tap external Homebrew repositories. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/homectl.py b/plugins/modules/homectl.py index c14dc4af9a..301e388d38 100644 --- a/plugins/modules/homectl.py +++ b/plugins/modules/homectl.py @@ -17,6 +17,13 @@ short_description: Manage user accounts with systemd-homed version_added: 4.4.0 description: - Manages a user's home directory managed by systemd-homed. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: description: diff --git a/plugins/modules/honeybadger_deployment.py b/plugins/modules/honeybadger_deployment.py index be8412f9af..820e4538e8 100644 --- a/plugins/modules/honeybadger_deployment.py +++ b/plugins/modules/honeybadger_deployment.py @@ -14,7 +14,14 @@ module: honeybadger_deployment author: "Benjamin Curtis (@stympy)" short_description: Notify Honeybadger.io about app deployments description: - - Notify Honeybadger.io about app deployments (see http://docs.honeybadger.io/article/188-deployment-tracking) + - Notify Honeybadger.io about app deployments (see U(http://docs.honeybadger.io/article/188-deployment-tracking)). +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: token: type: str diff --git a/plugins/modules/hpilo_boot.py b/plugins/modules/hpilo_boot.py index f663a7b5f5..ace79a493a 100644 --- a/plugins/modules/hpilo_boot.py +++ b/plugins/modules/hpilo_boot.py @@ -15,9 +15,16 @@ module: hpilo_boot author: Dag Wieers (@dagwieers) short_description: Boot system using specific media through HP iLO interface description: -- "This module boots a system through its HP iLO interface. The boot media - can be one of: cdrom, floppy, hdd, network or usb." -- This module requires the hpilo python module. + - "This module boots a system through its HP iLO interface. The boot media + can be one of: cdrom, floppy, hdd, network or usb." + - This module requires the hpilo python module. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: host: description: diff --git a/plugins/modules/hponcfg.py b/plugins/modules/hponcfg.py index 65e40c46ed..612a20d923 100644 --- a/plugins/modules/hponcfg.py +++ b/plugins/modules/hponcfg.py @@ -15,7 +15,14 @@ module: hponcfg author: Dag Wieers (@dagwieers) short_description: Configure HP iLO interface using hponcfg description: - - This modules configures the HP iLO interface using hponcfg. + - This modules configures the HP iLO interface using hponcfg. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: path: description: diff --git a/plugins/modules/htpasswd.py b/plugins/modules/htpasswd.py index 4f05d21b0d..180b020733 100644 --- a/plugins/modules/htpasswd.py +++ b/plugins/modules/htpasswd.py @@ -11,10 +11,15 @@ __metaclass__ = type DOCUMENTATION = ''' module: htpasswd -short_description: manage user files for basic authentication +short_description: Manage user files for basic authentication description: - Add and remove username/password entries in a password file using htpasswd. - This is used by web servers such as Apache and Nginx for basic authentication. +attributes: + check_mode: + support: full + diff_mode: + support: none options: path: type: path @@ -41,9 +46,12 @@ options: description: - Encryption scheme to be used. As well as the four choices listed here, you can also use any other hash supported by passlib, such as - md5_crypt and sha256_crypt, which are linux passwd hashes. If you - do so the password file will not be compatible with Apache or Nginx - - 'Some of the available choices might be: C(apr_md5_crypt), C(des_crypt), C(ldap_sha1), C(plaintext)' + C(portable_apache22) and C(host_apache24); or C(md5_crypt) and C(sha256_crypt), + which are Linux passwd hashes. Only some schemes in addition to + the four choices below will be compatible with Apache or Nginx, and + supported schemes depend on passlib version and its dependencies. + - See U(https://passlib.readthedocs.io/en/stable/lib/passlib.apache.html#passlib.apache.HtpasswdFile) parameter C(default_scheme). + - 'Some of the available choices might be: C(apr_md5_crypt), C(des_crypt), C(ldap_sha1), C(plaintext).' state: type: str required: false @@ -65,7 +73,9 @@ notes: - "On RHEL or CentOS: Enable EPEL, then install I(python-passlib)." requirements: [ passlib>=1.6 ] author: "Ansible Core Team" -extends_documentation_fragment: files +extends_documentation_fragment: + - files + - community.general.attributes ''' EXAMPLES = """ diff --git a/plugins/modules/hwc_ecs_instance.py b/plugins/modules/hwc_ecs_instance.py index 10d913f9b5..434db242f2 100644 --- a/plugins/modules/hwc_ecs_instance.py +++ b/plugins/modules/hwc_ecs_instance.py @@ -22,6 +22,11 @@ version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -225,7 +230,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_evs_disk.py b/plugins/modules/hwc_evs_disk.py index 7b5a99fb7f..7d445ddd21 100644 --- a/plugins/modules/hwc_evs_disk.py +++ b/plugins/modules/hwc_evs_disk.py @@ -22,6 +22,11 @@ version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -150,7 +155,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_network_vpc.py b/plugins/modules/hwc_network_vpc.py index 78f5925e0c..357fd55204 100644 --- a/plugins/modules/hwc_network_vpc.py +++ b/plugins/modules/hwc_network_vpc.py @@ -22,6 +22,11 @@ author: Huawei Inc. (@huaweicloud) requirements: - requests >= 2.18.4 - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -61,7 +66,8 @@ options: type: str required: true extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_smn_topic.py b/plugins/modules/hwc_smn_topic.py index 3752e1f18f..88207d3f93 100644 --- a/plugins/modules/hwc_smn_topic.py +++ b/plugins/modules/hwc_smn_topic.py @@ -22,6 +22,11 @@ author: Huawei Inc. (@huaweicloud) requirements: - requests >= 2.18.4 - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -45,7 +50,8 @@ options: type: str required: true extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_eip.py b/plugins/modules/hwc_vpc_eip.py index e14fb0e502..9fc0361b30 100644 --- a/plugins/modules/hwc_vpc_eip.py +++ b/plugins/modules/hwc_vpc_eip.py @@ -22,6 +22,11 @@ version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -121,7 +126,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_peering_connect.py b/plugins/modules/hwc_vpc_peering_connect.py index 01c52932ba..2d6832ce5d 100644 --- a/plugins/modules/hwc_vpc_peering_connect.py +++ b/plugins/modules/hwc_vpc_peering_connect.py @@ -23,6 +23,11 @@ version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -75,7 +80,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_port.py b/plugins/modules/hwc_vpc_port.py index aac9636f88..2d830493d4 100644 --- a/plugins/modules/hwc_vpc_port.py +++ b/plugins/modules/hwc_vpc_port.py @@ -22,6 +22,11 @@ version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -105,7 +110,8 @@ options: elements: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_private_ip.py b/plugins/modules/hwc_vpc_private_ip.py index e05c14f74d..c57ddc6708 100644 --- a/plugins/modules/hwc_vpc_private_ip.py +++ b/plugins/modules/hwc_vpc_private_ip.py @@ -19,13 +19,18 @@ description: - vpc private ip management. short_description: Creates a resource of Vpc/PrivateIP in Huawei Cloud notes: - - If I(id) option is provided, it takes precedence over I(subnet_id), I(ip_address) for private ip selection. - - I(subnet_id), I(ip_address) are used for private ip selection. If more than one private ip with this options exists, execution is aborted. - - No parameter support updating. If one of option is changed, the module will create a new resource. + - If I(id) option is provided, it takes precedence over I(subnet_id), I(ip_address) for private ip selection. + - I(subnet_id), I(ip_address) are used for private ip selection. If more than one private ip with this options exists, execution is aborted. + - No parameter support updating. If one of option is changed, the module will create a new resource. version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -48,7 +53,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_route.py b/plugins/modules/hwc_vpc_route.py index e08a9ebf38..1612cac50d 100644 --- a/plugins/modules/hwc_vpc_route.py +++ b/plugins/modules/hwc_vpc_route.py @@ -19,13 +19,18 @@ description: - vpc route management. short_description: Creates a resource of Vpc/Route in Huawei Cloud notes: - - If I(id) option is provided, it takes precedence over I(destination), I(vpc_id), I(type) and I(next_hop) for route selection. - - I(destination), I(vpc_id), I(type) and I(next_hop) are used for route selection. If more than one route with this options exists, execution is aborted. - - No parameter support updating. If one of option is changed, the module will create a new resource. + - If I(id) option is provided, it takes precedence over I(destination), I(vpc_id), I(type) and I(next_hop) for route selection. + - I(destination), I(vpc_id), I(type) and I(next_hop) are used for route selection. If more than one route with this options exists, execution is aborted. + - No parameter support updating. If one of option is changed, the module will create a new resource. version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -55,7 +60,8 @@ options: required: false default: 'peering' extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_security_group.py b/plugins/modules/hwc_vpc_security_group.py index 2338623890..c210b912d4 100644 --- a/plugins/modules/hwc_vpc_security_group.py +++ b/plugins/modules/hwc_vpc_security_group.py @@ -19,17 +19,22 @@ description: - vpc security group management. short_description: Creates a resource of Vpc/SecurityGroup in Huawei Cloud notes: - - If I(id) option is provided, it takes precedence over I(name), - I(enterprise_project_id) and I(vpc_id) for security group selection. - - I(name), I(enterprise_project_id) and I(vpc_id) are used for security - group selection. If more than one security group with this options exists, - execution is aborted. - - No parameter support updating. If one of option is changed, the module - will create a new resource. + - If I(id) option is provided, it takes precedence over I(name), + I(enterprise_project_id) and I(vpc_id) for security group selection. + - I(name), I(enterprise_project_id) and I(vpc_id) are used for security + group selection. If more than one security group with this options exists, + execution is aborted. + - No parameter support updating. If one of option is changed, the module + will create a new resource. version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -58,7 +63,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_security_group_rule.py b/plugins/modules/hwc_vpc_security_group_rule.py index ca6e2e9de8..bfb5d6a615 100644 --- a/plugins/modules/hwc_vpc_security_group_rule.py +++ b/plugins/modules/hwc_vpc_security_group_rule.py @@ -19,17 +19,22 @@ description: - vpc security group management. short_description: Creates a resource of Vpc/SecurityGroupRule in Huawei Cloud notes: - - If I(id) option is provided, it takes precedence over - I(enterprise_project_id) for security group rule selection. - - I(security_group_id) is used for security group rule selection. If more - than one security group rule with this options exists, execution is - aborted. - - No parameter support updating. If one of option is changed, the module - will create a new resource. + - If I(id) option is provided, it takes precedence over + I(enterprise_project_id) for security group rule selection. + - I(security_group_id) is used for security group rule selection. If more + than one security group rule with this options exists, execution is + aborted. + - No parameter support updating. If one of option is changed, the module + will create a new resource. version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -100,7 +105,8 @@ options: type: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/hwc_vpc_subnet.py b/plugins/modules/hwc_vpc_subnet.py index 4b192a5682..7fb107f534 100644 --- a/plugins/modules/hwc_vpc_subnet.py +++ b/plugins/modules/hwc_vpc_subnet.py @@ -22,6 +22,11 @@ version_added: '0.2.0' author: Huawei Inc. (@huaweicloud) requirements: - keystoneauth1 >= 3.6.0 +attributes: + check_mode: + support: full + diff_mode: + support: none options: state: description: @@ -94,7 +99,8 @@ options: elements: str required: false extends_documentation_fragment: -- community.general.hwc + - community.general.hwc + - community.general.attributes ''' diff --git a/plugins/modules/ibm_sa_domain.py b/plugins/modules/ibm_sa_domain.py index 26e4b7e47c..774f29134c 100644 --- a/plugins/modules/ibm_sa_domain.py +++ b/plugins/modules/ibm_sa_domain.py @@ -18,6 +18,12 @@ short_description: Manages domains on IBM Spectrum Accelerate Family storage sys description: - "This module can be used to add domains to or removes them from IBM Spectrum Accelerate Family storage systems." +attributes: + check_mode: + support: none + diff_mode: + support: none + options: domain: description: @@ -82,8 +88,8 @@ options: type: str extends_documentation_fragment: -- community.general.ibm_storage - + - community.general.ibm_storage + - community.general.attributes author: - Tzur Eliyahu (@tzure) diff --git a/plugins/modules/ibm_sa_host.py b/plugins/modules/ibm_sa_host.py index 2902a02028..614865ae01 100644 --- a/plugins/modules/ibm_sa_host.py +++ b/plugins/modules/ibm_sa_host.py @@ -13,11 +13,17 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ibm_sa_host -short_description: Adds hosts to or removes them from IBM Spectrum Accelerate Family storage systems. +short_description: Adds hosts to or removes them from IBM Spectrum Accelerate Family storage systems description: - "This module adds hosts to or removes them from IBM Spectrum Accelerate Family storage systems." +attributes: + check_mode: + support: none + diff_mode: + support: none + options: host: description: @@ -56,8 +62,8 @@ options: type: str extends_documentation_fragment: -- community.general.ibm_storage - + - community.general.ibm_storage + - community.general.attributes author: - Tzur Eliyahu (@tzure) diff --git a/plugins/modules/ibm_sa_host_ports.py b/plugins/modules/ibm_sa_host_ports.py index 147c434344..fdb27f85a2 100644 --- a/plugins/modules/ibm_sa_host_ports.py +++ b/plugins/modules/ibm_sa_host_ports.py @@ -13,12 +13,18 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ibm_sa_host_ports -short_description: Add host ports on IBM Spectrum Accelerate Family storage systems. +short_description: Add host ports on IBM Spectrum Accelerate Family storage systems description: - "This module adds ports to or removes them from the hosts on IBM Spectrum Accelerate Family storage systems." +attributes: + check_mode: + support: none + diff_mode: + support: none + options: host: description: @@ -48,7 +54,8 @@ options: type: str extends_documentation_fragment: -- community.general.ibm_storage + - community.general.ibm_storage + - community.general.attributes author: - Tzur Eliyahu (@tzure) diff --git a/plugins/modules/ibm_sa_pool.py b/plugins/modules/ibm_sa_pool.py index 6393a70686..88065aa4ec 100644 --- a/plugins/modules/ibm_sa_pool.py +++ b/plugins/modules/ibm_sa_pool.py @@ -13,11 +13,17 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ibm_sa_pool -short_description: Handles pools on IBM Spectrum Accelerate Family storage systems. +short_description: Handles pools on IBM Spectrum Accelerate Family storage systems description: - "This module creates or deletes pools to be used on IBM Spectrum Accelerate Family storage systems" +attributes: + check_mode: + support: none + diff_mode: + support: none + options: pool: description: @@ -52,8 +58,8 @@ options: type: str extends_documentation_fragment: -- community.general.ibm_storage - + - community.general.ibm_storage + - community.general.attributes author: - Tzur Eliyahu (@tzure) diff --git a/plugins/modules/ibm_sa_vol.py b/plugins/modules/ibm_sa_vol.py index 6e28fcfd05..bc5f81b32f 100644 --- a/plugins/modules/ibm_sa_vol.py +++ b/plugins/modules/ibm_sa_vol.py @@ -13,11 +13,17 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ibm_sa_vol -short_description: Handle volumes on IBM Spectrum Accelerate Family storage systems. +short_description: Handle volumes on IBM Spectrum Accelerate Family storage systems description: - "This module creates or deletes volumes to be used on IBM Spectrum Accelerate Family storage systems." +attributes: + check_mode: + support: none + diff_mode: + support: none + options: vol: description: @@ -42,8 +48,8 @@ options: type: str extends_documentation_fragment: -- community.general.ibm_storage - + - community.general.ibm_storage + - community.general.attributes author: - Tzur Eliyahu (@tzure) diff --git a/plugins/modules/ibm_sa_vol_map.py b/plugins/modules/ibm_sa_vol_map.py index 72de7d8c07..ea8b485ef1 100644 --- a/plugins/modules/ibm_sa_vol_map.py +++ b/plugins/modules/ibm_sa_vol_map.py @@ -13,12 +13,18 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ibm_sa_vol_map -short_description: Handles volume mapping on IBM Spectrum Accelerate Family storage systems. +short_description: Handles volume mapping on IBM Spectrum Accelerate Family storage systems description: - "This module maps volumes to or unmaps them from the hosts on IBM Spectrum Accelerate Family storage systems." +attributes: + check_mode: + support: none + diff_mode: + support: none + options: vol: description: @@ -55,8 +61,8 @@ options: type: str extends_documentation_fragment: -- community.general.ibm_storage - + - community.general.ibm_storage + - community.general.attributes author: - Tzur Eliyahu (@tzure) diff --git a/plugins/modules/icinga2_feature.py b/plugins/modules/icinga2_feature.py index 2f1d5629d5..6e6bc54161 100644 --- a/plugins/modules/icinga2_feature.py +++ b/plugins/modules/icinga2_feature.py @@ -21,6 +21,13 @@ short_description: Manage Icinga2 feature description: - This module can be used to enable or disable an Icinga2 feature. author: "Loic Blot (@nerzhul)" +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: name: type: str diff --git a/plugins/modules/icinga2_host.py b/plugins/modules/icinga2_host.py index 0f4e2b26a0..a740e68754 100644 --- a/plugins/modules/icinga2_host.py +++ b/plugins/modules/icinga2_host.py @@ -19,6 +19,11 @@ description: - "Add or remove a host to Icinga2 through the API." - "See U(https://www.icinga.com/docs/icinga2/latest/doc/12-icinga2-api/)" author: "Jurgen Brand (@t794104)" +attributes: + check_mode: + support: full + diff_mode: + support: none options: url: type: str @@ -107,7 +112,8 @@ options: description: - Dictionary of variables. extends_documentation_fragment: - - url + - ansible.builtin.url + - community.general.attributes ''' EXAMPLES = ''' diff --git a/plugins/modules/idrac_redfish_command.py b/plugins/modules/idrac_redfish_command.py index 9e523b6d11..3c81267f45 100644 --- a/plugins/modules/idrac_redfish_command.py +++ b/plugins/modules/idrac_redfish_command.py @@ -16,6 +16,13 @@ description: - Builds Redfish URIs locally and sends them to remote OOB controllers to perform an action. - For use with Dell iDRAC operations that require Redfish OEM extensions. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: category: required: true diff --git a/plugins/modules/idrac_redfish_config.py b/plugins/modules/idrac_redfish_config.py index f995258c7d..cc47e62d2e 100644 --- a/plugins/modules/idrac_redfish_config.py +++ b/plugins/modules/idrac_redfish_config.py @@ -16,6 +16,13 @@ description: - For use with Dell iDRAC operations that require Redfish OEM extensions - Builds Redfish URIs locally and sends them to remote iDRAC controllers to set or update a configuration attribute. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: category: required: true diff --git a/plugins/modules/idrac_redfish_info.py b/plugins/modules/idrac_redfish_info.py index cb44a75961..aece616641 100644 --- a/plugins/modules/idrac_redfish_info.py +++ b/plugins/modules/idrac_redfish_info.py @@ -21,6 +21,10 @@ description: extends_documentation_fragment: - community.general.attributes - community.general.attributes.info_module +attributes: + check_mode: + version_added: 3.3.0 + # This was backported to 2.5.4 and 1.3.11 as well, since this was a bugfix options: category: required: true diff --git a/plugins/modules/ilo_redfish_config.py b/plugins/modules/ilo_redfish_config.py index d13d10dfdc..1f021895dc 100644 --- a/plugins/modules/ilo_redfish_config.py +++ b/plugins/modules/ilo_redfish_config.py @@ -15,6 +15,13 @@ description: - Builds Redfish URIs locally and sends them to iLO to set or update a configuration attribute. - For use with HPE iLO operations that require Redfish OEM extensions. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: category: required: true @@ -125,7 +132,7 @@ def main(): password=dict(no_log=True), auth_token=dict(no_log=True), attribute_name=dict(required=True), - attribute_value=dict(), + attribute_value=dict(type='str'), timeout=dict(type='int', default=10) ), required_together=[ diff --git a/plugins/modules/imc_rest.py b/plugins/modules/imc_rest.py index 4d90bcc54d..4bbaad23af 100644 --- a/plugins/modules/imc_rest.py +++ b/plugins/modules/imc_rest.py @@ -13,15 +13,22 @@ DOCUMENTATION = r''' module: imc_rest short_description: Manage Cisco IMC hardware through its REST API description: -- Provides direct access to the Cisco IMC REST API. -- Perform any configuration changes and actions that the Cisco IMC supports. -- More information about the IMC REST API is available from - U(http://www.cisco.com/c/en/us/td/docs/unified_computing/ucs/c/sw/api/3_0/b_Cisco_IMC_api_301.html) + - Provides direct access to the Cisco IMC REST API. + - Perform any configuration changes and actions that the Cisco IMC supports. + - More information about the IMC REST API is available from + U(http://www.cisco.com/c/en/us/td/docs/unified_computing/ucs/c/sw/api/3_0/b_Cisco_IMC_api_301.html). author: -- Dag Wieers (@dagwieers) + - Dag Wieers (@dagwieers) requirements: -- lxml -- xmljson >= 0.1.8 + - lxml + - xmljson >= 0.1.8 +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: hostname: description: diff --git a/plugins/modules/imgadm.py b/plugins/modules/imgadm.py index 2357fffa30..6e4b810982 100644 --- a/plugins/modules/imgadm.py +++ b/plugins/modules/imgadm.py @@ -16,6 +16,13 @@ short_description: Manage SmartOS images description: - Manage SmartOS virtual machine images through imgadm(1M) author: Jasper Lievisse Adriaanse (@jasperla) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: force: required: false diff --git a/plugins/modules/infinity.py b/plugins/modules/infinity.py index 4b0e835209..65aa591f4c 100644 --- a/plugins/modules/infinity.py +++ b/plugins/modules/infinity.py @@ -15,6 +15,13 @@ description: - Manage Infinity IPAM using REST API. author: - Meirong Liu (@MeganLiu) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: none + diff_mode: + support: none options: server_ip: description: diff --git a/plugins/modules/influxdb_database.py b/plugins/modules/influxdb_database.py index 8ffbece606..046b16e18c 100644 --- a/plugins/modules/influxdb_database.py +++ b/plugins/modules/influxdb_database.py @@ -20,6 +20,11 @@ requirements: - "python >= 2.6" - "influxdb >= 0.9" - requests +attributes: + check_mode: + support: full + diff_mode: + support: none options: database_name: description: @@ -33,7 +38,8 @@ options: default: present type: str extends_documentation_fragment: -- community.general.influxdb + - community.general.influxdb + - community.general.attributes ''' diff --git a/plugins/modules/influxdb_query.py b/plugins/modules/influxdb_query.py index 14a65a60dc..c2e3d8acc4 100644 --- a/plugins/modules/influxdb_query.py +++ b/plugins/modules/influxdb_query.py @@ -18,6 +18,11 @@ author: "René Moser (@resmo)" requirements: - "python >= 2.6" - "influxdb >= 0.9" +attributes: + check_mode: + support: full + diff_mode: + support: none options: query: description: @@ -30,7 +35,8 @@ options: required: true type: str extends_documentation_fragment: -- community.general.influxdb + - community.general.influxdb + - community.general.attributes ''' diff --git a/plugins/modules/influxdb_retention_policy.py b/plugins/modules/influxdb_retention_policy.py index 1b7d7eec9a..28d5450ff0 100644 --- a/plugins/modules/influxdb_retention_policy.py +++ b/plugins/modules/influxdb_retention_policy.py @@ -20,6 +20,11 @@ requirements: - "python >= 2.6" - "influxdb >= 0.9" - requests +attributes: + check_mode: + support: full + diff_mode: + support: none options: database_name: description: @@ -64,7 +69,8 @@ options: type: str version_added: '2.0.0' extends_documentation_fragment: -- community.general.influxdb + - community.general.influxdb + - community.general.attributes ''' diff --git a/plugins/modules/influxdb_user.py b/plugins/modules/influxdb_user.py index 25bc2a95ce..bbd0f8f5ab 100644 --- a/plugins/modules/influxdb_user.py +++ b/plugins/modules/influxdb_user.py @@ -20,6 +20,11 @@ author: "Vitaliy Zhhuta (@zhhuta)" requirements: - "python >= 2.6" - "influxdb >= 0.9" +attributes: + check_mode: + support: full + diff_mode: + support: none options: user_name: description: @@ -52,7 +57,8 @@ options: type: list elements: dict extends_documentation_fragment: -- community.general.influxdb + - community.general.influxdb + - community.general.attributes ''' @@ -168,8 +174,14 @@ def drop_user(module, client, user_name): def set_user_grants(module, client, user_name, grants): changed = False + current_grants = [] try: current_grants = client.get_list_privileges(user_name) + except influx.exceptions.InfluxDBClientError as e: + if not module.check_mode or 'user not found' not in e.content: + module.fail_json(msg=e.content) + + try: parsed_grants = [] # Fix privileges wording for i, v in enumerate(current_grants): diff --git a/plugins/modules/influxdb_write.py b/plugins/modules/influxdb_write.py index 68e722ae1c..f95b6dae8c 100644 --- a/plugins/modules/influxdb_write.py +++ b/plugins/modules/influxdb_write.py @@ -18,6 +18,11 @@ author: "René Moser (@resmo)" requirements: - "python >= 2.6" - "influxdb >= 0.9" +attributes: + check_mode: + support: none + diff_mode: + support: none options: data_points: description: @@ -31,7 +36,8 @@ options: required: true type: str extends_documentation_fragment: -- community.general.influxdb + - community.general.influxdb + - community.general.attributes ''' diff --git a/plugins/modules/ini_file.py b/plugins/modules/ini_file.py index ee4ad62b72..231a0e836a 100644 --- a/plugins/modules/ini_file.py +++ b/plugins/modules/ini_file.py @@ -15,14 +15,21 @@ DOCUMENTATION = r''' --- module: ini_file short_description: Tweak settings in INI files -extends_documentation_fragment: files +extends_documentation_fragment: + - files + - community.general.attributes description: - - Manage (add, remove, change) individual settings in an INI-style file without having - to manage the file as a whole with, say, M(ansible.builtin.template) or M(ansible.builtin.assemble). - - Adds missing sections if they don't exist. - - Before Ansible 2.0, comments are discarded when the source file is read, and therefore will not show up in the destination file. - - Since Ansible 2.3, this module adds missing ending newlines to files to keep in line with the POSIX standard, even when - no other modifications need to be applied. + - Manage (add, remove, change) individual settings in an INI-style file without having + to manage the file as a whole with, say, M(ansible.builtin.template) or M(ansible.builtin.assemble). + - Adds missing sections if they don't exist. + - Before Ansible 2.0, comments are discarded when the source file is read, and therefore will not show up in the destination file. + - Since Ansible 2.3, this module adds missing ending newlines to files to keep in line with the POSIX standard, even when + no other modifications need to be applied. +attributes: + check_mode: + support: full + diff_mode: + support: full options: path: description: diff --git a/plugins/modules/installp.py b/plugins/modules/installp.py index f1421bc390..41064363db 100644 --- a/plugins/modules/installp.py +++ b/plugins/modules/installp.py @@ -12,10 +12,17 @@ DOCUMENTATION = r''' --- module: installp author: -- Kairo Araujo (@kairoaraujo) + - Kairo Araujo (@kairoaraujo) short_description: Manage packages on AIX description: - Manage packages using 'installp' on AIX +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none options: accept_license: description: diff --git a/plugins/modules/interfaces_file.py b/plugins/modules/interfaces_file.py index fe4223a12f..f19c019f4e 100644 --- a/plugins/modules/interfaces_file.py +++ b/plugins/modules/interfaces_file.py @@ -13,11 +13,18 @@ DOCUMENTATION = ''' --- module: interfaces_file short_description: Tweak settings in /etc/network/interfaces files -extends_documentation_fragment: files +extends_documentation_fragment: + - ansible.builtin.files + - community.general.attributes description: - - Manage (add, remove, change) individual interface options in an interfaces-style file without having - to manage the file as a whole with, say, M(ansible.builtin.template) or M(ansible.builtin.assemble). Interface has to be presented in a file. - - Read information about interfaces from interfaces-styled files + - Manage (add, remove, change) individual interface options in an interfaces-style file without having + to manage the file as a whole with, say, M(ansible.builtin.template) or M(ansible.builtin.assemble). Interface has to be presented in a file. + - Read information about interfaces from interfaces-styled files +attributes: + check_mode: + support: full + diff_mode: + support: none options: dest: type: path @@ -149,20 +156,22 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_bytes -def line_dict(line): +def lineDict(line): return {'line': line, 'line_type': 'unknown'} -def make_option_dict(line, iface, option, value, address_family): +def optionDict(line, iface, option, value, address_family): return {'line': line, 'iface': iface, 'option': option, 'value': value, 'line_type': 'option', 'address_family': address_family} -def get_option_value(line): - patt = re.compile(r'^\s+(?P