diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml index 52eaf65069..c8e03a164b 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) @@ -188,6 +189,24 @@ stages: - test: 3.5 ## Remote + - stage: Remote_devel_extra_vms + displayName: Remote devel extra VMs + dependsOn: [] + jobs: + - template: templates/matrix.yml + parameters: + testFormat: devel/{0} + targets: + - name: Alpine 3.16 + test: alpine/3.16 + # - name: Fedora 36 + # test: fedora/36 + # - 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: [] @@ -202,8 +221,6 @@ stages: test: rhel/7.9 - name: RHEL 9.0 test: rhel/9.0 - - name: FreeBSD 12.3 - test: freebsd/12.3 - name: FreeBSD 13.1 test: freebsd/13.1 groups: @@ -220,8 +237,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 @@ -385,7 +402,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 @@ -458,6 +475,7 @@ stages: - Units_2_12 - Units_2_13 - Units_2_14 + - Remote_devel_extra_vms - Remote_devel - Remote_2_11 - Remote_2_12 diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index dfacfbecfb..422048ca88 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -265,6 +265,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,9 +281,14 @@ 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 @@ -302,6 +309,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 +323,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: @@ -665,6 +677,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: @@ -822,7 +838,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_: @@ -1072,7 +1089,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: 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..b74e399820 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,13 +6,149 @@ Community General Release Notes This changelog describes changes after version 5.0.0. -v6.0.0-a1 -========= +v6.2.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. +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 +169,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 +178,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 +203,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 +216,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 +224,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 +267,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 +322,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 +365,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 +375,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..77888bdb7b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ 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) [![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 +72,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 +88,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 +116,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 +135,8 @@ See [this issue](https://github.com/ansible-collections/community.general/issues This collection is primarily licensed and distributed as a whole under the GNU General Public License v3.0 or later. -See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.general/blob/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..5d45cc7534 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,321 @@ 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' 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..f55ebce545 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -5,7 +5,7 @@ namespace: community name: general -version: 6.0.0 +version: 6.2.0 readme: README.md authors: - Ansible (https://github.com/ansible) 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/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/filter/jc.py b/plugins/filter/jc.py index 879647a04d..6708f573d3 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 diff --git a/plugins/inventory/lxd.py b/plugins/inventory/lxd.py index 291d12b037..2e37de70c1 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,6 +145,9 @@ groupby: vlan666: type: vlanid attribute: 666 + projectInternals: + type: project + attribute: internals ''' import binascii @@ -153,6 +161,7 @@ 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 +339,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 +368,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 +602,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 +707,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 +784,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 +944,7 @@ class InventoryModule(BaseInventoryPlugin): * 'profile' * 'vlanid' * 'type' + * 'project' Args: str(group_name): Group name @@ -926,6 +972,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 +1080,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..f0fa50e3b3 100644 --- a/plugins/inventory/nmap.py +++ b/plugins/inventory/nmap.py @@ -46,6 +46,25 @@ 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 + 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' @@ -166,6 +185,15 @@ 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') + cmd.append(self._options['address']) try: # execute diff --git a/plugins/inventory/proxmox.py b/plugins/inventory/proxmox.py index b24bcacf25..e33f7ed77d 100644 --- a/plugins/inventory/proxmox.py +++ b/plugins/inventory/proxmox.py @@ -408,7 +408,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 +615,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/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/onepassword.py b/plugins/lookup/onepassword.py index bbd11d6645..5e9549c2b7 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 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/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/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..09b22b7561 100644 --- a/plugins/module_utils/identity/keycloak/keycloak.py +++ b/plugins/module_utils/identity/keycloak/keycloak.py @@ -58,6 +58,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 +608,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 +1162,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. 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/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/lxd.py b/plugins/module_utils/lxd.py index bdf026313a..007de4d8db 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): @@ -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/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/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..4a56346c3f 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 '\ @@ -143,7 +145,7 @@ 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) @@ -155,6 +157,11 @@ class RedfishUtils(object): 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 +176,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 +226,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) @@ -1384,11 +1426,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 +1552,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 = {} 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/modules/ali_instance.py b/plugins/modules/ali_instance.py index 4acec0a109..96a042f5ca 100644 --- a/plugins/modules/ali_instance.py +++ b/plugins/modules/ali_instance.py @@ -27,7 +27,7 @@ __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. diff --git a/plugins/modules/ali_instance_info.py b/plugins/modules/ali_instance_info.py index ea7bcc8d4a..f489f96372 100644 --- a/plugins/modules/ali_instance_info.py +++ b/plugins/modules/ali_instance_info.py @@ -27,7 +27,7 @@ __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. diff --git a/plugins/modules/ansible_galaxy_install.py b/plugins/modules/ansible_galaxy_install.py index 5e5ec54eb0..694591d8cb 100644 --- a/plugins/modules/ansible_galaxy_install.py +++ b/plugins/modules/ansible_galaxy_install.py @@ -20,6 +20,10 @@ 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 options: @@ -185,7 +189,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 +230,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 +249,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 +355,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_module.py b/plugins/modules/apache2_module.py index 65d2f689bf..a58c0f0c54 100644 --- a/plugins/modules/apache2_module.py +++ b/plugins/modules/apache2_module.py @@ -16,7 +16,7 @@ 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. options: diff --git a/plugins/modules/apt_rpm.py b/plugins/modules/apt_rpm.py index 2b6bf3e8a2..d949a61e68 100644 --- a/plugins/modules/apt_rpm.py +++ b/plugins/modules/apt_rpm.py @@ -14,7 +14,7 @@ __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. options: diff --git a/plugins/modules/beadm.py b/plugins/modules/beadm.py index 8cb43c6cbb..7a84997089 100644 --- a/plugins/modules/beadm.py +++ b/plugins/modules/beadm.py @@ -12,7 +12,7 @@ __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. diff --git a/plugins/modules/circonus_annotation.py b/plugins/modules/circonus_annotation.py index 6248fd2f55..661c854e6d 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)" diff --git a/plugins/modules/clc_aa_policy.py b/plugins/modules/clc_aa_policy.py index d5d56b2a65..d1fba2429a 100644 --- a/plugins/modules/clc_aa_policy.py +++ b/plugins/modules/clc_aa_policy.py @@ -11,7 +11,7 @@ __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. options: diff --git a/plugins/modules/clc_alert_policy.py b/plugins/modules/clc_alert_policy.py index c7f02c2ffa..1d733013d2 100644 --- a/plugins/modules/clc_alert_policy.py +++ b/plugins/modules/clc_alert_policy.py @@ -12,7 +12,7 @@ __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. options: diff --git a/plugins/modules/clc_blueprint_package.py b/plugins/modules/clc_blueprint_package.py index 0dc29b0ce0..cb23df852b 100644 --- a/plugins/modules/clc_blueprint_package.py +++ b/plugins/modules/clc_blueprint_package.py @@ -11,7 +11,7 @@ __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. options: diff --git a/plugins/modules/clc_loadbalancer.py b/plugins/modules/clc_loadbalancer.py index d13c2d76ce..ab6d866fb6 100644 --- a/plugins/modules/clc_loadbalancer.py +++ b/plugins/modules/clc_loadbalancer.py @@ -12,7 +12,7 @@ __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. options: diff --git a/plugins/modules/clc_modify_server.py b/plugins/modules/clc_modify_server.py index ff0611e3f8..786cdf2ae4 100644 --- a/plugins/modules/clc_modify_server.py +++ b/plugins/modules/clc_modify_server.py @@ -11,7 +11,7 @@ __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. options: diff --git a/plugins/modules/clc_publicip.py b/plugins/modules/clc_publicip.py index 98d392adf9..5111b3cf19 100644 --- a/plugins/modules/clc_publicip.py +++ b/plugins/modules/clc_publicip.py @@ -11,7 +11,7 @@ __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. options: diff --git a/plugins/modules/clc_server.py b/plugins/modules/clc_server.py index 062c5ea411..d8e4f16217 100644 --- a/plugins/modules/clc_server.py +++ b/plugins/modules/clc_server.py @@ -11,7 +11,7 @@ __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. options: diff --git a/plugins/modules/clc_server_snapshot.py b/plugins/modules/clc_server_snapshot.py index 44f52ece64..096abfe29b 100644 --- a/plugins/modules/clc_server_snapshot.py +++ b/plugins/modules/clc_server_snapshot.py @@ -11,7 +11,7 @@ __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. options: 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/consul.py b/plugins/modules/consul.py index 0d75bde2eb..aabc1bd4b7 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 diff --git a/plugins/modules/cpanm.py b/plugins/modules/cpanm.py index 7ac8429bda..98f37d573e 100644 --- a/plugins/modules/cpanm.py +++ b/plugins/modules/cpanm.py @@ -13,7 +13,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: cpanm -short_description: Manages Perl library dependencies. +short_description: Manages Perl library dependencies description: - Manage Perl library dependencies using cpanminus. options: diff --git a/plugins/modules/deploy_helper.py b/plugins/modules/deploy_helper.py index 3d3fe08f28..afa63cba19 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 diff --git a/plugins/modules/dimensiondata_vlan.py b/plugins/modules/dimensiondata_vlan.py index ca25374dcb..86db5e5057 100644 --- a/plugins/modules/dimensiondata_vlan.py +++ b/plugins/modules/dimensiondata_vlan.py @@ -13,7 +13,7 @@ __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 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..b775f24ab0 100644 --- a/plugins/modules/dnsmadeeasy.py +++ b/plugins/modules/dnsmadeeasy.py @@ -12,7 +12,7 @@ __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 diff --git a/plugins/modules/etcd3.py b/plugins/modules/etcd3.py index e67227cc19..2a89c71968 100644 --- a/plugins/modules/etcd3.py +++ b/plugins/modules/etcd3.py @@ -12,7 +12,7 @@ __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: diff --git a/plugins/modules/gconftool2.py b/plugins/modules/gconftool2.py index 931b43d76c..a3ac8bb8f1 100644 --- a/plugins/modules/gconftool2.py +++ b/plugins/modules/gconftool2.py @@ -83,75 +83,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 +102,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/github_deploy_key.py b/plugins/modules/github_deploy_key.py index 97e7a1ac7f..bd32438b0a 100644 --- a/plugins/modules/github_deploy_key.py +++ b/plugins/modules/github_deploy_key.py @@ -13,7 +13,7 @@ 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 diff --git a/plugins/modules/github_issue.py b/plugins/modules/github_issue.py index 4f8f2363cc..d49837499a 100644 --- a/plugins/modules/github_issue.py +++ b/plugins/modules/github_issue.py @@ -12,7 +12,7 @@ __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. options: diff --git a/plugins/modules/github_key.py b/plugins/modules/github_key.py index 5dfd694275..3c7ee7bd7b 100644 --- a/plugins/modules/github_key.py +++ b/plugins/modules/github_key.py @@ -11,7 +11,7 @@ __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. options: diff --git a/plugins/modules/github_release.py b/plugins/modules/github_release.py index 84ce4ce4ec..0b3a5a886c 100644 --- a/plugins/modules/github_release.py +++ b/plugins/modules/github_release.py @@ -108,17 +108,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/gitlab_deploy_key.py b/plugins/modules/gitlab_deploy_key.py index 1b77801ec4..3ed2b2d7a5 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: diff --git a/plugins/modules/gitlab_group_variable.py b/plugins/modules/gitlab_group_variable.py index c273777ca3..4a185b2394 100644 --- a/plugins/modules/gitlab_group_variable.py +++ b/plugins/modules/gitlab_group_variable.py @@ -165,7 +165,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 +296,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 +327,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..70864207ed 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: 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_variable.py b/plugins/modules/gitlab_project_variable.py index cdd6402ae8..986847c07b 100644 --- a/plugins/modules/gitlab_project_variable.py +++ b/plugins/modules/gitlab_project_variable.py @@ -189,7 +189,7 @@ except Exception: 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 +255,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 +321,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 +353,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 +406,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..335e1445a2 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. @@ -25,7 +25,7 @@ extends_documentation_fragment: 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..1094df9424 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. diff --git a/plugins/modules/gunicorn.py b/plugins/modules/gunicorn.py index 9ed903dfd5..ff88cead7c 100644 --- a/plugins/modules/gunicorn.py +++ b/plugins/modules/gunicorn.py @@ -12,7 +12,7 @@ __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 diff --git a/plugins/modules/heroku_collaborator.py b/plugins/modules/heroku_collaborator.py index e29439ca2f..d76b2b6507 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. diff --git a/plugins/modules/hipchat.py b/plugins/modules/hipchat.py index 1871bd23ae..a5aa150f32 100644 --- a/plugins/modules/hipchat.py +++ b/plugins/modules/hipchat.py @@ -12,7 +12,7 @@ __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. options: diff --git a/plugins/modules/homebrew_tap.py b/plugins/modules/homebrew_tap.py index 0cc5b23ce8..7099773b21 100644 --- a/plugins/modules/homebrew_tap.py +++ b/plugins/modules/homebrew_tap.py @@ -19,7 +19,7 @@ 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. options: diff --git a/plugins/modules/htpasswd.py b/plugins/modules/htpasswd.py index 4f05d21b0d..231506984a 100644 --- a/plugins/modules/htpasswd.py +++ b/plugins/modules/htpasswd.py @@ -11,7 +11,7 @@ __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. @@ -41,9 +41,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 diff --git a/plugins/modules/ibm_sa_host.py b/plugins/modules/ibm_sa_host.py index 2902a02028..961e1bba19 100644 --- a/plugins/modules/ibm_sa_host.py +++ b/plugins/modules/ibm_sa_host.py @@ -13,7 +13,7 @@ __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." diff --git a/plugins/modules/ibm_sa_host_ports.py b/plugins/modules/ibm_sa_host_ports.py index 147c434344..fc543053a7 100644 --- a/plugins/modules/ibm_sa_host_ports.py +++ b/plugins/modules/ibm_sa_host_ports.py @@ -13,7 +13,7 @@ __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 diff --git a/plugins/modules/ibm_sa_pool.py b/plugins/modules/ibm_sa_pool.py index 6393a70686..998f3f74be 100644 --- a/plugins/modules/ibm_sa_pool.py +++ b/plugins/modules/ibm_sa_pool.py @@ -13,7 +13,7 @@ __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" diff --git a/plugins/modules/ibm_sa_vol.py b/plugins/modules/ibm_sa_vol.py index 6e28fcfd05..115ac9169f 100644 --- a/plugins/modules/ibm_sa_vol.py +++ b/plugins/modules/ibm_sa_vol.py @@ -13,7 +13,7 @@ __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." diff --git a/plugins/modules/ibm_sa_vol_map.py b/plugins/modules/ibm_sa_vol_map.py index 72de7d8c07..f493a2d979 100644 --- a/plugins/modules/ibm_sa_vol_map.py +++ b/plugins/modules/ibm_sa_vol_map.py @@ -13,7 +13,7 @@ __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 diff --git a/plugins/modules/ipa_subca.py b/plugins/modules/ipa_subca.py index ef9f74ed87..b470be6aef 100644 --- a/plugins/modules/ipa_subca.py +++ b/plugins/modules/ipa_subca.py @@ -11,7 +11,7 @@ DOCUMENTATION = r''' --- module: ipa_subca author: Abhijeet Kasurde (@Akasurde) -short_description: Manage FreeIPA Lightweight Sub Certificate Authorities. +short_description: Manage FreeIPA Lightweight Sub Certificate Authorities description: - Add, modify, enable, disable and delete an IPA Lightweight Sub Certificate Authorities using IPA API. options: diff --git a/plugins/modules/ipinfoio_facts.py b/plugins/modules/ipinfoio_facts.py index 676e88d84d..f29b3cbf4c 100644 --- a/plugins/modules/ipinfoio_facts.py +++ b/plugins/modules/ipinfoio_facts.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ipinfoio_facts -short_description: "Retrieve IP geolocation facts of a host's IP address" +short_description: Retrieve IP geolocation facts of a host's IP address description: - "Gather IP geolocation facts of a host's IP address using ipinfo.io API" author: "Aleksei Kostiuk (@akostyuk)" diff --git a/plugins/modules/iso_customize.py b/plugins/modules/iso_customize.py index c3a2ae2651..4f902f47e4 100644 --- a/plugins/modules/iso_customize.py +++ b/plugins/modules/iso_customize.py @@ -97,19 +97,14 @@ dest_iso: ''' import os -import traceback -PYCDLIB_IMP_ERR = None -try: - import pycdlib - HAS_PYCDLIB = True -except ImportError: - PYCDLIB_IMP_ERR = traceback.format_exc() - HAS_PYCDLIB = False - -from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible_collections.community.general.plugins.module_utils import deps +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.text.converters import to_native +with deps.declare("pycdlib"): + import pycdlib + # The upper dir exist, we only add subdirectoy def iso_add_dir(module, opened_iso, iso_type, dir_path): @@ -306,9 +301,7 @@ def main(): required_one_of=[('delete_files', 'add_files'), ], supports_check_mode=True, ) - if not HAS_PYCDLIB: - module.fail_json( - missing_required_lib('pycdlib'), exception=PYCDLIB_IMP_ERR) + deps.validate(module) src_iso = module.params['src_iso'] if not os.path.exists(src_iso): diff --git a/plugins/modules/java_cert.py b/plugins/modules/java_cert.py index 1d1327ed71..461f365a72 100644 --- a/plugins/modules/java_cert.py +++ b/plugins/modules/java_cert.py @@ -281,7 +281,8 @@ def _export_public_cert_from_pkcs12(module, executable, pkcs_file, alias, passwo (export_rc, export_stdout, export_err) = module.run_command(export_cmd, data=password, check_rc=False) if export_rc != 0: - module.fail_json(msg="Internal module failure, cannot extract public certificate from pkcs12, error: %s" % export_stdout, + module.fail_json(msg="Internal module failure, cannot extract public certificate from PKCS12, message: %s" % export_stdout, + stderr=export_err, rc=export_rc) with open(dest, 'w') as f: diff --git a/plugins/modules/jenkins_build.py b/plugins/modules/jenkins_build.py index 09304ccfbc..b02027f229 100644 --- a/plugins/modules/jenkins_build.py +++ b/plugins/modules/jenkins_build.py @@ -183,7 +183,10 @@ class JenkinsBuild: try: response = self.server.get_build_info(self.name, self.build_number) return response - + except jenkins.JenkinsException as e: + response = {} + response["result"] = "ABSENT" + return response except Exception as e: self.module.fail_json(msg='Unable to fetch build information, %s' % to_native(e), exception=traceback.format_exc()) @@ -231,7 +234,10 @@ class JenkinsBuild: if self.state == "stopped" and build_status['result'] == "ABORTED": result['changed'] = True result['build_info'] = build_status - elif build_status['result'] == "SUCCESS": + elif self.state == "absent" and build_status['result'] == "ABSENT": + result['changed'] = True + result['build_info'] = build_status + elif self.state != "absent" and build_status['result'] == "SUCCESS": result['changed'] = True result['build_info'] = build_status else: diff --git a/plugins/modules/jenkins_plugin.py b/plugins/modules/jenkins_plugin.py index 27261bf815..c4e1b7fb66 100644 --- a/plugins/modules/jenkins_plugin.py +++ b/plugins/modules/jenkins_plugin.py @@ -290,12 +290,6 @@ state: sample: "present" ''' -from ansible.module_utils.basic import AnsibleModule, to_bytes -from ansible.module_utils.six.moves import http_cookiejar as cookiejar -from ansible.module_utils.six.moves.urllib.parse import urlencode -from ansible.module_utils.urls import fetch_url, url_argument_spec -from ansible.module_utils.six import text_type, binary_type -from ansible.module_utils.common.text.converters import to_native import hashlib import io import json @@ -303,6 +297,15 @@ import os import tempfile import time +from ansible.module_utils.basic import AnsibleModule, to_bytes +from ansible.module_utils.six.moves import http_cookiejar as cookiejar +from ansible.module_utils.six.moves.urllib.parse import urlencode +from ansible.module_utils.urls import fetch_url, url_argument_spec +from ansible.module_utils.six import text_type, binary_type +from ansible.module_utils.common.text.converters import to_native + +from ansible_collections.community.general.plugins.module_utils.jenkins import download_updates_file + class FailedInstallingWithPluginManager(Exception): pass @@ -605,21 +608,12 @@ class JenkinsPlugin(object): return urls def _download_updates(self): - updates_filename = 'jenkins-plugin-cache.json' - updates_dir = os.path.expanduser('~/.ansible/tmp') - updates_file = "%s/%s" % (updates_dir, updates_filename) - download_updates = True - - # 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 < self.params['updates_expiration']: - download_updates = False - - updates_file_orig = updates_file + try: + updates_file, download_updates = download_updates_file(self.params['updates_expiration']) + except OSError as e: + self.module.fail_json( + msg="Cannot create temporal directory.", + details=to_native(e)) # Download the updates file if needed if download_updates: @@ -632,56 +626,39 @@ class JenkinsPlugin(object): msg_exception="Updates download failed.") # Write the updates file - update_fd, updates_file = tempfile.mkstemp() - os.write(update_fd, r.read()) + tmp_update_fd, tmp_updates_file = tempfile.mkstemp() + os.write(tmp_update_fd, r.read()) try: - os.close(update_fd) + os.close(tmp_update_fd) except IOError as e: self.module.fail_json( - msg="Cannot close the tmp updates file %s." % updates_file, + msg="Cannot close the tmp updates file %s." % tmp_updates_file, details=to_native(e)) # Open the updates file try: - f = io.open(updates_file, encoding='utf-8') + f = io.open(tmp_updates_file, encoding='utf-8') + + # Read only the second line + dummy = f.readline() + data = json.loads(f.readline()) except IOError as e: self.module.fail_json( msg="Cannot open temporal updates file.", details=to_native(e)) - - i = 0 - for line in f: - # Read only the second line - if i == 1: - try: - data = json.loads(line) - except Exception as e: - self.module.fail_json( - msg="Cannot load JSON data from the tmp updates file.", - details=to_native(e)) - - break - - i += 1 + except Exception as e: + self.module.fail_json( + msg="Cannot load JSON data from the tmp updates file.", + details=to_native(e)) # Move the updates file to the right place if we could read it if download_updates: - # Make sure the destination directory exists - if not os.path.isdir(updates_dir): - try: - os.makedirs(updates_dir, int('0700', 8)) - except OSError as e: - self.module.fail_json( - msg="Cannot create temporal directory.", - details=to_native(e)) - - self.module.atomic_move(updates_file, updates_file_orig) + self.module.atomic_move(tmp_updates_file, updates_file) # Check if we have the plugin data available - if 'plugins' not in data or self.params['name'] not in data['plugins']: - self.module.fail_json( - msg="Cannot find plugin data in the updates file.") + if not data.get('plugins', {}).get(self.params['name']): + self.module.fail_json(msg="Cannot find plugin data in the updates file.") return data['plugins'][self.params['name']] diff --git a/plugins/modules/jira.py b/plugins/modules/jira.py index 3b006a55bb..5e0a55119c 100644 --- a/plugins/modules/jira.py +++ b/plugins/modules/jira.py @@ -16,7 +16,7 @@ __metaclass__ = type DOCUMENTATION = r""" module: jira -short_description: create and modify issues in a JIRA instance +short_description: Create and modify issues in a JIRA instance description: - Create and modify issues in a JIRA instance. diff --git a/plugins/modules/keycloak_client_rolemapping.py b/plugins/modules/keycloak_client_rolemapping.py index 4f1f9b0d0f..f0da97ef59 100644 --- a/plugins/modules/keycloak_client_rolemapping.py +++ b/plugins/modules/keycloak_client_rolemapping.py @@ -295,7 +295,7 @@ def main(): assigned_roles_before = kc.get_client_group_composite_rolemappings(gid, cid, realm=realm) result['existing'] = assigned_roles_before - result['proposed'] = roles + result['proposed'] = list(assigned_roles_before) if assigned_roles_before else [] update_roles = [] for role_index, role in enumerate(roles, start=0): @@ -307,6 +307,7 @@ def main(): 'id': role['id'], 'name': role['name'], }) + result['proposed'].append(available_role) # Fetch roles to remove if state absent else: for assigned_role in assigned_roles_before: @@ -315,13 +316,15 @@ def main(): 'id': role['id'], 'name': role['name'], }) + if assigned_role in result['proposed']: # Handle double removal + result['proposed'].remove(assigned_role) if len(update_roles): if state == 'present': # Assign roles result['changed'] = True if module._diff: - result['diff'] = dict(before=assigned_roles_before, after=update_roles) + result['diff'] = dict(before=assigned_roles_before, after=result['proposed']) if module.check_mode: module.exit_json(**result) kc.add_group_rolemapping(gid, cid, update_roles, realm=realm) @@ -333,7 +336,7 @@ def main(): # Remove mapping of role result['changed'] = True if module._diff: - result['diff'] = dict(before=assigned_roles_before, after=update_roles) + result['diff'] = dict(before=assigned_roles_before, after=result['proposed']) if module.check_mode: module.exit_json(**result) kc.delete_group_rolemapping(gid, cid, update_roles, realm=realm) @@ -344,7 +347,7 @@ def main(): # Do nothing else: result['changed'] = False - result['msg'] = 'Nothing to do, roles %s are correctly mapped with group %s.' % (roles, group_name) + result['msg'] = 'Nothing to do, roles %s are %s with group %s.' % (roles, 'mapped' if state == 'present' else 'not mapped', group_name) module.exit_json(**result) diff --git a/plugins/modules/keycloak_clientsecret_info.py b/plugins/modules/keycloak_clientsecret_info.py new file mode 100644 index 0000000000..98a41ad20a --- /dev/null +++ b/plugins/modules/keycloak_clientsecret_info.py @@ -0,0 +1,161 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2022, Fynn Chen +# 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 + +DOCUMENTATION = ''' +--- +module: keycloak_clientsecret_info + +short_description: Retrieve client secret via Keycloak API + +version_added: 6.1.0 + +description: + - This module allows you to get a Keycloak client secret via the Keycloak + REST API. It requires access to the REST API via OpenID Connect; the user + connecting and the client being used must have the requisite access rights. + In a default Keycloak installation, admin-cli and an admin user would work, + as would a separate client definition with the scope tailored to your needs + and a user having the expected roles. + + - When retrieving a new client secret, where possible provide the client's + I(id) (not I(client_id)) to the module. This removes a lookup to the API to + translate the I(client_id) into the client ID. + + - "Note that this module returns the client secret. To avoid this showing up in the logs, + please add C(no_log: true) to the task." + +options: + realm: + type: str + description: + - They Keycloak realm under which this client resides. + default: 'master' + + id: + description: + - The unique identifier for this client. + - This parameter is not required for getting or generating a client secret but + providing it will reduce the number of API calls required. + type: str + + client_id: + description: + - The I(client_id) of the client. Passing this instead of I(id) results in an + extra API call. + aliases: + - clientId + type: str + + +extends_documentation_fragment: + - community.general.keycloak + - community.general.attributes + - community.general.attributes.info_module + +author: + - Fynn Chen (@fynncfchen) + - John Cant (@johncant) +''' + +EXAMPLES = ''' +- name: Get a Keycloak client secret, authentication with credentials + community.general.keycloak_clientsecret_info: + id: '9d59aa76-2755-48c6-b1af-beb70a82c3cd' + realm: MyCustomRealm + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + auth_realm: master + auth_username: USERNAME + auth_password: PASSWORD + delegate_to: localhost + no_log: true + +- name: Get a new Keycloak client secret, authentication with token + community.general.keycloak_clientsecret_info: + id: '9d59aa76-2755-48c6-b1af-beb70a82c3cd' + realm: MyCustomRealm + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + token: TOKEN + delegate_to: localhost + no_log: true + +- name: Get a new Keycloak client secret, passing client_id instead of id + community.general.keycloak_clientsecret_info: + client_id: 'myClientId' + realm: MyCustomRealm + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + token: TOKEN + delegate_to: localhost + no_log: true +''' + +RETURN = ''' +msg: + description: Textual description of whether we succeeded or failed + returned: always + type: str + +clientsecret_info: + description: Representation of the client secret + returned: on success + type: complex + contains: + type: + description: Credential type. + type: str + returned: always + sample: secret + value: + description: Client secret. + type: str + returned: always + sample: cUGnX1EIeTtPPAkcyGMv0ncyqDPu68P1 +''' + +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import ( + KeycloakAPI, KeycloakError, get_token) +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak_clientsecret import ( + keycloak_clientsecret_module, keycloak_clientsecret_module_resolve_params) + + +def main(): + """ + Module keycloak_clientsecret_info + + :return: + """ + + module = keycloak_clientsecret_module() + + # Obtain access token, initialize API + try: + connection_header = get_token(module.params) + except KeycloakError as e: + module.fail_json(msg=str(e)) + + kc = KeycloakAPI(module, connection_header) + + id, realm = keycloak_clientsecret_module_resolve_params(module, kc) + + clientsecret = kc.get_clientsecret(id=id, realm=realm) + + result = { + 'clientsecret_info': clientsecret, + 'msg': 'Get client secret successful for ID {id}'.format(id=id) + } + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/keycloak_clientsecret_regenerate.py b/plugins/modules/keycloak_clientsecret_regenerate.py new file mode 100644 index 0000000000..7a48a25d25 --- /dev/null +++ b/plugins/modules/keycloak_clientsecret_regenerate.py @@ -0,0 +1,167 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2022, Fynn Chen +# 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 + +DOCUMENTATION = ''' +--- +module: keycloak_clientsecret_regenerate + +short_description: Regenerate Keycloak client secret via Keycloak API + +version_added: 6.1.0 + +description: + - This module allows you to regenerate a Keycloak client secret via the + Keycloak REST API. It requires access to the REST API via OpenID Connect; + the user connecting and the client being used must have the requisite access + rights. In a default Keycloak installation, admin-cli and an admin user + would work, as would a separate client definition with the scope tailored to + your needs and a user having the expected roles. + + - When regenerating a client secret, where possible provide the client's id + (not client_id) to the module. This removes a lookup to the API to + translate the client_id into the client ID. + + - "Note that this module returns the client secret. To avoid this showing up in the logs, + please add C(no_log: true) to the task." + +options: + realm: + type: str + description: + - They Keycloak realm under which this client resides. + default: 'master' + + id: + description: + - The unique identifier for this client. + - This parameter is not required for getting or generating a client secret but + providing it will reduce the number of API calls required. + type: str + + client_id: + description: + - The client_id of the client. Passing this instead of id results in an + extra API call. + aliases: + - clientId + type: str + + +extends_documentation_fragment: + - community.general.keycloak + +author: + - Fynn Chen (@fynncfchen) + - John Cant (@johncant) +''' + +EXAMPLES = ''' +- name: Regenerate a Keycloak client secret, authentication with credentials + community.general.keycloak_clientsecret_regenerate: + id: '9d59aa76-2755-48c6-b1af-beb70a82c3cd' + realm: MyCustomRealm + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + auth_realm: master + auth_username: USERNAME + auth_password: PASSWORD + delegate_to: localhost + no_log: true + +- name: Regenerate a Keycloak client secret, authentication with token + community.general.keycloak_clientsecret_regenerate: + id: '9d59aa76-2755-48c6-b1af-beb70a82c3cd' + realm: MyCustomRealm + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + token: TOKEN + delegate_to: localhost + no_log: true + +- name: Regenerate a Keycloak client secret, passing client_id instead of id + community.general.keycloak_clientsecret_info: + client_id: 'myClientId' + realm: MyCustomRealm + auth_client_id: admin-cli + auth_keycloak_url: https://auth.example.com/auth + token: TOKEN + delegate_to: localhost + no_log: true +''' + +RETURN = ''' +msg: + description: Message as to what action was taken. + returned: always + type: str + +end_state: + description: Representation of the client credential after module execution + returned: on success + type: complex + contains: + type: + description: Credential type. + type: str + returned: always + sample: secret + value: + description: Client secret. + type: str + returned: always + sample: cUGnX1EIeTtPPAkcyGMv0ncyqDPu68P1 + +''' + +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import ( + KeycloakAPI, KeycloakError, get_token) +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak_clientsecret import ( + keycloak_clientsecret_module, keycloak_clientsecret_module_resolve_params) + + +def main(): + """ + Module keycloak_clientsecret_regenerate + + :return: + """ + + module = keycloak_clientsecret_module() + + # Obtain access token, initialize API + try: + connection_header = get_token(module.params) + except KeycloakError as e: + module.fail_json(msg=str(e)) + + kc = KeycloakAPI(module, connection_header) + + id, realm = keycloak_clientsecret_module_resolve_params(module, kc) + + if module.check_mode: + dummy_result = { + "msg": 'No action taken while in check mode', + "end_state": {'type': 'secret', 'value': 'X' * 32} + } + module.exit_json(**dummy_result) + + # Create new secret + clientsecret = kc.create_clientsecret(id=id, realm=realm) + + result = { + "msg": 'New client secret has been generated for ID {id}'.format(id=id), + "end_state": clientsecret + } + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/keycloak_realm.py b/plugins/modules/keycloak_realm.py index f457d20cd3..73a17cf10b 100644 --- a/plugins/modules/keycloak_realm.py +++ b/plugins/modules/keycloak_realm.py @@ -519,6 +519,7 @@ EXAMPLES = ''' auth_username: USERNAME auth_password: PASSWORD id: realm + realm: realm state: present - name: Delete a Keycloak realm diff --git a/plugins/modules/launchd.py b/plugins/modules/launchd.py index 6e14e4c10c..250bbffb9e 100644 --- a/plugins/modules/launchd.py +++ b/plugins/modules/launchd.py @@ -13,7 +13,7 @@ DOCUMENTATION = r''' module: launchd author: - Martin Migasiewicz (@martinm82) -short_description: Manage macOS services +short_description: Manage macOS services version_added: 1.0.0 description: - Manage launchd services on target macOS hosts. diff --git a/plugins/modules/ldap_entry.py b/plugins/modules/ldap_entry.py index d15b31f6d3..8cacbc42c1 100644 --- a/plugins/modules/ldap_entry.py +++ b/plugins/modules/ldap_entry.py @@ -14,7 +14,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ldap_entry -short_description: Add or remove LDAP entries. +short_description: Add or remove LDAP entries description: - Add or remove LDAP entries. This module only asserts the existence or non-existence of an LDAP entry, not its attributes. To assert the diff --git a/plugins/modules/ldap_passwd.py b/plugins/modules/ldap_passwd.py index 5ffd5e12ca..029b5df252 100644 --- a/plugins/modules/ldap_passwd.py +++ b/plugins/modules/ldap_passwd.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: ldap_passwd -short_description: Set passwords in LDAP. +short_description: Set passwords in LDAP description: - Set a password for an LDAP entry. This module only asserts that a given password is valid for a given entry. To assert the diff --git a/plugins/modules/librato_annotation.py b/plugins/modules/librato_annotation.py index 4ca2e7204a..70e17cfef4 100644 --- a/plugins/modules/librato_annotation.py +++ b/plugins/modules/librato_annotation.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: librato_annotation -short_description: create an annotation in librato +short_description: Create an annotation in librato description: - Create an annotation event on the given annotation stream :name. If the annotation stream does not exist, it will be created automatically author: "Seth Edwards (@Sedward)" diff --git a/plugins/modules/linode_v4.py b/plugins/modules/linode_v4.py index 6ef8786af8..3613fc7b50 100644 --- a/plugins/modules/linode_v4.py +++ b/plugins/modules/linode_v4.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: linode_v4 -short_description: Manage instances on the Linode cloud. +short_description: Manage instances on the Linode cloud description: Manage instances on the Linode cloud. requirements: - python >= 2.7 diff --git a/plugins/modules/listen_ports_facts.py b/plugins/modules/listen_ports_facts.py index eb1ba09237..bc630e1d2e 100644 --- a/plugins/modules/listen_ports_facts.py +++ b/plugins/modules/listen_ports_facts.py @@ -18,7 +18,7 @@ description: - This module currently supports Linux only. requirements: - netstat or ss -short_description: Gather facts on processes listening on TCP and UDP ports. +short_description: Gather facts on processes listening on TCP and UDP ports notes: - | C(ss) returns all processes for each listen address and port. diff --git a/plugins/modules/lldp.py b/plugins/modules/lldp.py index 9a74f37f37..3a050ae29e 100644 --- a/plugins/modules/lldp.py +++ b/plugins/modules/lldp.py @@ -13,7 +13,7 @@ DOCUMENTATION = ''' --- module: lldp requirements: [ lldpctl ] -short_description: get details reported by lldp +short_description: Get details reported by lldp description: - Reads data out of lldpctl options: {} diff --git a/plugins/modules/logentries_msg.py b/plugins/modules/logentries_msg.py index b1b77e1d62..723273165f 100644 --- a/plugins/modules/logentries_msg.py +++ b/plugins/modules/logentries_msg.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: logentries_msg -short_description: Send a message to logentries. +short_description: Send a message to logentries description: - Send a message to logentries requirements: diff --git a/plugins/modules/lxc_container.py b/plugins/modules/lxc_container.py index 7871f13972..9fe27b8d81 100644 --- a/plugins/modules/lxc_container.py +++ b/plugins/modules/lxc_container.py @@ -677,7 +677,7 @@ class LxcContainerManagement(object): false_values = BOOLEANS_FALSE.union([None, '']) result = dict( - (k, v) + (v, self.module.params[k]) for k, v in variables.items() if self.module.params[k] not in false_values ) diff --git a/plugins/modules/lxd_project.py b/plugins/modules/lxd_project.py index f0aa4058e7..ad6019c2ec 100644 --- a/plugins/modules/lxd_project.py +++ b/plugins/modules/lxd_project.py @@ -178,7 +178,9 @@ actions: sample: ["create"] ''' -from ansible_collections.community.general.plugins.module_utils.lxd import LXDClient, LXDClientException +from ansible_collections.community.general.plugins.module_utils.lxd import ( + LXDClient, LXDClientException, default_key_file, default_cert_file +) from ansible.module_utils.basic import AnsibleModule import os @@ -211,10 +213,10 @@ class LXDProjectManagement(object): self.key_file = self.module.params.get('client_key') if self.key_file is None: - self.key_file = os.path.expanduser('~/.config/lxc/client.key') + self.key_file = default_key_file() self.cert_file = self.module.params.get('client_cert') if self.cert_file is None: - self.cert_file = os.path.expanduser('~/.config/lxc/client.crt') + self.cert_file = default_cert_file() self.debug = self.module._verbosity >= 4 try: diff --git a/plugins/modules/manageiq_group.py b/plugins/modules/manageiq_group.py index 509de77e21..5772c19a7e 100644 --- a/plugins/modules/manageiq_group.py +++ b/plugins/modules/manageiq_group.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' module: manageiq_group -short_description: Management of groups in ManageIQ. +short_description: Management of groups in ManageIQ extends_documentation_fragment: - community.general.manageiq diff --git a/plugins/modules/manageiq_policies.py b/plugins/modules/manageiq_policies.py index ae36094768..fc185fcd58 100644 --- a/plugins/modules/manageiq_policies.py +++ b/plugins/modules/manageiq_policies.py @@ -13,7 +13,7 @@ DOCUMENTATION = ''' module: manageiq_policies -short_description: Management of resource policy_profiles in ManageIQ. +short_description: Management of resource policy_profiles in ManageIQ extends_documentation_fragment: - community.general.manageiq @@ -27,7 +27,10 @@ options: description: - C(absent) - policy_profiles should not exist, - C(present) - policy_profiles should exist, - - C(list) - list current policy_profiles and policies. + - > + C(list) - list current policy_profiles and policies. + This state is deprecated and will be removed 8.0.0. + Please use the module M(community.general.manageiq_policies_info) instead. choices: ['absent', 'present', 'list'] default: 'present' policy_profiles: @@ -163,6 +166,13 @@ def main(): resource_name = module.params['resource_name'] state = module.params['state'] + if state == "list": + module.deprecate( + 'The value "list" for "state" is deprecated. Please use community.general.manageiq_policies_info instead.', + version='8.0.0', + collection_name='community.general' + ) + # get the action and resource type action = actions[state] resource_type = manageiq_entities()[resource_type_key] diff --git a/plugins/modules/manageiq_provider.py b/plugins/modules/manageiq_provider.py index 951e9bb07c..5eb6d779ac 100644 --- a/plugins/modules/manageiq_provider.py +++ b/plugins/modules/manageiq_provider.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' module: manageiq_provider -short_description: Management of provider in ManageIQ. +short_description: Management of provider in ManageIQ extends_documentation_fragment: - community.general.manageiq diff --git a/plugins/modules/manageiq_tags.py b/plugins/modules/manageiq_tags.py index 209fb5ea9d..9d051a5aa1 100644 --- a/plugins/modules/manageiq_tags.py +++ b/plugins/modules/manageiq_tags.py @@ -13,7 +13,7 @@ DOCUMENTATION = ''' module: manageiq_tags -short_description: Management of resource tags in ManageIQ. +short_description: Management of resource tags in ManageIQ extends_documentation_fragment: - community.general.manageiq diff --git a/plugins/modules/manageiq_tenant.py b/plugins/modules/manageiq_tenant.py index 0f4473092b..f3700d2492 100644 --- a/plugins/modules/manageiq_tenant.py +++ b/plugins/modules/manageiq_tenant.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' module: manageiq_tenant -short_description: Management of tenants in ManageIQ. +short_description: Management of tenants in ManageIQ extends_documentation_fragment: - community.general.manageiq diff --git a/plugins/modules/manageiq_user.py b/plugins/modules/manageiq_user.py index b9b69182cb..9910e092e0 100644 --- a/plugins/modules/manageiq_user.py +++ b/plugins/modules/manageiq_user.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' module: manageiq_user -short_description: Management of users in ManageIQ. +short_description: Management of users in ManageIQ extends_documentation_fragment: - community.general.manageiq diff --git a/plugins/modules/memset_memstore_info.py b/plugins/modules/memset_memstore_info.py index 4de803f991..8ce3d5b05a 100644 --- a/plugins/modules/memset_memstore_info.py +++ b/plugins/modules/memset_memstore_info.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' --- module: memset_memstore_info author: "Simon Weald (@glitchcrab)" -short_description: Retrieve Memstore product usage information. +short_description: Retrieve Memstore product usage information notes: - An API key generated via the Memset customer control panel is needed with the following minimum scope - I(memstore.usage). diff --git a/plugins/modules/memset_server_info.py b/plugins/modules/memset_server_info.py index 44aa0d8442..0c78f2bd1d 100644 --- a/plugins/modules/memset_server_info.py +++ b/plugins/modules/memset_server_info.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' --- module: memset_server_info author: "Simon Weald (@glitchcrab)" -short_description: Retrieve server information. +short_description: Retrieve server information notes: - An API key generated via the Memset customer control panel is needed with the following minimum scope - I(server.info). diff --git a/plugins/modules/memset_zone.py b/plugins/modules/memset_zone.py index 9731e3a943..02b5fd28f0 100644 --- a/plugins/modules/memset_zone.py +++ b/plugins/modules/memset_zone.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' --- module: memset_zone author: "Simon Weald (@glitchcrab)" -short_description: Creates and deletes Memset DNS zones. +short_description: Creates and deletes Memset DNS zones notes: - Zones can be thought of as a logical group of domains, all of which share the same DNS records (i.e. they point to the same IP). An API key generated via the diff --git a/plugins/modules/memset_zone_domain.py b/plugins/modules/memset_zone_domain.py index 995c7bc8d3..1e18a984b6 100644 --- a/plugins/modules/memset_zone_domain.py +++ b/plugins/modules/memset_zone_domain.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' --- module: memset_zone_domain author: "Simon Weald (@glitchcrab)" -short_description: Create and delete domains in Memset DNS zones. +short_description: Create and delete domains in Memset DNS zones notes: - Zone domains can be thought of as a collection of domains, all of which share the same DNS records (i.e. they point to the same IP). An API key generated via the diff --git a/plugins/modules/memset_zone_record.py b/plugins/modules/memset_zone_record.py index c114532f90..925a034c56 100644 --- a/plugins/modules/memset_zone_record.py +++ b/plugins/modules/memset_zone_record.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' --- module: memset_zone_record author: "Simon Weald (@glitchcrab)" -short_description: Create and delete records in Memset DNS zones. +short_description: Create and delete records in Memset DNS zones notes: - Zones can be thought of as a logical group of domains, all of which share the same DNS records (i.e. they point to the same IP). An API key generated via the diff --git a/plugins/modules/mksysb.py b/plugins/modules/mksysb.py index a466bd9df6..15b6ad9442 100644 --- a/plugins/modules/mksysb.py +++ b/plugins/modules/mksysb.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' --- author: Kairo Araujo (@kairoaraujo) module: mksysb -short_description: Generates AIX mksysb rootvg backups. +short_description: Generates AIX mksysb rootvg backups description: - This module manages a basic AIX mksysb (image) of rootvg. options: diff --git a/plugins/modules/mssql_db.py b/plugins/modules/mssql_db.py index a7d16b2710..58a8c4dea9 100644 --- a/plugins/modules/mssql_db.py +++ b/plugins/modules/mssql_db.py @@ -13,7 +13,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: mssql_db -short_description: Add or remove MSSQL databases from a remote host. +short_description: Add or remove MSSQL databases from a remote host description: - Add or remove MSSQL databases from a remote host. options: diff --git a/plugins/modules/nagios.py b/plugins/modules/nagios.py index 4fb2a9ff45..ec526a3959 100644 --- a/plugins/modules/nagios.py +++ b/plugins/modules/nagios.py @@ -17,7 +17,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: nagios -short_description: Perform common tasks in Nagios related to downtime and notifications. +short_description: Perform common tasks in Nagios related to downtime and notifications description: - "The C(nagios) module has two basic functions: scheduling downtime and toggling alerts for services or hosts." - The C(nagios) module is not idempotent. diff --git a/plugins/modules/netcup_dns.py b/plugins/modules/netcup_dns.py index 5d082c2980..4c3c015ebd 100644 --- a/plugins/modules/netcup_dns.py +++ b/plugins/modules/netcup_dns.py @@ -13,7 +13,7 @@ DOCUMENTATION = ''' --- module: netcup_dns notes: [] -short_description: manage Netcup DNS records +short_description: Manage Netcup DNS records description: - "Manages DNS records via the Netcup API, see the docs U(https://ccp.netcup.net/run/webservice/servers/endpoint.php)." options: diff --git a/plugins/modules/nginx_status_info.py b/plugins/modules/nginx_status_info.py index 1e1bb10495..6bbea078b0 100644 --- a/plugins/modules/nginx_status_info.py +++ b/plugins/modules/nginx_status_info.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: nginx_status_info -short_description: Retrieve information on nginx status. +short_description: Retrieve information on nginx status description: - Gathers information from nginx from an URL having C(stub_status) enabled. author: "René Moser (@resmo)" diff --git a/plugins/modules/nosh.py b/plugins/modules/nosh.py index 756f5fed42..432990a4d2 100644 --- a/plugins/modules/nosh.py +++ b/plugins/modules/nosh.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' module: nosh author: - "Thomas Caravia (@tacatac)" -short_description: Manage services with nosh +short_description: Manage services with nosh description: - Control running and enabled state for system-wide or user services. - BSD and Linux systems are supported. diff --git a/plugins/modules/nsupdate.py b/plugins/modules/nsupdate.py index 43b951fe61..2be4863b68 100644 --- a/plugins/modules/nsupdate.py +++ b/plugins/modules/nsupdate.py @@ -18,7 +18,7 @@ DOCUMENTATION = ''' --- module: nsupdate -short_description: Manage DNS records. +short_description: Manage DNS records description: - Create, update and remove DNS records using DDNS updates requirements: diff --git a/plugins/modules/omapi_host.py b/plugins/modules/omapi_host.py index 7d1897ca55..4e3a6247d6 100644 --- a/plugins/modules/omapi_host.py +++ b/plugins/modules/omapi_host.py @@ -13,7 +13,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: omapi_host -short_description: Setup OMAPI hosts. +short_description: Setup OMAPI hosts description: Manage OMAPI hosts into compatible DHCPd servers requirements: - pypureomapi diff --git a/plugins/modules/one_vm.py b/plugins/modules/one_vm.py index 6bfc793603..7122907d38 100644 --- a/plugins/modules/one_vm.py +++ b/plugins/modules/one_vm.py @@ -970,7 +970,7 @@ def get_vm_labels_and_attributes_dict(client, vm_id): if key != 'LABELS': attrs_dict[key] = value else: - if key is not None: + if key is not None and value is not None: labels_list = value.split(',') return labels_list, attrs_dict diff --git a/plugins/modules/oneandone_firewall_policy.py b/plugins/modules/oneandone_firewall_policy.py index 23203e8d05..5cceffa812 100644 --- a/plugins/modules/oneandone_firewall_policy.py +++ b/plugins/modules/oneandone_firewall_policy.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneandone_firewall_policy -short_description: Configure 1&1 firewall policy. +short_description: Configure 1&1 firewall policy description: - Create, remove, reconfigure, update firewall policies. This module has a dependency on 1and1 >= 1.0 diff --git a/plugins/modules/oneandone_load_balancer.py b/plugins/modules/oneandone_load_balancer.py index 04aefde63a..432fc456b1 100644 --- a/plugins/modules/oneandone_load_balancer.py +++ b/plugins/modules/oneandone_load_balancer.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneandone_load_balancer -short_description: Configure 1&1 load balancer. +short_description: Configure 1&1 load balancer description: - Create, remove, update load balancers. This module has a dependency on 1and1 >= 1.0 diff --git a/plugins/modules/oneandone_monitoring_policy.py b/plugins/modules/oneandone_monitoring_policy.py index 46c68e86e0..04e9c67570 100644 --- a/plugins/modules/oneandone_monitoring_policy.py +++ b/plugins/modules/oneandone_monitoring_policy.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneandone_monitoring_policy -short_description: Configure 1&1 monitoring policy. +short_description: Configure 1&1 monitoring policy description: - Create, remove, update monitoring policies (and add/remove ports, processes, and servers). diff --git a/plugins/modules/oneandone_private_network.py b/plugins/modules/oneandone_private_network.py index a6db23310a..4a912a0f35 100644 --- a/plugins/modules/oneandone_private_network.py +++ b/plugins/modules/oneandone_private_network.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneandone_private_network -short_description: Configure 1&1 private networking. +short_description: Configure 1&1 private networking description: - Create, remove, reconfigure, update a private network. This module has a dependency on 1and1 >= 1.0 diff --git a/plugins/modules/oneandone_public_ip.py b/plugins/modules/oneandone_public_ip.py index a5970735ce..31ed082c74 100644 --- a/plugins/modules/oneandone_public_ip.py +++ b/plugins/modules/oneandone_public_ip.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneandone_public_ip -short_description: Configure 1&1 public IPs. +short_description: Configure 1&1 public IPs description: - Create, update, and remove public IPs. This module has a dependency on 1and1 >= 1.0 diff --git a/plugins/modules/oneandone_server.py b/plugins/modules/oneandone_server.py index 22b4b9dd69..e0f1b0eb03 100644 --- a/plugins/modules/oneandone_server.py +++ b/plugins/modules/oneandone_server.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneandone_server -short_description: Create, destroy, start, stop, and reboot a 1&1 Host server. +short_description: Create, destroy, start, stop, and reboot a 1&1 Host server description: - Create, destroy, update, start, stop, and reboot a 1&1 Host server. When the server is created it can optionally wait for it to be 'running' before returning. diff --git a/plugins/modules/oneview_fc_network.py b/plugins/modules/oneview_fc_network.py index e4b1a17339..2898447c0b 100644 --- a/plugins/modules/oneview_fc_network.py +++ b/plugins/modules/oneview_fc_network.py @@ -10,7 +10,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: oneview_fc_network -short_description: Manage OneView Fibre Channel Network resources. +short_description: Manage OneView Fibre Channel Network resources description: - Provides an interface to manage Fibre Channel Network resources. Can create, update, and delete. requirements: diff --git a/plugins/modules/online_server_info.py b/plugins/modules/online_server_info.py index 533e0453f9..f6d03cb275 100644 --- a/plugins/modules/online_server_info.py +++ b/plugins/modules/online_server_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: online_server_info -short_description: Gather information about Online servers. +short_description: Gather information about Online servers description: - Gather information about the servers. - U(https://www.online.net/en/dedicated-server) diff --git a/plugins/modules/online_user_info.py b/plugins/modules/online_user_info.py index 17cbc7d662..1d91418caf 100644 --- a/plugins/modules/online_user_info.py +++ b/plugins/modules/online_user_info.py @@ -9,7 +9,7 @@ __metaclass__ = type DOCUMENTATION = r''' module: online_user_info -short_description: Gather information about Online user. +short_description: Gather information about Online user description: - Gather information about the user. author: diff --git a/plugins/modules/opendj_backendprop.py b/plugins/modules/opendj_backendprop.py index dfdf6a903a..7e620c4305 100644 --- a/plugins/modules/opendj_backendprop.py +++ b/plugins/modules/opendj_backendprop.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: opendj_backendprop -short_description: Will update the backend configuration of OpenDJ via the dsconfig set-backend-prop command. +short_description: Will update the backend configuration of OpenDJ via the dsconfig set-backend-prop command description: - This module will update settings for OpenDJ with the command set-backend-prop. - It will check first via de get-backend-prop if configuration needs to be applied. diff --git a/plugins/modules/openwrt_init.py b/plugins/modules/openwrt_init.py index be7031791e..978a6bec35 100644 --- a/plugins/modules/openwrt_init.py +++ b/plugins/modules/openwrt_init.py @@ -12,7 +12,7 @@ DOCUMENTATION = ''' module: openwrt_init author: - "Andrew Gaffney (@agaffney)" -short_description: Manage services on OpenWrt. +short_description: Manage services on OpenWrt description: - Controls OpenWrt services on remote hosts. options: diff --git a/plugins/modules/opkg.py b/plugins/modules/opkg.py index 60d2adc958..7e2b8c4ac2 100644 --- a/plugins/modules/opkg.py +++ b/plugins/modules/opkg.py @@ -22,6 +22,9 @@ options: name: description: - Name of package(s) to install/remove. + - C(NAME=VERSION) syntax is also supported to install a package + in a certain version. See the examples. This is supported since + community.general 6.2.0. aliases: [pkg] required: true type: list @@ -64,6 +67,11 @@ EXAMPLES = ''' name: foo state: present +- name: Install foo in version 1.2 + community.general.opkg: + name: foo=1.2 + state: present + - name: Update cache and install foo community.general.opkg: name: foo @@ -96,22 +104,39 @@ from ansible.module_utils.six.moves import shlex_quote def update_package_db(module, opkg_path): """ Updates packages list. """ - rc, out, err = module.run_command("%s update" % opkg_path) + rc, out, err = module.run_command([opkg_path, "update"]) if rc != 0: module.fail_json(msg="could not update package db") -def query_package(module, opkg_path, name, state="present"): +def query_package(module, opkg_path, name, version=None, state="present"): """ Returns whether a package is installed or not. """ if state == "present": + rc, out, err = module.run_command([opkg_path, "list-installed", name]) + if rc != 0: + return False + # variable out is one line if the package is installed: + # "NAME - VERSION - DESCRIPTION" + if version is not None: + if not out.startswith("%s - %s " % (name, version)): + return False + else: + if not out.startswith(name + " "): + return False + return True + else: + raise NotImplementedError() - rc, out, err = module.run_command("%s list-installed | grep -q \"^%s \"" % (shlex_quote(opkg_path), shlex_quote(name)), use_unsafe_shell=True) - if rc == 0: - return True - return False +def split_name_and_version(module, package): + """ Split the name and the version when using the NAME=VERSION syntax """ + splitted = package.split('=', 1) + if len(splitted) == 1: + return splitted[0], None + else: + return splitted[0], splitted[1] def remove_packages(module, opkg_path, packages): @@ -125,11 +150,16 @@ def remove_packages(module, opkg_path, packages): remove_c = 0 # Using a for loop in case of error, we can report the package that failed for package in packages: + package, version = split_name_and_version(module, package) + # Query the package first, to see if we even need to remove if not query_package(module, opkg_path, package): continue - rc, out, err = module.run_command("%s remove %s %s" % (opkg_path, force, package)) + if force: + rc, out, err = module.run_command([opkg_path, "remove", force, package]) + else: + rc, out, err = module.run_command([opkg_path, "remove", package]) if query_package(module, opkg_path, package): module.fail_json(msg="failed to remove %s: %s" % (package, out)) @@ -154,13 +184,23 @@ def install_packages(module, opkg_path, packages): install_c = 0 for package in packages: - if query_package(module, opkg_path, package): + package, version = split_name_and_version(module, package) + + if query_package(module, opkg_path, package, version) and (force != '--force-reinstall'): continue - rc, out, err = module.run_command("%s install %s %s" % (opkg_path, force, package)) + if version is not None: + version_str = "=%s" % version + else: + version_str = "" - if not query_package(module, opkg_path, package): - module.fail_json(msg="failed to install %s: %s" % (package, out)) + if force: + rc, out, err = module.run_command([opkg_path, "install", force, package + version_str]) + else: + rc, out, err = module.run_command([opkg_path, "install", package + version_str]) + + if not query_package(module, opkg_path, package, version): + module.fail_json(msg="failed to install %s%s: %s" % (package, version_str, out)) install_c += 1 diff --git a/plugins/modules/packet_device.py b/plugins/modules/packet_device.py index 6b78406a6e..500a400273 100644 --- a/plugins/modules/packet_device.py +++ b/plugins/modules/packet_device.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' --- module: packet_device -short_description: Manage a bare metal server in the Packet Host. +short_description: Manage a bare metal server in the Packet Host description: - Manage a bare metal server in the Packet Host (a "device" in the API terms). diff --git a/plugins/modules/packet_ip_subnet.py b/plugins/modules/packet_ip_subnet.py index 79f93a6413..63790e1c6a 100644 --- a/plugins/modules/packet_ip_subnet.py +++ b/plugins/modules/packet_ip_subnet.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' --- module: packet_ip_subnet -short_description: Assign IP subnet to a bare metal server. +short_description: Assign IP subnet to a bare metal server description: - Assign or unassign IPv4 or IPv6 subnets to or from a device in the Packet host. diff --git a/plugins/modules/packet_project.py b/plugins/modules/packet_project.py index 18b6f73f99..9a82c2ec72 100644 --- a/plugins/modules/packet_project.py +++ b/plugins/modules/packet_project.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' --- module: packet_project -short_description: Create/delete a project in Packet host. +short_description: Create/delete a project in Packet host description: - Create/delete a project in Packet host. diff --git a/plugins/modules/packet_sshkey.py b/plugins/modules/packet_sshkey.py index d39e419747..87beb01aa8 100644 --- a/plugins/modules/packet_sshkey.py +++ b/plugins/modules/packet_sshkey.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: packet_sshkey -short_description: Create/delete an SSH key in Packet host. +short_description: Create/delete an SSH key in Packet host description: - Create/delete an SSH key in Packet host. - API is documented at U(https://www.packet.net/help/api/#page:ssh-keys,header:ssh-keys-ssh-keys-post). diff --git a/plugins/modules/packet_volume.py b/plugins/modules/packet_volume.py index f0508c5891..b06e57b56c 100644 --- a/plugins/modules/packet_volume.py +++ b/plugins/modules/packet_volume.py @@ -13,7 +13,7 @@ DOCUMENTATION = ''' --- module: packet_volume -short_description: Create/delete a volume in Packet host. +short_description: Create/delete a volume in Packet host description: - Create/delete a volume in Packet host. diff --git a/plugins/modules/packet_volume_attachment.py b/plugins/modules/packet_volume_attachment.py index 4f55d60cfa..74b42ab479 100644 --- a/plugins/modules/packet_volume_attachment.py +++ b/plugins/modules/packet_volume_attachment.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' --- module: packet_volume_attachment -short_description: Attach/detach a volume to a device in the Packet host. +short_description: Attach/detach a volume to a device in the Packet host description: - Attach/detach a volume to a device in the Packet host. diff --git a/plugins/modules/pagerduty_user.py b/plugins/modules/pagerduty_user.py index 4d8e32248f..e16fe59e76 100644 --- a/plugins/modules/pagerduty_user.py +++ b/plugins/modules/pagerduty_user.py @@ -80,25 +80,12 @@ EXAMPLES = r''' RETURN = r''' # ''' -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -import traceback from os import path +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils import deps -try: - from pdpyras import APISession - HAS_PD_PY = True - PD_IMPORT_ERR = None -except ImportError: - HAS_PD_PY = False - PD_IMPORT_ERR = traceback.format_exc() - -try: - from pdpyras import PDClientError - HAS_PD_CLIENT_ERR = True - PD_CLIENT_ERR_IMPORT_ERR = None -except ImportError: - HAS_PD_CLIENT_ERR = False - PD_CLIENT_ERR_IMPORT_ERR = traceback.format_exc() +with deps.declare("pdpyras", url="https://github.com/PagerDuty/pdpyras"): + from pdpyras import APISession, PDClientError class PagerDutyUser(object): @@ -202,11 +189,7 @@ def main(): supports_check_mode=True, ) - if not HAS_PD_PY: - module.fail_json(msg=missing_required_lib('pdpyras', url='https://github.com/PagerDuty/pdpyras'), exception=PD_IMPORT_ERR) - - if not HAS_PD_CLIENT_ERR: - module.fail_json(msg=missing_required_lib('PDClientError', url='https://github.com/PagerDuty/pdpyras'), exception=PD_CLIENT_ERR_IMPORT_ERR) + deps.validate(module) access_token = module.params['access_token'] pd_user = module.params['pd_user'] diff --git a/plugins/modules/pids.py b/plugins/modules/pids.py index 072a2bb7a4..2fe2a6b8ac 100644 --- a/plugins/modules/pids.py +++ b/plugins/modules/pids.py @@ -3,14 +3,14 @@ # Copyright (c) 2019, Saranya Sridharan # 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) +from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = ''' module: pids description: "Retrieves a list of PIDs of given process name in Ansible controller/controlled machines.Returns an empty list if no process in that name exists." -short_description: "Retrieves process IDs list if the process is running otherwise return empty list" +short_description: Retrieves process IDs list if the process is running otherwise return empty list author: - Saranya Sridharan (@saranyasridharan) requirements: @@ -60,18 +60,15 @@ import re from os.path import basename from ansible.module_utils import six -from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils import deps from ansible.module_utils.common.text.converters import to_native from ansible_collections.community.general.plugins.module_utils.version import LooseVersion -try: +with deps.declare("psutil"): import psutil - HAS_PSUTIL = True -except ImportError: - HAS_PSUTIL = False - class PSAdapterError(Exception): pass @@ -177,8 +174,8 @@ def compare_lower(a, b): class Pids(object): def __init__(self, module): - if not HAS_PSUTIL: - module.fail_json(msg=missing_required_lib('psutil')) + + deps.validate(module) self._ps = PSAdapter.from_package(psutil) diff --git a/plugins/modules/pip_package_info.py b/plugins/modules/pip_package_info.py index c89e47014e..2cde7218d4 100644 --- a/plugins/modules/pip_package_info.py +++ b/plugins/modules/pip_package_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' module: pip_package_info -short_description: pip package information +short_description: Pip package information description: - Return information about installed pip packages extends_documentation_fragment: diff --git a/plugins/modules/pkgin.py b/plugins/modules/pkgin.py index 477460e0e3..0da06e0502 100644 --- a/plugins/modules/pkgin.py +++ b/plugins/modules/pkgin.py @@ -19,7 +19,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: pkgin -short_description: Package manager for SmartOS, NetBSD, et al. +short_description: Package manager for SmartOS, NetBSD, et al description: - "The standard package manager for SmartOS, but also usable on NetBSD or any OS that uses C(pkgsrc). (Home: U(http://pkgin.net/))" diff --git a/plugins/modules/profitbricks.py b/plugins/modules/profitbricks.py index cb41491464..6b0134cb99 100644 --- a/plugins/modules/profitbricks.py +++ b/plugins/modules/profitbricks.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: profitbricks -short_description: Create, destroy, start, stop, and reboot a ProfitBricks virtual machine. +short_description: Create, destroy, start, stop, and reboot a ProfitBricks virtual machine description: - Create, destroy, update, start, stop, and reboot a ProfitBricks virtual machine. When the virtual machine is created it can optionally wait for it to be 'running' before returning. This module has a dependency on profitbricks >= 1.0.0 diff --git a/plugins/modules/profitbricks_datacenter.py b/plugins/modules/profitbricks_datacenter.py index cc3184320d..a11d12bfd9 100644 --- a/plugins/modules/profitbricks_datacenter.py +++ b/plugins/modules/profitbricks_datacenter.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: profitbricks_datacenter -short_description: Create or destroy a ProfitBricks Virtual Datacenter. +short_description: Create or destroy a ProfitBricks Virtual Datacenter description: - This is a simple module that supports creating or removing vDCs. A vDC is required before you can create servers. This module has a dependency on profitbricks >= 1.0.0 diff --git a/plugins/modules/profitbricks_nic.py b/plugins/modules/profitbricks_nic.py index facb146b60..c6239f5ef5 100644 --- a/plugins/modules/profitbricks_nic.py +++ b/plugins/modules/profitbricks_nic.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: profitbricks_nic -short_description: Create or Remove a NIC. +short_description: Create or Remove a NIC description: - This module allows you to create or restore a volume snapshot. This module has a dependency on profitbricks >= 1.0.0 options: diff --git a/plugins/modules/profitbricks_volume.py b/plugins/modules/profitbricks_volume.py index 2d449fd92a..1d21897715 100644 --- a/plugins/modules/profitbricks_volume.py +++ b/plugins/modules/profitbricks_volume.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: profitbricks_volume -short_description: Create or destroy a volume. +short_description: Create or destroy a volume description: - Allows you to create or remove a volume from a ProfitBricks datacenter. This module has a dependency on profitbricks >= 1.0.0 options: diff --git a/plugins/modules/profitbricks_volume_attachments.py b/plugins/modules/profitbricks_volume_attachments.py index 5637aca67f..49b418362b 100644 --- a/plugins/modules/profitbricks_volume_attachments.py +++ b/plugins/modules/profitbricks_volume_attachments.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: profitbricks_volume_attachments -short_description: Attach or detach a volume. +short_description: Attach or detach a volume description: - Allows you to attach or detach a volume from a ProfitBricks server. This module has a dependency on profitbricks >= 1.0.0 options: diff --git a/plugins/modules/proxmox.py b/plugins/modules/proxmox.py index 5a89ee7796..640f9b4114 100644 --- a/plugins/modules/proxmox.py +++ b/plugins/modules/proxmox.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: proxmox -short_description: management of instances in Proxmox VE cluster +short_description: Management of instances in Proxmox VE cluster description: - allows you to create/delete/stop instances in Proxmox VE cluster - Starting in Ansible 2.1, it automatically detects containerization type (lxc for PVE 4, openvz for older) @@ -106,6 +106,14 @@ options: description: - sets DNS search domain for a container type: str + tags: + description: + - List of tags to apply to the container. + - Tags must start with C([a-z0-9_]) followed by zero or more of the following characters C([a-z0-9_-+.]). + - Tags are only available in Proxmox 7+. + type: list + elements: str + version_added: 6.2.0 timeout: description: - timeout for operations @@ -391,6 +399,7 @@ EXAMPLES = r''' state: absent ''' +import re import time from ansible_collections.community.general.plugins.module_utils.version import LooseVersion @@ -415,11 +424,25 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): return config['template'] def create_instance(self, vmid, node, disk, storage, cpus, memory, swap, timeout, clone, **kwargs): + + # Version limited features + minimum_version = { + 'tags': 7, + } proxmox_node = self.proxmox_api.nodes(node) # Remove all empty kwarg entries kwargs = dict((k, v) for k, v in kwargs.items() if v is not None) + version = self.version() + pve_major_version = 3 if version < LooseVersion('4.0') else version.version[0] + + # Fail on unsupported features + for option, version in minimum_version.items(): + if pve_major_version < version and option in kwargs: + self.module.fail_json(changed=False, msg="Feature {option} is only supported in PVE {version}+, and you're using PVE {pve_major_version}". + format(option=option, version=version, pve_major_version=pve_major_version)) + if VZ_TYPE == 'lxc': kwargs['cpulimit'] = cpus kwargs['rootfs'] = disk @@ -437,6 +460,14 @@ class ProxmoxLxcAnsible(ProxmoxAnsible): kwargs['cpus'] = cpus kwargs['disk'] = disk + # LXC tags are expected to be valid and presented as a comma/semi-colon delimited string + if 'tags' in kwargs: + re_tag = re.compile(r'^[a-z0-9_][a-z0-9_\-\+\.]*$') + for tag in kwargs['tags']: + if not re_tag.match(tag): + self.module.fail_json(msg='%s is not a valid tag' % tag) + kwargs['tags'] = ",".join(kwargs['tags']) + if clone is not None: if VZ_TYPE != 'lxc': self.module.fail_json(changed=False, msg="Clone operator is only supported for LXC enabled proxmox clusters.") @@ -569,6 +600,7 @@ def main(): proxmox_default_behavior=dict(type='str', default='no_defaults', choices=['compatibility', 'no_defaults']), clone=dict(type='int'), clone_type=dict(default='opportunistic', choices=['full', 'linked', 'opportunistic']), + tags=dict(type='list', elements='str') ) module_args.update(proxmox_args) @@ -674,7 +706,8 @@ def main(): features=",".join(module.params['features']) if module.params['features'] is not None else None, unprivileged=ansible_to_proxmox_bool(module.params['unprivileged']), description=module.params['description'], - hookscript=module.params['hookscript']) + hookscript=module.params['hookscript'], + tags=module.params['tags']) module.exit_json(changed=True, msg="Deployed VM %s from template %s" % (vmid, module.params['ostemplate'])) except Exception as e: diff --git a/plugins/modules/proxmox_disk.py b/plugins/modules/proxmox_disk.py index 182a0d25f2..8a81f18a3f 100644 --- a/plugins/modules/proxmox_disk.py +++ b/plugins/modules/proxmox_disk.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: proxmox_disk -short_description: Management of a disk of a Qemu(KVM) VM in a Proxmox VE cluster. +short_description: Management of a disk of a Qemu(KVM) VM in a Proxmox VE cluster version_added: 5.7.0 description: - Allows you to perform some supported operations on a disk in Qemu(KVM) Virtual Machines in a Proxmox VE cluster. @@ -699,7 +699,7 @@ def main(): module.exit_json(changed=False, vmid=vmid, msg='Disk %s already detached in VM %s' % (disk, vmid)) if disk not in vm_config: module.exit_json(changed=False, vmid=vmid, msg="Disk %s not present in VM %s config" % (disk, vmid)) - proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).unlink.put(vmid=vmid, idlist=disk, force=0) + proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).unlink.put(idlist=disk, force=0) module.exit_json(changed=True, vmid=vmid, msg="Disk %s detached from VM %s" % (disk, vmid)) except Exception as e: module.fail_json(msg="Failed to detach disk %s from VM %s with exception: %s" % (disk, vmid, str(e))) @@ -725,7 +725,7 @@ def main(): actual_size = disk_config['size'] if size == actual_size: module.exit_json(changed=False, vmid=vmid, msg="Disk %s is already %s size" % (disk, size)) - proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).resize.set(vmid=vmid, disk=disk, size=size) + proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).resize.set(disk=disk, size=size) module.exit_json(changed=True, vmid=vmid, msg="Disk %s resized in VM %s" % (disk, vmid)) except Exception as e: module.fail_json(msg="Failed to resize disk %s in VM %s with exception: %s" % (disk, vmid, str(e))) @@ -734,7 +734,7 @@ def main(): try: if disk not in vm_config: module.exit_json(changed=False, vmid=vmid, msg="Disk %s is already absent in VM %s" % (disk, vmid)) - proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).unlink.put(vmid=vmid, idlist=disk, force=1) + proxmox.proxmox_api.nodes(vm['node']).qemu(vmid).unlink.put(idlist=disk, force=1) module.exit_json(changed=True, vmid=vmid, msg="Disk %s removed from VM %s" % (disk, vmid)) except Exception as e: module.fail_json(vmid=vmid, msg='Unable to remove disk %s from VM %s: %s' % (disk, vmid, str(e))) diff --git a/plugins/modules/proxmox_kvm.py b/plugins/modules/proxmox_kvm.py index 92aaf6a904..25e252bee9 100644 --- a/plugins/modules/proxmox_kvm.py +++ b/plugins/modules/proxmox_kvm.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: proxmox_kvm -short_description: Management of Qemu(KVM) Virtual Machines in Proxmox VE cluster. +short_description: Management of Qemu(KVM) Virtual Machines in Proxmox VE cluster description: - Allows you to create/delete/stop Qemu(KVM) Virtual Machines in Proxmox VE cluster. - Since community.general 4.0.0 on, there are no more default values, see I(proxmox_default_behavior). diff --git a/plugins/modules/proxmox_nic.py b/plugins/modules/proxmox_nic.py index 49f7f91bcd..5c8c3f47df 100644 --- a/plugins/modules/proxmox_nic.py +++ b/plugins/modules/proxmox_nic.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: proxmox_nic -short_description: Management of a NIC of a Qemu(KVM) VM in a Proxmox VE cluster. +short_description: Management of a NIC of a Qemu(KVM) VM in a Proxmox VE cluster version_added: 3.1.0 description: - Allows you to create/update/delete a NIC on Qemu(KVM) Virtual Machines in a Proxmox VE cluster. @@ -223,7 +223,7 @@ class ProxmoxNicAnsible(ProxmoxAnsible): if interface in vminfo: if not self.module.check_mode: - self.proxmox_api.nodes(vm['node']).qemu(vmid).config.set(vmid=vmid, delete=interface) + self.proxmox_api.nodes(vm['node']).qemu(vmid).config.set(delete=interface) return True return False diff --git a/plugins/modules/proxmox_template.py b/plugins/modules/proxmox_template.py index 24a6c87d31..a09af2f2a3 100644 --- a/plugins/modules/proxmox_template.py +++ b/plugins/modules/proxmox_template.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: proxmox_template -short_description: management of OS templates in Proxmox VE cluster +short_description: Management of OS templates in Proxmox VE cluster description: - allows you to upload/delete templates in Proxmox VE cluster options: diff --git a/plugins/modules/pubnub_blocks.py b/plugins/modules/pubnub_blocks.py index 9942c57134..28b17b5431 100644 --- a/plugins/modules/pubnub_blocks.py +++ b/plugins/modules/pubnub_blocks.py @@ -17,7 +17,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: pubnub_blocks -short_description: PubNub blocks management module. +short_description: PubNub blocks management module description: - "This module allows Ansible to interface with the PubNub BLOCKS infrastructure by providing the following operations: create / remove, diff --git a/plugins/modules/pulp_repo.py b/plugins/modules/pulp_repo.py index 030d2fd9af..9e100ba93e 100644 --- a/plugins/modules/pulp_repo.py +++ b/plugins/modules/pulp_repo.py @@ -14,7 +14,7 @@ DOCUMENTATION = ''' --- module: pulp_repo author: "Joe Adams (@sysadmind)" -short_description: Add or remove Pulp repos from a remote host. +short_description: Add or remove Pulp repos from a remote host description: - Add or remove Pulp repos from a remote host. - Note, this is for Pulp 2 only. diff --git a/plugins/modules/puppet.py b/plugins/modules/puppet.py index c787a7f00c..8454bb60fd 100644 --- a/plugins/modules/puppet.py +++ b/plugins/modules/puppet.py @@ -152,15 +152,9 @@ import json import os import stat +import ansible_collections.community.general.plugins.module_utils.puppet as puppet_utils + from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six.moves import shlex_quote - - -def _get_facter_dir(): - if os.getuid() == 0: - return '/etc/facter/facts.d' - else: - return os.path.expanduser('~/.facter/facts.d') def _write_structured_data(basedir, basename, data): @@ -212,16 +206,6 @@ def main(): ) p = module.params - global PUPPET_CMD - PUPPET_CMD = module.get_bin_path("puppet", False, ['/opt/puppetlabs/bin']) - - if not PUPPET_CMD: - module.fail_json( - msg="Could not find puppet. Please ensure it is installed.") - - global TIMEOUT_CMD - TIMEOUT_CMD = module.get_bin_path("timeout", False) - if p['manifest']: if not os.path.exists(p['manifest']): module.fail_json( @@ -230,90 +214,24 @@ def main(): # Check if puppet is disabled here if not p['manifest']: - rc, stdout, stderr = module.run_command( - PUPPET_CMD + " config print agent_disabled_lockfile") - 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.") + puppet_utils.ensure_agent_enabled(module) if module.params['facts'] and not module.check_mode: _write_structured_data( - _get_facter_dir(), + puppet_utils.get_facter_dir(), module.params['facter_basename'], module.params['facts']) - if TIMEOUT_CMD: - base_cmd = "%(timeout_cmd)s -s 9 %(timeout)s %(puppet_cmd)s" % dict( - timeout_cmd=TIMEOUT_CMD, - timeout=shlex_quote(p['timeout']), - puppet_cmd=PUPPET_CMD) - else: - base_cmd = PUPPET_CMD + runner = puppet_utils.puppet_runner(module) if not p['manifest'] and not p['execute']: - cmd = ("%(base_cmd)s agent --onetime" - " --no-daemonize --no-usecacheonfailure --no-splay" - " --detailed-exitcodes --verbose --color 0") % dict(base_cmd=base_cmd) - if p['puppetmaster']: - cmd += " --server %s" % shlex_quote(p['puppetmaster']) - if p['show_diff']: - cmd += " --show_diff" - if p['confdir']: - cmd += " --confdir %s" % shlex_quote(p['confdir']) - if p['environment']: - cmd += " --environment '%s'" % p['environment'] - if p['tags']: - cmd += " --tags '%s'" % ','.join(p['tags']) - if p['certname']: - cmd += " --certname='%s'" % p['certname'] - if module.check_mode: - cmd += " --noop" - elif 'noop' in p: - if p['noop']: - cmd += " --noop" - else: - cmd += " --no-noop" - if p['use_srv_records'] is not None: - if not p['use_srv_records']: - cmd += " --no-use_srv_records" - else: - cmd += " --use_srv_records" + args_order = "_agent_fixed puppetmaster show_diff confdir environment tags certname noop use_srv_records" + with runner(args_order) as ctx: + rc, stdout, stderr = ctx.run() else: - cmd = "%s apply --detailed-exitcodes " % base_cmd - if p['logdest'] == 'syslog': - cmd += "--logdest syslog " - if p['logdest'] == 'all': - cmd += " --logdest syslog --logdest console" - if p['modulepath']: - cmd += "--modulepath='%s'" % p['modulepath'] - if p['environment']: - cmd += "--environment '%s' " % p['environment'] - if p['certname']: - cmd += " --certname='%s'" % p['certname'] - if p['tags']: - cmd += " --tags '%s'" % ','.join(p['tags']) - if module.check_mode: - cmd += "--noop " - elif 'noop' in p: - if p['noop']: - cmd += " --noop" - else: - cmd += " --no-noop" - if p['execute']: - cmd += " --execute '%s'" % p['execute'] - else: - cmd += " %s" % shlex_quote(p['manifest']) - if p['summarize']: - cmd += " --summarize" - if p['debug']: - cmd += " --debug" - if p['verbose']: - cmd += " --verbose" - rc, stdout, stderr = module.run_command(cmd) + args_order = "_apply_fixed logdest modulepath environment certname tags noop _execute summarize debug verbose" + with runner(args_order) as ctx: + rc, stdout, stderr = ctx.run(_execute=[p['execute'], p['manifest']]) if rc == 0: # success @@ -335,11 +253,11 @@ def main(): elif rc == 124: # timeout module.exit_json( - rc=rc, msg="%s timed out" % cmd, stdout=stdout, stderr=stderr) + rc=rc, msg="%s timed out" % ctx.cmd, stdout=stdout, stderr=stderr) else: # failure module.fail_json( - rc=rc, msg="%s failed with return code: %d" % (cmd, rc), + rc=rc, msg="%s failed with return code: %d" % (ctx.cmd, rc), stdout=stdout, stderr=stderr) diff --git a/plugins/modules/rax.py b/plugins/modules/rax.py index b35384173a..fa929f7971 100644 --- a/plugins/modules/rax.py +++ b/plugins/modules/rax.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax -short_description: create / delete an instance in Rackspace Public Cloud +short_description: Create / delete an instance in Rackspace Public Cloud description: - creates / deletes a Rackspace Public Cloud instance and optionally waits for it to be 'running'. diff --git a/plugins/modules/rax_cbs.py b/plugins/modules/rax_cbs.py index dd8bcefa35..42e5693614 100644 --- a/plugins/modules/rax_cbs.py +++ b/plugins/modules/rax_cbs.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_cbs short_description: Manipulate Rackspace Cloud Block Storage Volumes description: - - Manipulate Rackspace Cloud Block Storage Volumes + - Manipulate Rackspace Cloud Block Storage Volumes + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: description: type: str diff --git a/plugins/modules/rax_cbs_attachments.py b/plugins/modules/rax_cbs_attachments.py index 82a1f30cfb..7cb68cf2b3 100644 --- a/plugins/modules/rax_cbs_attachments.py +++ b/plugins/modules/rax_cbs_attachments.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_cbs_attachments short_description: Manipulate Rackspace Cloud Block Storage Volume Attachments description: - - Manipulate Rackspace Cloud Block Storage Volume Attachments + - Manipulate Rackspace Cloud Block Storage Volume Attachments + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: device: type: str diff --git a/plugins/modules/rax_cdb.py b/plugins/modules/rax_cdb.py index 6703a8dd4b..a6c3ca56c3 100644 --- a/plugins/modules/rax_cdb.py +++ b/plugins/modules/rax_cdb.py @@ -11,11 +11,13 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_cdb -short_description: create/delete or resize a Rackspace Cloud Databases instance +short_description: Create/delete or resize a Rackspace Cloud Databases instance description: - creates / deletes or resize a Rackspace Cloud Databases instance and optionally waits for it to be 'running'. The name option needs to be unique since it's used to identify the instance. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: name: type: str diff --git a/plugins/modules/rax_cdb_database.py b/plugins/modules/rax_cdb_database.py index 5b5ebc6e29..6a1995f771 100644 --- a/plugins/modules/rax_cdb_database.py +++ b/plugins/modules/rax_cdb_database.py @@ -10,9 +10,11 @@ __metaclass__ = type DOCUMENTATION = ''' module: rax_cdb_database -short_description: 'create / delete a database in the Cloud Databases' +short_description: Create / delete a database in the Cloud Databases description: - create / delete a database in the Cloud Databases. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: cdb_id: type: str diff --git a/plugins/modules/rax_cdb_user.py b/plugins/modules/rax_cdb_user.py index ccc3e677a5..e489bd2e5c 100644 --- a/plugins/modules/rax_cdb_user.py +++ b/plugins/modules/rax_cdb_user.py @@ -11,9 +11,11 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_cdb_user -short_description: create / delete a Rackspace Cloud Database +short_description: Create / delete a Rackspace Cloud Database description: - create / delete a database in the Cloud Databases. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: cdb_id: type: str diff --git a/plugins/modules/rax_clb.py b/plugins/modules/rax_clb.py index 7d45c865f0..091a33d81c 100644 --- a/plugins/modules/rax_clb.py +++ b/plugins/modules/rax_clb.py @@ -11,9 +11,11 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_clb -short_description: create / delete a load balancer in Rackspace Public Cloud +short_description: Create / delete a load balancer in Rackspace Public Cloud description: - - creates / deletes a Rackspace Public Cloud load balancer. + - creates / deletes a Rackspace Public Cloud load balancer. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: algorithm: type: str diff --git a/plugins/modules/rax_clb_nodes.py b/plugins/modules/rax_clb_nodes.py index 04341f7ceb..8c72107372 100644 --- a/plugins/modules/rax_clb_nodes.py +++ b/plugins/modules/rax_clb_nodes.py @@ -11,9 +11,11 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_clb_nodes -short_description: add, modify and remove nodes from a Rackspace Cloud Load Balancer +short_description: Add, modify and remove nodes from a Rackspace Cloud Load Balancer description: - - Adds, modifies and removes nodes from a Rackspace Cloud Load Balancer + - Adds, modifies and removes nodes from a Rackspace Cloud Load Balancer. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: address: type: str diff --git a/plugins/modules/rax_clb_ssl.py b/plugins/modules/rax_clb_ssl.py index db192368b4..f31c5b37f7 100644 --- a/plugins/modules/rax_clb_ssl.py +++ b/plugins/modules/rax_clb_ssl.py @@ -10,9 +10,11 @@ __metaclass__ = type DOCUMENTATION = ''' module: rax_clb_ssl -short_description: Manage SSL termination for a Rackspace Cloud Load Balancer. +short_description: Manage SSL termination for a Rackspace Cloud Load Balancer description: -- Set up, reconfigure, or remove SSL termination for an existing load balancer. + - Set up, reconfigure, or remove SSL termination for an existing load balancer. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: loadbalancer: type: str diff --git a/plugins/modules/rax_dns.py b/plugins/modules/rax_dns.py index a97a4bb175..646d296589 100644 --- a/plugins/modules/rax_dns.py +++ b/plugins/modules/rax_dns.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_dns short_description: Manage domains on Rackspace Cloud DNS description: - - Manage domains on Rackspace Cloud DNS + - Manage domains on Rackspace Cloud DNS. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: comment: type: str diff --git a/plugins/modules/rax_dns_record.py b/plugins/modules/rax_dns_record.py index e51424dc04..0175161c68 100644 --- a/plugins/modules/rax_dns_record.py +++ b/plugins/modules/rax_dns_record.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_dns_record short_description: Manage DNS records on Rackspace Cloud DNS description: - - Manage DNS records on Rackspace Cloud DNS + - Manage DNS records on Rackspace Cloud DNS. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: comment: type: str diff --git a/plugins/modules/rax_facts.py b/plugins/modules/rax_facts.py index 53657f3c9a..a2e4f2a901 100644 --- a/plugins/modules/rax_facts.py +++ b/plugins/modules/rax_facts.py @@ -14,6 +14,8 @@ module: rax_facts short_description: Gather facts for Rackspace Cloud Servers description: - Gather facts for Rackspace Cloud Servers. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: address: type: str diff --git a/plugins/modules/rax_files.py b/plugins/modules/rax_files.py index 1c549827cc..7a07bc6356 100644 --- a/plugins/modules/rax_files.py +++ b/plugins/modules/rax_files.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_files short_description: Manipulate Rackspace Cloud Files Containers description: - - Manipulate Rackspace Cloud Files Containers + - Manipulate Rackspace Cloud Files Containers. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: clear_meta: description: diff --git a/plugins/modules/rax_files_objects.py b/plugins/modules/rax_files_objects.py index 82bedffddb..96ab0a5731 100644 --- a/plugins/modules/rax_files_objects.py +++ b/plugins/modules/rax_files_objects.py @@ -14,6 +14,8 @@ module: rax_files_objects short_description: Upload, download, and delete objects in Rackspace Cloud Files description: - Upload, download, and delete objects in Rackspace Cloud Files. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: clear_meta: description: diff --git a/plugins/modules/rax_identity.py b/plugins/modules/rax_identity.py index 6f7472bcf4..f78d5d7329 100644 --- a/plugins/modules/rax_identity.py +++ b/plugins/modules/rax_identity.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_identity short_description: Load Rackspace Cloud Identity description: - - Verifies Rackspace Cloud credentials and returns identity information + - Verifies Rackspace Cloud credentials and returns identity information. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: state: type: str diff --git a/plugins/modules/rax_keypair.py b/plugins/modules/rax_keypair.py index 6664ac8bd0..5a8dae334d 100644 --- a/plugins/modules/rax_keypair.py +++ b/plugins/modules/rax_keypair.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_keypair short_description: Create a keypair for use with Rackspace Cloud Servers description: - - Create a keypair for use with Rackspace Cloud Servers + - Create a keypair for use with Rackspace Cloud Servers. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: name: type: str diff --git a/plugins/modules/rax_meta.py b/plugins/modules/rax_meta.py index 33acad365c..0cb21832ec 100644 --- a/plugins/modules/rax_meta.py +++ b/plugins/modules/rax_meta.py @@ -13,7 +13,9 @@ DOCUMENTATION = ''' module: rax_meta short_description: Manipulate metadata for Rackspace Cloud Servers description: - - Manipulate metadata for Rackspace Cloud Servers + - Manipulate metadata for Rackspace Cloud Servers. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: address: type: str diff --git a/plugins/modules/rax_mon_alarm.py b/plugins/modules/rax_mon_alarm.py index dd971f7243..5b34e3cf9f 100644 --- a/plugins/modules/rax_mon_alarm.py +++ b/plugins/modules/rax_mon_alarm.py @@ -11,14 +11,16 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_mon_alarm -short_description: Create or delete a Rackspace Cloud Monitoring alarm. +short_description: Create or delete a Rackspace Cloud Monitoring alarm description: -- Create or delete a Rackspace Cloud Monitoring alarm that associates an - existing rax_mon_entity, rax_mon_check, and rax_mon_notification_plan with - criteria that specify what conditions will trigger which levels of - notifications. Rackspace monitoring module flow | rax_mon_entity -> - rax_mon_check -> rax_mon_notification -> rax_mon_notification_plan -> - *rax_mon_alarm* + - Create or delete a Rackspace Cloud Monitoring alarm that associates an + existing rax_mon_entity, rax_mon_check, and rax_mon_notification_plan with + criteria that specify what conditions will trigger which levels of + notifications. Rackspace monitoring module flow | rax_mon_entity -> + rax_mon_check -> rax_mon_notification -> rax_mon_notification_plan -> + *rax_mon_alarm*. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: state: type: str diff --git a/plugins/modules/rax_mon_check.py b/plugins/modules/rax_mon_check.py index c6259dab47..b9c543e723 100644 --- a/plugins/modules/rax_mon_check.py +++ b/plugins/modules/rax_mon_check.py @@ -14,12 +14,14 @@ module: rax_mon_check short_description: Create or delete a Rackspace Cloud Monitoring check for an existing entity. description: -- Create or delete a Rackspace Cloud Monitoring check associated with an - existing rax_mon_entity. A check is a specific test or measurement that is - performed, possibly from different monitoring zones, on the systems you - monitor. Rackspace monitoring module flow | rax_mon_entity -> - *rax_mon_check* -> rax_mon_notification -> rax_mon_notification_plan -> - rax_mon_alarm + - Create or delete a Rackspace Cloud Monitoring check associated with an + existing rax_mon_entity. A check is a specific test or measurement that is + performed, possibly from different monitoring zones, on the systems you + monitor. Rackspace monitoring module flow | rax_mon_entity -> + *rax_mon_check* -> rax_mon_notification -> rax_mon_notification_plan -> + rax_mon_alarm + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: state: type: str diff --git a/plugins/modules/rax_mon_entity.py b/plugins/modules/rax_mon_entity.py index cc502496dc..cd5526719b 100644 --- a/plugins/modules/rax_mon_entity.py +++ b/plugins/modules/rax_mon_entity.py @@ -13,11 +13,13 @@ DOCUMENTATION = ''' module: rax_mon_entity short_description: Create or delete a Rackspace Cloud Monitoring entity description: -- Create or delete a Rackspace Cloud Monitoring entity, which represents a device - to monitor. Entities associate checks and alarms with a target system and - provide a convenient, centralized place to store IP addresses. Rackspace - monitoring module flow | *rax_mon_entity* -> rax_mon_check -> - rax_mon_notification -> rax_mon_notification_plan -> rax_mon_alarm + - Create or delete a Rackspace Cloud Monitoring entity, which represents a device + to monitor. Entities associate checks and alarms with a target system and + provide a convenient, centralized place to store IP addresses. Rackspace + monitoring module flow | *rax_mon_entity* -> rax_mon_check -> + rax_mon_notification -> rax_mon_notification_plan -> rax_mon_alarm. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: label: type: str diff --git a/plugins/modules/rax_mon_notification.py b/plugins/modules/rax_mon_notification.py index c26b0315db..73bfd1a78f 100644 --- a/plugins/modules/rax_mon_notification.py +++ b/plugins/modules/rax_mon_notification.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_mon_notification -short_description: Create or delete a Rackspace Cloud Monitoring notification. +short_description: Create or delete a Rackspace Cloud Monitoring notification description: - Create or delete a Rackspace Cloud Monitoring notification that specifies a channel that can be used to communicate alarms, such as email, webhooks, or diff --git a/plugins/modules/rax_mon_notification_plan.py b/plugins/modules/rax_mon_notification_plan.py index 7800ea0fe9..84de4a2fa3 100644 --- a/plugins/modules/rax_mon_notification_plan.py +++ b/plugins/modules/rax_mon_notification_plan.py @@ -14,10 +14,12 @@ module: rax_mon_notification_plan short_description: Create or delete a Rackspace Cloud Monitoring notification plan. description: -- Create or delete a Rackspace Cloud Monitoring notification plan by - associating existing rax_mon_notifications with severity levels. Rackspace - monitoring module flow | rax_mon_entity -> rax_mon_check -> - rax_mon_notification -> *rax_mon_notification_plan* -> rax_mon_alarm + - Create or delete a Rackspace Cloud Monitoring notification plan by + associating existing rax_mon_notifications with severity levels. Rackspace + monitoring module flow | rax_mon_entity -> rax_mon_check -> + rax_mon_notification -> *rax_mon_notification_plan* -> rax_mon_alarm. + - This module relies on the C(pyrax) package which is deprecated in favour of using Openstack API. + - 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. options: state: type: str diff --git a/plugins/modules/rax_network.py b/plugins/modules/rax_network.py index 02de3ce011..edb7773b72 100644 --- a/plugins/modules/rax_network.py +++ b/plugins/modules/rax_network.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_network -short_description: create / delete an isolated network in Rackspace Public Cloud +short_description: Create / delete an isolated network in Rackspace Public Cloud description: - creates / deletes a Rackspace Public Cloud isolated network. options: diff --git a/plugins/modules/rax_queue.py b/plugins/modules/rax_queue.py index 366c1c77e3..e053f3266d 100644 --- a/plugins/modules/rax_queue.py +++ b/plugins/modules/rax_queue.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: rax_queue -short_description: create / delete a queue in Rackspace Public Cloud +short_description: Create / delete a queue in Rackspace Public Cloud description: - creates / deletes a Rackspace Public Cloud queue. options: diff --git a/plugins/modules/rax_scaling_group.py b/plugins/modules/rax_scaling_group.py index ef31cbb031..ed974ef0f9 100644 --- a/plugins/modules/rax_scaling_group.py +++ b/plugins/modules/rax_scaling_group.py @@ -161,8 +161,11 @@ except ImportError: HAS_PYRAX = False from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.rax import (rax_argument_spec, rax_find_image, rax_find_network, - rax_required_together, rax_to_dict, setup_rax_module) +from ansible_collections.community.general.plugins.module_utils.rax import ( + rax_argument_spec, rax_find_image, rax_find_network, + rax_required_together, rax_to_dict, setup_rax_module, + rax_scaling_group_personality_file, +) from ansible.module_utils.six import string_types @@ -223,19 +226,7 @@ def rax_asg(module, cooldown=300, disk_config=None, files=None, flavor=None, del nic['net-id'] # Handle the file contents - personality = [] - if files: - for rpath in files.keys(): - lpath = os.path.expanduser(files[rpath]) - try: - f = open(lpath, 'r') - personality.append({ - 'path': rpath, - 'contents': f.read() - }) - f.close() - except Exception as e: - module.fail_json(msg='Failed to load %s' % lpath) + personality = rax_scaling_group_personality_file(module, files) lbs = [] if loadbalancers: diff --git a/plugins/modules/redfish_command.py b/plugins/modules/redfish_command.py index 43443cf38e..9d5640996a 100644 --- a/plugins/modules/redfish_command.py +++ b/plugins/modules/redfish_command.py @@ -161,6 +161,24 @@ options: description: - Password for retrieving the update image. type: str + update_apply_time: + required: false + description: + - Time when to apply the update. + type: str + choices: + - Immediate + - OnReset + - AtMaintenanceWindowStart + - InMaintenanceWindowOnReset + - OnStartUpdateRequest + version_added: '6.1.0' + update_handle: + required: false + description: + - Handle to check the status of an update in progress. + type: str + version_added: '6.1.0' virtual_media: required: false description: @@ -508,6 +526,15 @@ EXAMPLES = ''' username: operator password: supersecretpwd + - name: Perform requested operations to continue the update + community.general.redfish_command: + category: Update + command: PerformRequestedOperations + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + update_handle: /redfish/v1/TaskService/TaskMonitors/735 + - name: Insert Virtual Media community.general.redfish_command: category: Systems @@ -610,6 +637,20 @@ msg: returned: always type: str sample: "Action was successful" +return_values: + description: Dictionary containing command-specific response data from the action. + returned: on success + type: dict + version_added: 6.1.0 + sample: { + "update_status": { + "handle": "/redfish/v1/TaskService/TaskMonitors/735", + "messages": [], + "resets_requested": [], + "ret": true, + "status": "New" + } + } ''' from ansible.module_utils.basic import AnsibleModule @@ -630,12 +671,13 @@ CATEGORY_COMMANDS_ALL = { "Manager": ["GracefulRestart", "ClearLogs", "VirtualMediaInsert", "VirtualMediaEject", "PowerOn", "PowerForceOff", "PowerForceRestart", "PowerGracefulRestart", "PowerGracefulShutdown", "PowerReboot"], - "Update": ["SimpleUpdate"] + "Update": ["SimpleUpdate", "PerformRequestedOperations"], } def main(): result = {} + return_values = {} module = AnsibleModule( argument_spec=dict( category=dict(required=True), @@ -667,6 +709,9 @@ def main(): password=dict(no_log=True) ) ), + update_apply_time=dict(choices=['Immediate', 'OnReset', 'AtMaintenanceWindowStart', + 'InMaintenanceWindowOnReset', 'OnStartUpdateRequest']), + update_handle=dict(), virtual_media=dict( type='dict', options=dict( @@ -721,7 +766,9 @@ def main(): 'update_image_uri': module.params['update_image_uri'], 'update_protocol': module.params['update_protocol'], 'update_targets': module.params['update_targets'], - 'update_creds': module.params['update_creds'] + 'update_creds': module.params['update_creds'], + 'update_apply_time': module.params['update_apply_time'], + 'update_handle': module.params['update_handle'], } # Boot override options @@ -859,6 +906,10 @@ def main(): for command in command_list: if command == "SimpleUpdate": result = rf_utils.simple_update(update_opts) + if 'update_status' in result: + return_values['update_status'] = result['update_status'] + elif command == "PerformRequestedOperations": + result = rf_utils.perform_requested_update_operations(update_opts['update_handle']) # Return data back or fail with proper message if result['ret'] is True: @@ -866,7 +917,8 @@ def main(): changed = result.get('changed', True) session = result.get('session', dict()) module.exit_json(changed=changed, session=session, - msg='Action was successful') + msg='Action was successful', + return_values=return_values) else: module.fail_json(msg=to_native(result['msg'])) diff --git a/plugins/modules/redfish_info.py b/plugins/modules/redfish_info.py index fd81695368..e6df4813ad 100644 --- a/plugins/modules/redfish_info.py +++ b/plugins/modules/redfish_info.py @@ -58,6 +58,12 @@ options: - Timeout in seconds for HTTP requests to OOB controller. default: 10 type: int + update_handle: + required: false + description: + - Handle to check the status of an update in progress. + type: str + version_added: '6.1.0' author: "Jose Delarosa (@jose-delarosa)" ''' @@ -247,6 +253,15 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" + - name: Get the status of an update operation + community.general.redfish_info: + category: Update + command: GetUpdateStatus + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + update_handle: /redfish/v1/TaskService/TaskMonitors/735 + - name: Get Manager Services community.general.redfish_info: category: Manager @@ -324,7 +339,8 @@ CATEGORY_COMMANDS_ALL = { "GetChassisThermals", "GetChassisInventory", "GetHealthReport"], "Accounts": ["ListUsers"], "Sessions": ["GetSessions"], - "Update": ["GetFirmwareInventory", "GetFirmwareUpdateCapabilities", "GetSoftwareInventory"], + "Update": ["GetFirmwareInventory", "GetFirmwareUpdateCapabilities", "GetSoftwareInventory", + "GetUpdateStatus"], "Manager": ["GetManagerNicInventory", "GetVirtualMedia", "GetLogs", "GetNetworkProtocols", "GetHealthReport", "GetHostInterfaces", "GetManagerInventory"], } @@ -350,7 +366,8 @@ def main(): username=dict(), password=dict(no_log=True), auth_token=dict(no_log=True), - timeout=dict(type='int', default=10) + timeout=dict(type='int', default=10), + update_handle=dict(), ), required_together=[ ('username', 'password'), @@ -372,6 +389,9 @@ def main(): # timeout timeout = module.params['timeout'] + # update handle + update_handle = module.params['update_handle'] + # Build root URI root_uri = "https://" + module.params['baseuri'] rf_utils = RedfishUtils(creds, root_uri, timeout, module) @@ -482,6 +502,8 @@ def main(): result["software"] = rf_utils.get_software_inventory() elif command == "GetFirmwareUpdateCapabilities": result["firmware_update_capabilities"] = rf_utils.get_firmware_update_capabilities() + elif command == "GetUpdateStatus": + result["update_status"] = rf_utils.get_update_status(update_handle) elif category == "Sessions": # execute only if we find SessionService resources diff --git a/plugins/modules/redhat_subscription.py b/plugins/modules/redhat_subscription.py index 69aa550c5d..8836b78564 100644 --- a/plugins/modules/redhat_subscription.py +++ b/plugins/modules/redhat_subscription.py @@ -34,15 +34,15 @@ options: type: str username: description: - - access.redhat.com or Sat6 username + - access.redhat.com or Red Hat Satellite or Katello username type: str password: description: - - access.redhat.com or Sat6 password + - access.redhat.com or Red Hat Satellite or Katello password type: str server_hostname: description: - - Specify an alternative Red Hat Subscription Management or Sat6 server + - Specify an alternative Red Hat Subscription Management or Red Hat Satellite or Katello server type: str server_insecure: description: @@ -50,12 +50,12 @@ options: type: str server_prefix: description: - - Specify the prefix when registering to the Red Hat Subscription Management or Sat6 server. + - Specify the prefix when registering to the Red Hat Subscription Management or Red Hat Satellite or Katello server. type: str version_added: 3.3.0 server_port: description: - - Specify the port when registering to the Red Hat Subscription Management or Sat6 server. + - Specify the port when registering to the Red Hat Subscription Management or Red Hat Satellite or Katello server. type: str version_added: 3.3.0 rhsm_baseurl: @@ -70,6 +70,11 @@ options: description: - Specify an HTTP proxy hostname. type: str + server_proxy_scheme: + description: + - Specify an HTTP proxy scheme, for example C(http) or C(https). + type: str + version_added: 6.2.0 server_proxy_port: description: - Specify an HTTP proxy port. @@ -98,7 +103,7 @@ options: type: str environment: description: - - Register with a specific environment in the destination org. Used with Red Hat Satellite 6.x or Katello + - Register with a specific environment in the destination org. Used with Red Hat Satellite or Katello type: str pool: description: @@ -229,7 +234,7 @@ EXAMPLES = ''' org_id: 222333444 pool: '^Red Hat Enterprise Server$' -- name: Register as user credentials into given environment (against Red Hat Satellite 6.x), and auto-subscribe. +- name: Register as user credentials into given environment (against Red Hat Satellite or Katello), and auto-subscribe. community.general.redhat_subscription: state: present username: joe_user @@ -264,7 +269,7 @@ RETURN = ''' subscribed_pool_ids: description: List of pool IDs to which system is now subscribed returned: success - type: complex + type: dict sample: { "8a85f9815ab905d3015ab928c7005de4": "1" } @@ -394,10 +399,11 @@ class Rhsm(RegistrationBase): def register(self, username, password, auto_attach, activationkey, org_id, consumer_type, consumer_name, consumer_id, force_register, environment, - rhsm_baseurl, server_insecure, server_hostname, server_proxy_hostname, - server_proxy_port, server_proxy_user, server_proxy_password, release): + release): ''' - Register the current system to the provided RHSM or Sat6 server + Register the current system to the provided RHSM or Red Hat Satellite + or Katello server + Raises: * Exception - if error occurs while running command ''' @@ -407,44 +413,31 @@ class Rhsm(RegistrationBase): if force_register: args.extend(['--force']) - if rhsm_baseurl: - args.extend(['--baseurl', rhsm_baseurl]) - - if server_insecure: - args.extend(['--insecure']) - - if server_hostname: - args.extend(['--serverurl', server_hostname]) - if org_id: args.extend(['--org', org_id]) - if server_proxy_hostname and server_proxy_port: - args.extend(['--proxy', server_proxy_hostname + ':' + server_proxy_port]) + if auto_attach: + args.append('--auto-attach') - if server_proxy_user: - args.extend(['--proxyuser', server_proxy_user]) + if consumer_type: + args.extend(['--type', consumer_type]) - if server_proxy_password: - args.extend(['--proxypassword', server_proxy_password]) + if consumer_name: + args.extend(['--name', consumer_name]) + + if consumer_id: + args.extend(['--consumerid', consumer_id]) + + if environment: + args.extend(['--environment', environment]) if activationkey: args.extend(['--activationkey', activationkey]) else: - if auto_attach: - args.append('--auto-attach') if username: args.extend(['--username', username]) if password: args.extend(['--password', password]) - if consumer_type: - args.extend(['--type', consumer_type]) - if consumer_name: - args.extend(['--name', consumer_name]) - if consumer_id: - args.extend(['--consumerid', consumer_id]) - if environment: - args.extend(['--environment', environment]) if release: args.extend(['--release', release]) @@ -818,6 +811,7 @@ def main(): 'consumer_id': {}, 'force_register': {'default': False, 'type': 'bool'}, 'server_proxy_hostname': {}, + 'server_proxy_scheme': {}, 'server_proxy_port': {}, 'server_proxy_user': {}, 'server_proxy_password': {'no_log': True}, @@ -922,8 +916,7 @@ def main(): rhsm.configure(**module.params) rhsm.register(username, password, auto_attach, activationkey, org_id, consumer_type, consumer_name, consumer_id, force_register, - environment, rhsm_baseurl, server_insecure, server_hostname, - server_proxy_hostname, server_proxy_port, server_proxy_user, server_proxy_password, release) + environment, release) if syspurpose and 'sync' in syspurpose and syspurpose['sync'] is True: rhsm.sync_syspurpose() if pool_ids: diff --git a/plugins/modules/rundeck_acl_policy.py b/plugins/modules/rundeck_acl_policy.py index 6168cb5b64..4830c44784 100644 --- a/plugins/modules/rundeck_acl_policy.py +++ b/plugins/modules/rundeck_acl_policy.py @@ -15,7 +15,7 @@ DOCUMENTATION = ''' --- module: rundeck_acl_policy -short_description: Manage Rundeck ACL policies. +short_description: Manage Rundeck ACL policies description: - Create, update and remove Rundeck ACL policies through HTTP API. author: "Loic Blot (@nerzhul)" diff --git a/plugins/modules/rundeck_project.py b/plugins/modules/rundeck_project.py index 88f4a78100..2039a00a02 100644 --- a/plugins/modules/rundeck_project.py +++ b/plugins/modules/rundeck_project.py @@ -17,7 +17,7 @@ DOCUMENTATION = ''' --- module: rundeck_project -short_description: Manage Rundeck projects. +short_description: Manage Rundeck projects description: - Create and remove Rundeck projects through HTTP API. author: "Loic Blot (@nerzhul)" diff --git a/plugins/modules/say.py b/plugins/modules/say.py index fe36b02d15..04b5027cae 100644 --- a/plugins/modules/say.py +++ b/plugins/modules/say.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: say -short_description: Makes a computer to speak. +short_description: Makes a computer to speak description: - makes a computer speak! Amuse your friends, annoy your coworkers! notes: diff --git a/plugins/modules/scaleway_image_info.py b/plugins/modules/scaleway_image_info.py index ee0134a52a..56ca689c38 100644 --- a/plugins/modules/scaleway_image_info.py +++ b/plugins/modules/scaleway_image_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_image_info -short_description: Gather information about the Scaleway images available. +short_description: Gather information about the Scaleway images available description: - Gather information about the Scaleway images available. author: diff --git a/plugins/modules/scaleway_ip_info.py b/plugins/modules/scaleway_ip_info.py index d8725894e6..1fd4be5898 100644 --- a/plugins/modules/scaleway_ip_info.py +++ b/plugins/modules/scaleway_ip_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_ip_info -short_description: Gather information about the Scaleway ips available. +short_description: Gather information about the Scaleway ips available description: - Gather information about the Scaleway ips available. author: diff --git a/plugins/modules/scaleway_organization_info.py b/plugins/modules/scaleway_organization_info.py index aca8a0c43f..717c47db19 100644 --- a/plugins/modules/scaleway_organization_info.py +++ b/plugins/modules/scaleway_organization_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_organization_info -short_description: Gather information about the Scaleway organizations available. +short_description: Gather information about the Scaleway organizations available description: - Gather information about the Scaleway organizations available. author: @@ -44,7 +44,8 @@ RETURN = r''' scaleway_organization_info: description: Response from Scaleway API returned: success - type: complex + type: list + elements: dict sample: "scaleway_organization_info": [ { diff --git a/plugins/modules/scaleway_security_group_info.py b/plugins/modules/scaleway_security_group_info.py index 7fd96fd067..fb28e87740 100644 --- a/plugins/modules/scaleway_security_group_info.py +++ b/plugins/modules/scaleway_security_group_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_security_group_info -short_description: Gather information about the Scaleway security groups available. +short_description: Gather information about the Scaleway security groups available description: - Gather information about the Scaleway security groups available. author: diff --git a/plugins/modules/scaleway_server_info.py b/plugins/modules/scaleway_server_info.py index 7a31882ef7..01e9410da8 100644 --- a/plugins/modules/scaleway_server_info.py +++ b/plugins/modules/scaleway_server_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_server_info -short_description: Gather information about the Scaleway servers available. +short_description: Gather information about the Scaleway servers available description: - Gather information about the Scaleway servers available. author: diff --git a/plugins/modules/scaleway_snapshot_info.py b/plugins/modules/scaleway_snapshot_info.py index 47cd14cee8..687f43c85b 100644 --- a/plugins/modules/scaleway_snapshot_info.py +++ b/plugins/modules/scaleway_snapshot_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_snapshot_info -short_description: Gather information about the Scaleway snapshots available. +short_description: Gather information about the Scaleway snapshots available description: - Gather information about the Scaleway snapshot available. author: diff --git a/plugins/modules/scaleway_volume_info.py b/plugins/modules/scaleway_volume_info.py index 369fadbe64..471845c43e 100644 --- a/plugins/modules/scaleway_volume_info.py +++ b/plugins/modules/scaleway_volume_info.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: scaleway_volume_info -short_description: Gather information about the Scaleway volumes available. +short_description: Gather information about the Scaleway volumes available description: - Gather information about the Scaleway volumes available. author: diff --git a/plugins/modules/sl_vm.py b/plugins/modules/sl_vm.py index 56b3ccdd39..ca925c345f 100644 --- a/plugins/modules/sl_vm.py +++ b/plugins/modules/sl_vm.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: sl_vm -short_description: create or cancel a virtual instance in SoftLayer +short_description: Create or cancel a virtual instance in SoftLayer description: - Creates or cancels SoftLayer instances. - When created, optionally waits for it to be 'running'. diff --git a/plugins/modules/slack.py b/plugins/modules/slack.py index 46602a5d16..2854277f60 100644 --- a/plugins/modules/slack.py +++ b/plugins/modules/slack.py @@ -129,6 +129,21 @@ options: type: list elements: dict version_added: 1.0.0 + prepend_hash: + type: str + description: + - Setting for automatically prepending a C(#) symbol on the passed in I(channel_id). + - The C(auto) method prepends a C(#) unless I(channel_id) starts with one of C(#), C(@), C(C0), C(GF), C(G0), C(CP). + These prefixes only cover a small set of the prefixes that should not have a C(#) prepended. + Since an exact condition which I(channel_id) values must not have the C(#) prefix is not known, + the value C(auto) for this option will be deprecated in the future. It is best to explicitly set + I(prepend_hash=always) or I(prepend_hash=never) to obtain the needed behavior. + choices: + - 'always' + - 'never' + - 'auto' + default: 'auto' + version_added: 6.1.0 """ EXAMPLES = """ @@ -289,7 +304,7 @@ def recursive_escape_quotes(obj, keys): def build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_emoji, link_names, - parse, color, attachments, blocks, message_id): + parse, color, attachments, blocks, message_id, prepend_hash): payload = {} if color == "normal" and text is not None: payload = dict(text=escape_quotes(text)) @@ -297,10 +312,15 @@ def build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_e # With a custom color we have to set the message as attachment, and explicitly turn markdown parsing on for it. payload = dict(attachments=[dict(text=escape_quotes(text), color=color, mrkdwn_in=["text"])]) if channel is not None: - if channel.startswith(('#', '@', 'C0', 'GF', 'G0', 'CP')): - payload['channel'] = channel - else: + if prepend_hash == 'auto': + if channel.startswith(('#', '@', 'C0', 'GF', 'G0', 'CP')): + payload['channel'] = channel + else: + payload['channel'] = '#' + channel + elif prepend_hash == 'always': payload['channel'] = '#' + channel + elif prepend_hash == 'never': + payload['channel'] = channel if thread_id is not None: payload['thread_ts'] = thread_id if username is not None: @@ -428,6 +448,7 @@ def main(): attachments=dict(type='list', elements='dict'), blocks=dict(type='list', elements='dict'), message_id=dict(type='str'), + prepend_hash=dict(type='str', default='auto', choices=['always', 'never', 'auto']), ), supports_check_mode=True, ) @@ -446,6 +467,7 @@ def main(): attachments = module.params['attachments'] blocks = module.params['blocks'] message_id = module.params['message_id'] + prepend_hash = module.params['prepend_hash'] color_choices = ['normal', 'good', 'warning', 'danger'] if color not in color_choices and not is_valid_hex_color(color): @@ -470,7 +492,7 @@ def main(): module.exit_json(changed=changed) payload = build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_emoji, link_names, - parse, color, attachments, blocks, message_id) + parse, color, attachments, blocks, message_id, prepend_hash) slack_response = do_notify_slack(module, domain, token, payload) if 'ok' in slack_response: diff --git a/plugins/modules/smartos_image_info.py b/plugins/modules/smartos_image_info.py index 0b5117fc45..37267d115a 100644 --- a/plugins/modules/smartos_image_info.py +++ b/plugins/modules/smartos_image_info.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: smartos_image_info -short_description: Get SmartOS image details. +short_description: Get SmartOS image details description: - Retrieve information about all installed images on SmartOS. - This module was called C(smartos_image_facts) before Ansible 2.9, returning C(ansible_facts). diff --git a/plugins/modules/snmp_facts.py b/plugins/modules/snmp_facts.py index 71821faaa8..0242bc6dde 100644 --- a/plugins/modules/snmp_facts.py +++ b/plugins/modules/snmp_facts.py @@ -183,20 +183,14 @@ ansible_interfaces: ''' import binascii -import traceback from collections import defaultdict +from ansible_collections.community.general.plugins.module_utils import deps +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.converters import to_text -PYSNMP_IMP_ERR = None -try: +with deps.declare("pysnmp"): from pysnmp.entity.rfc3413.oneliner import cmdgen from pysnmp.proto.rfc1905 import EndOfMibView - HAS_PYSNMP = True -except Exception: - PYSNMP_IMP_ERR = traceback.format_exc() - HAS_PYSNMP = False - -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -from ansible.module_utils.common.text.converters import to_text class DefineOid(object): @@ -299,8 +293,7 @@ def main(): m_args = module.params - if not HAS_PYSNMP: - module.fail_json(msg=missing_required_lib('pysnmp'), exception=PYSNMP_IMP_ERR) + deps.validate(module) cmdGen = cmdgen.CommandGenerator() transport_opts = dict((k, m_args[k]) for k in ('timeout', 'retries') if m_args[k] is not None) diff --git a/plugins/modules/spectrum_device.py b/plugins/modules/spectrum_device.py index c2bab55016..40093aa3a9 100644 --- a/plugins/modules/spectrum_device.py +++ b/plugins/modules/spectrum_device.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: spectrum_device -short_description: Creates/deletes devices in CA Spectrum. +short_description: Creates/deletes devices in CA Spectrum description: - This module allows you to create and delete devices in CA Spectrum U(https://www.ca.com/us/products/ca-spectrum.html). - Tested on CA Spectrum 9.4.2, 10.1.1 and 10.2.1 diff --git a/plugins/modules/spectrum_model_attrs.py b/plugins/modules/spectrum_model_attrs.py index 5a92802f5f..de771b0ad3 100644 --- a/plugins/modules/spectrum_model_attrs.py +++ b/plugins/modules/spectrum_model_attrs.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = r''' --- module: spectrum_model_attrs -short_description: Enforce a model's attributes in CA Spectrum. +short_description: Enforce a model's attributes in CA Spectrum description: - This module can be used to enforce a model's attributes in CA Spectrum. version_added: 2.5.0 diff --git a/plugins/modules/spotinst_aws_elastigroup.py b/plugins/modules/spotinst_aws_elastigroup.py index df2b8d84db..481ba70cb9 100644 --- a/plugins/modules/spotinst_aws_elastigroup.py +++ b/plugins/modules/spotinst_aws_elastigroup.py @@ -35,6 +35,13 @@ options: By default this is retrieved from the credentials path. type: str + token: + description: + - A Personal API Access Token issued by Spotinst. + - >- + When not specified, the module will try to obtain it, in that order, from: environment variable C(SPOTINST_TOKEN), or from the credentials path. + type: str + availability_vs_cost: description: - The strategy orientation. @@ -507,8 +514,25 @@ options: description: - TODO document. type: list + elements: str default: [] + multai_token: + description: + - Token used for Multai configuration. + type: str + + multai_load_balancers: + description: + - Configuration parameters for Multai load balancers. + type: list + elements: dict + + elastic_beanstalk: + description: + - Placeholder parameter for future implementation of Elastic Beanstalk configurations. + type: dict + ''' EXAMPLES = ''' # Basic configuration YAML example @@ -1455,7 +1479,7 @@ def main(): block_device_mappings=dict(type='list', elements='dict'), chef=dict(type='dict'), credentials_path=dict(type='path', default="~/.spotinst/credentials"), - do_not_update=dict(default=[], type='list'), + do_not_update=dict(default=[], type='list', elements='str'), down_scaling_policies=dict(type='list', elements='dict'), draining_timeout=dict(type='int'), ebs_optimized=dict(type='bool'), @@ -1479,7 +1503,7 @@ def main(): mesosphere=dict(type='dict'), min_size=dict(type='int', required=True), monitoring=dict(type='str'), - multai_load_balancers=dict(type='list'), + multai_load_balancers=dict(type='list', elements='dict'), multai_token=dict(type='str', no_log=True), name=dict(type='str', required=True), network_interfaces=dict(type='list', elements='dict'), diff --git a/plugins/modules/ssh_config.py b/plugins/modules/ssh_config.py index 00a0525d6c..f2fa8aa457 100644 --- a/plugins/modules/ssh_config.py +++ b/plugins/modules/ssh_config.py @@ -88,6 +88,11 @@ options: - If I(user) and this option are not specified, C(/etc/ssh/ssh_config) is used. - Mutually exclusive with I(user). type: path + host_key_algorithms: + description: + - Sets the C(HostKeyAlgorithms) option. + type: str + version_added: 6.1.0 requirements: - StormSSH notes: @@ -164,6 +169,7 @@ except ImportError: from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils.common.text.converters import to_native +from ansible_collections.community.general.plugins.module_utils.ssh import determine_config_file class SSHConfig(): @@ -183,10 +189,7 @@ class SSHConfig(): self.config.load() def check_ssh_config_path(self): - if self.user: - self.config_file = os.path.join(os.path.expanduser('~%s' % self.user), '.ssh', 'config') - elif self.config_file is None: - self.config_file = '/etc/ssh/ssh_config' + self.config_file = determine_config_file(self.user, self.config_file) # See if the identity file exists or not, relative to the config file if os.path.exists(self.config_file) and self.identity_file is not None: @@ -207,6 +210,7 @@ class SSHConfig(): strict_host_key_checking=self.params.get('strict_host_key_checking'), user_known_hosts_file=self.params.get('user_known_hosts_file'), proxycommand=self.params.get('proxycommand'), + host_key_algorithms=self.params.get('host_key_algorithms'), ) # Convert True / False to 'yes' / 'no' for usage in ssh_config @@ -297,6 +301,7 @@ def main(): group=dict(default=None, type='str'), host=dict(type='str', required=True), hostname=dict(type='str'), + host_key_algorithms=dict(type='str', no_log=False), identity_file=dict(type='path'), port=dict(type='str'), proxycommand=dict(type='str', default=None), diff --git a/plugins/modules/sudoers.py b/plugins/modules/sudoers.py index 2c0aa879bc..f2bcb20b75 100644 --- a/plugins/modules/sudoers.py +++ b/plugins/modules/sudoers.py @@ -43,6 +43,12 @@ options: - Whether a password will be required to run the sudo'd command. default: true type: bool + host: + description: + - Specify the host the rule is for. + default: ALL + type: str + version_added: 6.2.0 runas: description: - Specify the target user the command(s) will run as. @@ -95,10 +101,11 @@ EXAMPLES = ''' - name: >- Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics - without requiring a password + without requiring a password on the host called webserver community.general.sudoers: name: monitor-app group: monitoring + host: webserver commands: /usr/local/bin/gather-app-metrics - name: >- @@ -136,6 +143,7 @@ class Sudoers(object): self.group = module.params['group'] self.state = module.params['state'] self.nopassword = module.params['nopassword'] + self.host = module.params['host'] self.runas = module.params['runas'] self.sudoers_path = module.params['sudoers_path'] self.file = os.path.join(self.sudoers_path, self.name) @@ -178,7 +186,13 @@ class Sudoers(object): commands_str = ', '.join(self.commands) nopasswd_str = 'NOPASSWD:' if self.nopassword else '' runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else '' - return "{owner} ALL={runas}{nopasswd} {commands}\n".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str) + return "{owner} {host}={runas}{nopasswd} {commands}\n".format( + owner=owner, + host=self.host, + runas=runas_str, + nopasswd=nopasswd_str, + commands=commands_str + ) def validate(self): if self.validation == 'absent': @@ -225,6 +239,10 @@ def main(): 'type': 'bool', 'default': True, }, + 'host': { + 'type': 'str', + 'default': 'ALL', + }, 'runas': { 'type': 'str', 'default': None, diff --git a/plugins/modules/svc.py b/plugins/modules/svc.py index 2800c9d2bf..4d92892ce3 100644 --- a/plugins/modules/svc.py +++ b/plugins/modules/svc.py @@ -13,7 +13,7 @@ DOCUMENTATION = ''' module: svc author: - Brian Coca (@bcoca) -short_description: Manage daemontools services +short_description: Manage daemontools services description: - Controls daemontools services on remote hosts using the svc utility. options: diff --git a/plugins/modules/swupd.py b/plugins/modules/swupd.py index 4567709f48..a47dd667ae 100644 --- a/plugins/modules/swupd.py +++ b/plugins/modules/swupd.py @@ -13,7 +13,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: swupd -short_description: Manages updates and bundles in ClearLinux systems. +short_description: Manages updates and bundles in ClearLinux systems description: - Manages updates and bundles with the swupd bundle manager, which is used by the Clear Linux Project for Intel Architecture. diff --git a/plugins/modules/telegram.py b/plugins/modules/telegram.py index 499af4ef1a..4e89825120 100644 --- a/plugins/modules/telegram.py +++ b/plugins/modules/telegram.py @@ -16,7 +16,7 @@ author: - "Artem Feofanov (@tyouxa)" - "Nikolai Lomov (@lomserman)" -short_description: module for sending notifications via telegram +short_description: Send notifications via telegram description: - Send notifications via telegram bot, to a verified group or user. diff --git a/plugins/modules/twilio.py b/plugins/modules/twilio.py index 1934820fe3..6d22563d1c 100644 --- a/plugins/modules/twilio.py +++ b/plugins/modules/twilio.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: twilio -short_description: Sends a text message to a mobile phone through Twilio. +short_description: Sends a text message to a mobile phone through Twilio description: - Sends a text message to a phone number through the Twilio messaging API. notes: diff --git a/plugins/modules/udm_share.py b/plugins/modules/udm_share.py index b1d7e13287..8120df1b00 100644 --- a/plugins/modules/udm_share.py +++ b/plugins/modules/udm_share.py @@ -125,6 +125,7 @@ options: description: - Option name in smb.conf and its value. type: list + elements: dict aliases: [ samba_custom_settings ] sambaDirectoryMode: default: '0755' @@ -200,12 +201,14 @@ options: description: - Allowed host/network. type: list + elements: str aliases: [ samba_hosts_allow ] sambaHostsDeny: default: [] description: - Denied host/network. type: list + elements: str aliases: [ samba_hosts_deny ] sambaInheritAcls: default: true @@ -314,11 +317,13 @@ options: description: - Only allow access for this host, IP address or network. type: list + elements: str nfsCustomSettings: default: [] description: - Option name in exports file. type: list + elements: str aliases: [ nfs_custom_settings ] ''' @@ -382,6 +387,7 @@ def main(): aliases=['samba_csc_policy'], default='manual'), sambaCustomSettings=dict(type='list', + elements='dict', aliases=['samba_custom_settings'], default=[]), sambaDirectoryMode=dict(type='str', @@ -418,9 +424,11 @@ def main(): aliases=['samba_hide_unreadable'], default=False), sambaHostsAllow=dict(type='list', + elements='str', aliases=['samba_hosts_allow'], default=[]), sambaHostsDeny=dict(type='list', + elements='str', aliases=['samba_hosts_deny'], default=[]), sambaInheritAcls=dict(type='bool', @@ -474,8 +482,10 @@ def main(): aliases=['samba_writeable'], default=True), nfs_hosts=dict(type='list', + elements='str', default=[]), nfsCustomSettings=dict(type='list', + elements='str', aliases=['nfs_custom_settings'], default=[]), state=dict(default='present', diff --git a/plugins/modules/udm_user.py b/plugins/modules/udm_user.py index d5b26fbb28..96bbdfbc10 100644 --- a/plugins/modules/udm_user.py +++ b/plugins/modules/udm_user.py @@ -78,6 +78,7 @@ options: description: - A list of e-mail addresses. type: list + elements: str employee_number: description: - Employee number @@ -99,6 +100,7 @@ options: LDAP filter for each group as $GROUP: C((&(objectClass=posixGroup)(cn=$GROUP)))." type: list + elements: str home_share: description: - "Home NFS share. Must be a LDAP DN, e.g. @@ -116,6 +118,7 @@ options: - List of private telephone numbers. aliases: [ homeTelephoneNumber ] type: list + elements: str homedrive: description: - Windows home drive, e.g. C("H:"). @@ -126,6 +129,7 @@ options: - List of alternative e-mail addresses. aliases: [ mailAlternativeAddress ] type: list + elements: str mail_home_server: description: - FQDN of mail server @@ -142,6 +146,7 @@ options: - Mobile phone number aliases: [ mobileTelephoneNumber ] type: list + elements: str organisation: description: - Organisation @@ -165,10 +170,12 @@ options: - List of pager telephone numbers. aliases: [ pagerTelephonenumber ] type: list + elements: str phone: description: - List of telephone numbers. type: list + elements: str default: [] postcode: description: @@ -201,12 +208,14 @@ options: join." aliases: [ sambaPrivileges ] type: list + elements: str default: [] samba_user_workstations: description: - Allow the authentication only on this Microsoft Windows host. aliases: [ sambaUserWorkstations ] type: list + elements: str default: [] sambahome: description: @@ -221,11 +230,13 @@ options: description: - A list of superiors as LDAP DNs. type: list + elements: str serviceprovider: default: [''] description: - Enable user for the following service providers. type: list + elements: str shell: default: '/bin/bash' description: @@ -333,7 +344,8 @@ def main(): display_name=dict(type='str', aliases=['displayName']), email=dict(default=[''], - type='list'), + type='list', + elements='str'), employee_number=dict(type='str', aliases=['employeeNumber']), employee_type=dict(type='str', @@ -341,18 +353,21 @@ def main(): firstname=dict(type='str'), gecos=dict(type='str'), groups=dict(default=[], - type='list'), + type='list', + elements='str'), home_share=dict(type='str', aliases=['homeShare']), home_share_path=dict(type='str', aliases=['homeSharePath']), home_telephone_number=dict(default=[], type='list', + elements='str', aliases=['homeTelephoneNumber']), homedrive=dict(type='str'), lastname=dict(type='str'), mail_alternative_address=dict(default=[], type='list', + elements='str', aliases=['mailAlternativeAddress']), mail_home_server=dict(type='str', aliases=['mailHomeServer']), @@ -360,6 +375,7 @@ def main(): aliases=['mailPrimaryAddress']), mobile_telephone_number=dict(default=[], type='list', + elements='str', aliases=['mobileTelephoneNumber']), organisation=dict(type='str', aliases=['organization']), @@ -371,11 +387,13 @@ def main(): aliases=['override_pw_length']), pager_telephonenumber=dict(default=[], type='list', + elements='str', aliases=['pagerTelephonenumber']), password=dict(type='str', no_log=True), phone=dict(default=[], - type='list'), + type='list', + elements='str'), postcode=dict(type='str'), primary_group=dict(type='str', aliases=['primaryGroup']), @@ -387,16 +405,20 @@ def main(): aliases=['roomNumber']), samba_privileges=dict(default=[], type='list', + elements='str', aliases=['sambaPrivileges']), samba_user_workstations=dict(default=[], type='list', + elements='str', aliases=['sambaUserWorkstations']), sambahome=dict(type='str'), scriptpath=dict(type='str'), secretary=dict(default=[], - type='list'), + type='list', + elements='str'), serviceprovider=dict(default=[''], - type='list'), + type='list', + elements='str'), shell=dict(default='/bin/bash', type='str'), street=dict(type='str'), diff --git a/plugins/modules/utm_aaa_group.py b/plugins/modules/utm_aaa_group.py index cff1834c93..3d5cbc2007 100644 --- a/plugins/modules/utm_aaa_group.py +++ b/plugins/modules/utm_aaa_group.py @@ -15,7 +15,7 @@ module: utm_aaa_group author: - Johannes Brunswicker (@MatrixCrawler) -short_description: Create, update or destroy an aaa group object in Sophos UTM. +short_description: Create, update or destroy an aaa group object in Sophos UTM description: - Create, update or destroy an aaa group object in Sophos UTM. diff --git a/plugins/modules/utm_aaa_group_info.py b/plugins/modules/utm_aaa_group_info.py index a01dad92ac..8798fda7f3 100644 --- a/plugins/modules/utm_aaa_group_info.py +++ b/plugins/modules/utm_aaa_group_info.py @@ -17,7 +17,7 @@ module: utm_aaa_group_info author: - Johannes Brunswicker (@MatrixCrawler) -short_description: get info for reverse_proxy frontend entry in Sophos UTM +short_description: Get info for reverse_proxy frontend entry in Sophos UTM description: - get info for a reverse_proxy frontend entry in SOPHOS UTM. diff --git a/plugins/modules/utm_ca_host_key_cert.py b/plugins/modules/utm_ca_host_key_cert.py index 693f25964a..318bc1fd31 100644 --- a/plugins/modules/utm_ca_host_key_cert.py +++ b/plugins/modules/utm_ca_host_key_cert.py @@ -16,7 +16,7 @@ module: utm_ca_host_key_cert author: - Stephan Schwarz (@stearz) -short_description: create, update or destroy ca host_key_cert entry in Sophos UTM +short_description: Create, update or destroy ca host_key_cert entry in Sophos UTM description: - Create, update or destroy a ca host_key_cert entry in SOPHOS UTM. diff --git a/plugins/modules/utm_dns_host.py b/plugins/modules/utm_dns_host.py index af91e2433b..3a1744651a 100644 --- a/plugins/modules/utm_dns_host.py +++ b/plugins/modules/utm_dns_host.py @@ -15,7 +15,7 @@ module: utm_dns_host author: - Johannes Brunswicker (@MatrixCrawler) -short_description: create, update or destroy dns entry in Sophos UTM +short_description: Create, update or destroy dns entry in Sophos UTM description: - Create, update or destroy a dns entry in SOPHOS UTM. diff --git a/plugins/modules/utm_proxy_auth_profile.py b/plugins/modules/utm_proxy_auth_profile.py index aab426cc03..0c53a92380 100644 --- a/plugins/modules/utm_proxy_auth_profile.py +++ b/plugins/modules/utm_proxy_auth_profile.py @@ -16,7 +16,7 @@ module: utm_proxy_auth_profile author: - Stephan Schwarz (@stearz) -short_description: create, update or destroy reverse_proxy auth_profile entry in Sophos UTM +short_description: Create, update or destroy reverse_proxy auth_profile entry in Sophos UTM description: - Create, update or destroy a reverse_proxy auth_profile entry in SOPHOS UTM. diff --git a/plugins/modules/utm_proxy_frontend.py b/plugins/modules/utm_proxy_frontend.py index 8f5a1e8686..127c7d4d43 100644 --- a/plugins/modules/utm_proxy_frontend.py +++ b/plugins/modules/utm_proxy_frontend.py @@ -16,7 +16,7 @@ module: utm_proxy_frontend author: - Johannes Brunswicker (@MatrixCrawler) -short_description: create, update or destroy reverse_proxy frontend entry in Sophos UTM +short_description: Create, update or destroy reverse_proxy frontend entry in Sophos UTM description: - Create, update or destroy a reverse_proxy frontend entry in Sophos UTM. diff --git a/plugins/modules/utm_proxy_frontend_info.py b/plugins/modules/utm_proxy_frontend_info.py index 0b8e124379..65faecfbc8 100644 --- a/plugins/modules/utm_proxy_frontend_info.py +++ b/plugins/modules/utm_proxy_frontend_info.py @@ -16,7 +16,7 @@ module: utm_proxy_frontend_info author: - Johannes Brunswicker (@MatrixCrawler) -short_description: create, update or destroy reverse_proxy frontend entry in Sophos UTM +short_description: Create, update or destroy reverse_proxy frontend entry in Sophos UTM description: - Create, update or destroy a reverse_proxy frontend entry in SOPHOS UTM. diff --git a/plugins/modules/utm_proxy_location.py b/plugins/modules/utm_proxy_location.py index c6ff1bd26b..0efeea5a27 100644 --- a/plugins/modules/utm_proxy_location.py +++ b/plugins/modules/utm_proxy_location.py @@ -16,7 +16,7 @@ module: utm_proxy_location author: - Johannes Brunswicker (@MatrixCrawler) -short_description: create, update or destroy reverse_proxy location entry in Sophos UTM +short_description: Create, update or destroy reverse_proxy location entry in Sophos UTM description: - Create, update or destroy a reverse_proxy location entry in SOPHOS UTM. diff --git a/plugins/modules/utm_proxy_location_info.py b/plugins/modules/utm_proxy_location_info.py index 0e7b165903..4b502df296 100644 --- a/plugins/modules/utm_proxy_location_info.py +++ b/plugins/modules/utm_proxy_location_info.py @@ -16,7 +16,7 @@ module: utm_proxy_location_info author: - Johannes Brunswicker (@MatrixCrawler) -short_description: create, update or destroy reverse_proxy location entry in Sophos UTM +short_description: Create, update or destroy reverse_proxy location entry in Sophos UTM description: - Create, update or destroy a reverse_proxy location entry in SOPHOS UTM. diff --git a/plugins/modules/vdo.py b/plugins/modules/vdo.py index 21e8a96100..d2d4afe944 100644 --- a/plugins/modules/vdo.py +++ b/plugins/modules/vdo.py @@ -332,7 +332,7 @@ def inventory_vdos(module, vdocmd): if rc != 0: module.fail_json(msg="Inventorying VDOs failed: %s" % vdostatusout, rc=rc, err=err) - vdostatusyaml = yaml.load(vdostatusout) + vdostatusyaml = yaml.safe_load(vdostatusout) if vdostatusyaml is None: return vdolist @@ -548,7 +548,7 @@ def run_module(): # Modify the current parameters of a VDO that exists. if desiredvdo in vdolist and state == 'present': rc, vdostatusoutput, err = module.run_command([vdocmd, "status"]) - vdostatusyaml = yaml.load(vdostatusoutput) + vdostatusyaml = yaml.safe_load(vdostatusoutput) # An empty dictionary to contain dictionaries of VDO statistics processedvdos = {} diff --git a/plugins/modules/vertica_configuration.py b/plugins/modules/vertica_configuration.py index f3ac067d0f..553630da39 100644 --- a/plugins/modules/vertica_configuration.py +++ b/plugins/modules/vertica_configuration.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: vertica_configuration -short_description: Updates Vertica configuration parameters. +short_description: Updates Vertica configuration parameters description: - Updates Vertica configuration parameters. options: diff --git a/plugins/modules/vertica_info.py b/plugins/modules/vertica_info.py index a51187de1d..3106be3b38 100644 --- a/plugins/modules/vertica_info.py +++ b/plugins/modules/vertica_info.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: vertica_info -short_description: Gathers Vertica database facts. +short_description: Gathers Vertica database facts description: - Gathers Vertica database information. - This module was called C(vertica_facts) before Ansible 2.9, returning C(ansible_facts). diff --git a/plugins/modules/vertica_role.py b/plugins/modules/vertica_role.py index e9f2ef34df..dde9919511 100644 --- a/plugins/modules/vertica_role.py +++ b/plugins/modules/vertica_role.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: vertica_role -short_description: Adds or removes Vertica database roles and assigns roles to them. +short_description: Adds or removes Vertica database roles and assigns roles to them description: - Adds or removes Vertica database role and, optionally, assign other roles. options: diff --git a/plugins/modules/vertica_schema.py b/plugins/modules/vertica_schema.py index b1bdf944b9..3c4071473a 100644 --- a/plugins/modules/vertica_schema.py +++ b/plugins/modules/vertica_schema.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: vertica_schema -short_description: Adds or removes Vertica database schema and roles. +short_description: Adds or removes Vertica database schema and roles description: - Adds or removes Vertica database schema and, optionally, roles with schema access privileges. diff --git a/plugins/modules/vertica_user.py b/plugins/modules/vertica_user.py index ff2f02d0cc..89f1cb92a3 100644 --- a/plugins/modules/vertica_user.py +++ b/plugins/modules/vertica_user.py @@ -11,7 +11,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: vertica_user -short_description: Adds or removes Vertica database users and assigns roles. +short_description: Adds or removes Vertica database users and assigns roles description: - Adds or removes Vertica database user and, optionally, assigns roles. - A user will not be removed until all the dependencies have been dropped. diff --git a/plugins/modules/vmadm.py b/plugins/modules/vmadm.py index 3bc016d679..d6110726f4 100644 --- a/plugins/modules/vmadm.py +++ b/plugins/modules/vmadm.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: vmadm -short_description: Manage SmartOS virtual machines and zones. +short_description: Manage SmartOS virtual machines and zones description: - Manage SmartOS virtual machines through vmadm(1M). author: Jasper Lievisse Adriaanse (@jasperla) @@ -438,7 +438,7 @@ def get_vm_prop(module, uuid, prop): def get_vm_uuid(module, alias): # Lookup the uuid that goes with the given alias. # Returns the uuid or '' if not found. - cmd = [module.vmadm, 'lookup', '-j', '-o', 'uuid', 'alias={1}'.format(alias)] + cmd = [module.vmadm, 'lookup', '-j', '-o', 'uuid', 'alias={0}'.format(alias)] (rc, stdout, stderr) = module.run_command(cmd) diff --git a/plugins/modules/xenserver_facts.py b/plugins/modules/xenserver_facts.py index 10ec3cd50f..59c457c7f7 100644 --- a/plugins/modules/xenserver_facts.py +++ b/plugins/modules/xenserver_facts.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: xenserver_facts -short_description: get facts reported on xenserver +short_description: Get facts reported on xenserver description: - Reads data out of XenAPI, can be used instead of multiple xe commands. author: diff --git a/plugins/modules/xfconf.py b/plugins/modules/xfconf.py index aaf80d4db2..c231a47484 100644 --- a/plugins/modules/xfconf.py +++ b/plugins/modules/xfconf.py @@ -16,9 +16,9 @@ author: short_description: Edit XFCE4 Configurations description: - This module allows for the manipulation of Xfce 4 Configuration with the help of - xfconf-query. Please see the xfconf-query(1) man pages for more details. + xfconf-query. Please see the xfconf-query(1) man page for more details. seealso: - - name: C(xfconf-query) man page + - name: xfconf-query(1) man page description: Manual page of the C(xfconf-query) tool at the XFCE documentation site. link: 'https://docs.xfce.org/xfce/xfconf/xfconf-query' @@ -70,7 +70,7 @@ options: default: "present" force_array: description: - - Force array even if only one element + - Force array even if only one element. type: bool default: false aliases: ['array'] diff --git a/plugins/modules/zfs_facts.py b/plugins/modules/zfs_facts.py index 734659a7ea..bb4530c473 100644 --- a/plugins/modules/zfs_facts.py +++ b/plugins/modules/zfs_facts.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: zfs_facts -short_description: Gather facts about ZFS datasets. +short_description: Gather facts about ZFS datasets description: - Gather facts from ZFS dataset properties. author: Adam Števko (@xen0l) diff --git a/plugins/modules/zpool_facts.py b/plugins/modules/zpool_facts.py index ec5fdc4442..2477a920b0 100644 --- a/plugins/modules/zpool_facts.py +++ b/plugins/modules/zpool_facts.py @@ -12,7 +12,7 @@ __metaclass__ = type DOCUMENTATION = ''' --- module: zpool_facts -short_description: Gather facts about ZFS pools. +short_description: Gather facts about ZFS pools description: - Gather facts from ZFS pool properties. author: Adam Števko (@xen0l) diff --git a/tests/integration/targets/ansible_galaxy_install/tasks/main.yml b/tests/integration/targets/ansible_galaxy_install/tasks/main.yml index 44ad697930..1ecd9980d4 100644 --- a/tests/integration/targets/ansible_galaxy_install/tasks/main.yml +++ b/tests/integration/targets/ansible_galaxy_install/tasks/main.yml @@ -10,7 +10,7 @@ name: netbox.netbox register: install_c0 -- name: Assert collection was installed +- name: Assert collection netbox.netbox was installed assert: that: - install_c0 is changed @@ -34,7 +34,7 @@ name: ansistrano.deploy register: install_r0 -- name: Assert collection was installed +- name: Assert collection ansistrano.deploy was installed assert: that: - install_r0 is changed @@ -52,7 +52,7 @@ - install_r1 is not changed ################################################### -- name: +- name: Set requirements file path set_fact: reqs_file: '{{ remote_tmp_dir }}/reqs.yaml' diff --git a/tests/integration/targets/copr/tasks/main.yml b/tests/integration/targets/copr/tasks/main.yml index ac78255d48..917e44b7ec 100644 --- a/tests/integration/targets/copr/tasks/main.yml +++ b/tests/integration/targets/copr/tasks/main.yml @@ -3,14 +3,27 @@ # 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 -- when: ansible_distribution == 'Fedora' +- when: + # Fedora or RHEL >= 8 + # This module requires the dnf module which is not available on RHEL 7. + - > + ansible_distribution == 'Fedora' + or (ansible_os_family == 'RedHat' and ansible_distribution != 'Fedora' + and ansible_distribution_major_version | int >= 8) + # The copr module imports dnf which is only available for the system Python + # interpreter. + - > + not (ansible_distribution == 'CentOS' and + ansible_distribution_major_version | int == 8 and not + ansible_python_version.startswith('3.6')) block: + - debug: var=copr_chroot - name: enable copr project copr: host: copr.fedorainfracloud.org state: enabled - name: '@copr/integration_tests' - chroot: fedora-rawhide-x86_64 + name: '{{ copr_fullname }}' + chroot: "{{ copr_chroot }}" register: result - name: assert that the copr project was enabled @@ -24,8 +37,8 @@ check_mode: yes copr: state: enabled - name: '@copr/integration_tests' - chroot: fedora-rawhide-x86_64 + name: '{{ copr_fullname }}' + chroot: '{{ copr_chroot }}' register: result - name: assert that the copr project was enabled @@ -34,10 +47,53 @@ - result is not changed - result.msg == 'enabled' + - name: Ensure the repo is installed and enabled | slurp + register: result + ansible.builtin.slurp: + src: "{{ copr_repofile }}" + + - name: Ensure the repo is installed and enabled + vars: + content: "{{ result.content | b64decode }}" + _baseurl: "{{ 'https://download.copr.fedorainfracloud.org/results/gotmax23/community.general.copr_integration_tests' | regex_escape }}" + baseurl: "{{ content | regex_search('baseurl=' ~ _baseurl) }}" + block: + - ansible.builtin.debug: + var: content + - ansible.builtin.debug: + var: baseurl + - name: Ensure the repo is installed and enabled + ansible.builtin.assert: + that: + - "'enabled=1' in content" + - baseurl | length > 0 + + - name: Install test package from Copr + when: + # Copr does not build new packages for EOL Fedoras. + - > + not (ansible_distribution == 'Fedora' and + ansible_distribution_major_version | int < 35) + block: + - name: install test package from the copr + ansible.builtin.package: + update_cache: true + name: copr-module-integration-dummy-package + + - name: uninstall test package + register: result + ansible.builtin.package: + name: copr-module-integration-dummy-package + state: absent + + - name: check uninstall test package + ansible.builtin.assert: + that: result.changed | bool + - name: remove copr project copr: state: absent - name: '@copr/integration_tests' + name: '{{ copr_fullname }}' register: result - name: assert that the copr project was removed @@ -46,11 +102,20 @@ - 'result is changed' - result.msg == 'absent' + - name: Ensure the repo file was removed | stat + register: result + ansible.builtin.stat: + dest: "{{ copr_repofile }}" + + - name: Ensure the repo file was removed + ansible.builtin.assert: + that: not result.stat.exists | bool + - name: disable copr project copr: state: disabled - name: '@copr/integration_tests' - chroot: fedora-rawhide-x86_64 + name: '{{ copr_fullname }}' + chroot: '{{ copr_chroot }}' register: result - name: assert that the copr project was disabled @@ -59,10 +124,37 @@ - 'result is changed' - result.msg == 'disabled' + - name: Ensure the repo is installed but disabled | slurp + register: result + ansible.builtin.slurp: + src: "{{ copr_repofile }}" + + - name: Ensure the repo is installed but disabled + vars: + content: "{{ result.content | b64decode }}" + _baseurl: "{{ 'https://download.copr.fedorainfracloud.org/results/gotmax23/community.general.copr_integration_tests' | regex_escape }}" + baseurl: "{{ content | regex_search('baseurl=' ~ _baseurl) }}" + block: + - ansible.builtin.debug: + var: content + - ansible.builtin.debug: + var: baseurl + - name: Ensure the repo is installed but disabled + ansible.builtin.assert: + that: + - "'enabled=0' in content" + - baseurl | length > 0 + always: - name: clean up + ignore_errors: true copr: host: copr.fedorainfracloud.org state: absent - name: '@copr/integration_tests' - chroot: fedora-rawhide-x86_64 + name: '{{ copr_fullname }}' + chroot: '{{ copr_chroot }}' + + - name: cleanup test package + ansible.builtin.package: + name: copr-module-integration-dummy-package + state: absent diff --git a/tests/integration/targets/copr/vars/main.yml b/tests/integration/targets/copr/vars/main.yml new file mode 100644 index 0000000000..a37a44d478 --- /dev/null +++ b/tests/integration/targets/copr/vars/main.yml @@ -0,0 +1,15 @@ +# Copyright (c) 2022 Maxwell G +# 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 +--- +copr_host: copr.fedorainfracloud.org +copr_namespace: gotmax23 +copr_name: community.general.copr_integration_tests +copr_fullname: '{{ copr_namespace }}/{{ copr_name }}' +copr_repofile: '/etc/yum.repos.d/_copr:{{ copr_host }}:{{ copr_namespace }}:{{ copr_name }}.repo' + +# TODO: Fix chroot autodetection so this isn't necessary +_copr_chroot_fedora: "fedora-rawhide-x86_64" +_copr_chroot_rhelish: "epel-{{ ansible_distribution_major_version }}-x86_64" +copr_chroot: "{{ _copr_chroot_fedora if ansible_distribution == 'Fedora' else _copr_chroot_rhelish }}" diff --git a/tests/integration/targets/filesize/aliases b/tests/integration/targets/filesize/aliases index afda346c4e..7642e70daf 100644 --- a/tests/integration/targets/filesize/aliases +++ b/tests/integration/targets/filesize/aliases @@ -3,3 +3,4 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm diff --git a/tests/integration/targets/filesystem/aliases b/tests/integration/targets/filesystem/aliases index 007bed5386..a666f7a142 100644 --- a/tests/integration/targets/filesystem/aliases +++ b/tests/integration/targets/filesystem/aliases @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm destructive skip/aix skip/osx diff --git a/tests/integration/targets/filesystem/tasks/create_device.yml b/tests/integration/targets/filesystem/tasks/create_device.yml index 5229e19f25..8966ec2e61 100644 --- a/tests/integration/targets/filesystem/tasks/create_device.yml +++ b/tests/integration/targets/filesystem/tasks/create_device.yml @@ -14,10 +14,14 @@ block: - when: fstype == 'lvm' block: + - name: 'Show next free loop device' + ansible.builtin.command: + cmd: 'losetup -f' + register: loop_device_cmd + - name: 'Create a loop device for LVM' ansible.builtin.command: - cmd: 'losetup --show -f {{ dev }}' - register: loop_device_cmd + cmd: 'losetup -f {{ dev }}' - name: 'Switch to loop device target for further tasks' ansible.builtin.set_fact: diff --git a/tests/integration/targets/gitlab_project_badge/aliases b/tests/integration/targets/gitlab_project_badge/aliases new file mode 100644 index 0000000000..9f72f37111 --- /dev/null +++ b/tests/integration/targets/gitlab_project_badge/aliases @@ -0,0 +1,6 @@ +# 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 + +gitlab/ci +disabled diff --git a/tests/integration/targets/gitlab_project_badge/defaults/main.yml b/tests/integration/targets/gitlab_project_badge/defaults/main.yml new file mode 100644 index 0000000000..bf84a4751a --- /dev/null +++ b/tests/integration/targets/gitlab_project_badge/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +gitlab_api_token: glpat-XXXXXXXXXXXXXXXXXXXX +gitlab_api_url: https://gitlab.com +gitlab_project_name: ansible_test_project +gitlab_badge_link_url: 'https://example.gitlab.com/%{project_path}' +updated_gitlab_badge_link_url: 'https://test.gitlab.com/%{project_path}' +gitlab_badge_image_url: 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg' \ No newline at end of file diff --git a/tests/integration/targets/gitlab_project_badge/tasks/main.yml b/tests/integration/targets/gitlab_project_badge/tasks/main.yml new file mode 100644 index 0000000000..fa8a806efe --- /dev/null +++ b/tests/integration/targets/gitlab_project_badge/tasks/main.yml @@ -0,0 +1,214 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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: Install required libs + pip: + name: python-gitlab + state: present + +- name: Create {{ gitlab_project_name }} + gitlab_project: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + name: "{{ gitlab_project_name }}" + initialize_with_readme: True + state: present + +- name: Create Badge (check) + check_mode: yes + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_create_check_task + +- ansible.builtin.debug: + var: gitlab_badge_create_check_task + +- name: Check module call result + assert: + that: + - gitlab_badge_create_check_task.changed + - not gitlab_badge_create_check_task.failed + +- name: Create Badge + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_create_task + +- ansible.builtin.debug: + var: gitlab_badge_create_task + +- name: Check module call result + assert: + that: + - gitlab_badge_create_task.changed + - not gitlab_badge_create_task.failed + +- name: Create Badge (confirmation) + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_create_confirmation_task + +- ansible.builtin.debug: + var: gitlab_badge_create_confirmation_task + +- name: Check module call result + assert: + that: + - not gitlab_badge_create_confirmation_task.changed + - not gitlab_badge_create_confirmation_task.failed + +- name: Update Badge (check) + check_mode: yes + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_update_check_task + +- ansible.builtin.debug: + var: gitlab_badge_update_check_task + +- name: Check module call result + assert: + that: + - gitlab_badge_update_check_task.changed + - not gitlab_badge_update_check_task.failed + +- name: Update Badge + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_update_task + +- ansible.builtin.debug: + var: gitlab_badge_update_task + +- name: Check module call result + assert: + that: + - gitlab_badge_update_task.changed + - not gitlab_badge_update_task.failed + +- name: Update Badge (confirmation) + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_update_confirmation_task + +- ansible.builtin.debug: + var: gitlab_badge_update_confirmation_task + +- name: Check module call result + assert: + that: + - not gitlab_badge_update_confirmation_task.changed + - not gitlab_badge_update_confirmation_task.failed + +- name: Delete Badge (check) + check_mode: yes + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: absent + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_delete_check_task + +- ansible.builtin.debug: + var: gitlab_badge_delete_check_task + +- name: Check module call result + assert: + that: + - gitlab_badge_delete_check_task.changed + - not gitlab_badge_delete_check_task.failed + +- name: Delete Badge + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: absent + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_delete_task + +- ansible.builtin.debug: + var: gitlab_badge_delete_task + +- name: Check module call result + assert: + that: + - gitlab_badge_delete_task.changed + - not gitlab_badge_delete_task.failed + +- name: Delete Badge (confirmation) + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: absent + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_delete_confirmation_task + +- ansible.builtin.debug: + var: gitlab_badge_delete_confirmation_task + +- name: Check module call result + assert: + that: + - not gitlab_badge_delete_confirmation_task.changed + - not gitlab_badge_delete_confirmation_task.failed + +- name: Clean up {{ gitlab_project_name }} + gitlab_project: + api_url: "{{ gitlab_api_url }}" + validate_certs: False + api_token: "{{ gitlab_api_token }}" + name: "{{ gitlab_project_name }}" + state: absent diff --git a/tests/integration/targets/iptables_state/aliases b/tests/integration/targets/iptables_state/aliases index 80f7c7e32f..5a02a630bc 100644 --- a/tests/integration/targets/iptables_state/aliases +++ b/tests/integration/targets/iptables_state/aliases @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm destructive skip/docker # kernel modules not loadable skip/freebsd # no iptables/netfilter (Linux specific) diff --git a/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml b/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml index 2f00e175b4..929928c8e7 100644 --- a/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml +++ b/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml @@ -167,13 +167,12 @@ -- name: "get security, raw and mangle tables states" +- name: "get raw and mangle tables states" iptables_state: path: "{{ iptables_saved }}" state: saved table: "{{ item }}" loop: - - security - raw - mangle changed_when: false @@ -198,8 +197,6 @@ - "'*nat' in iptables_state.saved" - "'raw' in iptables_state.tables" - "'*raw' in iptables_state.saved" - - "'security' in iptables_state.tables" - - "'*security' in iptables_state.saved" quiet: yes @@ -233,17 +230,14 @@ - "'*mangle' in iptables_state.initial_state" - "'*nat' in iptables_state.initial_state" - "'*raw' in iptables_state.initial_state" - - "'*security' in iptables_state.initial_state" - "'filter' in iptables_state.tables" - "'mangle' not in iptables_state.tables" - "'nat' not in iptables_state.tables" - "'raw' not in iptables_state.tables" - - "'security' not in iptables_state.tables" - "'*filter' in iptables_state.restored" - "'*mangle' not in iptables_state.restored" - "'*nat' not in iptables_state.restored" - "'*raw' not in iptables_state.restored" - - "'*security' not in iptables_state.restored" - iptables_state is not changed quiet: yes @@ -264,17 +258,14 @@ - "'*mangle' in iptables_state.initial_state" - "'*nat' in iptables_state.initial_state" - "'*raw' in iptables_state.initial_state" - - "'*security' in iptables_state.initial_state" - "'filter' in iptables_state.tables" - "'mangle' in iptables_state.tables" - "'nat' in iptables_state.tables" - "'raw' in iptables_state.tables" - - "'security' in iptables_state.tables" - "'*filter' in iptables_state.restored" - "'*mangle' in iptables_state.restored" - "'*nat' in iptables_state.restored" - "'*raw' in iptables_state.restored" - - "'*security' in iptables_state.restored" - iptables_state is not changed quiet: yes diff --git a/tests/integration/targets/keycloak_clientsecret_info/README.md b/tests/integration/targets/keycloak_clientsecret_info/README.md new file mode 100644 index 0000000000..fb721801da --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_info/README.md @@ -0,0 +1,17 @@ + + +The integration test can be performed as follows: + +``` +# 1. Start docker-compose: +docker-compose -f tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml stop +docker-compose -f tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml rm -f -v +docker-compose -f tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml up -d + +# 2. Run the integration tests: +ansible-test integration keycloak_clientsecret_info --allow-unsupported -v +``` diff --git a/tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml b/tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml new file mode 100644 index 0000000000..5e14e9aac1 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml @@ -0,0 +1,31 @@ +--- +# 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 + +version: '3.4' + +services: + postgres: + image: postgres:9.6 + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + + keycloak: + image: jboss/keycloak:12.0.4 + ports: + - 8080:8080 + + environment: + DB_VENDOR: postgres + DB_ADDR: postgres + DB_DATABASE: postgres + DB_USER: postgres + DB_SCHEMA: public + DB_PASSWORD: postgres + + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: password diff --git a/tests/integration/targets/keycloak_clientsecret_info/tasks/main.yml b/tests/integration/targets/keycloak_clientsecret_info/tasks/main.yml new file mode 100644 index 0000000000..a0cacf1889 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_info/tasks/main.yml @@ -0,0 +1,48 @@ +--- +# 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: Create realm + community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + id: "{{ realm }}" + realm: "{{ realm }}" + state: present + +- name: Keycloak Client + community.general.keycloak_client: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + state: present + register: client + +- name: Keycloak Client fetch clientsecret by client_id + community.general.keycloak_clientsecret_info: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + register: fetch_by_client_id_result + +- name: Assert that the client secret was retrieved + assert: + that: + - fetch_by_client_id_result.clientsecret_info.type == "secret" + - "{{ fetch_by_client_id_result.clientsecret_info.value | length }} >= 32" + +- name: Keycloak Client fetch clientsecret by id + community.general.keycloak_clientsecret_info: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + id: "{{ client.end_state.id }}" + register: fetch_by_id_result + +- name: Assert that the same client secret was retrieved both times + assert: + that: + - fetch_by_id_result.clientsecret_info.value == fetch_by_client_id_result.clientsecret_info.value diff --git a/tests/integration/targets/keycloak_clientsecret_info/vars/main.yml b/tests/integration/targets/keycloak_clientsecret_info/vars/main.yml new file mode 100644 index 0000000000..8c913705f7 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_info/vars/main.yml @@ -0,0 +1,20 @@ +--- +# 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 + +url: http://localhost:8080/auth +admin_realm: master +admin_user: admin +admin_password: password +realm: myrealm +client_id: myclient +role: myrole +description_1: desc 1 +description_2: desc 2 + +auth_args: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" diff --git a/tests/integration/targets/keycloak_clientsecret_regenerate/README.md b/tests/integration/targets/keycloak_clientsecret_regenerate/README.md new file mode 100644 index 0000000000..08251b4c52 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_regenerate/README.md @@ -0,0 +1,17 @@ + + +The integration test can be performed as follows: + +``` +# 1. Start docker-compose: +docker-compose -f tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml stop +docker-compose -f tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml rm -f -v +docker-compose -f tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml up -d + +# 2. Run the integration tests: +ansible-test integration keycloak_clientsecret_regenerate --allow-unsupported -v +``` diff --git a/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml b/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml new file mode 100644 index 0000000000..5e14e9aac1 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml @@ -0,0 +1,31 @@ +--- +# 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 + +version: '3.4' + +services: + postgres: + image: postgres:9.6 + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + + keycloak: + image: jboss/keycloak:12.0.4 + ports: + - 8080:8080 + + environment: + DB_VENDOR: postgres + DB_ADDR: postgres + DB_DATABASE: postgres + DB_USER: postgres + DB_SCHEMA: public + DB_PASSWORD: postgres + + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: password diff --git a/tests/integration/targets/keycloak_clientsecret_regenerate/tasks/main.yml b/tests/integration/targets/keycloak_clientsecret_regenerate/tasks/main.yml new file mode 100644 index 0000000000..9bd52698a2 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_regenerate/tasks/main.yml @@ -0,0 +1,49 @@ +--- +# 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: Create realm + community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + id: "{{ realm }}" + realm: "{{ realm }}" + state: present + +- name: Keycloak Client + community.general.keycloak_client: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + state: present + register: client + +- name: Keycloak Client regenerate clientsecret by client_id + community.general.keycloak_clientsecret_regenerate: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + register: regenerate_by_client_id + +- name: Assert that the client secret was retrieved + assert: + that: + - regenerate_by_client_id.end_state.type == "secret" + - "{{ regenerate_by_client_id.end_state.value | length }} >= 32" + +- name: Keycloak Client regenerate clientsecret by id + community.general.keycloak_clientsecret_regenerate: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + id: "{{ client.end_state.id }}" + register: regenerate_by_id + +- name: Assert that client secret was regenerated + assert: + that: + - "{{ regenerate_by_id.end_state.value | length }} >= 32" + - regenerate_by_id.end_state.value != regenerate_by_client_id.end_state.value diff --git a/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.yml b/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.yml new file mode 100644 index 0000000000..8c913705f7 --- /dev/null +++ b/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.yml @@ -0,0 +1,20 @@ +--- +# 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 + +url: http://localhost:8080/auth +admin_realm: master +admin_user: admin +admin_password: password +realm: myrealm +client_id: myclient +role: myrole +description_1: desc 1 +description_2: desc 2 + +auth_args: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" diff --git a/tests/integration/targets/lvg/aliases b/tests/integration/targets/lvg/aliases index f4617b3377..3b92ba75c4 100644 --- a/tests/integration/targets/lvg/aliases +++ b/tests/integration/targets/lvg/aliases @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm destructive needs/privileged skip/aix diff --git a/tests/integration/targets/lvg/tasks/setup.yml b/tests/integration/targets/lvg/tasks/setup.yml index 92785e6d3d..3984b9fc3a 100644 --- a/tests/integration/targets/lvg/tasks/setup.yml +++ b/tests/integration/targets/lvg/tasks/setup.yml @@ -7,12 +7,21 @@ command: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=10" with_sequence: 'count=2' +- name: "Show next free loop device" + command: "losetup -f" + register: loop_device1 + - name: "Create loop device for file" - command: "losetup --show -f {{ remote_tmp_dir }}/img{{ item }}" - with_sequence: 'count=2' - register: loop_devices + command: "losetup -f {{ remote_tmp_dir }}/img1" + +- name: "Show next free loop device" + command: "losetup -f" + register: loop_device2 + +- name: "Create loop device for file" + command: "losetup -f {{ remote_tmp_dir }}/img2" - name: "Affect name on disk to work on" set_fact: - loop_device1: "{{ loop_devices.results[0] }}" - loop_device2: "{{ loop_devices.results[1] }}" + loop_device1: "{{ loop_device1.stdout }}" + loop_device2: "{{ loop_device2.stdout }}" diff --git a/tests/integration/targets/lvg/tasks/teardown.yml b/tests/integration/targets/lvg/tasks/teardown.yml index 027c1257fe..de49573214 100644 --- a/tests/integration/targets/lvg/tasks/teardown.yml +++ b/tests/integration/targets/lvg/tasks/teardown.yml @@ -8,15 +8,16 @@ vg: testvg state: absent -- name: Detach loop device - command: "losetup -d {{ item.stdout }}" - loop: "{{ loop_devices.results|default([]) }}" +- name: Detach loop devices + command: "losetup -d {{ item }}" + loop: + - "{{ loop_device1 | default('') }}" + - "{{ loop_device2 | default('') }}" when: - - item.stdout is defined - - item.stdout is match("/dev/.*") + - item != '' - name: Remove device files file: path: "{{ remote_tmp_dir }}/img{{ item }}" state: absent - with_sequence: 'count={{ loop_devices.results|length }}' + with_sequence: 'count=2' diff --git a/tests/integration/targets/lvg/tasks/test_grow_reduce.yml b/tests/integration/targets/lvg/tasks/test_grow_reduce.yml index 5974a88aaf..857df92464 100644 --- a/tests/integration/targets/lvg/tasks/test_grow_reduce.yml +++ b/tests/integration/targets/lvg/tasks/test_grow_reduce.yml @@ -6,7 +6,7 @@ - name: "Create volume group on first disk" lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" - name: "get lvm facts" setup: @@ -16,14 +16,14 @@ - name: "Assert the testvg span only on first disk" assert: that: - - ansible_lvm.pvs[loop_device1.stdout].vg == "testvg" - - 'loop_device2.stdout not in ansible_lvm.pvs or - ansible_lvm.pvs[loop_device2.stdout].vg == ""' + - ansible_lvm.pvs[loop_device1].vg == "testvg" + - 'loop_device2 not in ansible_lvm.pvs or + ansible_lvm.pvs[loop_device2].vg == ""' - name: "Extend to second disk AND reduce from the first disk" lvg: vg: testvg - pvs: "{{ loop_device2.stdout }}" + pvs: "{{ loop_device2 }}" - name: "get lvm facts" setup: @@ -33,6 +33,6 @@ - name: "Assert the testvg span only on first disk" assert: that: - - 'loop_device1.stdout not in ansible_lvm.pvs or - ansible_lvm.pvs[loop_device1.stdout].vg == ""' - - ansible_lvm.pvs[loop_device2.stdout].vg == "testvg" + - 'loop_device1 not in ansible_lvm.pvs or + ansible_lvm.pvs[loop_device1].vg == ""' + - ansible_lvm.pvs[loop_device2].vg == "testvg" diff --git a/tests/integration/targets/lvg/tasks/test_indempotency.yml b/tests/integration/targets/lvg/tasks/test_indempotency.yml index abaa262881..758912484c 100644 --- a/tests/integration/targets/lvg/tasks/test_indempotency.yml +++ b/tests/integration/targets/lvg/tasks/test_indempotency.yml @@ -6,12 +6,12 @@ - name: Create volume group on disk device lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" - name: Create the volume group again to verify idempotence lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" register: repeat_vg_create - name: Do all assertions to verify expected results diff --git a/tests/integration/targets/lvg/tasks/test_pvresize.yml b/tests/integration/targets/lvg/tasks/test_pvresize.yml index c8a2c8edb5..eef9503040 100644 --- a/tests/integration/targets/lvg/tasks/test_pvresize.yml +++ b/tests/integration/targets/lvg/tasks/test_pvresize.yml @@ -6,7 +6,7 @@ - name: "Create volume group on first disk" lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" - name: Gets current vg size shell: vgs -v testvg -o pv_size --noheading --units b | xargs @@ -21,12 +21,12 @@ command: "dd if=/dev/zero bs=8MiB count=1 of={{ remote_tmp_dir }}/img1 conv=notrunc oflag=append" - name: "Reread size of file associated with loop_device1" - command: "losetup -c {{ loop_device1.stdout }}" + command: "losetup -c {{ loop_device1 }}" - name: "Reruns lvg with pvresize:no" lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" pvresize: no register: cmd_result @@ -46,7 +46,7 @@ - name: "Reruns lvg with pvresize:yes and check_mode:yes" lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" pvresize: yes check_mode: yes register: cmd_result @@ -68,7 +68,7 @@ - name: "Reruns lvg with pvresize:yes" lvg: vg: testvg - pvs: "{{ loop_device1.stdout }}" + pvs: "{{ loop_device1 }}" pvresize: yes - name: Gets current vg size diff --git a/tests/integration/targets/pipx_info/tasks/main.yml b/tests/integration/targets/pipx_info/tasks/main.yml index 61163afd08..0a01f0af9c 100644 --- a/tests/integration/targets/pipx_info/tasks/main.yml +++ b/tests/integration/targets/pipx_info/tasks/main.yml @@ -56,7 +56,8 @@ - info_all_deps.application|length == 1 - info_all_deps.application[0].name == "tox" - "'version' in info_all_deps.application[0]" - - info_all_deps.application[0].dependencies == ["virtualenv"] + - info_all_deps.application[0].dependencies == ["chardet", "virtualenv"] + or info_all_deps.application[0].dependencies == ["virtualenv"] - "'injected' not in info_all.application[0]" - info_tox.application == info_all_deps.application diff --git a/tests/integration/targets/snap/aliases b/tests/integration/targets/snap/aliases index dcb4aa199e..a50e25cc5c 100644 --- a/tests/integration/targets/snap/aliases +++ b/tests/integration/targets/snap/aliases @@ -3,8 +3,12 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm skip/aix +skip/alpine +skip/fedora skip/freebsd skip/osx skip/macos skip/docker +skip/ubuntu # FIXME! diff --git a/tests/integration/targets/snap_alias/aliases b/tests/integration/targets/snap_alias/aliases index dcb4aa199e..a50e25cc5c 100644 --- a/tests/integration/targets/snap_alias/aliases +++ b/tests/integration/targets/snap_alias/aliases @@ -3,8 +3,12 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm skip/aix +skip/alpine +skip/fedora skip/freebsd skip/osx skip/macos skip/docker +skip/ubuntu # FIXME! diff --git a/tests/integration/targets/ssh_config/tasks/options.yml b/tests/integration/targets/ssh_config/tasks/options.yml index 04586873ad..65ce691cf8 100644 --- a/tests/integration/targets/ssh_config/tasks/options.yml +++ b/tests/integration/targets/ssh_config/tasks/options.yml @@ -15,6 +15,7 @@ host: "options.example.com" proxycommand: "ssh jumphost.example.com -W %h:%p" forward_agent: true + host_key_algorithms: "+ssh-rsa" state: present register: options_add check_mode: yes @@ -43,6 +44,7 @@ host: "options.example.com" proxycommand: "ssh jumphost.example.com -W %h:%p" forward_agent: true + host_key_algorithms: "+ssh-rsa" state: present register: options_add @@ -60,6 +62,7 @@ host: "options.example.com" proxycommand: "ssh jumphost.example.com -W %h:%p" forward_agent: true + host_key_algorithms: "+ssh-rsa" state: present register: options_add_again @@ -81,6 +84,7 @@ that: - "'proxycommand ssh jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode" - "'forwardagent yes' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-rsa' in slurp_ssh_config['content'] | b64decode" - name: Options - Update host community.general.ssh_config: @@ -88,6 +92,7 @@ host: "options.example.com" proxycommand: "ssh new-jumphost.example.com -W %h:%p" forward_agent: no + host_key_algorithms: "+ssh-ed25519" state: present register: options_update @@ -107,6 +112,7 @@ host: "options.example.com" proxycommand: "ssh new-jumphost.example.com -W %h:%p" forward_agent: no + host_key_algorithms: "+ssh-ed25519" state: present register: options_update @@ -129,6 +135,7 @@ that: - "'proxycommand ssh new-jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode" - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode" - name: Options - Ensure no update in case option exist in ssh_config file but wasn't defined in playbook community.general.ssh_config: @@ -156,6 +163,11 @@ that: - "'proxycommand ssh new-jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode" - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode" + +- name: Debug + debug: + msg: "{{ slurp_ssh_config['content'] | b64decode }}" - name: Options - Delete a host community.general.ssh_config: @@ -197,3 +209,4 @@ that: - "'proxycommand ssh new-jumphost.example.com -W %h:%p' not in slurp_ssh_config['content'] | b64decode" - "'forwardagent no' not in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode" diff --git a/tests/integration/targets/sudoers/tasks/main.yml b/tests/integration/targets/sudoers/tasks/main.yml index 682bd7efff..a44307ad9e 100644 --- a/tests/integration/targets/sudoers/tasks/main.yml +++ b/tests/integration/targets/sudoers/tasks/main.yml @@ -131,6 +131,19 @@ src: "{{ sudoers_path }}/my-sudo-rule-6" register: rule_6_contents +- name: Create rule to allow user to sudo just on host-1 + community.general.sudoers: + name: my-sudo-rule-7 + state: present + user: alice + host: host-1 + commands: /usr/local/bin/command + register: rule_7 + +- name: Grab contents of my-sudo-rule-7 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-7" + register: rule_7_contents - name: Revoke rule 1 community.general.sudoers: @@ -229,6 +242,7 @@ - "rule_4_contents['content'] | b64decode == '%students ALL=NOPASSWD: /usr/local/bin/command\n'" - "rule_5_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command\n'" - "rule_6_contents['content'] | b64decode == 'alice ALL=(bob)NOPASSWD: /usr/local/bin/command\n'" + - "rule_7_contents['content'] | b64decode == 'alice host-1=NOPASSWD: /usr/local/bin/command\n'" - name: Check revocation stat ansible.builtin.assert: diff --git a/tests/integration/targets/ufw/aliases b/tests/integration/targets/ufw/aliases index fa4f2a6cd3..1f974ecd19 100644 --- a/tests/integration/targets/ufw/aliases +++ b/tests/integration/targets/ufw/aliases @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/2 +azp/posix/vm skip/aix skip/osx skip/macos diff --git a/tests/integration/targets/xattr/aliases b/tests/integration/targets/xattr/aliases index db3751be49..5cd9c012e1 100644 --- a/tests/integration/targets/xattr/aliases +++ b/tests/integration/targets/xattr/aliases @@ -3,6 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/2 +azp/posix/vm skip/aix skip/docker skip/freebsd diff --git a/tests/integration/targets/xfs_quota/aliases b/tests/integration/targets/xfs_quota/aliases index 56dffecaa4..dd4714509e 100644 --- a/tests/integration/targets/xfs_quota/aliases +++ b/tests/integration/targets/xfs_quota/aliases @@ -3,9 +3,11 @@ # SPDX-License-Identifier: GPL-3.0-or-later azp/posix/1 +azp/posix/vm needs/privileged needs/root skip/aix +skip/alpine # FIXME skip/osx skip/macos skip/freebsd diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 603981df04..77738afd40 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -7,12 +7,10 @@ plugins/modules/consul.py validate-modules:doc-missing-type plugins/modules/consul.py validate-modules:undocumented-parameter plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice -plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice +plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0 plugins/modules/iptables_state.py validate-modules:undocumented-parameter -plugins/modules/jenkins_plugin.py use-argspec-type-path plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen -plugins/modules/lxd_project.py use-argspec-type-path # expanduser() applied to constants -plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice +plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0 plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions @@ -20,21 +18,11 @@ plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/puppet.py use-argspec-type-path -plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 +plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 plugins/modules/rax_files_objects.py use-argspec-type-path plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/rax.py use-argspec-type-path # fix needed -plugins/modules/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/redhat_subscription.py validate-modules:return-syntax-error plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc -plugins/modules/spotinst_aws_elastigroup.py validate-modules:undocumented-parameter -plugins/modules/ssh_config.py use-argspec-type-path # Required since module uses other methods to specify path -plugins/modules/udm_share.py validate-modules:parameter-list-no-elements -plugins/modules/udm_user.py validate-modules:parameter-list-no-elements plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/modules/yarn.py use-argspec-type-path tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.6 # django generated code diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index 778f25ae53..61494a1ab1 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -2,12 +2,10 @@ plugins/modules/consul.py validate-modules:doc-missing-type plugins/modules/consul.py validate-modules:undocumented-parameter plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice -plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice +plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0 plugins/modules/iptables_state.py validate-modules:undocumented-parameter -plugins/modules/jenkins_plugin.py use-argspec-type-path plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen -plugins/modules/lxd_project.py use-argspec-type-path # expanduser() applied to constants -plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice +plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0 plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions @@ -15,20 +13,10 @@ plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/puppet.py use-argspec-type-path -plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 +plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 plugins/modules/rax_files_objects.py use-argspec-type-path plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/rax.py use-argspec-type-path # fix needed -plugins/modules/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/redhat_subscription.py validate-modules:return-syntax-error plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc -plugins/modules/spotinst_aws_elastigroup.py validate-modules:undocumented-parameter -plugins/modules/ssh_config.py use-argspec-type-path # Required since module uses other methods to specify path -plugins/modules/udm_share.py validate-modules:parameter-list-no-elements -plugins/modules/udm_user.py validate-modules:parameter-list-no-elements plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/modules/yarn.py use-argspec-type-path diff --git a/tests/sanity/ignore-2.13.txt b/tests/sanity/ignore-2.13.txt index 778f25ae53..61494a1ab1 100644 --- a/tests/sanity/ignore-2.13.txt +++ b/tests/sanity/ignore-2.13.txt @@ -2,12 +2,10 @@ plugins/modules/consul.py validate-modules:doc-missing-type plugins/modules/consul.py validate-modules:undocumented-parameter plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice -plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice +plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0 plugins/modules/iptables_state.py validate-modules:undocumented-parameter -plugins/modules/jenkins_plugin.py use-argspec-type-path plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen -plugins/modules/lxd_project.py use-argspec-type-path # expanduser() applied to constants -plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice +plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0 plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions @@ -15,20 +13,10 @@ plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/puppet.py use-argspec-type-path -plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 +plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 plugins/modules/rax_files_objects.py use-argspec-type-path plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/rax.py use-argspec-type-path # fix needed -plugins/modules/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/redhat_subscription.py validate-modules:return-syntax-error plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc -plugins/modules/spotinst_aws_elastigroup.py validate-modules:undocumented-parameter -plugins/modules/ssh_config.py use-argspec-type-path # Required since module uses other methods to specify path -plugins/modules/udm_share.py validate-modules:parameter-list-no-elements -plugins/modules/udm_user.py validate-modules:parameter-list-no-elements plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/modules/yarn.py use-argspec-type-path diff --git a/tests/sanity/ignore-2.14.txt b/tests/sanity/ignore-2.14.txt index 20f87d1af2..6b9026caea 100644 --- a/tests/sanity/ignore-2.14.txt +++ b/tests/sanity/ignore-2.14.txt @@ -2,13 +2,11 @@ plugins/modules/consul.py validate-modules:doc-missing-type plugins/modules/consul.py validate-modules:undocumented-parameter plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice -plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice +plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0 plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/iptables_state.py validate-modules:undocumented-parameter -plugins/modules/jenkins_plugin.py use-argspec-type-path plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen -plugins/modules/lxd_project.py use-argspec-type-path # expanduser() applied to constants -plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice +plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0 plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions @@ -16,21 +14,11 @@ plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/puppet.py use-argspec-type-path -plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 +plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 plugins/modules/rax_files_objects.py use-argspec-type-path plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/rax.py use-argspec-type-path # fix needed -plugins/modules/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/redhat_subscription.py validate-modules:return-syntax-error plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc -plugins/modules/spotinst_aws_elastigroup.py validate-modules:undocumented-parameter -plugins/modules/ssh_config.py use-argspec-type-path # Required since module uses other methods to specify path -plugins/modules/udm_share.py validate-modules:parameter-list-no-elements plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' -plugins/modules/udm_user.py validate-modules:parameter-list-no-elements plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/modules/yarn.py use-argspec-type-path diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 20f87d1af2..6b9026caea 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -2,13 +2,11 @@ plugins/modules/consul.py validate-modules:doc-missing-type plugins/modules/consul.py validate-modules:undocumented-parameter plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice -plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice +plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0 plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/iptables_state.py validate-modules:undocumented-parameter -plugins/modules/jenkins_plugin.py use-argspec-type-path plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen -plugins/modules/lxd_project.py use-argspec-type-path # expanduser() applied to constants -plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice +plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0 plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions @@ -16,21 +14,11 @@ plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/puppet.py use-argspec-type-path -plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 +plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0 plugins/modules/rax_files_objects.py use-argspec-type-path plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/rax.py use-argspec-type-path # fix needed -plugins/modules/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/redhat_subscription.py validate-modules:return-syntax-error plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements -plugins/modules/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc -plugins/modules/spotinst_aws_elastigroup.py validate-modules:undocumented-parameter -plugins/modules/ssh_config.py use-argspec-type-path # Required since module uses other methods to specify path -plugins/modules/udm_share.py validate-modules:parameter-list-no-elements plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' -plugins/modules/udm_user.py validate-modules:parameter-list-no-elements plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/modules/yarn.py use-argspec-type-path diff --git a/tests/unit/plugins/lookup/onepassword/test_onepassword.py b/tests/unit/plugins/lookup/onepassword/test_onepassword.py index 9e943dbfa0..e9f8f42c96 100644 --- a/tests/unit/plugins/lookup/onepassword/test_onepassword.py +++ b/tests/unit/plugins/lookup/onepassword/test_onepassword.py @@ -5,6 +5,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import operator import itertools import json import pytest @@ -158,7 +159,7 @@ def test_op_get_field(mocker, op_fixture, output, expected, request): ("cli_class", "vault", "queries", "kwargs", "output", "expected"), ( (_cli_class, item["vault_name"], item["queries"], item.get("kwargs", {}), item["output"], item["expected"]) - for _cli_class in MOCK_ENTRIES + for _cli_class in sorted(MOCK_ENTRIES, key=operator.attrgetter("__name__")) for item in MOCK_ENTRIES[_cli_class] ) ) diff --git a/tests/unit/plugins/module_utils/test_cmd_runner.py b/tests/unit/plugins/module_utils/test_cmd_runner.py index 5fdc5fb5fc..7cec215a76 100644 --- a/tests/unit/plugins/module_utils/test_cmd_runner.py +++ b/tests/unit/plugins/module_utils/test_cmd_runner.py @@ -18,6 +18,10 @@ TC_FORMATS = dict( simple_boolean__true=(fmt.as_bool, ("--superflag",), True, ["--superflag"]), simple_boolean__false=(fmt.as_bool, ("--superflag",), False, []), simple_boolean__none=(fmt.as_bool, ("--superflag",), None, []), + simple_boolean_both__true=(fmt.as_bool, ("--superflag", "--falseflag"), True, ["--superflag"]), + simple_boolean_both__false=(fmt.as_bool, ("--superflag", "--falseflag"), False, ["--falseflag"]), + simple_boolean_both__none=(fmt.as_bool, ("--superflag", "--falseflag"), None, ["--falseflag"]), + simple_boolean_both__none_ig=(fmt.as_bool, ("--superflag", "--falseflag", True), None, []), simple_boolean_not__true=(fmt.as_bool_not, ("--superflag",), True, []), simple_boolean_not__false=(fmt.as_bool_not, ("--superflag",), False, ["--superflag"]), simple_boolean_not__none=(fmt.as_bool_not, ("--superflag",), None, ["--superflag"]), @@ -240,6 +244,60 @@ TC_RUNNER = dict( ), ), ), + aa_bb_fixed=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=fmt.as_fixed, fmt_arg=["fixed", "args"]), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', 'fixed', 'args'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + aa_bb_map=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=fmt.as_map, fmt_arg={"v1": 111, "v2": 222}), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb="v2"), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', '222'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + aa_bb_map_default=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=fmt.as_map, fmt_arg={"v1": 111, "v2": 222}), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb="v123456789"), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), ) TC_RUNNER_IDS = sorted(TC_RUNNER.keys()) @@ -301,70 +359,16 @@ def test_runner_context(runner_input, cmd_execution, expected): results = ctx.run(**cmd_execution['runner_ctx_run_args']) _assert_run(runner_input, cmd_execution, expected, ctx, results) + with pytest.raises(exc): + with runner(**runner_input['runner_ctx_args']) as ctx2: + results2 = ctx2.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx2, results2) + else: with runner.context(**runner_input['runner_ctx_args']) as ctx: results = ctx.run(**cmd_execution['runner_ctx_run_args']) _assert_run(runner_input, cmd_execution, expected, ctx, results) - -@pytest.mark.parametrize('runner_input, cmd_execution, expected', - (TC_RUNNER[tc] for tc in TC_RUNNER_IDS), - ids=TC_RUNNER_IDS) -def test_runner_callable(runner_input, cmd_execution, expected): - arg_spec = {} - params = {} - arg_formats = {} - for k, v in runner_input['args_bundle'].items(): - try: - arg_spec[k] = {'type': v['type']} - except KeyError: - pass - try: - params[k] = v['value'] - except KeyError: - pass - try: - arg_formats[k] = v['fmt_func'](v['fmt_arg']) - except KeyError: - pass - - orig_results = tuple(cmd_execution[x] for x in ('rc', 'out', 'err')) - - print("arg_spec={0}\nparams={1}\narg_formats={2}\n".format( - arg_spec, - params, - arg_formats, - )) - - module = MagicMock() - type(module).argument_spec = PropertyMock(return_value=arg_spec) - type(module).params = PropertyMock(return_value=params) - module.get_bin_path.return_value = '/mock/bin/testing' - module.run_command.return_value = orig_results - - runner = CmdRunner( - module=module, - command="testing", - arg_formats=arg_formats, - **runner_input['runner_init_args'] - ) - - def _assert_run_info(actual, expected): - reduced = dict((k, actual[k]) for k in expected.keys()) - assert reduced == expected, "{0}".format(reduced) - - def _assert_run(runner_input, cmd_execution, expected, ctx, results): - _assert_run_info(ctx.run_info, expected['run_info']) - assert results == expected.get('results', orig_results) - - exc = expected.get("exc") - if exc: - with pytest.raises(exc): - with runner(**runner_input['runner_ctx_args']) as ctx: - results = ctx.run(**cmd_execution['runner_ctx_run_args']) - _assert_run(runner_input, cmd_execution, expected, ctx, results) - - else: - with runner(**runner_input['runner_ctx_args']) as ctx: - results = ctx.run(**cmd_execution['runner_ctx_run_args']) - _assert_run(runner_input, cmd_execution, expected, ctx, results) + with runner(**runner_input['runner_ctx_args']) as ctx2: + results2 = ctx2.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx2, results2) diff --git a/tests/unit/plugins/modules/test_gconftool2.py b/tests/unit/plugins/modules/test_gconftool2.py new file mode 100644 index 0000000000..f01f15ef82 --- /dev/null +++ b/tests/unit/plugins/modules/test_gconftool2.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Alexei Znamensky (russoz@gmail.com) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible_collections.community.general.plugins.modules import gconftool2 + +import pytest + +TESTED_MODULE = gconftool2.__name__ + + +@pytest.fixture +def patch_gconftool2(mocker): + """ + Function used for mocking some parts of redhat_subscription module + """ + mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path', + return_value='/testbin/gconftool-2') + + +TEST_CASES = [ + [ + {'state': 'get', 'key': '/desktop/gnome/background/picture_filename'}, + { + 'id': 'test_simple_element_get', + 'run_command.calls': [ + ( + ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'], + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, + (0, '100\n', '',), + ), + ], + 'new_value': '100', + } + ], + [ + {'state': 'get', 'key': '/desktop/gnome/background/picture_filename'}, + { + 'id': 'test_simple_element_get_not_found', + 'run_command.calls': [ + ( + ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'], + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, + (0, '', "No value set for `/desktop/gnome/background/picture_filename'\n",), + ), + ], + 'new_value': None, + } + ], + [ + {'state': 'present', 'key': '/desktop/gnome/background/picture_filename', 'value': '200', 'value_type': 'int'}, + { + 'id': 'test_simple_element_set', + 'run_command.calls': [ + ( + ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'], + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, + (0, '100\n', '',), + ), + ( + ['/testbin/gconftool-2', '--type', 'int', '--set', '/desktop/gnome/background/picture_filename', '200'], + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, + (0, '200\n', '',), + ), + ], + 'new_value': '200', + } + ], +] +TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES] + + +@pytest.mark.parametrize('patch_ansible_module, testcase', + TEST_CASES, + ids=TEST_CASES_IDS, + indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_gconftool2(mocker, capfd, patch_gconftool2, testcase): + """ + Run unit tests for test cases listen in TEST_CASES + """ + + # Mock function used for running commands first + call_results = [item[2] for item in testcase['run_command.calls']] + mock_run_command = mocker.patch( + 'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command', + side_effect=call_results) + + # Try to run test case + with pytest.raises(SystemExit): + gconftool2.main() + + out, err = capfd.readouterr() + results = json.loads(out) + print("testcase =\n%s" % testcase) + print("results =\n%s" % results) + + for conditional_test_result in ('value',): + if conditional_test_result in testcase: + assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results) + assert results[conditional_test_result] == testcase[conditional_test_result], \ + "'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result]) + + assert mock_run_command.call_count == len(testcase['run_command.calls']) + if mock_run_command.call_count: + call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list] + expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']] + print("call args list =\n%s" % call_args_list) + print("expected args list =\n%s" % expected_call_args_list) + assert call_args_list == expected_call_args_list diff --git a/tests/unit/plugins/modules/test_jenkins_build.py b/tests/unit/plugins/modules/test_jenkins_build.py index adee2fce99..44c6307ac9 100644 --- a/tests/unit/plugins/modules/test_jenkins_build.py +++ b/tests/unit/plugins/modules/test_jenkins_build.py @@ -43,6 +43,28 @@ def fail_json(*args, **kwargs): raise AnsibleFailJson(kwargs) +class jenkins: + class JenkinsException(Exception): + pass + + class NotFoundException(JenkinsException): + pass + + +class JenkinsBuildMock(): + def get_build_status(self): + try: + instance = JenkinsMock() + response = JenkinsMock.get_build_info(instance, 'host-delete', 1234) + return response + except jenkins.JenkinsException as e: + response = {} + response["result"] = "ABSENT" + return response + except Exception as e: + fail_json(msg='Unable to fetch build information, {0}'.format(e)) + + class JenkinsMock(): def get_job_info(self, name): @@ -51,6 +73,8 @@ class JenkinsMock(): } def get_build_info(self, name, build_number): + if name == "host-delete": + raise jenkins.JenkinsException("job {0} number {1} does not exist".format(name, build_number)) return { "building": True, "result": "SUCCESS" @@ -83,7 +107,7 @@ class JenkinsMockIdempotent(): return None def delete_build(self, name, build_number): - return None + raise jenkins.NotFoundException("job {0} number {1} does not exist".format(name, build_number)) def stop_build(self, name, build_number): return None @@ -167,13 +191,31 @@ class TestJenkinsBuild(unittest.TestCase): @patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies') @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_jenkins_connection') - def test_module_delete_build(self, jenkins_connection, test_deps): + @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_build_status') + def test_module_delete_build(self, build_status, jenkins_connection, test_deps): test_deps.return_value = None jenkins_connection.return_value = JenkinsMock() + build_status.return_value = JenkinsBuildMock().get_build_status() with self.assertRaises(AnsibleExitJson): set_module_args({ - "name": "host-check", + "name": "host-delete", + "build_number": "1234", + "state": "absent", + "user": "abc", + "token": "xyz" + }) + jenkins_build.main() + + @patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies') + @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_jenkins_connection') + def test_module_delete_build_again(self, jenkins_connection, test_deps): + test_deps.return_value = None + jenkins_connection.return_value = JenkinsMockIdempotent() + + with self.assertRaises(AnsibleFailJson): + set_module_args({ + "name": "host-delete", "build_number": "1234", "state": "absent", "user": "abc", diff --git a/tests/unit/plugins/modules/test_puppet.py b/tests/unit/plugins/modules/test_puppet.py new file mode 100644 index 0000000000..632385957e --- /dev/null +++ b/tests/unit/plugins/modules/test_puppet.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +# Author: Alexei Znamensky (russoz@gmail.com) +# Largely adapted from test_redhat_subscription by +# Jiri Hnidek (jhnidek@redhat.com) +# +# Copyright (c) Alexei Znamensky (russoz@gmail.com) +# Copyright (c) Jiri Hnidek (jhnidek@redhat.com) +# +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible_collections.community.general.plugins.modules import puppet + +import pytest + +TESTED_MODULE = puppet.__name__ + + +@pytest.fixture +def patch_get_bin_path(mocker): + """ + Function used for mocking AnsibleModule.get_bin_path + """ + def mockie(self, path, *args, **kwargs): + return "/testbin/{0}".format(path) + mocker.patch("ansible.module_utils.basic.AnsibleModule.get_bin_path", mockie) + + +TEST_CASES = [ + [ + {}, + { + "id": "puppet_agent_plain", + "run_command.calls": [ + ( + ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"], + {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False}, + (0, "blah, anything", "",), # output rc, out, err + ), + ( + [ + "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize", + "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0" + ], + {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False}, + (0, "", "",), # output rc, out, err + ), + ], + "changed": False, + } + ], + [ + { + "certname": "potatobox" + }, + { + "id": "puppet_agent_certname", + "run_command.calls": [ + ( + ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"], + {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False}, + (0, "blah, anything", "",), # output rc, out, err + ), + ( + [ + "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize", + "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--certname=potatobox" + ], + {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False}, + (0, "", "",), # output rc, out, err + ), + ], + "changed": False, + } + ], + [ + { + "tags": ["a", "b", "c"] + }, + { + "id": "puppet_agent_tags_abc", + "run_command.calls": [ + ( + ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"], + {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False}, + (0, "blah, anything", "",), # output rc, out, err + ), + ( + [ + "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize", + "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--tags", "a,b,c" + ], + {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False}, + (0, "", "",), # output rc, out, err + ), + ], + "changed": False, + } + ], +] +TEST_CASES_IDS = [item[1]["id"] for item in TEST_CASES] + + +@pytest.mark.parametrize("patch_ansible_module, testcase", + TEST_CASES, + ids=TEST_CASES_IDS, + indirect=["patch_ansible_module"]) +@pytest.mark.usefixtures("patch_ansible_module") +def test_puppet(mocker, capfd, patch_get_bin_path, testcase): + """ + Run unit tests for test cases listen in TEST_CASES + """ + + # Mock function used for running commands first + call_results = [item[2] for item in testcase["run_command.calls"]] + mock_run_command = mocker.patch( + "ansible.module_utils.basic.AnsibleModule.run_command", + side_effect=call_results) + + # Try to run test case + with pytest.raises(SystemExit): + puppet.main() + + out, err = capfd.readouterr() + results = json.loads(out) + print("results =\n%s" % results) + + assert mock_run_command.call_count == len(testcase["run_command.calls"]) + if mock_run_command.call_count: + call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list] + expected_call_args_list = [(item[0], item[1]) for item in testcase["run_command.calls"]] + print("call args list =\n%s" % call_args_list) + print("expected args list =\n%s" % expected_call_args_list) + assert call_args_list == expected_call_args_list + + assert results.get("changed", False) == testcase["changed"] + if "failed" in testcase: + assert results.get("failed", False) == testcase["failed"] + if "msg" in testcase: + assert results.get("msg", "") == testcase["msg"] diff --git a/tests/unit/plugins/modules/test_redhat_subscription.py b/tests/unit/plugins/modules/test_redhat_subscription.py index bf65671419..865f041141 100644 --- a/tests/unit/plugins/modules/test_redhat_subscription.py +++ b/tests/unit/plugins/modules/test_redhat_subscription.py @@ -92,7 +92,6 @@ TEST_CASES = [ ), ( ['/testbin/subscription-manager', 'register', - '--serverurl', 'satellite.company.com', '--username', 'admin', '--password', 'admin'], {'check_rc': True, 'expand_user_and_vars': False}, @@ -180,7 +179,6 @@ TEST_CASES = [ [ '/testbin/subscription-manager', 'register', - '--serverurl', 'satellite.company.com', '--org', 'admin', '--activationkey', 'some-activation-key' ], @@ -310,6 +308,7 @@ TEST_CASES = [ 'org_id': 'admin', 'force_register': 'true', 'server_proxy_hostname': 'proxy.company.com', + 'server_proxy_scheme': 'https', 'server_proxy_port': '12345', 'server_proxy_user': 'proxy_user', 'server_proxy_password': 'secret_proxy_password' @@ -329,6 +328,7 @@ TEST_CASES = [ '--server.proxy_hostname=proxy.company.com', '--server.proxy_password=secret_proxy_password', '--server.proxy_port=12345', + '--server.proxy_scheme=https', '--server.proxy_user=proxy_user' ], {'check_rc': True}, @@ -340,9 +340,6 @@ TEST_CASES = [ 'register', '--force', '--org', 'admin', - '--proxy', 'proxy.company.com:12345', - '--proxyuser', 'proxy_user', - '--proxypassword', 'secret_proxy_password', '--username', 'admin', '--password', 'admin' ], diff --git a/tests/utils/shippable/alpine.sh b/tests/utils/shippable/alpine.sh new file mode 120000 index 0000000000..6ddb776854 --- /dev/null +++ b/tests/utils/shippable/alpine.sh @@ -0,0 +1 @@ +remote.sh \ No newline at end of file diff --git a/tests/utils/shippable/fedora.sh b/tests/utils/shippable/fedora.sh new file mode 120000 index 0000000000..6ddb776854 --- /dev/null +++ b/tests/utils/shippable/fedora.sh @@ -0,0 +1 @@ +remote.sh \ No newline at end of file diff --git a/tests/utils/shippable/ubuntu.sh b/tests/utils/shippable/ubuntu.sh new file mode 120000 index 0000000000..6ddb776854 --- /dev/null +++ b/tests/utils/shippable/ubuntu.sh @@ -0,0 +1 @@ +remote.sh \ No newline at end of file