diff --git a/.azure-pipelines/azure-pipelines.yml b/.azure-pipelines/azure-pipelines.yml index 8c0804ab31..8dc49e5c03 100644 --- a/.azure-pipelines/azure-pipelines.yml +++ b/.azure-pipelines/azure-pipelines.yml @@ -14,12 +14,24 @@ pr: schedules: - cron: 0 8 * * * - displayName: Nightly + displayName: Nightly (main) always: true branches: include: - main - - stable-* + - cron: 0 10 * * * + displayName: Nightly (active stable branches) + always: true + branches: + include: + - stable-2 + - stable-3 + - cron: 0 11 * * 0 + displayName: Weekly (old stable branches) + always: true + branches: + include: + - stable-1 variables: - name: checkoutPath @@ -112,6 +124,7 @@ stages: - test: 3.7 - test: 3.8 - test: 3.9 + - test: '3.10' - stage: Units_2_11 displayName: Units 2.11 dependsOn: [] @@ -256,10 +269,10 @@ stages: test: centos7 - name: CentOS 8 test: centos8 - - name: Fedora 32 - test: fedora32 - name: Fedora 33 test: fedora33 + - name: Fedora 34 + test: fedora34 - name: openSUSE 15 py2 test: opensuse15py2 - name: openSUSE 15 py3 @@ -282,7 +295,7 @@ stages: targets: - name: CentOS 8 test: centos8 - - name: Fedora 32 + - name: Fedora 33 test: fedora33 - name: openSUSE 15 py3 test: opensuse15 diff --git a/.azure-pipelines/scripts/publish-codecov.sh b/.azure-pipelines/scripts/publish-codecov.sh index 7aeabda0c0..6d184f0b8d 100755 --- a/.azure-pipelines/scripts/publish-codecov.sh +++ b/.azure-pipelines/scripts/publish-codecov.sh @@ -7,7 +7,7 @@ set -o pipefail -eu output_path="$1" -curl --silent --show-error https://codecov.io/bash > codecov.sh +curl --silent --show-error https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh for file in "${output_path}"/reports/coverage*.xml; do name="${file}" diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 06501fc2aa..d9f99c60dc 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -1,17 +1,12 @@ automerge: true files: - plugins/: - supershipit: aminvakil russoz changelogs/fragments/: support: community $actions: labels: action - $actions/aireos.py: - labels: aireos cisco networking - $actions/ironware.py: - maintainers: paulquack - labels: ironware networking - $actions/shutdown.py: + $actions/system/iptables_state.py: + maintainers: quidame + $actions/system/shutdown.py: maintainers: nitzmahone samdoran aminvakil $becomes/: labels: become @@ -88,6 +83,8 @@ files: maintainers: $team_linode labels: cloud linode keywords: linode dynamic inventory script + $inventories/lxd.py: + maintainers: conloos $inventories/proxmox.py: maintainers: $team_virt ilijamt $inventories/scaleway.py: @@ -118,6 +115,8 @@ files: $lookups/nios: maintainers: $team_networking sganesh-infoblox labels: infoblox networking + $lookups/random_string.py: + maintainers: Akasurde $module_utils/: labels: module_utils $module_utils/gitlab.py: @@ -140,6 +139,9 @@ files: $module_utils/memset.py: maintainers: glitchcrab labels: cloud memset + $module_utils/mh/: + maintainers: russoz + labels: module_helper $module_utils/module_helper.py: maintainers: russoz labels: module_helper @@ -225,7 +227,7 @@ files: $modules/cloud/misc/: ignore: ryansb $modules/cloud/misc/terraform.py: - maintainers: m-yosefpor + maintainers: m-yosefpor rainerleber $modules/cloud/misc/xenserver_facts.py: maintainers: caphrim007 cheese labels: xenserver_facts @@ -342,6 +344,8 @@ files: $modules/database/mssql/mssql_db.py: maintainers: vedit Jmainguy kenichi-ogawa-1988 labels: mssql_db + $modules/database/saphana/hana_query.py: + maintainers: rainerleber $modules/database/vertica/: maintainers: dareko $modules/files/archive.py: @@ -373,6 +377,8 @@ files: maintainers: $team_keycloak $modules/identity/keycloak/keycloak_group.py: maintainers: adamgoossens + $modules/identity/keycloak/keycloak_realm.py: + maintainers: kris2kris $modules/identity/onepassword_info.py: maintainers: Rylon $modules/identity/opendj/opendj_backendprop.py: @@ -560,7 +566,8 @@ files: $modules/packaging/language/bundler.py: maintainers: thoiberg $modules/packaging/language/composer.py: - maintainers: dmtrs resmo + maintainers: dmtrs + ignore: resmo $modules/packaging/language/cpanm.py: maintainers: fcuny russoz $modules/packaging/language/easy_install.py: @@ -642,6 +649,9 @@ files: maintainers: elasticdog indrajitr tchernomax labels: pacman ignore: elasticdog + $modules/packaging/os/pacman_key.py: + maintainers: grawlinson + labels: pacman $modules/packaging/os/pkgin.py: maintainers: $team_solaris L2G jasperla szinck martinm82 labels: pkgin solaris @@ -707,7 +717,9 @@ files: labels: zypper ignore: dirtyharrycallahan robinro $modules/packaging/os/zypper_repository.py: - maintainers: matze + maintainers: $team_suse + labels: zypper + ignore: matze $modules/remote_management/cobbler/: maintainers: dagwieers $modules/remote_management/hpilo/: @@ -836,6 +848,8 @@ files: labels: interfaces_file $modules/system/iptables_state.py: maintainers: quidame + $modules/system/shutdown.py: + maintainers: nitzmahone samdoran aminvakil $modules/system/java_cert.py: maintainers: haad absynth76 $modules/system/java_keystore.py: @@ -1025,5 +1039,5 @@ macros: team_rhn: FlossWare alikins barnabycourt vritant team_scaleway: QuentinBrosse abarbare jerome-quere kindermoumoute remyleone sieben team_solaris: bcoca fishman jasperla jpdasma mator scathatheworm troy2914 xen0l - team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom + team_suse: commel dcermak evrardjp lrupp toabctl AnderEnder alxgu andytom sealor team_virt: joshainglis karmab tleguern Thulium-Drake Ajpantuso diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7b796ddb34..bc968361bf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,3 +3,754 @@ Community General Release Notes =============================== .. contents:: Topics + +This changelog describes changes after version 2.0.0. + +v3.2.0 +====== + +Release Summary +--------------- + +Regular bugfix and feature release. + +Minor Changes +------------- + +- Remove unnecessary ``__init__.py`` files from ``plugins/`` (https://github.com/ansible-collections/community.general/pull/2632). +- archive - added ``exclusion_patterns`` option to exclude files or subdirectories from archives (https://github.com/ansible-collections/community.general/pull/2616). +- cloud_init_data_facts - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). +- composer - add ``composer_executable`` option (https://github.com/ansible-collections/community.general/issues/2649). +- flatpak - add ``no_dependencies`` parameter (https://github.com/ansible/ansible/pull/55452, https://github.com/ansible-collections/community.general/pull/2751). +- ini_file - opening file with encoding ``utf-8-sig`` (https://github.com/ansible-collections/community.general/issues/2189). +- jira - add comment visibility parameter for comment operation (https://github.com/ansible-collections/community.general/pull/2556). +- maven_artifact - added ``checksum_alg`` option to support SHA1 checksums in order to support FIPS systems (https://github.com/ansible-collections/community.general/pull/2662). +- module_helper module utils - method ``CmdMixin.run_command()`` now accepts ``process_output`` specifying a function to process the outcome of the underlying ``module.run_command()`` (https://github.com/ansible-collections/community.general/pull/2564). +- nmcli - add new options to ignore automatic DNS servers and gateways (https://github.com/ansible-collections/community.general/issues/1087). +- onepassword lookup plugin - add ``domain`` option (https://github.com/ansible-collections/community.general/issues/2734). +- open_iscsi - add ``auto_portal_startup`` parameter to allow ``node.startup`` setting per portal (https://github.com/ansible-collections/community.general/issues/2685). +- open_iscsi - also consider ``portal`` and ``port`` to check if already logged in or not (https://github.com/ansible-collections/community.general/issues/2683). +- proxmox_group_info - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). +- proxmox_kvm - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). +- rhevm - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). +- serverless - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). +- stacki_host - minor refactoring (https://github.com/ansible-collections/community.general/pull/2681). +- terraform - add option ``overwrite_init`` to skip init if exists (https://github.com/ansible-collections/community.general/pull/2573). +- terraform - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + +Deprecated Features +------------------- + +- All inventory and vault scripts will be removed from community.general in version 4.0.0. If you are referencing them, please update your references to the new `contrib-scripts GitHub repository `_ so your workflow will not break once community.general 4.0.0 is released (https://github.com/ansible-collections/community.general/pull/2697). + +Bugfixes +-------- + +- consul_kv lookup plugin - allow to set ``recurse``, ``index``, ``datacenter`` and ``token`` as keyword arguments (https://github.com/ansible-collections/community.general/issues/2124). +- cpanm - also use ``LC_ALL`` to enforce locale choice (https://github.com/ansible-collections/community.general/pull/2731). +- influxdb_user - fix bug which removed current privileges instead of appending them to existing ones (https://github.com/ansible-collections/community.general/issues/2609, https://github.com/ansible-collections/community.general/pull/2614). +- iptables_state - call ``async_status`` action plugin rather than its module (https://github.com/ansible-collections/community.general/issues/2700). +- iptables_state - fix a broken query of ``async_status`` result with current ansible-core development version (https://github.com/ansible-collections/community.general/issues/2627, https://github.com/ansible-collections/community.general/pull/2671). +- java_cert - fix issue with incorrect alias used on PKCS#12 certificate import (https://github.com/ansible-collections/community.general/pull/2560). +- jenkins_plugin - use POST method for sending request to jenkins API when ``state`` option is one of ``enabled``, ``disabled``, ``pinned``, ``unpinned``, or ``absent`` (https://github.com/ansible-collections/community.general/issues/2510). +- json_query filter plugin - avoid 'unknown type' errors for more Ansible internal types (https://github.com/ansible-collections/community.general/pull/2607). +- keycloak_realm - ``ssl_required`` changed from a boolean type to accept the strings ``none``, ``external`` or ``all``. This is not a breaking change since the module always failed when a boolean was supplied (https://github.com/ansible-collections/community.general/pull/2693). +- keycloak_realm - remove warning that ``reset_password_allowed`` needs to be marked as ``no_log`` (https://github.com/ansible-collections/community.general/pull/2694). +- module_helper module utils - ``CmdMixin`` must also use ``LC_ALL`` to enforce locale choice (https://github.com/ansible-collections/community.general/pull/2731). +- netcup_dns - use ``str(ex)`` instead of unreliable ``ex.message`` in exception handling to fix ``AttributeError`` in error cases (https://github.com/ansible-collections/community.general/pull/2590). +- ovir4 inventory script - improve configparser creation to avoid crashes for options without values (https://github.com/ansible-collections/community.general/issues/674). +- proxmox_kvm - fixed ``vmid`` return value when VM with ``name`` already exists (https://github.com/ansible-collections/community.general/issues/2648). +- redis cache - improved connection string parsing (https://github.com/ansible-collections/community.general/issues/497). +- rhsm_release - fix the issue that module considers 8, 7Client and 7Workstation as invalid releases (https://github.com/ansible-collections/community.general/pull/2571). +- ssh_config - reduce stormssh searches based on host (https://github.com/ansible-collections/community.general/pull/2568/). +- stacki_host - when adding a new server, ``rack`` and ``rank`` must be passed, and network parameters are optional (https://github.com/ansible-collections/community.general/pull/2681). +- terraform - ensure the workspace is set back to its previous value when the apply fails (https://github.com/ansible-collections/community.general/pull/2634). +- xfconf - also use ``LC_ALL`` to enforce locale choice (https://github.com/ansible-collections/community.general/issues/2715). +- zypper_repository - fix idempotency on adding repository with ``$releasever`` and ``$basearch`` variables (https://github.com/ansible-collections/community.general/issues/1985). + +New Plugins +----------- + +Lookup +~~~~~~ + +- random_string - Generates random string + +New Modules +----------- + +Database +~~~~~~~~ + +saphana +^^^^^^^ + +- hana_query - Execute SQL on HANA + +Files +~~~~~ + +- sapcar_extract - Manages SAP SAPCAR archives + +Packaging +~~~~~~~~~ + +os +^^ + +- pacman_key - Manage pacman's list of trusted keys + +v3.1.0 +====== + +Release Summary +--------------- + +Regular feature and bugfix release. + +Minor Changes +------------- + +- ModuleHelper module utils - improved mechanism for customizing the calculation of ``changed`` (https://github.com/ansible-collections/community.general/pull/2514). +- chroot connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- cmd (Module Helper) module utils - ``CmdMixin`` now pulls the value for ``run_command()`` params from ``self.vars``, as opposed to previously retrieving those from ``self.module.params`` (https://github.com/ansible-collections/community.general/pull/2517). +- filesystem - cleanup and revamp module, tests and doc. Pass all commands to ``module.run_command()`` as lists. Move the device-vs-mountpoint logic to ``grow()`` method. Give to all ``get_fs_size()`` the same logic and error handling. (https://github.com/ansible-collections/community.general/pull/2472). +- funcd connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- gitlab_user - add ``expires_at`` option (https://github.com/ansible-collections/community.general/issues/2325). +- idrac_redfish_config - modified set_manager_attributes function to skip invalid attribute instead of returning. Added skipped attributes to output. Modified module exit to add warning variable (https://github.com/ansible-collections/community.general/issues/1995). +- influxdb_retention_policy - add ``state`` parameter with allowed values ``present`` and ``absent`` to support deletion of existing retention policies (https://github.com/ansible-collections/community.general/issues/2383). +- influxdb_retention_policy - simplify duration logic parsing (https://github.com/ansible-collections/community.general/pull/2385). +- iocage connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- jail connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- java_keystore - added ``ssl_backend`` parameter for using the cryptography library instead of the OpenSSL binary (https://github.com/ansible-collections/community.general/pull/2485). +- java_keystore - replace envvar by stdin to pass secret to ``keytool`` (https://github.com/ansible-collections/community.general/pull/2526). +- linode - added proper traceback when failing due to exceptions (https://github.com/ansible-collections/community.general/pull/2410). +- linode - parameter ``additional_disks`` is now validated as a list of dictionaries (https://github.com/ansible-collections/community.general/pull/2410). +- lxc connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- module_helper module utils - break down of the long file into smaller pieces (https://github.com/ansible-collections/community.general/pull/2393). +- nmcli - remove dead code, ``options`` never contains keys from ``param_alias`` (https://github.com/ansible-collections/community.general/pull/2417). +- pacman - add ``executable`` option to use an alternative pacman binary (https://github.com/ansible-collections/community.general/issues/2524). +- passwordstore lookup - add option ``missing`` to choose what to do if the password file is missing (https://github.com/ansible-collections/community.general/pull/2500). +- qubes connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- redfish_config - modified module exit to add warning variable (https://github.com/ansible-collections/community.general/issues/1995). +- redfish_utils module utils - modified set_bios_attributes function to skip invalid attribute instead of returning. Added skipped attributes to output (https://github.com/ansible-collections/community.general/issues/1995). +- saltstack connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). +- spotinst_aws_elastigroup - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2355). +- zfs_delegate_admin - drop choices from permissions, allowing any permission supported by the underlying zfs commands (https://github.com/ansible-collections/community.general/pull/2540). +- zone connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + +Deprecated Features +------------------- + +- The nios, nios_next_ip, nios_next_network lookup plugins, the nios documentation fragment, and the nios_host_record, nios_ptr_record, nios_mx_record, nios_fixed_address, nios_zone, nios_member, nios_a_record, nios_aaaa_record, nios_network, nios_dns_view, nios_txt_record, nios_naptr_record, nios_srv_record, nios_cname_record, nios_nsgroup, and nios_network_view module have been deprecated and will be removed from community.general 5.0.0. Please install the `infoblox.nios_modules `_ collection instead and use its plugins and modules (https://github.com/ansible-collections/community.general/pull/2458). +- The vendored copy of ``ipaddress`` will be removed in community.general 4.0.0. Please switch to ``ipaddress`` from the Python 3 standard library, or `from pypi `_, if your code relies on the vendored version of ``ipaddress`` (https://github.com/ansible-collections/community.general/pull/2459). +- linode - parameter ``backupsenabled`` is deprecated and will be removed in community.general 5.0.0 (https://github.com/ansible-collections/community.general/pull/2410). +- lxd inventory plugin - the plugin will require ``ipaddress`` installed when used with Python 2 from community.general 4.0.0 on. ``ipaddress`` is part of the Python 3 standard library, but can be installed for Python 2 from pypi (https://github.com/ansible-collections/community.general/pull/2459). +- scaleway_security_group_rule - the module will require ``ipaddress`` installed when used with Python 2 from community.general 4.0.0 on. ``ipaddress`` is part of the Python 3 standard library, but can be installed for Python 2 from pypi (https://github.com/ansible-collections/community.general/pull/2459). + +Bugfixes +-------- + +- consul_acl - update the hcl allowlist to include all supported options (https://github.com/ansible-collections/community.general/pull/2495). +- filesystem - repair ``reiserfs`` fstype support after adding it to integration tests (https://github.com/ansible-collections/community.general/pull/2472). +- influxdb_user - allow creation of admin users when InfluxDB authentication is enabled but no other user exists on the database. In this scenario, InfluxDB 1.x allows only ``CREATE USER`` queries and rejects any other query (https://github.com/ansible-collections/community.general/issues/2364). +- influxdb_user - fix bug where an influxdb user has no privileges for 2 or more databases (https://github.com/ansible-collections/community.general/pull/2499). +- iptables_state - fix a 'FutureWarning' in a regex and do some basic code clean up (https://github.com/ansible-collections/community.general/pull/2525). +- iptables_state - fix initialization of iptables from null state when adressing more than one table (https://github.com/ansible-collections/community.general/issues/2523). +- nmap inventory plugin - fix local variable error when cache is disabled (https://github.com/ansible-collections/community.general/issues/2512). + +New Plugins +----------- + +Filter +~~~~~~ + +- groupby_as_dict - Transform a sequence of dictionaries to a dictionary where the dictionaries are indexed by an attribute + +Lookup +~~~~~~ + +- dependent - Composes a list with nested elements of other lists or dicts which can depend on previous loop variables +- random_pet - Generates random pet names + +New Modules +----------- + +Cloud +~~~~~ + +misc +^^^^ + +- proxmox_nic - Management of a NIC of a Qemu(KVM) VM in a Proxmox VE cluster. + +Notification +~~~~~~~~~~~~ + +- discord - Send Discord messages + +v3.0.2 +====== + +Release Summary +--------------- + +Bugfix release for the first Ansible 4.0.0 release candidate. + +Bugfixes +-------- + +- stackpath_compute inventory script - fix broken validation checks for client ID and client secret (https://github.com/ansible-collections/community.general/pull/2448). +- zfs - certain ZFS properties, especially sizes, would lead to a task being falsely marked as "changed" even when no actual change was made (https://github.com/ansible-collections/community.general/issues/975, https://github.com/ansible-collections/community.general/pull/2454). + +v3.0.1 +====== + +Release Summary +--------------- + +Bugfix release for the next Ansible 4.0.0 beta. + +Bugfixes +-------- + +- composer - use ``no-interaction`` option when discovering available options to avoid an issue where composer hangs (https://github.com/ansible-collections/community.general/pull/2348). +- influxdb_retention_policy - fix bug where ``INF`` duration values failed parsing (https://github.com/ansible-collections/community.general/pull/2385). +- inventory and vault scripts - change file permissions to make vendored inventory and vault scripts exectuable (https://github.com/ansible-collections/community.general/pull/2337). +- linode_v4 - changed the error message to point to the correct bugtracker URL (https://github.com/ansible-collections/community.general/pull/2430). +- lvol - fixed rounding errors (https://github.com/ansible-collections/community.general/issues/2370). +- lvol - fixed size unit capitalization to match units used between different tools for comparison (https://github.com/ansible-collections/community.general/issues/2360). +- nmcli - compare MAC addresses case insensitively to fix idempotency issue (https://github.com/ansible-collections/community.general/issues/2409). +- nmcli - if type is ``bridge-slave`` add ``slave-type bridge`` to ``nmcli`` command (https://github.com/ansible-collections/community.general/issues/2408). +- one_vm - Allow missing NIC keys (https://github.com/ansible-collections/community.general/pull/2435). +- puppet - replace ``console` with ``stdout`` in ``logdest`` option when ``all`` has been chosen (https://github.com/ansible-collections/community.general/issues/1190). +- svr4pkg - convert string to a bytes-like object to avoid ``TypeError`` with Python 3 (https://github.com/ansible-collections/community.general/issues/2373). + +v3.0.0 +====== + +Release Summary +--------------- + +This is release 3.0.0 of ``community.general``, released on 2021-04-26. + +Minor Changes +------------- + +- apache2_mod_proxy - refactored/cleaned-up part of the code (https://github.com/ansible-collections/community.general/pull/2142). +- archive - refactored some reused code out into a couple of functions (https://github.com/ansible-collections/community.general/pull/2061). +- atomic_container - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). +- atomic_host - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). +- atomic_image - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). +- beadm - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- bitbucket_pipeline_variable - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). +- bundler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- clc_* modules - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1771). +- consul - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- consul_acl - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- consul_io inventory script - conf options - allow custom configuration options via env variables (https://github.com/ansible-collections/community.general/pull/620). +- consul_session - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- cpanm - honor and install specified version when running in ``new`` mode; that feature is not available in ``compatibility`` mode (https://github.com/ansible-collections/community.general/issues/208). +- cpanm - rewritten using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2218). +- csv module utils - new module_utils for shared functions between ``from_csv`` filter and ``read_csv`` module (https://github.com/ansible-collections/community.general/pull/2037). +- datadog_monitor - add missing monitor types ``query alert``, ``trace-analytics alert``, ``rum alert`` (https://github.com/ansible-collections/community.general/pull/1723). +- datadog_monitor - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- dnsimple - add CAA records to the whitelist of valid record types (https://github.com/ansible-collections/community.general/pull/1814). +- dnsimple - elements of list parameters ``record_ids`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- gitlab_deploy_key - when the given key title already exists but has a different public key, the public key will now be updated to given value (https://github.com/ansible-collections/community.general/pull/1661). +- gitlab_runner - elements of list parameters ``tag_list`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- grove - the option ``message`` has been renamed to ``message_content``. The old name ``message`` is kept as an alias and will be removed for community.general 4.0.0. This was done because ``message`` is used internally by Ansible (https://github.com/ansible-collections/community.general/pull/1929). +- heroku_collaborator - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- hiera lookup - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- homebrew_tap - add support to specify search path for ``brew`` executable (https://github.com/ansible-collections/community.general/issues/1702). +- ipa_config - add new options ``ipaconfigstring``, ``ipadefaultprimarygroup``, ``ipagroupsearchfields``, ``ipahomesrootdir``, ``ipabrkauthzdata``, ``ipamaxusernamelength``, ``ipapwdexpadvnotify``, ``ipasearchrecordslimit``, ``ipasearchtimelimit``, ``ipauserauthtype``, and ``ipausersearchfields`` (https://github.com/ansible-collections/community.general/pull/2116). +- ipa_sudorule - add support for setting sudo runasuser (https://github.com/ansible-collections/community.general/pull/2031). +- ipa_user - fix ``userauthtype`` option to take in list of strings for the multi-select field instead of single string (https://github.com/ansible-collections/community.general/pull/2174). +- ipwcli_dns - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- java_cert - change ``state: present`` to check certificates by hash, not just alias name (https://github.com/ansible/ansible/issues/43249). +- java_keystore - add options ``certificate_path`` and ``private_key_path``, mutually exclusive with ``certificate`` and ``private_key`` respectively, and targetting files on remote hosts rather than their contents on the controller. (https://github.com/ansible-collections/community.general/issues/1669). +- jenkins_job - add a ``validate_certs`` parameter that allows disabling TLS/SSL certificate validation (https://github.com/ansible-collections/community.general/issues/255). +- jira - added ``attach`` operation, which allows a user to attach a file to an issue (https://github.com/ansible-collections/community.general/pull/2192). +- jira - added parameter ``account_id`` for compatibility with recent versions of JIRA (https://github.com/ansible-collections/community.general/issues/818, https://github.com/ansible-collections/community.general/pull/1978). +- jira - revamped the module as a class using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2208). +- keycloak_* modules - allow the keycloak modules to use a token for the authentication, the modules can take either a token or the credentials (https://github.com/ansible-collections/community.general/pull/2250). +- keycloak_client - elements of list parameters ``default_roles``, ``redirect_uris``, ``web_origins`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- kibana_plugin - add parameter for passing ``--allow-root`` flag to kibana and kibana-plugin commands (https://github.com/ansible-collections/community.general/pull/2014). +- known_hosts module utils - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- librato_annotation - elements of list parameters ``links`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- linode_v4 - add support for ``private_ip`` option (https://github.com/ansible-collections/community.general/pull/2249). +- linode_v4 - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- lvol - added proper support for ``+-`` options when extending or reducing the logical volume (https://github.com/ansible-collections/community.general/issues/1988). +- lxd_container - ``client_key`` and ``client_cert`` are now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741). +- lxd_container - elements of list parameter ``profiles`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- lxd_profile - ``client_key`` and ``client_cert`` are now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741). +- lxd_profile - added ``merge_profile`` parameter to merge configurations from the play to an existing profile (https://github.com/ansible-collections/community.general/pull/1813). +- mail - elements of list parameters ``to``, ``cc``, ``bcc``, ``attach``, ``headers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- manageiq_alert_profiles - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- manageiq_policies - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- manageiq_tags - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- manageiq_tags and manageiq_policies - added new parameter ``resource_id``. This parameter can be used instead of parameter ``resource_name`` (https://github.com/ansible-collections/community.general/pull/719). +- module_helper module utils - ``CmdMixin.run_command()`` now accepts ``dict`` command arguments, providing the parameter and its value (https://github.com/ansible-collections/community.general/pull/1867). +- module_helper module utils - added management of facts and adhoc setting of the initial value for variables (https://github.com/ansible-collections/community.general/pull/2188). +- module_helper module utils - added mechanism to manage variables, providing automatic output of variables, change status and diff information (https://github.com/ansible-collections/community.general/pull/2162). +- na_ontap_gather_facts - elements of list parameters ``gather_subset`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- nexmo - elements of list parameters ``dest`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- nictagadm - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- nmcli - add ability to connect to a Wifi network and also to attach it to a master (bond) (https://github.com/ansible-collections/community.general/pull/2220). +- nmcli - do not set IP configuration on slave connection (https://github.com/ansible-collections/community.general/pull/2223). +- nmcli - don't restrict the ability to manually set the MAC address to the bridge (https://github.com/ansible-collections/community.general/pull/2224). +- npm - add ``no_bin_links`` option (https://github.com/ansible-collections/community.general/issues/2128). +- nsupdate - elements of list parameters ``value`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- oci_vcn - ``api_user_key_file`` is now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741). +- omapi_host - elements of list parameters ``statements`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- one_host - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- one_image_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- one_vm - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- oneandone_firewall_policy - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- oneandone_load_balancer - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- oneandone_monitoring_policy - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- oneandone_private_network - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- oneandone_server - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- onepassword_info - elements of list parameters ``search_terms`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- oneview_datacenter_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- oneview_enclosure_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- oneview_ethernet_network_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- oneview_network_set_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- ovh_ip_failover - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). +- packet_device - elements of list parameters ``device_ids``, ``hostnames`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- pagerduty - elements of list parameters ``service`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- pids - new options ``pattern`` and `ignore_case`` for retrieving PIDs of processes matching a supplied pattern (https://github.com/ansible-collections/community.general/pull/2280). +- plugins/module_utils/oracle/oci_utils.py - elements of list parameter ``key_by`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- profitbricks - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- profitbricks_volume - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- proxmox - added ``purge`` module parameter for use when deleting lxc's with HA options (https://github.com/ansible-collections/community.general/pull/2013). +- proxmox inventory plugin - added ``Constructable`` class to the inventory to provide options ``strict``, ``keyed_groups``, ``groups``, and ``compose`` (https://github.com/ansible-collections/community.general/pull/2180). +- proxmox inventory plugin - added ``proxmox_agent_interfaces`` fact describing network interfaces returned from a QEMU guest agent (https://github.com/ansible-collections/community.general/pull/2148). +- proxmox inventory plugin - added ``tags_parsed`` fact containing tags parsed as a list (https://github.com/ansible-collections/community.general/pull/1949). +- proxmox inventory plugin - allow to select whether ``ansible_host`` should be set for the proxmox nodes (https://github.com/ansible-collections/community.general/pull/2263). +- proxmox_kvm - added new module parameter ``tags`` for use with PVE 6+ (https://github.com/ansible-collections/community.general/pull/2000). +- proxmox_kvm module - actually implemented ``vmid`` and ``status`` return values. Updated documentation to reflect current situation (https://github.com/ansible-collections/community.general/issues/1410, https://github.com/ansible-collections/community.general/pull/1715). +- pubnub_blocks - elements of list parameters ``event_handlers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- rax - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). +- rax_cdb_user - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). +- rax_scaling_group - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). +- read_csv - refactored read_csv module to use shared csv functions from csv module_utils (https://github.com/ansible-collections/community.general/pull/2037). +- redfish modules - explicitly setting lists' elements to ``str`` (https://github.com/ansible-collections/community.general/pull/1761). +- redfish_* modules, redfish_utils module utils - add support for Redfish session create, delete, and authenticate (https://github.com/ansible-collections/community.general/issues/1975). +- redfish_config - case insensitive search for situations where the hostname/FQDN case on iLO doesn't match variable's case (https://github.com/ansible-collections/community.general/pull/1744). +- redhat_subscription - elements of list parameters ``pool_ids``, ``addons`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- rhevm - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). +- rocketchat - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- scaleway_compute - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- scaleway_lb - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). +- sendgrid - elements of list parameters ``to_addresses``, ``cc``, ``bcc``, ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- sensu_check - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- sensu_client - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- sensu_handler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- sl_vm - elements of list parameters ``disks``, ``ssh_keys`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- slack - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- smartos_image_info - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- snmp_facts - added parameters ``timeout`` and ``retries`` to module (https://github.com/ansible-collections/community.general/issues/980). +- statusio_maintenance - elements of list parameters ``components``, ``containers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- svr4pkg - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- terraform - add ``plugin_paths`` parameter which allows disabling Terraform from performing plugin discovery and auto-download (https://github.com/ansible-collections/community.general/pull/2308). +- timezone - add Gentoo and Alpine Linux support (https://github.com/ansible-collections/community.general/issues/781). +- twilio - elements of list parameters ``to_numbers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- udm_dns_zone - elements of list parameters ``nameserver``, ``interfaces``, and ``mx`` are now validated (https://github.com/ansible-collections/community.general/pull/2268). +- vdo - add ``force`` option (https://github.com/ansible-collections/community.general/issues/2101). +- vmadm - elements of list parameters ``disks``, ``nics``, ``resolvers``, ``filesystems`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- webfaction_domain - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- webfaction_site - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). +- xattr - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- xfconf - added option ``disable_facts`` to disable facts and its associated deprecation warning (https://github.com/ansible-collections/community.general/issues/1475). +- xfconf - changed implementation to use ``ModuleHelper`` new features (https://github.com/ansible-collections/community.general/pull/2188). +- xml - elements of list parameters ``add_children``, ``set_children`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). +- yum_versionlock - Do the lock/unlock concurrently to speed up (https://github.com/ansible-collections/community.general/pull/1912). +- zfs_facts - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). +- zpool_facts - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). + +Breaking Changes / Porting Guide +-------------------------------- + +- If you use Ansible 2.9 and these plugins or modules from this collection, community.general 3.0.0 results in errors when trying to use the DellEMC content by FQCN, like ``community.general.idrac_firmware``. + Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``dellemc.openmanage.idrac_firmware`` for the previous example) and to make sure that you have ``dellemc.openmanage`` installed. + + If you use ansible-base 2.10 or newer and did not install Ansible 4.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install the ``dellemc.openmanage`` collection if you are using any of these plugins or modules. + While ansible-base 2.10 or newer can use the redirects that community.general 3.0.0 adds, the collection they point to (such as dellemc.openmanage) must be installed for them to work. +- gitlab_deploy_key - if for an already existing key title a different public key was given as parameter nothing happened, now this changed so that the public key is updated to the new value (https://github.com/ansible-collections/community.general/pull/1661). +- java_keystore - instead of failing, now overwrites keystore if the alias (name) is changed. This was originally the intended behavior, but did not work due to a logic error. Make sure that your playbooks and roles do not depend on the old behavior of failing instead of overwriting (https://github.com/ansible-collections/community.general/issues/1671). +- java_keystore - instead of failing, now overwrites keystore if the passphrase is changed. Make sure that your playbooks and roles do not depend on the old behavior of failing instead of overwriting (https://github.com/ansible-collections/community.general/issues/1671). +- one_image - use pyone instead of python-oca (https://github.com/ansible-collections/community.general/pull/2032). +- utm_proxy_auth_profile - the ``frontend_cookie_secret`` return value now contains a placeholder string instead of the module's ``frontend_cookie_secret`` parameter (https://github.com/ansible-collections/community.general/pull/1736). + +Deprecated Features +------------------- + +- apt_rpm - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- composer - deprecated invalid parameter aliases ``working-dir``, ``global-command``, ``prefer-source``, ``prefer-dist``, ``no-dev``, ``no-scripts``, ``no-plugins``, ``optimize-autoloader``, ``classmap-authoritative``, ``apcu-autoloader``, ``ignore-platform-reqs``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- cpanm - parameter ``system_lib`` deprecated in favor of using ``become`` (https://github.com/ansible-collections/community.general/pull/2218). +- github_deploy_key - deprecated invalid parameter alias ``2fa_token``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- grove - the option ``message`` will be removed in community.general 4.0.0. Use the new option ``message_content`` instead (https://github.com/ansible-collections/community.general/pull/1929). +- homebrew - deprecated invalid parameter alias ``update-brew``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- homebrew_cask - deprecated invalid parameter alias ``update-brew``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- opkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- pacman - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- puppet - deprecated undocumented parameter ``show_diff``, will be removed in 7.0.0. (https://github.com/ansible-collections/community.general/pull/1927). +- runit - unused parameter ``dist`` marked for deprecation (https://github.com/ansible-collections/community.general/pull/1830). +- slackpkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- urmpi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- xbps - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). +- xfconf - returning output as facts is deprecated, this will be removed in community.general 4.0.0. Please register the task output in a variable and use it instead. You can already switch to the new behavior now by using the new ``disable_facts`` option (https://github.com/ansible-collections/community.general/pull/1747). + +Removed Features (previously deprecated) +---------------------------------------- + +- The ``ome_device_info``, ``idrac_firmware`` and ``idrac_server_config_profile`` modules have now been migrated from community.general to the `dellemc.openmanage `_ Ansible collection. + If you use ansible-base 2.10 or newer, redirections have been provided. + + If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.idrac_firmware`` → ``dellemc.openmanage.idrac_firmware``) and make sure to install the dellemc.openmanage collection. +- The deprecated ali_instance_facts module has been removed. Use ali_instance_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated gluster_heal_info module has been removed. Use gluster.gluster.gluster_heal_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated gluster_peer module has been removed. Use gluster.gluster.gluster_peer instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated gluster_volume module has been removed. Use gluster.gluster.gluster_volume instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated helm module has been removed. Use community.kubernetes.helm instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated hpilo_facts module has been removed. Use hpilo_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated idrac_redfish_facts module has been removed. Use idrac_redfish_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated jenkins_job_facts module has been removed. Use jenkins_job_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ldap_attr module has been removed. Use ldap_attrs instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated memset_memstore_facts module has been removed. Use memset_memstore_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated memset_server_facts module has been removed. Use memset_server_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated na_ontap_gather_facts module has been removed. Use netapp.ontap.na_ontap_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated nginx_status_facts module has been removed. Use nginx_status_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated one_image_facts module has been removed. Use one_image_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated onepassword_facts module has been removed. Use onepassword_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_datacenter_facts module has been removed. Use oneview_datacenter_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_enclosure_facts module has been removed. Use oneview_enclosure_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_ethernet_network_facts module has been removed. Use oneview_ethernet_network_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_fc_network_facts module has been removed. Use oneview_fc_network_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_fcoe_network_facts module has been removed. Use oneview_fcoe_network_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_logical_interconnect_group_facts module has been removed. Use oneview_logical_interconnect_group_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_network_set_facts module has been removed. Use oneview_network_set_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated oneview_san_manager_facts module has been removed. Use oneview_san_manager_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated online_server_facts module has been removed. Use online_server_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated online_user_facts module has been removed. Use online_user_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt module has been removed. Use ovirt.ovirt.ovirt_vm instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_affinity_label_facts module has been removed. Use ovirt.ovirt.ovirt_affinity_label_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_api_facts module has been removed. Use ovirt.ovirt.ovirt_api_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_cluster_facts module has been removed. Use ovirt.ovirt.ovirt_cluster_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_datacenter_facts module has been removed. Use ovirt.ovirt.ovirt_datacenter_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_disk_facts module has been removed. Use ovirt.ovirt.ovirt_disk_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_event_facts module has been removed. Use ovirt.ovirt.ovirt_event_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_external_provider_facts module has been removed. Use ovirt.ovirt.ovirt_external_provider_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_group_facts module has been removed. Use ovirt.ovirt.ovirt_group_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_host_facts module has been removed. Use ovirt.ovirt.ovirt_host_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_host_storage_facts module has been removed. Use ovirt.ovirt.ovirt_host_storage_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_network_facts module has been removed. Use ovirt.ovirt.ovirt_network_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_nic_facts module has been removed. Use ovirt.ovirt.ovirt_nic_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_permission_facts module has been removed. Use ovirt.ovirt.ovirt_permission_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_quota_facts module has been removed. Use ovirt.ovirt.ovirt_quota_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_scheduling_policy_facts module has been removed. Use ovirt.ovirt.ovirt_scheduling_policy_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_snapshot_facts module has been removed. Use ovirt.ovirt.ovirt_snapshot_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_storage_domain_facts module has been removed. Use ovirt.ovirt.ovirt_storage_domain_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_storage_template_facts module has been removed. Use ovirt.ovirt.ovirt_storage_template_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_storage_vm_facts module has been removed. Use ovirt.ovirt.ovirt_storage_vm_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_tag_facts module has been removed. Use ovirt.ovirt.ovirt_tag_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_template_facts module has been removed. Use ovirt.ovirt.ovirt_template_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_user_facts module has been removed. Use ovirt.ovirt.ovirt_user_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_vm_facts module has been removed. Use ovirt.ovirt.ovirt_vm_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated ovirt_vmpool_facts module has been removed. Use ovirt.ovirt.ovirt_vmpool_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated purefa_facts module has been removed. Use purestorage.flasharray.purefa_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated purefb_facts module has been removed. Use purestorage.flasharray.purefb_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated python_requirements_facts module has been removed. Use python_requirements_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated redfish_facts module has been removed. Use redfish_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_image_facts module has been removed. Use scaleway_image_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_ip_facts module has been removed. Use scaleway_ip_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_organization_facts module has been removed. Use scaleway_organization_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_security_group_facts module has been removed. Use scaleway_security_group_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_server_facts module has been removed. Use scaleway_server_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_snapshot_facts module has been removed. Use scaleway_snapshot_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated scaleway_volume_facts module has been removed. Use scaleway_volume_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated smartos_image_facts module has been removed. Use smartos_image_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated vertica_facts module has been removed. Use vertica_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The deprecated xenserver_guest_facts module has been removed. Use xenserver_guest_info instead (https://github.com/ansible-collections/community.general/pull/1924). +- The ovirt_facts docs fragment has been removed (https://github.com/ansible-collections/community.general/pull/1924). +- airbrake_deployment - removed deprecated ``token`` parameter. Use ``project_id`` and ``project_key`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- bigpanda - the alias ``message`` has been removed. Use ``deployment_message`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- cisco_spark, cisco_webex - the alias ``message`` has been removed. Use ``msg`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- clc_aa_policy - the ``wait`` parameter has been removed. It did not have any effect (https://github.com/ansible-collections/community.general/pull/1926). +- datadog_monitor - the alias ``message`` has been removed. Use ``notification_message`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- django_manage - the parameter ``liveserver`` has been removed (https://github.com/ansible-collections/community.general/pull/1926). +- idrac_redfish_config - the parameters ``manager_attribute_name`` and ``manager_attribute_value`` have been removed. Use ``manager_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- iso_extract - the alias ``thirsty`` has been removed. Use ``force`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- ldap_entry - the ``params`` parameter is now completely removed. Using it already triggered an error since community.general 0.1.2 (https://github.com/ansible-collections/community.general/pull/2257). +- pulp_repo - the ``feed_client_cert`` parameter no longer defaults to the value of the ``client_cert`` parameter (https://github.com/ansible-collections/community.general/pull/1926). +- pulp_repo - the ``feed_client_key`` parameter no longer defaults to the value of the ``client_key`` parameter (https://github.com/ansible-collections/community.general/pull/1926). +- pulp_repo - the alias ``ca_cert`` has been removed. Use ``feed_ca_cert`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- rax - unused parameter ``service`` removed (https://github.com/ansible-collections/community.general/pull/2020). +- redfish modules - issuing a data modification command without specifying the ID of the target System, Chassis or Manager resource when there is more than one is no longer allowed. Use the ``resource_id`` option to specify the target ID (https://github.com/ansible-collections/community.general/pull/1926). +- redfish_config - the parameters ``bios_attribute_name`` and ``bios_attribute_value`` have been removed. Use ``bios_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926). +- syspatch - the ``apply`` parameter has been removed. This is the default mode, so simply removing it will not change the behavior (https://github.com/ansible-collections/community.general/pull/1926). +- xbps - the ``force`` parameter has been removed. It did not have any effect (https://github.com/ansible-collections/community.general/pull/1926). + +Security Fixes +-------------- + +- dnsmadeeasy - mark the ``account_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- gitlab_runner - mark the ``registration_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- hwc_ecs_instance - mark the ``admin_pass`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- ibm_sa_host - mark the ``iscsi_chap_secret`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- java_cert - remove password from ``run_command`` arguments (https://github.com/ansible-collections/community.general/pull/2008). +- java_keystore - pass secret to keytool through an environment variable to not expose it as a commandline argument (https://github.com/ansible-collections/community.general/issues/1668). +- keycloak_* modules - mark the ``auth_client_secret`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- keycloak_client - mark the ``registration_access_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- librato_annotation - mark the ``api_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- logentries_msg - mark the ``token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- module_utils/_netapp, na_ontap_gather_facts - enabled ``no_log`` for the options ``api_key`` and ``secret_key`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). +- module_utils/identity/keycloak, keycloak_client, keycloak_clienttemplate, keycloak_group - enabled ``no_log`` for the option ``auth_client_secret`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). +- nios_nsgroup - mark the ``tsig_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- oneandone_firewall_policy, oneandone_load_balancer, oneandone_monitoring_policy, oneandone_private_network, oneandone_public_ip - mark the ``auth_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- ovirt - mark the ``instance_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- ovirt - mark the ``instance_rootpw`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- pagerduty_alert - mark the ``api_key``, ``service_key`` and ``integration_key`` parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- pagerduty_change - mark the ``integration_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- pingdom - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- pulp_repo - mark the ``feed_client_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- rax_clb_ssl - mark the ``private_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- redfish_command - mark the ``update_creds.password`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- rollbar_deployment - mark the ``token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- spotinst_aws_elastigroup - mark the ``multai_token`` and ``token`` parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- stackdriver - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). +- utm_proxy_auth_profile - enabled ``no_log`` for the option ``frontend_cookie_secret`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). +- utm_proxy_auth_profile - mark the ``frontend_cookie_secret`` parameter as ``no_log`` to avoid leakage of secrets. This causes the ``utm_proxy_auth_profile`` return value to no longer containing the correct value, but a placeholder (https://github.com/ansible-collections/community.general/pull/1736). + +Bugfixes +-------- + +- Mark various module options with ``no_log=False`` which have a name that potentially could leak secrets, but which do not (https://github.com/ansible-collections/community.general/pull/2001). +- aerospike_migration - fix typo that caused ``migrate_tx_key`` instead of ``migrate_rx_key`` being used (https://github.com/ansible-collections/community.general/pull/1739). +- alternatives - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- beadm - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- bigpanda - actually use the ``deployment_message`` option (https://github.com/ansible-collections/community.general/pull/1928). +- chef_databag lookup plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- cloudforms inventory - fixed issue that non-existing (archived) VMs were synced (https://github.com/ansible-collections/community.general/pull/720). +- cobbler_sync, cobbler_system - fix SSL/TLS certificate check when ``validate_certs`` set to ``false`` (https://github.com/ansible-collections/community.general/pull/1880). +- consul_io inventory script - kv_groups - fix byte chain decoding for Python 3 (https://github.com/ansible-collections/community.general/pull/620). +- cronvar - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- dconf - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- deploy_helper - allow ``state=clean`` to be used without defining a ``release`` (https://github.com/ansible-collections/community.general/issues/1852). +- dimensiondata_network - bug when formatting message, instead of % a simple comma was used (https://github.com/ansible-collections/community.general/pull/2139). +- diy callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- elasticsearch_plugin - ``state`` parameter choices must use ``list()`` in python3 (https://github.com/ansible-collections/community.general/pull/1830). +- filesystem - do not fail when ``resizefs=yes`` and ``fstype=xfs`` if there is nothing to do, even if the filesystem is not mounted. This only covers systems supporting access to unmounted XFS filesystems. Others will still fail (https://github.com/ansible-collections/community.general/issues/1457, https://github.com/ansible-collections/community.general/pull/1478). +- filesystem - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- filesystem - remove ``swap`` from list of FS supported by ``resizefs=yes`` (https://github.com/ansible-collections/community.general/issues/790). +- funcd connection plugin - can now load (https://github.com/ansible-collections/community.general/pull/2235). +- git_config - fixed scope ``file`` behaviour and added integraton test for it (https://github.com/ansible-collections/community.general/issues/2117). +- git_config - prevent ``run_command`` from expanding values (https://github.com/ansible-collections/community.general/issues/1776). +- github_repo - PyGithub bug does not allow explicit port in ``base_url``. Specifying port is not required (https://github.com/PyGithub/PyGithub/issues/1913). +- gitlab_runner - parameter ``registration_token`` was required but is used only when ``state`` is ``present`` (https://github.com/ansible-collections/community.general/issues/1714). +- gitlab_user - make updates to the ``isadmin``, ``password`` and ``confirm`` options of an already existing GitLab user work (https://github.com/ansible-collections/community.general/pull/1724). +- haproxy - fix a bug preventing haproxy from properly entering ``DRAIN`` mode (https://github.com/ansible-collections/community.general/issues/1913). +- hiera lookup plugin - converts the return type of plugin to unicode string (https://github.com/ansible-collections/community.general/pull/2329). +- hipchat - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- idrac_redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- idrac_redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- idrac_redfish_info - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- imc_rest - explicitly logging out instead of registering the call in ```atexit``` (https://github.com/ansible-collections/community.general/issues/1735). +- influxdb_retention_policy - ensure idempotent module execution with different duration and shard duration parameter values (https://github.com/ansible-collections/community.general/issues/2281). +- infoblox inventory script - make sure that the script also works with Ansible 2.9, and returns a more helpful error when community.general is not installed as part of Ansible 2.10/3 (https://github.com/ansible-collections/community.general/pull/1871). +- ini_file - allows an empty string as a value for an option (https://github.com/ansible-collections/community.general/pull/1972). +- interfaces_file - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- ipa_user - allow ``sshpubkey`` to permit multiple word comments (https://github.com/ansible-collections/community.general/pull/2159). +- iso_extract - use proper alias deprecation mechanism for ``thirsty`` alias of ``force`` (https://github.com/ansible-collections/community.general/pull/1830). +- java_cert - allow setting ``state: absent`` by providing just the ``cert_alias`` (https://github.com/ansible/ansible/issues/27982). +- java_cert - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- java_cert - properly handle proxy arguments when the scheme is provided (https://github.com/ansible/ansible/issues/54481). +- java_keystore - improve error handling and return ``cmd`` as documented. Force ``LANG``, ``LC_ALL`` and ``LC_MESSAGES`` environment variables to ``C`` to rely on ``keytool`` output parsing. Fix pylint's ``unused-variable`` and ``no-else-return`` hints (https://github.com/ansible-collections/community.general/pull/2183). +- java_keystore - use tempfile lib to create temporary files with randomized names, and remove the temporary PKCS#12 keystore as well as other materials (https://github.com/ansible-collections/community.general/issues/1667). +- jenkins_plugin - fixes Python 2 compatibility issue (https://github.com/ansible-collections/community.general/pull/2340). +- jira - fixed calling of ``isinstance`` (https://github.com/ansible-collections/community.general/issues/2234). +- jira - fixed error when loading base64-encoded content as attachment (https://github.com/ansible-collections/community.general/pull/2349). +- jira - fixed fields' update in ticket transitions (https://github.com/ansible-collections/community.general/issues/818). +- kibana_plugin - ``state`` parameter choices must use ``list()`` in python3 (https://github.com/ansible-collections/community.general/pull/1830). +- kibana_plugin - added missing parameters to ``remove_plugin`` when using ``state=present force=true``, and fix potential quoting errors when invoking ``kibana`` (https://github.com/ansible-collections/community.general/pull/2143). +- logstash_plugin - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` setting (https://github.com/ansible-collections/community.general/pull/1830). +- lvg - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- lvol - fixed sizing calculation rounding to match the underlying tools (https://github.com/ansible-collections/community.general/issues/1988). +- lvol - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- lxc - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- lxc_container - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- lxc_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- lxd_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- manageiq_provider - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` setting (https://github.com/ansible-collections/community.general/pull/1970). +- memcached cache plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- meta/runtime.yml - improve deprecation messages (https://github.com/ansible-collections/community.general/pull/1918). +- module_helper module utils - actually ignoring formatting of parameters with value ``None`` (https://github.com/ansible-collections/community.general/pull/2024). +- module_helper module utils - fixed decorator ``cause_changes`` (https://github.com/ansible-collections/community.general/pull/2203). +- module_helper module utils - handling ``ModuleHelperException`` now properly calls ``fail_json()`` (https://github.com/ansible-collections/community.general/pull/2024). +- module_helper module utils - use the command name as-is in ``CmdMixin`` if it fails ``get_bin_path()`` - allowing full path names to be passed (https://github.com/ansible-collections/community.general/pull/2024). +- net_tools.nios.api module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- nios* modules - fix modules to work with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/2057). +- nios_host_record - allow DNS Bypass for views other than default (https://github.com/ansible-collections/community.general/issues/1786). +- nmap inventory plugin - fix cache and constructed group support (https://github.com/ansible-collections/community.general/issues/2242). +- nmcli - add ``method4`` and ``method6`` options (https://github.com/ansible-collections/community.general/pull/1894). +- nmcli - ensure the ``slave-type`` option is passed to ``nmcli`` for type ``bond-slave`` (https://github.com/ansible-collections/community.general/pull/1882). +- nomad_job_info - fix module failure when nomad client returns no jobs (https://github.com/ansible-collections/community.general/pull/1721). +- nsot inventory script - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- oci_vcn - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- oneandone_monitoring_policy - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- packet_volume_attachment - removed extraneous ``print`` call - old debug? (https://github.com/ansible-collections/community.general/pull/1970). +- parted - change the regex that decodes the partition size to better support different formats that parted uses. Change the regex that validates parted's version string (https://github.com/ansible-collections/community.general/pull/1695). +- parted - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- pkgutil - fixed calls to ``list.extend()`` (https://github.com/ansible-collections/community.general/pull/2161). +- proxmox - removed requirement that root password is provided when containter state is ``present`` (https://github.com/ansible-collections/community.general/pull/1999). +- proxmox inventory - added handling of commas in KVM agent configuration string (https://github.com/ansible-collections/community.general/pull/2245). +- proxmox inventory - added handling of extra trailing slashes in the URL (https://github.com/ansible-collections/community.general/pull/1914). +- proxmox inventory - exclude qemu templates from inclusion to the inventory via pools (https://github.com/ansible-collections/community.general/issues/1986, https://github.com/ansible-collections/community.general/pull/1991). +- proxmox inventory plugin - allowed proxomox tag string to contain commas when returned as fact (https://github.com/ansible-collections/community.general/pull/1949). +- proxmox inventory plugin - support network interfaces without IP addresses, multiple network interfaces and unsupported/commanddisabled guest error (https://github.com/ansible-collections/community.general/pull/2263). +- proxmox lxc - only add the features flag when module parameter ``features`` is set. Before an empty string was send to proxmox in case the parameter was not used, which required to use ``root@pam`` for module execution (https://github.com/ansible-collections/community.general/pull/1763). +- proxmox* modules - refactored some parameter validation code into use of ``env_fallback``, ``required_if``, ``required_together``, ``required_one_of`` (https://github.com/ansible-collections/community.general/pull/1765). +- proxmox_kvm - do not add ``args`` if ``proxmox_default_behavior`` is set to no_defaults (https://github.com/ansible-collections/community.general/issues/1641). +- proxmox_kvm - fix parameter ``vmid`` passed twice to ``exit_json`` while creating a virtual machine without cloning (https://github.com/ansible-collections/community.general/issues/1875, https://github.com/ansible-collections/community.general/pull/1895). +- proxmox_kvm - fix undefined local variable ``status`` when the parameter ``state`` is either ``stopped``, ``started``, ``restarted`` or ``absent`` (https://github.com/ansible-collections/community.general/pull/1847). +- proxmox_kvm - stop implicitly adding ``force`` equal to ``false``. Proxmox API requires not implemented parameters otherwise, and assumes ``force`` to be ``false`` by default anyways (https://github.com/ansible-collections/community.general/pull/1783). +- redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- redfish_config module, redfish_utils module utils - fix IndexError in ``SetManagerNic`` command (https://github.com/ansible-collections/community.general/issues/1692). +- redfish_info module, redfish_utils module utils - add ``Name`` and ``Id`` properties to output of Redfish inventory commands (https://github.com/ansible-collections/community.general/issues/1650). +- redhat_subscription - ``mutually_exclusive`` was referring to parameter alias instead of name (https://github.com/ansible-collections/community.general/pull/1795). +- redhat_subscription - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- redis cache plugin - wrapped usages of ``keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- riak - parameters ``wait_for_handoffs`` and ``wait_for_ring`` are ``int`` but the default value was ``false`` (https://github.com/ansible-collections/community.general/pull/1830). +- rundeck_acl_policy - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- runit - removed unused code, and passing command as ``list`` instead of ``str`` to ``run_command()`` (https://github.com/ansible-collections/community.general/pull/1830). +- scaleway inventory plugin - fix pagination on scaleway inventory plugin (https://github.com/ansible-collections/community.general/pull/2036). +- selective callback plugin - adjust import so that the plugin also works with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/1807). +- selective callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- sensu-silence module - fix json parsing of sensu API responses on Python 3.5 (https://github.com/ansible-collections/community.general/pull/1703). +- sensu_check - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- spotinst_aws_elastigroup - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- stacki_host - replaced ``default`` to environment variables with ``fallback`` to them (https://github.com/ansible-collections/community.general/pull/2072). +- statusio_maintenance - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- terraform - fix issue that cause the destroy to fail because from Terraform 0.15 on, the ``terraform destroy -force`` option is replaced with ``terraform destroy -auto-approve`` (https://github.com/ansible-collections/community.general/issues/2247). +- terraform - fix issue that cause the execution fail because from Terraform 0.15 on, the ``-var`` and ``-var-file`` options are no longer available on ``terraform validate`` (https://github.com/ansible-collections/community.general/pull/2246). +- terraform - remove uses of ``use_unsafe_shell=True`` (https://github.com/ansible-collections/community.general/pull/2246). +- timezone - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819). +- udm_dns_record - fixed default value of parameter ``data`` to match its type (https://github.com/ansible-collections/community.general/pull/2268). +- utm_utils module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- vdo - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). +- vmadm - correct type of list elements in ``resolvers`` parameter (https://github.com/ansible-collections/community.general/issues/2135). +- xfconf - module was not honoring check mode when ``state`` was ``absent`` (https://github.com/ansible-collections/community.general/pull/2185). +- xfs_quota - the feedback for initializing project quota using xfs_quota binary from ``xfsprogs`` has changed since the version it was written for (https://github.com/ansible-collections/community.general/pull/1596). +- zfs - some ZFS properties could be passed when the dataset/volume did not exist, but would fail if the dataset already existed, even if the property matched what was specified in the ansible task (https://github.com/ansible-collections/community.general/issues/868, https://github.com/ansible-collections/community.general/pull/1833). +- zfs_delegate_admin - the elements of ``users``, ``groups`` and ``permissions`` are now enforced to be strings (https://github.com/ansible-collections/community.general/pull/1766). +- zypper, zypper_repository - respect ``PATH`` environment variable when resolving zypper executable path (https://github.com/ansible-collections/community.general/pull/2094). + +New Plugins +----------- + +Become +~~~~~~ + +- sudosu - Run tasks using sudo su - + +Callback +~~~~~~~~ + +- loganalytics - Posts task results to Azure Log Analytics + +Filter +~~~~~~ + +- dict - The ``dict`` function as a filter: converts a list of tuples to a dictionary +- from_csv - Converts CSV text input into list of dicts +- hashids_decode - Decodes a sequence of numbers from a YouTube-like hash +- hashids_encode - Encodes YouTube-like hashes from a sequence of integers +- path_join - Redirects to ansible.builtin.path_join for ansible-base 2.10 or newer, and provides a compatible implementation for Ansible 2.9 +- version_sort - Sort a list according to version order instead of pure alphabetical one + +Inventory +~~~~~~~~~ + +- lxd - Returns Ansible inventory from lxd host + +New Modules +----------- + +Cloud +~~~~~ + +misc +^^^^ + +- proxmox_storage_info - Retrieve information about one or more Proxmox VE storages + +opennebula +^^^^^^^^^^ + +- one_template - Manages OpenNebula templates + +Files +~~~~~ + +- filesize - Create a file with a given size, or resize it if it exists + +Identity +~~~~~~~~ + +ipa +^^^ + +- ipa_otpconfig - Manage FreeIPA OTP Configuration Settings +- ipa_otptoken - Manage FreeIPA OTPs + +keycloak +^^^^^^^^ + +- keycloak_realm - Allows administration of Keycloak realm via Keycloak API + +Monitoring +~~~~~~~~~~ + +- spectrum_model_attrs - Enforce a model's attributes in CA Spectrum. +- statsd - Send metrics to StatsD + +Net Tools +~~~~~~~~~ + +- gandi_livedns - Manage Gandi LiveDNS records + +pritunl +^^^^^^^ + +- pritunl_org - Manages Pritunl Organizations using the Pritunl API +- pritunl_org_info - List Pritunl Organizations using the Pritunl API +- pritunl_user - Manage Pritunl Users using the Pritunl API +- pritunl_user_info - List Pritunl Users using the Pritunl API + +Remote Management +~~~~~~~~~~~~~~~~~ + +lenovoxcc +^^^^^^^^^ + +- xcc_redfish_command - Manages Lenovo Out-Of-Band controllers using Redfish APIs + +Source Control +~~~~~~~~~~~~~~ + +github +^^^^^^ + +- github_repo - Manage your repositories on Github + +gitlab +^^^^^^ + +- gitlab_project_members - Manage project members on GitLab Server + +Web Infrastructure +~~~~~~~~~~~~~~~~~~ + +- jenkins_build - Manage jenkins builds diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..959d363236 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing + +We follow [Ansible Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) in all our contributions and interactions within this repository. + +If you are a committer, also refer to the [collection's committer guidelines](https://github.com/ansible-collections/community.general/blob/main/commit-rights.md). + +## Issue tracker + +Whether you are looking for an opportunity to contribute or you found a bug and already know how to solve it, please go to the [issue tracker](https://github.com/ansible-collections/community.general/issues). +There you can find feature ideas to implement, reports about bugs to solve, or submit an issue to discuss your idea before implementing it which can help choose a right direction at the beginning of your work and potentially save a lot of time and effort. +Also somebody may already have started discussing or working on implementing the same or a similar idea, +so you can cooperate to create a better solution together. + +* If you are interested in starting with an easy issue, look for [issues with an `easyfix` label](https://github.com/ansible-collections/community.general/labels/easyfix). +* Often issues that are waiting for contributors to pick up have [the `waiting_on_contributor` label](https://github.com/ansible-collections/community.general/labels/waiting_on_contributor). + +## Open pull requests + +Look through currently [open pull requests](https://github.com/ansible-collections/community.general/pulls). +You can help by reviewing them. Reviews help move pull requests to merge state. Some good pull requests cannot be merged only due to a lack of reviews. And it is always worth saying that good reviews are often more valuable than pull requests themselves. +Note that reviewing does not only mean code review, but also offering comments on new interfaces added to existing plugins/modules, interfaces of new plugins/modules, improving language (not everyone is a native english speaker), or testing bugfixes and new features! + +Also, consider taking up a valuable, reviewed, but abandoned pull request which you could politely ask the original authors to complete yourself. + +* Try committing your changes with an informative but short commit message. +* All commits of a pull request branch will be squashed into one commit at last. That does not mean you must have only one commit on your pull request, though! +* Please try not to force-push if it is not needed, so reviewers and other users looking at your pull request later can see the pull request commit history. +* 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 respository checkout. + +You can also read [our Quick-start development guide](https://github.com/ansible/community-docs/blob/main/create_pr_quick_start_guide.rst). + +If you find any inconsistencies or places in this document which can be improved, feel free to raise an issue or pull request to fix it. diff --git a/README.md b/README.md index 935f0ecabd..deab9071a7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # 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-3)](https://dev.azure.com/ansible/community.general/_build?definitionId=31) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.general)](https://codecov.io/gh/ansible-collections/community.general) This repo contains the `community.general` Ansible Collection. The collection includes many modules and plugins supported by Ansible community which are not part of more specialized community collections. You can find [documentation for this collection on the Ansible docs site](https://docs.ansible.com/ansible/latest/collections/community/general/). +Please note that this collection does **not** support Windows targets. Only connection plugins included in this collection might support Windows targets, and will explicitly mention that in their documentation if they do so. + ## Tested with Ansible Tested with the current Ansible 2.9, ansible-base 2.10 and ansible-core 2.11 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported. @@ -48,6 +50,8 @@ export COLLECTIONS_PATH=$(pwd)/collections:$COLLECTIONS_PATH 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). + ### Running tests See [here](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#testing-collections). @@ -56,10 +60,10 @@ See [here](https://docs.ansible.com/ansible/devel/dev_guide/developing_collectio We have a dedicated Working Group for Ansible development. -You can find other people interested on the following Freenode IRC channels - +You can find other people interested on the following [Libera.chat](https://libera.chat/) IRC channels - - `#ansible` - For general use questions and support. -- `#ansible-devel` - For discussions on developer topics and code related to features or bugs. -- `#ansible-community` - For discussions on community topics and community meetings. +- `#ansible-devel` - For discussions on developer topics and code related to features or bugs in ansible-core. +- `#ansible-community` - For discussions on community topics and community meetings, and for general development questions for community collections. For more information about communities, meetings and agendas see [Community Wiki](https://github.com/ansible/community/wiki/Community). @@ -76,7 +80,7 @@ Basic instructions without release branches: ## 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-3/CHANGELOG.rst). ## Roadmap diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index e78468a3ca..b4eb74fac4 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -1,2 +1,1288 @@ ancestor: 2.0.0 -releases: {} +releases: + 3.0.0: + changes: + breaking_changes: + - 'If you use Ansible 2.9 and these plugins or modules from this collection, + community.general 3.0.0 results in errors when trying to use the DellEMC content + by FQCN, like ``community.general.idrac_firmware``. + + Since Ansible 2.9 is not able to use redirections, you will have to adjust + your playbooks and roles manually to use the new FQCNs (``dellemc.openmanage.idrac_firmware`` + for the previous example) and to make sure that you have ``dellemc.openmanage`` + installed. + + + If you use ansible-base 2.10 or newer and did not install Ansible 4.0.0, but + installed (and/or upgraded) community.general manually, you need to make sure + to also install the ``dellemc.openmanage`` collection if you are using any + of these plugins or modules. + + While ansible-base 2.10 or newer can use the redirects that community.general + 3.0.0 adds, the collection they point to (such as dellemc.openmanage) must + be installed for them to work. + + ' + - gitlab_deploy_key - if for an already existing key title a different public + key was given as parameter nothing happened, now this changed so that the + public key is updated to the new value (https://github.com/ansible-collections/community.general/pull/1661). + - java_keystore - instead of failing, now overwrites keystore if the alias (name) + is changed. This was originally the intended behavior, but did not work due + to a logic error. Make sure that your playbooks and roles do not depend on + the old behavior of failing instead of overwriting (https://github.com/ansible-collections/community.general/issues/1671). + - java_keystore - instead of failing, now overwrites keystore if the passphrase + is changed. Make sure that your playbooks and roles do not depend on the old + behavior of failing instead of overwriting (https://github.com/ansible-collections/community.general/issues/1671). + - one_image - use pyone instead of python-oca (https://github.com/ansible-collections/community.general/pull/2032). + - utm_proxy_auth_profile - the ``frontend_cookie_secret`` return value now contains + a placeholder string instead of the module's ``frontend_cookie_secret`` parameter + (https://github.com/ansible-collections/community.general/pull/1736). + bugfixes: + - Mark various module options with ``no_log=False`` which have a name that potentially + could leak secrets, but which do not (https://github.com/ansible-collections/community.general/pull/2001). + - aerospike_migration - fix typo that caused ``migrate_tx_key`` instead of ``migrate_rx_key`` + being used (https://github.com/ansible-collections/community.general/pull/1739). + - 'alternatives - internal refactoring: replaced uses of ``_`` with ``dummy`` + (https://github.com/ansible-collections/community.general/pull/1819).' + - 'beadm - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - bigpanda - actually use the ``deployment_message`` option (https://github.com/ansible-collections/community.general/pull/1928). + - chef_databag lookup plugin - wrapped usages of ``dict.keys()`` in ``list()`` + for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - cloudforms inventory - fixed issue that non-existing (archived) VMs were synced + (https://github.com/ansible-collections/community.general/pull/720). + - cobbler_sync, cobbler_system - fix SSL/TLS certificate check when ``validate_certs`` + set to ``false`` (https://github.com/ansible-collections/community.general/pull/1880). + - consul_io inventory script - kv_groups - fix byte chain decoding for Python + 3 (https://github.com/ansible-collections/community.general/pull/620). + - 'cronvar - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - 'dconf - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - deploy_helper - allow ``state=clean`` to be used without defining a ``release`` + (https://github.com/ansible-collections/community.general/issues/1852). + - dimensiondata_network - bug when formatting message, instead of % a simple + comma was used (https://github.com/ansible-collections/community.general/pull/2139). + - diy callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - elasticsearch_plugin - ``state`` parameter choices must use ``list()`` in + python3 (https://github.com/ansible-collections/community.general/pull/1830). + - filesystem - do not fail when ``resizefs=yes`` and ``fstype=xfs`` if there + is nothing to do, even if the filesystem is not mounted. This only covers + systems supporting access to unmounted XFS filesystems. Others will still + fail (https://github.com/ansible-collections/community.general/issues/1457, + https://github.com/ansible-collections/community.general/pull/1478). + - 'filesystem - internal refactoring: replaced uses of ``_`` with ``dummy`` + (https://github.com/ansible-collections/community.general/pull/1819).' + - filesystem - remove ``swap`` from list of FS supported by ``resizefs=yes`` + (https://github.com/ansible-collections/community.general/issues/790). + - funcd connection plugin - can now load (https://github.com/ansible-collections/community.general/pull/2235). + - git_config - fixed scope ``file`` behaviour and added integraton test for + it (https://github.com/ansible-collections/community.general/issues/2117). + - git_config - prevent ``run_command`` from expanding values (https://github.com/ansible-collections/community.general/issues/1776). + - github_repo - PyGithub bug does not allow explicit port in ``base_url``. Specifying + port is not required (https://github.com/PyGithub/PyGithub/issues/1913). + - gitlab_runner - parameter ``registration_token`` was required but is used + only when ``state`` is ``present`` (https://github.com/ansible-collections/community.general/issues/1714). + - gitlab_user - make updates to the ``isadmin``, ``password`` and ``confirm`` + options of an already existing GitLab user work (https://github.com/ansible-collections/community.general/pull/1724). + - haproxy - fix a bug preventing haproxy from properly entering ``DRAIN`` mode + (https://github.com/ansible-collections/community.general/issues/1913). + - hiera lookup plugin - converts the return type of plugin to unicode string + (https://github.com/ansible-collections/community.general/pull/2329). + - 'hipchat - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - idrac_redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - idrac_redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - idrac_redfish_info - wrapped usages of ``dict.keys()`` in ``list()`` for Python + 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - imc_rest - explicitly logging out instead of registering the call in ```atexit``` + (https://github.com/ansible-collections/community.general/issues/1735). + - influxdb_retention_policy - ensure idempotent module execution with different + duration and shard duration parameter values (https://github.com/ansible-collections/community.general/issues/2281). + - infoblox inventory script - make sure that the script also works with Ansible + 2.9, and returns a more helpful error when community.general is not installed + as part of Ansible 2.10/3 (https://github.com/ansible-collections/community.general/pull/1871). + - ini_file - allows an empty string as a value for an option (https://github.com/ansible-collections/community.general/pull/1972). + - 'interfaces_file - internal refactoring: replaced uses of ``_`` with ``dummy`` + (https://github.com/ansible-collections/community.general/pull/1819).' + - ipa_user - allow ``sshpubkey`` to permit multiple word comments (https://github.com/ansible-collections/community.general/pull/2159). + - iso_extract - use proper alias deprecation mechanism for ``thirsty`` alias + of ``force`` (https://github.com/ansible-collections/community.general/pull/1830). + - 'java_cert - allow setting ``state: absent`` by providing just the ``cert_alias`` + (https://github.com/ansible/ansible/issues/27982).' + - 'java_cert - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - java_cert - properly handle proxy arguments when the scheme is provided (https://github.com/ansible/ansible/issues/54481). + - java_keystore - improve error handling and return ``cmd`` as documented. Force + ``LANG``, ``LC_ALL`` and ``LC_MESSAGES`` environment variables to ``C`` to + rely on ``keytool`` output parsing. Fix pylint's ``unused-variable`` and ``no-else-return`` + hints (https://github.com/ansible-collections/community.general/pull/2183). + - java_keystore - use tempfile lib to create temporary files with randomized + names, and remove the temporary PKCS#12 keystore as well as other materials + (https://github.com/ansible-collections/community.general/issues/1667). + - jenkins_plugin - fixes Python 2 compatibility issue (https://github.com/ansible-collections/community.general/pull/2340). + - jira - fixed calling of ``isinstance`` (https://github.com/ansible-collections/community.general/issues/2234). + - jira - fixed error when loading base64-encoded content as attachment (https://github.com/ansible-collections/community.general/pull/2349). + - jira - fixed fields' update in ticket transitions (https://github.com/ansible-collections/community.general/issues/818). + - kibana_plugin - ``state`` parameter choices must use ``list()`` in python3 + (https://github.com/ansible-collections/community.general/pull/1830). + - kibana_plugin - added missing parameters to ``remove_plugin`` when using ``state=present + force=true``, and fix potential quoting errors when invoking ``kibana`` (https://github.com/ansible-collections/community.general/pull/2143). + - logstash_plugin - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` + setting (https://github.com/ansible-collections/community.general/pull/1830). + - 'lvg - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - lvol - fixed sizing calculation rounding to match the underlying tools (https://github.com/ansible-collections/community.general/issues/1988). + - 'lvol - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - 'lxc - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - 'lxc_container - internal refactoring: replaced uses of ``_`` with ``dummy`` + (https://github.com/ansible-collections/community.general/pull/1819).' + - lxc_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python + 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - lxd_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python + 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - manageiq_provider - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` + setting (https://github.com/ansible-collections/community.general/pull/1970). + - memcached cache plugin - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - meta/runtime.yml - improve deprecation messages (https://github.com/ansible-collections/community.general/pull/1918). + - module_helper module utils - actually ignoring formatting of parameters with + value ``None`` (https://github.com/ansible-collections/community.general/pull/2024). + - module_helper module utils - fixed decorator ``cause_changes`` (https://github.com/ansible-collections/community.general/pull/2203). + - module_helper module utils - handling ``ModuleHelperException`` now properly + calls ``fail_json()`` (https://github.com/ansible-collections/community.general/pull/2024). + - module_helper module utils - use the command name as-is in ``CmdMixin`` if + it fails ``get_bin_path()`` - allowing full path names to be passed (https://github.com/ansible-collections/community.general/pull/2024). + - net_tools.nios.api module_utils - wrapped usages of ``dict.keys()`` in ``list()`` + for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - nios* modules - fix modules to work with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/2057). + - nios_host_record - allow DNS Bypass for views other than default (https://github.com/ansible-collections/community.general/issues/1786). + - nmap inventory plugin - fix cache and constructed group support (https://github.com/ansible-collections/community.general/issues/2242). + - nmcli - add ``method4`` and ``method6`` options (https://github.com/ansible-collections/community.general/pull/1894). + - nmcli - ensure the ``slave-type`` option is passed to ``nmcli`` for type ``bond-slave`` + (https://github.com/ansible-collections/community.general/pull/1882). + - nomad_job_info - fix module failure when nomad client returns no jobs (https://github.com/ansible-collections/community.general/pull/1721). + - nsot inventory script - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - oci_vcn - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility + (https://github.com/ansible-collections/community.general/pull/1861). + - oneandone_monitoring_policy - wrapped usages of ``dict.keys()`` in ``list()`` + for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - packet_volume_attachment - removed extraneous ``print`` call - old debug? + (https://github.com/ansible-collections/community.general/pull/1970). + - parted - change the regex that decodes the partition size to better support + different formats that parted uses. Change the regex that validates parted's + version string (https://github.com/ansible-collections/community.general/pull/1695). + - 'parted - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - pkgutil - fixed calls to ``list.extend()`` (https://github.com/ansible-collections/community.general/pull/2161). + - proxmox - removed requirement that root password is provided when containter + state is ``present`` (https://github.com/ansible-collections/community.general/pull/1999). + - proxmox inventory - added handling of commas in KVM agent configuration string + (https://github.com/ansible-collections/community.general/pull/2245). + - proxmox inventory - added handling of extra trailing slashes in the URL (https://github.com/ansible-collections/community.general/pull/1914). + - proxmox inventory - exclude qemu templates from inclusion to the inventory + via pools (https://github.com/ansible-collections/community.general/issues/1986, + https://github.com/ansible-collections/community.general/pull/1991). + - proxmox inventory plugin - allowed proxomox tag string to contain commas when + returned as fact (https://github.com/ansible-collections/community.general/pull/1949). + - proxmox inventory plugin - support network interfaces without IP addresses, + multiple network interfaces and unsupported/commanddisabled guest error (https://github.com/ansible-collections/community.general/pull/2263). + - proxmox lxc - only add the features flag when module parameter ``features`` + is set. Before an empty string was send to proxmox in case the parameter was + not used, which required to use ``root@pam`` for module execution (https://github.com/ansible-collections/community.general/pull/1763). + - proxmox* modules - refactored some parameter validation code into use of ``env_fallback``, + ``required_if``, ``required_together``, ``required_one_of`` (https://github.com/ansible-collections/community.general/pull/1765). + - proxmox_kvm - do not add ``args`` if ``proxmox_default_behavior`` is set to + no_defaults (https://github.com/ansible-collections/community.general/issues/1641). + - proxmox_kvm - fix parameter ``vmid`` passed twice to ``exit_json`` while creating + a virtual machine without cloning (https://github.com/ansible-collections/community.general/issues/1875, + https://github.com/ansible-collections/community.general/pull/1895). + - proxmox_kvm - fix undefined local variable ``status`` when the parameter ``state`` + is either ``stopped``, ``started``, ``restarted`` or ``absent`` (https://github.com/ansible-collections/community.general/pull/1847). + - proxmox_kvm - stop implicitly adding ``force`` equal to ``false``. Proxmox + API requires not implemented parameters otherwise, and assumes ``force`` to + be ``false`` by default anyways (https://github.com/ansible-collections/community.general/pull/1783). + - redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python + 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python + 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - redfish_config module, redfish_utils module utils - fix IndexError in ``SetManagerNic`` + command (https://github.com/ansible-collections/community.general/issues/1692). + - redfish_info module, redfish_utils module utils - add ``Name`` and ``Id`` + properties to output of Redfish inventory commands (https://github.com/ansible-collections/community.general/issues/1650). + - redhat_subscription - ``mutually_exclusive`` was referring to parameter alias + instead of name (https://github.com/ansible-collections/community.general/pull/1795). + - redhat_subscription - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - redis cache plugin - wrapped usages of ``keys()`` in ``list()`` for Python + 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - riak - parameters ``wait_for_handoffs`` and ``wait_for_ring`` are ``int`` + but the default value was ``false`` (https://github.com/ansible-collections/community.general/pull/1830). + - 'rundeck_acl_policy - internal refactoring: replaced uses of ``_`` with ``dummy`` + (https://github.com/ansible-collections/community.general/pull/1819).' + - runit - removed unused code, and passing command as ``list`` instead of ``str`` + to ``run_command()`` (https://github.com/ansible-collections/community.general/pull/1830). + - scaleway inventory plugin - fix pagination on scaleway inventory plugin (https://github.com/ansible-collections/community.general/pull/2036). + - selective callback plugin - adjust import so that the plugin also works with + ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/1807). + - selective callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` + for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - sensu-silence module - fix json parsing of sensu API responses on Python 3.5 + (https://github.com/ansible-collections/community.general/pull/1703). + - sensu_check - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 + compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - spotinst_aws_elastigroup - wrapped usages of ``dict.keys()`` in ``list()`` + for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - stacki_host - replaced ``default`` to environment variables with ``fallback`` + to them (https://github.com/ansible-collections/community.general/pull/2072). + - 'statusio_maintenance - internal refactoring: replaced uses of ``_`` with + ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - terraform - fix issue that cause the destroy to fail because from Terraform + 0.15 on, the ``terraform destroy -force`` option is replaced with ``terraform + destroy -auto-approve`` (https://github.com/ansible-collections/community.general/issues/2247). + - terraform - fix issue that cause the execution fail because from Terraform + 0.15 on, the ``-var`` and ``-var-file`` options are no longer available on + ``terraform validate`` (https://github.com/ansible-collections/community.general/pull/2246). + - terraform - remove uses of ``use_unsafe_shell=True`` (https://github.com/ansible-collections/community.general/pull/2246). + - 'timezone - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819).' + - udm_dns_record - fixed default value of parameter ``data`` to match its type + (https://github.com/ansible-collections/community.general/pull/2268). + - utm_utils module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for + Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). + - vdo - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility + (https://github.com/ansible-collections/community.general/pull/1861). + - vmadm - correct type of list elements in ``resolvers`` parameter (https://github.com/ansible-collections/community.general/issues/2135). + - xfconf - module was not honoring check mode when ``state`` was ``absent`` + (https://github.com/ansible-collections/community.general/pull/2185). + - xfs_quota - the feedback for initializing project quota using xfs_quota binary + from ``xfsprogs`` has changed since the version it was written for (https://github.com/ansible-collections/community.general/pull/1596). + - zfs - some ZFS properties could be passed when the dataset/volume did not + exist, but would fail if the dataset already existed, even if the property + matched what was specified in the ansible task (https://github.com/ansible-collections/community.general/issues/868, + https://github.com/ansible-collections/community.general/pull/1833). + - zfs_delegate_admin - the elements of ``users``, ``groups`` and ``permissions`` + are now enforced to be strings (https://github.com/ansible-collections/community.general/pull/1766). + - zypper, zypper_repository - respect ``PATH`` environment variable when resolving + zypper executable path (https://github.com/ansible-collections/community.general/pull/2094). + deprecated_features: + - apt_rpm - deprecated invalid parameter alias ``update-cache``, will be removed + in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - composer - deprecated invalid parameter aliases ``working-dir``, ``global-command``, + ``prefer-source``, ``prefer-dist``, ``no-dev``, ``no-scripts``, ``no-plugins``, + ``optimize-autoloader``, ``classmap-authoritative``, ``apcu-autoloader``, + ``ignore-platform-reqs``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - cpanm - parameter ``system_lib`` deprecated in favor of using ``become`` (https://github.com/ansible-collections/community.general/pull/2218). + - github_deploy_key - deprecated invalid parameter alias ``2fa_token``, will + be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - grove - the option ``message`` will be removed in community.general 4.0.0. + Use the new option ``message_content`` instead (https://github.com/ansible-collections/community.general/pull/1929). + - homebrew - deprecated invalid parameter alias ``update-brew``, will be removed + in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - homebrew_cask - deprecated invalid parameter alias ``update-brew``, will be + removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - opkg - deprecated invalid parameter alias ``update-cache``, will be removed + in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - pacman - deprecated invalid parameter alias ``update-cache``, will be removed + in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - puppet - deprecated undocumented parameter ``show_diff``, will be removed + in 7.0.0. (https://github.com/ansible-collections/community.general/pull/1927). + - runit - unused parameter ``dist`` marked for deprecation (https://github.com/ansible-collections/community.general/pull/1830). + - slackpkg - deprecated invalid parameter alias ``update-cache``, will be removed + in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - urmpi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``, + will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - xbps - deprecated invalid parameter alias ``update-cache``, will be removed + in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). + - xfconf - returning output as facts is deprecated, this will be removed in + community.general 4.0.0. Please register the task output in a variable and + use it instead. You can already switch to the new behavior now by using the + new ``disable_facts`` option (https://github.com/ansible-collections/community.general/pull/1747). + minor_changes: + - apache2_mod_proxy - refactored/cleaned-up part of the code (https://github.com/ansible-collections/community.general/pull/2142). + - archive - refactored some reused code out into a couple of functions (https://github.com/ansible-collections/community.general/pull/2061). + - atomic_container - using ``get_bin_path()`` before calling ``run_command()`` + (https://github.com/ansible-collections/community.general/pull/2144). + - atomic_host - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). + - atomic_image - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). + - beadm - minor refactor converting multiple statements to a single list literal + (https://github.com/ansible-collections/community.general/pull/2160). + - bitbucket_pipeline_variable - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). + - bundler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - clc_* modules - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1771). + - consul - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - consul_acl - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - consul_io inventory script - conf options - allow custom configuration options + via env variables (https://github.com/ansible-collections/community.general/pull/620). + - consul_session - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - cpanm - honor and install specified version when running in ``new`` mode; + that feature is not available in ``compatibility`` mode (https://github.com/ansible-collections/community.general/issues/208). + - cpanm - rewritten using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2218). + - csv module utils - new module_utils for shared functions between ``from_csv`` + filter and ``read_csv`` module (https://github.com/ansible-collections/community.general/pull/2037). + - datadog_monitor - add missing monitor types ``query alert``, ``trace-analytics + alert``, ``rum alert`` (https://github.com/ansible-collections/community.general/pull/1723). + - datadog_monitor - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - dnsimple - add CAA records to the whitelist of valid record types (https://github.com/ansible-collections/community.general/pull/1814). + - dnsimple - elements of list parameters ``record_ids`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - gitlab_deploy_key - when the given key title already exists but has a different + public key, the public key will now be updated to given value (https://github.com/ansible-collections/community.general/pull/1661). + - gitlab_runner - elements of list parameters ``tag_list`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - grove - the option ``message`` has been renamed to ``message_content``. The + old name ``message`` is kept as an alias and will be removed for community.general + 4.0.0. This was done because ``message`` is used internally by Ansible (https://github.com/ansible-collections/community.general/pull/1929). + - heroku_collaborator - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - hiera lookup - minor refactor converting multiple statements to a single list + literal (https://github.com/ansible-collections/community.general/pull/2160). + - homebrew_tap - add support to specify search path for ``brew`` executable + (https://github.com/ansible-collections/community.general/issues/1702). + - ipa_config - add new options ``ipaconfigstring``, ``ipadefaultprimarygroup``, + ``ipagroupsearchfields``, ``ipahomesrootdir``, ``ipabrkauthzdata``, ``ipamaxusernamelength``, + ``ipapwdexpadvnotify``, ``ipasearchrecordslimit``, ``ipasearchtimelimit``, + ``ipauserauthtype``, and ``ipausersearchfields`` (https://github.com/ansible-collections/community.general/pull/2116). + - ipa_sudorule - add support for setting sudo runasuser (https://github.com/ansible-collections/community.general/pull/2031). + - ipa_user - fix ``userauthtype`` option to take in list of strings for the + multi-select field instead of single string (https://github.com/ansible-collections/community.general/pull/2174). + - ipwcli_dns - minor refactor converting multiple statements to a single list + literal (https://github.com/ansible-collections/community.general/pull/2160). + - 'java_cert - change ``state: present`` to check certificates by hash, not + just alias name (https://github.com/ansible/ansible/issues/43249).' + - java_keystore - add options ``certificate_path`` and ``private_key_path``, + mutually exclusive with ``certificate`` and ``private_key`` respectively, + and targetting files on remote hosts rather than their contents on the controller. + (https://github.com/ansible-collections/community.general/issues/1669). + - jenkins_job - add a ``validate_certs`` parameter that allows disabling TLS/SSL + certificate validation (https://github.com/ansible-collections/community.general/issues/255). + - jira - added ``attach`` operation, which allows a user to attach a file to + an issue (https://github.com/ansible-collections/community.general/pull/2192). + - jira - added parameter ``account_id`` for compatibility with recent versions + of JIRA (https://github.com/ansible-collections/community.general/issues/818, + https://github.com/ansible-collections/community.general/pull/1978). + - jira - revamped the module as a class using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2208). + - keycloak_* modules - allow the keycloak modules to use a token for the authentication, + the modules can take either a token or the credentials (https://github.com/ansible-collections/community.general/pull/2250). + - keycloak_client - elements of list parameters ``default_roles``, ``redirect_uris``, + ``web_origins`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - kibana_plugin - add parameter for passing ``--allow-root`` flag to kibana + and kibana-plugin commands (https://github.com/ansible-collections/community.general/pull/2014). + - known_hosts module utils - minor refactor converting multiple statements to + a single list literal (https://github.com/ansible-collections/community.general/pull/2160). + - librato_annotation - elements of list parameters ``links`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - linode_v4 - add support for ``private_ip`` option (https://github.com/ansible-collections/community.general/pull/2249). + - linode_v4 - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - lvol - added proper support for ``+-`` options when extending or reducing + the logical volume (https://github.com/ansible-collections/community.general/issues/1988). + - lxd_container - ``client_key`` and ``client_cert`` are now of type ``path`` + and no longer ``str``. A side effect is that certain expansions are made, + like ``~`` is replaced by the user's home directory, and environment variables + like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741). + - lxd_container - elements of list parameter ``profiles`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - lxd_profile - ``client_key`` and ``client_cert`` are now of type ``path`` + and no longer ``str``. A side effect is that certain expansions are made, + like ``~`` is replaced by the user's home directory, and environment variables + like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741). + - lxd_profile - added ``merge_profile`` parameter to merge configurations from + the play to an existing profile (https://github.com/ansible-collections/community.general/pull/1813). + - mail - elements of list parameters ``to``, ``cc``, ``bcc``, ``attach``, ``headers`` + are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - manageiq_alert_profiles - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - manageiq_policies - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - manageiq_tags - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - manageiq_tags and manageiq_policies - added new parameter ``resource_id``. + This parameter can be used instead of parameter ``resource_name`` (https://github.com/ansible-collections/community.general/pull/719). + - module_helper module utils - ``CmdMixin.run_command()`` now accepts ``dict`` + command arguments, providing the parameter and its value (https://github.com/ansible-collections/community.general/pull/1867). + - module_helper module utils - added management of facts and adhoc setting of + the initial value for variables (https://github.com/ansible-collections/community.general/pull/2188). + - module_helper module utils - added mechanism to manage variables, providing + automatic output of variables, change status and diff information (https://github.com/ansible-collections/community.general/pull/2162). + - na_ontap_gather_facts - elements of list parameters ``gather_subset`` are + now validated (https://github.com/ansible-collections/community.general/pull/1795). + - nexmo - elements of list parameters ``dest`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - nictagadm - minor refactor converting multiple statements to a single list + literal (https://github.com/ansible-collections/community.general/pull/2160). + - nmcli - add ability to connect to a Wifi network and also to attach it to + a master (bond) (https://github.com/ansible-collections/community.general/pull/2220). + - nmcli - do not set IP configuration on slave connection (https://github.com/ansible-collections/community.general/pull/2223). + - nmcli - don't restrict the ability to manually set the MAC address to the + bridge (https://github.com/ansible-collections/community.general/pull/2224). + - npm - add ``no_bin_links`` option (https://github.com/ansible-collections/community.general/issues/2128). + - nsupdate - elements of list parameters ``value`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - oci_vcn - ``api_user_key_file`` is now of type ``path`` and no longer ``str``. + A side effect is that certain expansions are made, like ``~`` is replaced + by the user's home directory, and environment variables like ``$HOME`` or + ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741). + - omapi_host - elements of list parameters ``statements`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - one_host - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - one_image_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - one_vm - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - oneandone_firewall_policy - elements of list parameters are now validated + (https://github.com/ansible-collections/community.general/pull/1885). + - oneandone_load_balancer - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - oneandone_monitoring_policy - elements of list parameters are now validated + (https://github.com/ansible-collections/community.general/pull/1885). + - oneandone_private_network - elements of list parameters are now validated + (https://github.com/ansible-collections/community.general/pull/1885). + - oneandone_server - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - onepassword_info - elements of list parameters ``search_terms`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - oneview_datacenter_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - oneview_enclosure_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - oneview_ethernet_network_info - elements of list parameters are now validated + (https://github.com/ansible-collections/community.general/pull/1970). + - oneview_network_set_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - ovh_ip_failover - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). + - packet_device - elements of list parameters ``device_ids``, ``hostnames`` + are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - pagerduty - elements of list parameters ``service`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - pids - new options ``pattern`` and `ignore_case`` for retrieving PIDs of + processes matching a supplied pattern (https://github.com/ansible-collections/community.general/pull/2280). + - plugins/module_utils/oracle/oci_utils.py - elements of list parameter ``key_by`` + are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - profitbricks - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - profitbricks_volume - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - proxmox - added ``purge`` module parameter for use when deleting lxc's with + HA options (https://github.com/ansible-collections/community.general/pull/2013). + - proxmox inventory plugin - added ``Constructable`` class to the inventory + to provide options ``strict``, ``keyed_groups``, ``groups``, and ``compose`` + (https://github.com/ansible-collections/community.general/pull/2180). + - proxmox inventory plugin - added ``proxmox_agent_interfaces`` fact describing + network interfaces returned from a QEMU guest agent (https://github.com/ansible-collections/community.general/pull/2148). + - proxmox inventory plugin - added ``tags_parsed`` fact containing tags parsed + as a list (https://github.com/ansible-collections/community.general/pull/1949). + - proxmox inventory plugin - allow to select whether ``ansible_host`` should + be set for the proxmox nodes (https://github.com/ansible-collections/community.general/pull/2263). + - proxmox_kvm - added new module parameter ``tags`` for use with PVE 6+ (https://github.com/ansible-collections/community.general/pull/2000). + - proxmox_kvm module - actually implemented ``vmid`` and ``status`` return values. + Updated documentation to reflect current situation (https://github.com/ansible-collections/community.general/issues/1410, + https://github.com/ansible-collections/community.general/pull/1715). + - pubnub_blocks - elements of list parameters ``event_handlers`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - rax - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). + - rax_cdb_user - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). + - rax_scaling_group - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). + - read_csv - refactored read_csv module to use shared csv functions from csv + module_utils (https://github.com/ansible-collections/community.general/pull/2037). + - redfish modules - explicitly setting lists' elements to ``str`` (https://github.com/ansible-collections/community.general/pull/1761). + - redfish_* modules, redfish_utils module utils - add support for Redfish session + create, delete, and authenticate (https://github.com/ansible-collections/community.general/issues/1975). + - redfish_config - case insensitive search for situations where the hostname/FQDN + case on iLO doesn't match variable's case (https://github.com/ansible-collections/community.general/pull/1744). + - redhat_subscription - elements of list parameters ``pool_ids``, ``addons`` + are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - rhevm - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). + - rocketchat - elements of list parameters ``attachments`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - scaleway_compute - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - scaleway_lb - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). + - sendgrid - elements of list parameters ``to_addresses``, ``cc``, ``bcc``, + ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - sensu_check - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - sensu_client - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - sensu_handler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - sl_vm - elements of list parameters ``disks``, ``ssh_keys`` are now validated + (https://github.com/ansible-collections/community.general/pull/1795). + - slack - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - smartos_image_info - minor refactor converting multiple statements to a single + list literal (https://github.com/ansible-collections/community.general/pull/2160). + - snmp_facts - added parameters ``timeout`` and ``retries`` to module (https://github.com/ansible-collections/community.general/issues/980). + - statusio_maintenance - elements of list parameters ``components``, ``containers`` + are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - svr4pkg - minor refactor converting multiple statements to a single list literal + (https://github.com/ansible-collections/community.general/pull/2160). + - terraform - add ``plugin_paths`` parameter which allows disabling Terraform + from performing plugin discovery and auto-download (https://github.com/ansible-collections/community.general/pull/2308). + - timezone - add Gentoo and Alpine Linux support (https://github.com/ansible-collections/community.general/issues/781). + - twilio - elements of list parameters ``to_numbers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - udm_dns_zone - elements of list parameters ``nameserver``, ``interfaces``, + and ``mx`` are now validated (https://github.com/ansible-collections/community.general/pull/2268). + - vdo - add ``force`` option (https://github.com/ansible-collections/community.general/issues/2101). + - vmadm - elements of list parameters ``disks``, ``nics``, ``resolvers``, ``filesystems`` + are now validated (https://github.com/ansible-collections/community.general/pull/1795). + - webfaction_domain - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - webfaction_site - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). + - xattr - minor refactor converting multiple statements to a single list literal + (https://github.com/ansible-collections/community.general/pull/2160). + - xfconf - added option ``disable_facts`` to disable facts and its associated + deprecation warning (https://github.com/ansible-collections/community.general/issues/1475). + - xfconf - changed implementation to use ``ModuleHelper`` new features (https://github.com/ansible-collections/community.general/pull/2188). + - xml - elements of list parameters ``add_children``, ``set_children`` are now + validated (https://github.com/ansible-collections/community.general/pull/1795). + - yum_versionlock - Do the lock/unlock concurrently to speed up (https://github.com/ansible-collections/community.general/pull/1912). + - zfs_facts - minor refactor converting multiple statements to a single list + literal (https://github.com/ansible-collections/community.general/pull/2160). + - zpool_facts - minor refactor converting multiple statements to a single list + literal (https://github.com/ansible-collections/community.general/pull/2160). + release_summary: This is release 3.0.0 of ``community.general``, released on + 2021-04-26. + removed_features: + - "The ``ome_device_info``, ``idrac_firmware`` and ``idrac_server_config_profile`` + \ modules have now been migrated from community.general to the `dellemc.openmanage + `_ Ansible collection.\nIf + you use ansible-base 2.10 or newer, redirections have been provided.\n\nIf + you use Ansible 2.9 and installed this collection, you need to adjust the + FQCNs (``community.general.idrac_firmware`` \u2192 ``dellemc.openmanage.idrac_firmware``) + and make sure to install the dellemc.openmanage collection.\n" + - The deprecated ali_instance_facts module has been removed. Use ali_instance_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated gluster_heal_info module has been removed. Use gluster.gluster.gluster_heal_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated gluster_peer module has been removed. Use gluster.gluster.gluster_peer + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated gluster_volume module has been removed. Use gluster.gluster.gluster_volume + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated helm module has been removed. Use community.kubernetes.helm + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated hpilo_facts module has been removed. Use hpilo_info instead + (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated idrac_redfish_facts module has been removed. Use idrac_redfish_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated jenkins_job_facts module has been removed. Use jenkins_job_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ldap_attr module has been removed. Use ldap_attrs instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated memset_memstore_facts module has been removed. Use memset_memstore_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated memset_server_facts module has been removed. Use memset_server_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated na_ontap_gather_facts module has been removed. Use netapp.ontap.na_ontap_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated nginx_status_facts module has been removed. Use nginx_status_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated one_image_facts module has been removed. Use one_image_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated onepassword_facts module has been removed. Use onepassword_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_datacenter_facts module has been removed. Use oneview_datacenter_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_enclosure_facts module has been removed. Use oneview_enclosure_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_ethernet_network_facts module has been removed. Use + oneview_ethernet_network_info instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_fc_network_facts module has been removed. Use oneview_fc_network_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_fcoe_network_facts module has been removed. Use oneview_fcoe_network_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_logical_interconnect_group_facts module has been removed. + Use oneview_logical_interconnect_group_info instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_network_set_facts module has been removed. Use oneview_network_set_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated oneview_san_manager_facts module has been removed. Use oneview_san_manager_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated online_server_facts module has been removed. Use online_server_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated online_user_facts module has been removed. Use online_user_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt module has been removed. Use ovirt.ovirt.ovirt_vm instead + (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_affinity_label_facts module has been removed. Use ovirt.ovirt.ovirt_affinity_label_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_api_facts module has been removed. Use ovirt.ovirt.ovirt_api_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_cluster_facts module has been removed. Use ovirt.ovirt.ovirt_cluster_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_datacenter_facts module has been removed. Use ovirt.ovirt.ovirt_datacenter_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_disk_facts module has been removed. Use ovirt.ovirt.ovirt_disk_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_event_facts module has been removed. Use ovirt.ovirt.ovirt_event_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_external_provider_facts module has been removed. Use + ovirt.ovirt.ovirt_external_provider_info instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_group_facts module has been removed. Use ovirt.ovirt.ovirt_group_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_host_facts module has been removed. Use ovirt.ovirt.ovirt_host_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_host_storage_facts module has been removed. Use ovirt.ovirt.ovirt_host_storage_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_network_facts module has been removed. Use ovirt.ovirt.ovirt_network_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_nic_facts module has been removed. Use ovirt.ovirt.ovirt_nic_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_permission_facts module has been removed. Use ovirt.ovirt.ovirt_permission_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_quota_facts module has been removed. Use ovirt.ovirt.ovirt_quota_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_scheduling_policy_facts module has been removed. Use + ovirt.ovirt.ovirt_scheduling_policy_info instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_snapshot_facts module has been removed. Use ovirt.ovirt.ovirt_snapshot_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_storage_domain_facts module has been removed. Use ovirt.ovirt.ovirt_storage_domain_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_storage_template_facts module has been removed. Use ovirt.ovirt.ovirt_storage_template_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_storage_vm_facts module has been removed. Use ovirt.ovirt.ovirt_storage_vm_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_tag_facts module has been removed. Use ovirt.ovirt.ovirt_tag_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_template_facts module has been removed. Use ovirt.ovirt.ovirt_template_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_user_facts module has been removed. Use ovirt.ovirt.ovirt_user_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_vm_facts module has been removed. Use ovirt.ovirt.ovirt_vm_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated ovirt_vmpool_facts module has been removed. Use ovirt.ovirt.ovirt_vmpool_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated purefa_facts module has been removed. Use purestorage.flasharray.purefa_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated purefb_facts module has been removed. Use purestorage.flasharray.purefb_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated python_requirements_facts module has been removed. Use python_requirements_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated redfish_facts module has been removed. Use redfish_info instead + (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_image_facts module has been removed. Use scaleway_image_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_ip_facts module has been removed. Use scaleway_ip_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_organization_facts module has been removed. Use scaleway_organization_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_security_group_facts module has been removed. Use + scaleway_security_group_info instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_server_facts module has been removed. Use scaleway_server_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_snapshot_facts module has been removed. Use scaleway_snapshot_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated scaleway_volume_facts module has been removed. Use scaleway_volume_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated smartos_image_facts module has been removed. Use smartos_image_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated vertica_facts module has been removed. Use vertica_info instead + (https://github.com/ansible-collections/community.general/pull/1924). + - The deprecated xenserver_guest_facts module has been removed. Use xenserver_guest_info + instead (https://github.com/ansible-collections/community.general/pull/1924). + - The ovirt_facts docs fragment has been removed (https://github.com/ansible-collections/community.general/pull/1924). + - airbrake_deployment - removed deprecated ``token`` parameter. Use ``project_id`` + and ``project_key`` instead (https://github.com/ansible-collections/community.general/pull/1926). + - bigpanda - the alias ``message`` has been removed. Use ``deployment_message`` + instead (https://github.com/ansible-collections/community.general/pull/1926). + - cisco_spark, cisco_webex - the alias ``message`` has been removed. Use ``msg`` + instead (https://github.com/ansible-collections/community.general/pull/1926). + - clc_aa_policy - the ``wait`` parameter has been removed. It did not have any + effect (https://github.com/ansible-collections/community.general/pull/1926). + - datadog_monitor - the alias ``message`` has been removed. Use ``notification_message`` + instead (https://github.com/ansible-collections/community.general/pull/1926). + - django_manage - the parameter ``liveserver`` has been removed (https://github.com/ansible-collections/community.general/pull/1926). + - idrac_redfish_config - the parameters ``manager_attribute_name`` and ``manager_attribute_value`` + have been removed. Use ``manager_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926). + - iso_extract - the alias ``thirsty`` has been removed. Use ``force`` instead + (https://github.com/ansible-collections/community.general/pull/1926). + - ldap_entry - the ``params`` parameter is now completely removed. Using it + already triggered an error since community.general 0.1.2 (https://github.com/ansible-collections/community.general/pull/2257). + - pulp_repo - the ``feed_client_cert`` parameter no longer defaults to the value + of the ``client_cert`` parameter (https://github.com/ansible-collections/community.general/pull/1926). + - pulp_repo - the ``feed_client_key`` parameter no longer defaults to the value + of the ``client_key`` parameter (https://github.com/ansible-collections/community.general/pull/1926). + - pulp_repo - the alias ``ca_cert`` has been removed. Use ``feed_ca_cert`` instead + (https://github.com/ansible-collections/community.general/pull/1926). + - rax - unused parameter ``service`` removed (https://github.com/ansible-collections/community.general/pull/2020). + - redfish modules - issuing a data modification command without specifying the + ID of the target System, Chassis or Manager resource when there is more than + one is no longer allowed. Use the ``resource_id`` option to specify the target + ID (https://github.com/ansible-collections/community.general/pull/1926). + - redfish_config - the parameters ``bios_attribute_name`` and ``bios_attribute_value`` + have been removed. Use ``bios_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926). + - syspatch - the ``apply`` parameter has been removed. This is the default mode, + so simply removing it will not change the behavior (https://github.com/ansible-collections/community.general/pull/1926). + - xbps - the ``force`` parameter has been removed. It did not have any effect + (https://github.com/ansible-collections/community.general/pull/1926). + security_fixes: + - dnsmadeeasy - mark the ``account_key`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - gitlab_runner - mark the ``registration_token`` parameter as ``no_log`` to + avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - hwc_ecs_instance - mark the ``admin_pass`` parameter as ``no_log`` to avoid + leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - ibm_sa_host - mark the ``iscsi_chap_secret`` parameter as ``no_log`` to avoid + leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - java_cert - remove password from ``run_command`` arguments (https://github.com/ansible-collections/community.general/pull/2008). + - java_keystore - pass secret to keytool through an environment variable to + not expose it as a commandline argument (https://github.com/ansible-collections/community.general/issues/1668). + - keycloak_* modules - mark the ``auth_client_secret`` parameter as ``no_log`` + to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - keycloak_client - mark the ``registration_access_token`` parameter as ``no_log`` + to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - librato_annotation - mark the ``api_key`` parameter as ``no_log`` to avoid + leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - logentries_msg - mark the ``token`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - module_utils/_netapp, na_ontap_gather_facts - enabled ``no_log`` for the options + ``api_key`` and ``secret_key`` to prevent accidental disclosure (CVE-2021-20191, + https://github.com/ansible-collections/community.general/pull/1725). + - module_utils/identity/keycloak, keycloak_client, keycloak_clienttemplate, + keycloak_group - enabled ``no_log`` for the option ``auth_client_secret`` + to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). + - nios_nsgroup - mark the ``tsig_key`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - oneandone_firewall_policy, oneandone_load_balancer, oneandone_monitoring_policy, + oneandone_private_network, oneandone_public_ip - mark the ``auth_token`` parameter + as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - ovirt - mark the ``instance_key`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - ovirt - mark the ``instance_rootpw`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - pagerduty_alert - mark the ``api_key``, ``service_key`` and ``integration_key`` + parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - pagerduty_change - mark the ``integration_key`` parameter as ``no_log`` to + avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - pingdom - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets + (https://github.com/ansible-collections/community.general/pull/1736). + - pulp_repo - mark the ``feed_client_key`` parameter as ``no_log`` to avoid + leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - rax_clb_ssl - mark the ``private_key`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - redfish_command - mark the ``update_creds.password`` parameter as ``no_log`` + to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - rollbar_deployment - mark the ``token`` parameter as ``no_log`` to avoid leakage + of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - spotinst_aws_elastigroup - mark the ``multai_token`` and ``token`` parameters + as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736). + - stackdriver - mark the ``key`` parameter as ``no_log`` to avoid leakage of + secrets (https://github.com/ansible-collections/community.general/pull/1736). + - utm_proxy_auth_profile - enabled ``no_log`` for the option ``frontend_cookie_secret`` + to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). + - utm_proxy_auth_profile - mark the ``frontend_cookie_secret`` parameter as + ``no_log`` to avoid leakage of secrets. This causes the ``utm_proxy_auth_profile`` + return value to no longer containing the correct value, but a placeholder + (https://github.com/ansible-collections/community.general/pull/1736). + fragments: + - 1475-xfconf-facts.yml + - 1478-filesystem-fix-1457-resizefs-idempotency.yml + - 1596-xfs_quota-feedback_on_projects_not_initialized_has_changed.yml + - 1661-gitlab-deploy-key-update-pubkey.yml + - 1691-add-name-and-id-props-to-redfish-inventory-output.yml + - 1695-parted-updatedregex.yaml + - 1702_homebrew_tap.yml + - 1703-sensu_silence-fix_json_parsing.yml + - 1714-gitlab_runner-required-reg-token.yml + - 1715-proxmox_kvm-add-vmid-to-returns.yml + - 1721-fix-nomad_job_info-no-jobs-failure.yml + - 1722_timezone.yml + - 1723-datadog_monitor-add-missing-monitor-types.yml + - 1724-various-fixes-for-updating-existing-gitlab-user.yml + - 1735-imc-sessions.yml + - 1740-aerospike_migration.yml + - 1741-use-path-argspec.yml + - 1744-case-insensitive-hostname-fqdn-matching.yml + - 1753-document-fstypes-supported-by-resizefs.yml + - 1761-redfish-tidy-up-validation.yml + - 1765-proxmox-params.yml + - 1766-zfs-fixed-sanity.yml + - 1771-centurylink-validation-elements.yml + - 1776-git_config-tilde_value.yml + - 1783-proxmox-kvm-fix-args-500-error.yaml + - 1788-ease-nios_host_record-dns-bypass-check.yml + - 1795-list-elements-batch1.yml + - 1813-lxd_profile-merge-profiles.yml + - 1814-dnsimple-add-support-for-caa-records.yml + - 1819-tidyup-pylint-blacklistnames.yml + - 1830-valmod_docmissingtype_batch1.yml + - 1833-zfs-creation-only-properties.yaml + - 1838-runit-deprecate-param-dist.yml + - 1847-proxmox-kvm-fix-status.yml + - 1852-deploy-helper-fix-state-is-clean-without-release.yaml + - 1861-python3-keys.yml + - 1867-modhelper-cmdmixin-dict-params.yml + - 1871-infoblox-inventory.yml + - 1880-fix_cobbler_system_ssl.yml + - 1882-fix-nmcli-ensure-slave-type-for-bond-slave.yml + - 1885-sanity-check-fixes-batch3.yml + - 1894-feat-nmcli-add-method4-and-method6.yml + - 1895-proxmox-kvm-fix-issue-1875.yml + - 1912-yum_versionlock-lock_unlock_concurrently.yml + - 1914-add-sanitization-to-url.yml + - 1916-add-version-sort-filter.yml + - 1927-removed-parameter-invalid.yml + - 1928-bigpanda-message.yml + - 1929-grove-message.yml + - 1949-proxmox-inventory-tags.yml + - 1970-valmod-batch7.yml + - 1972-ini_file-empty-str-value.yml + - 1977-jenkinsjob-validate-certs.yml + - 1978-jira-transition-logic.yml + - 1991-proxmox-inventory-fix-template-in-pool.yml + - 1993-haproxy-fix-draining.yml + - 1999-proxmox-fix-issue-1955.yml + - 2000-proxmox_kvm-tag-support.yml + - 2001-no_log-false.yml + - 2006-valmod-batch8.yml + - 2008-update-java-cert-replace-cert-when-changed.yml + - 2013-proxmox-purge-parameter.yml + - 2014-allow-root-for-kibana-plugin.yaml + - 2020-remove-unused-param-in-rax.yml + - 2024-module-helper-fixes.yml + - 2027-add-redfish-session-create-delete-authenticate.yml + - 2031-ipa_sudorule_add_runasextusers.yml + - 2032-one_image-pyone.yml + - 2036-scaleway-inventory.yml + - 2037-add-from-csv-filter.yml + - 2040-fix-index-error-in-redfish-set-manager-nic.yml + - 2057-nios-devel.yml + - 2061-archive-refactor1.yml + - 2065-snmp-facts-timeout.yml + - 2072-stacki-host-params-fallback.yml + - 2094-bugfix-respect-PATH-env-variable-in-zypper-modules.yaml + - 2110-vdo-add_force_option.yaml + - 2116-add-fields-to-ipa-config-module.yml + - 2125-git-config-scope-file.yml + - 2135-vmadm-resolvers-type-fix.yml + - 2139-dimensiondata_network-str-format.yml + - 2142-apache2_mod_proxy-cleanup.yml + - 2143-kibana_plugin-fixed-function-calls.yml + - 2144-atomic_get_bin_path.yml + - 2146-npm-add_no_bin_links_option.yaml + - 2148-proxmox-inventory-agent-interfaces.yml + - 2157-unreachable-code.yml + - 2159-ipa-user-sshpubkey-multi-word-comments.yaml + - 2160-list-literals.yml + - 2161-pkgutil-list-extend.yml + - 2162-modhelper-variables.yml + - 2162-proxmox-constructable.yml + - 2163-java_keystore_1667_improve_temp_files_storage.yml + - 2174-ipa-user-userauthtype-multiselect.yml + - 2177-java_keystore_1668_dont_expose_secrets_on_cmdline.yml + - 2183-java_keystore_improve_error_handling.yml + - 2185-xfconf-absent-check-mode.yml + - 2188-xfconf-modhelper-variables.yml + - 2192-add-jira-attach.yml + - 2203-modhelper-cause-changes-deco.yml + - 2204-github_repo-fix-baseurl_port.yml + - 2208-jira-revamp.yml + - 2218-cpanm-revamp.yml + - 2220_nmcli_wifi_support.yaml + - 2223_nmcli_no_IP_config_on_slave.yaml + - 2224_nmcli_allow_MAC_overwrite.yaml + - 2230-java_keystore-1669-ssl-input-files-by-path.yml + - 2236-jira-isinstance.yml + - 2244-hashids-filters.yml + - 2245-proxmox_fix_agent_string_handling.yml + - 2246-terraform.yaml + - 2249-linode_v4-support-private_ip-option.yaml + - 2250-allow-keycloak-modules-to-take-token-as-param.yml + - 2257-ldap_entry-params.yml + - 2259-proxmox-multi-nic-and-unsupported.yml + - 2262-java_keystore-passphrase.yml + - 2267-lvol_size_addition-subtraction_support.yaml + - 2268-validation-univetion.yml + - 2280-pids-new-pattern-option.yml + - 2282-nmap-fix-cache-support.yml + - 2284-influxdb_retention_policy-idempotence.yml + - 2308-terraform-add-plugin_paths-parameter.yaml + - 2329-hiera-lookup-plugin-return-type.yaml + - 2340-jenkins_plugin-py2.yml + - 2349-jira-bugfix-b64decode.yml + - 3.0.0.yml + - 620-consul_io-env-variables-conf-based.yml + - 719-manageiq-resource_id.yml + - 720-cloudforms_inventory.yml + - 816-only-invocate-feature-when-variable-is-set.yml + - 948-dellemc-migration-removal.yml + - CVE-2021-20191_no_log.yml + - allow_funcd_to_load.yml + - dict-filter.yml + - meta-runtime-deprecations.yml + - no_log-fixes.yml + - path_join-shim-filter.yml + - remove-deprecated-features.yml + - remove-deprecated-modules.yml + - selective-core-2.11.yml + modules: + - description: Create a file with a given size, or resize it if it exists + name: filesize + namespace: files + - description: Manage Gandi LiveDNS records + name: gandi_livedns + namespace: net_tools + - description: Manage your repositories on Github + name: github_repo + namespace: source_control.github + - description: Manage project members on GitLab Server + name: gitlab_project_members + namespace: source_control.gitlab + - description: Manage FreeIPA OTP Configuration Settings + name: ipa_otpconfig + namespace: identity.ipa + - description: Manage FreeIPA OTPs + name: ipa_otptoken + namespace: identity.ipa + - description: Manage jenkins builds + name: jenkins_build + namespace: web_infrastructure + - description: Allows administration of Keycloak realm via Keycloak API + name: keycloak_realm + namespace: identity.keycloak + - description: Manages OpenNebula templates + name: one_template + namespace: cloud.opennebula + - description: Manages Pritunl Organizations using the Pritunl API + name: pritunl_org + namespace: net_tools.pritunl + - description: List Pritunl Organizations using the Pritunl API + name: pritunl_org_info + namespace: net_tools.pritunl + - description: Manage Pritunl Users using the Pritunl API + name: pritunl_user + namespace: net_tools.pritunl + - description: List Pritunl Users using the Pritunl API + name: pritunl_user_info + namespace: net_tools.pritunl + - description: Retrieve information about one or more Proxmox VE storages + name: proxmox_storage_info + namespace: cloud.misc + - description: Enforce a model's attributes in CA Spectrum. + name: spectrum_model_attrs + namespace: monitoring + - description: Send metrics to StatsD + name: statsd + namespace: monitoring + - description: Manages Lenovo Out-Of-Band controllers using Redfish APIs + name: xcc_redfish_command + namespace: remote_management.lenovoxcc + plugins: + become: + - description: Run tasks using sudo su - + name: sudosu + namespace: null + callback: + - description: Posts task results to Azure Log Analytics + name: loganalytics + namespace: null + filter: + - description: 'The ``dict`` function as a filter: converts a list of tuples + to a dictionary' + name: dict + namespace: null + - description: Converts CSV text input into list of dicts + name: from_csv + namespace: null + - description: Decodes a sequence of numbers from a YouTube-like hash + name: hashids_decode + namespace: null + - description: Encodes YouTube-like hashes from a sequence of integers + name: hashids_encode + namespace: null + - description: Redirects to ansible.builtin.path_join for ansible-base 2.10 + or newer, and provides a compatible implementation for Ansible 2.9 + name: path_join + namespace: null + - description: Sort a list according to version order instead of pure alphabetical + one + name: version_sort + namespace: null + inventory: + - description: Returns Ansible inventory from lxd host + name: lxd + namespace: null + release_date: '2021-04-26' + 3.0.1: + changes: + bugfixes: + - composer - use ``no-interaction`` option when discovering available options + to avoid an issue where composer hangs (https://github.com/ansible-collections/community.general/pull/2348). + - influxdb_retention_policy - fix bug where ``INF`` duration values failed parsing + (https://github.com/ansible-collections/community.general/pull/2385). + - inventory and vault scripts - change file permissions to make vendored inventory + and vault scripts exectuable (https://github.com/ansible-collections/community.general/pull/2337). + - linode_v4 - changed the error message to point to the correct bugtracker URL + (https://github.com/ansible-collections/community.general/pull/2430). + - lvol - fixed rounding errors (https://github.com/ansible-collections/community.general/issues/2370). + - lvol - fixed size unit capitalization to match units used between different + tools for comparison (https://github.com/ansible-collections/community.general/issues/2360). + - nmcli - compare MAC addresses case insensitively to fix idempotency issue + (https://github.com/ansible-collections/community.general/issues/2409). + - nmcli - if type is ``bridge-slave`` add ``slave-type bridge`` to ``nmcli`` + command (https://github.com/ansible-collections/community.general/issues/2408). + - one_vm - Allow missing NIC keys (https://github.com/ansible-collections/community.general/pull/2435). + - puppet - replace ``console` with ``stdout`` in ``logdest`` option when ``all`` + has been chosen (https://github.com/ansible-collections/community.general/issues/1190). + - svr4pkg - convert string to a bytes-like object to avoid ``TypeError`` with + Python 3 (https://github.com/ansible-collections/community.general/issues/2373). + release_summary: Bugfix release for the next Ansible 4.0.0 beta. + fragments: + - 2284-influxdb_retention_policy-fix_duration_parsing.yml + - 2337-mark-inventory-scripts-executable.yml + - 2348-composer-no-interaction-option-discovery-to-avoid-hang.yaml + - 2369-lvol_size_bug_fixes.yml + - 2373-svr4pkg-fix-typeerror.yml + - 2407-puppet-change_stdout_to_console.yaml + - 2409-nmcli_add_slave-type_bridge_to_nmcli_command_if_type_is_bridge-slave.yml + - 2416-nmcli_compare_mac_addresses_case_insensitively.yml + - 2430-linodev4-error-message.yml + - 2435-one_vm-fix_missing_keys.yml + - 3.0.1.yml + release_date: '2021-05-04' + 3.0.2: + changes: + bugfixes: + - stackpath_compute inventory script - fix broken validation checks for client + ID and client secret (https://github.com/ansible-collections/community.general/pull/2448). + - zfs - certain ZFS properties, especially sizes, would lead to a task being + falsely marked as "changed" even when no actual change was made (https://github.com/ansible-collections/community.general/issues/975, + https://github.com/ansible-collections/community.general/pull/2454). + release_summary: Bugfix release for the first Ansible 4.0.0 release candidate. + fragments: + - 2448-stackpath_compute-fix.yml + - 2454-detect_zfs_changed.yml + - 3.0.2.yml + release_date: '2021-05-11' + 3.1.0: + changes: + bugfixes: + - consul_acl - update the hcl allowlist to include all supported options (https://github.com/ansible-collections/community.general/pull/2495). + - filesystem - repair ``reiserfs`` fstype support after adding it to integration + tests (https://github.com/ansible-collections/community.general/pull/2472). + - influxdb_user - allow creation of admin users when InfluxDB authentication + is enabled but no other user exists on the database. In this scenario, InfluxDB + 1.x allows only ``CREATE USER`` queries and rejects any other query (https://github.com/ansible-collections/community.general/issues/2364). + - influxdb_user - fix bug where an influxdb user has no privileges for 2 or + more databases (https://github.com/ansible-collections/community.general/pull/2499). + - iptables_state - fix a 'FutureWarning' in a regex and do some basic code clean + up (https://github.com/ansible-collections/community.general/pull/2525). + - iptables_state - fix initialization of iptables from null state when adressing + more than one table (https://github.com/ansible-collections/community.general/issues/2523). + - nmap inventory plugin - fix local variable error when cache is disabled (https://github.com/ansible-collections/community.general/issues/2512). + deprecated_features: + - The nios, nios_next_ip, nios_next_network lookup plugins, the nios documentation + fragment, and the nios_host_record, nios_ptr_record, nios_mx_record, nios_fixed_address, + nios_zone, nios_member, nios_a_record, nios_aaaa_record, nios_network, nios_dns_view, + nios_txt_record, nios_naptr_record, nios_srv_record, nios_cname_record, nios_nsgroup, + and nios_network_view module have been deprecated and will be removed from + community.general 5.0.0. Please install the `infoblox.nios_modules `_ + collection instead and use its plugins and modules (https://github.com/ansible-collections/community.general/pull/2458). + - The vendored copy of ``ipaddress`` will be removed in community.general 4.0.0. + Please switch to ``ipaddress`` from the Python 3 standard library, or `from + pypi `_, if your code relies on the vendored + version of ``ipaddress`` (https://github.com/ansible-collections/community.general/pull/2459). + - linode - parameter ``backupsenabled`` is deprecated and will be removed in + community.general 5.0.0 (https://github.com/ansible-collections/community.general/pull/2410). + - lxd inventory plugin - the plugin will require ``ipaddress`` installed when + used with Python 2 from community.general 4.0.0 on. ``ipaddress`` is part + of the Python 3 standard library, but can be installed for Python 2 from pypi + (https://github.com/ansible-collections/community.general/pull/2459). + - scaleway_security_group_rule - the module will require ``ipaddress`` installed + when used with Python 2 from community.general 4.0.0 on. ``ipaddress`` is + part of the Python 3 standard library, but can be installed for Python 2 from + pypi (https://github.com/ansible-collections/community.general/pull/2459). + minor_changes: + - ModuleHelper module utils - improved mechanism for customizing the calculation + of ``changed`` (https://github.com/ansible-collections/community.general/pull/2514). + - chroot connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - cmd (Module Helper) module utils - ``CmdMixin`` now pulls the value for ``run_command()`` + params from ``self.vars``, as opposed to previously retrieving those from + ``self.module.params`` (https://github.com/ansible-collections/community.general/pull/2517). + - filesystem - cleanup and revamp module, tests and doc. Pass all commands to + ``module.run_command()`` as lists. Move the device-vs-mountpoint logic to + ``grow()`` method. Give to all ``get_fs_size()`` the same logic and error + handling. (https://github.com/ansible-collections/community.general/pull/2472). + - funcd connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - gitlab_user - add ``expires_at`` option (https://github.com/ansible-collections/community.general/issues/2325). + - idrac_redfish_config - modified set_manager_attributes function to skip invalid + attribute instead of returning. Added skipped attributes to output. Modified + module exit to add warning variable (https://github.com/ansible-collections/community.general/issues/1995). + - influxdb_retention_policy - add ``state`` parameter with allowed values ``present`` + and ``absent`` to support deletion of existing retention policies (https://github.com/ansible-collections/community.general/issues/2383). + - influxdb_retention_policy - simplify duration logic parsing (https://github.com/ansible-collections/community.general/pull/2385). + - iocage connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - jail connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - java_keystore - added ``ssl_backend`` parameter for using the cryptography + library instead of the OpenSSL binary (https://github.com/ansible-collections/community.general/pull/2485). + - java_keystore - replace envvar by stdin to pass secret to ``keytool`` (https://github.com/ansible-collections/community.general/pull/2526). + - linode - added proper traceback when failing due to exceptions (https://github.com/ansible-collections/community.general/pull/2410). + - linode - parameter ``additional_disks`` is now validated as a list of dictionaries + (https://github.com/ansible-collections/community.general/pull/2410). + - lxc connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - module_helper module utils - break down of the long file into smaller pieces + (https://github.com/ansible-collections/community.general/pull/2393). + - nmcli - remove dead code, ``options`` never contains keys from ``param_alias`` + (https://github.com/ansible-collections/community.general/pull/2417). + - pacman - add ``executable`` option to use an alternative pacman binary (https://github.com/ansible-collections/community.general/issues/2524). + - passwordstore lookup - add option ``missing`` to choose what to do if the + password file is missing (https://github.com/ansible-collections/community.general/pull/2500). + - qubes connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - redfish_config - modified module exit to add warning variable (https://github.com/ansible-collections/community.general/issues/1995). + - redfish_utils module utils - modified set_bios_attributes function to skip + invalid attribute instead of returning. Added skipped attributes to output + (https://github.com/ansible-collections/community.general/issues/1995). + - saltstack connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + - spotinst_aws_elastigroup - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2355). + - zfs_delegate_admin - drop choices from permissions, allowing any permission + supported by the underlying zfs commands (https://github.com/ansible-collections/community.general/pull/2540). + - zone connection - minor refactor to make lints and IDEs happy (https://github.com/ansible-collections/community.general/pull/2520). + release_summary: Regular feature and bugfix release. + fragments: + - 1085-consul-acl-hcl-whitelist-update.yml + - 2323-groupby_as_dict-filter.yml + - 2334-redfish_config-skip-incorrect-attributes.yml + - 2355-spotinst_aws_elastigroup-list-elements.yml + - 2364-influxdb_user-first_user.yml + - 2383-influxdb_retention_policy-add-state-option.yml + - 2393-module_helper-breakdown.yml + - 2410-linode-improvements.yml + - 2417-nmcli_remove_dead_code.yml + - 2450-gitlab_user-add_expires_at_option.yaml + - 2472_filesystem_module_revamp.yml + - 2485-java_keystore-ssl_backend-parameter.yml + - 2499-influxdb_user-fix-multiple-no-privileges.yml + - 2500-passwordstore-add_option_ignore_missing.yml + - 2514-mh-improved-changed.yml + - 2517-cmd-params-from-vars.yml + - 2518-nmap-fix-cache-disabled.yml + - 2520-connection-refactors.yml + - 2524-pacman_add_bin_option.yml + - 2525-iptables_state-fix-initialization-command.yml + - 2526-java_keystore-password-via-stdin.yml + - 2540-zfs-delegate-choices.yml + - 3.1.0.yml + - deprecate-ipaddress.yml + - nios-deprecation.yml + modules: + - description: Send Discord messages + name: discord + namespace: notification + - description: Management of a NIC of a Qemu(KVM) VM in a Proxmox VE cluster. + name: proxmox_nic + namespace: cloud.misc + plugins: + filter: + - description: Transform a sequence of dictionaries to a dictionary where the + dictionaries are indexed by an attribute + name: groupby_as_dict + namespace: null + lookup: + - description: Composes a list with nested elements of other lists or dicts + which can depend on previous loop variables + name: dependent + namespace: null + - description: Generates random pet names + name: random_pet + namespace: null + release_date: '2021-05-18' + 3.2.0: + changes: + bugfixes: + - consul_kv lookup plugin - allow to set ``recurse``, ``index``, ``datacenter`` + and ``token`` as keyword arguments (https://github.com/ansible-collections/community.general/issues/2124). + - cpanm - also use ``LC_ALL`` to enforce locale choice (https://github.com/ansible-collections/community.general/pull/2731). + - influxdb_user - fix bug which removed current privileges instead of appending + them to existing ones (https://github.com/ansible-collections/community.general/issues/2609, + https://github.com/ansible-collections/community.general/pull/2614). + - iptables_state - call ``async_status`` action plugin rather than its module + (https://github.com/ansible-collections/community.general/issues/2700). + - iptables_state - fix a broken query of ``async_status`` result with current + ansible-core development version (https://github.com/ansible-collections/community.general/issues/2627, + https://github.com/ansible-collections/community.general/pull/2671). + - java_cert - fix issue with incorrect alias used on PKCS#12 certificate import + (https://github.com/ansible-collections/community.general/pull/2560). + - jenkins_plugin - use POST method for sending request to jenkins API when ``state`` + option is one of ``enabled``, ``disabled``, ``pinned``, ``unpinned``, or ``absent`` + (https://github.com/ansible-collections/community.general/issues/2510). + - json_query filter plugin - avoid 'unknown type' errors for more Ansible internal + types (https://github.com/ansible-collections/community.general/pull/2607). + - keycloak_realm - ``ssl_required`` changed from a boolean type to accept the + strings ``none``, ``external`` or ``all``. This is not a breaking change since + the module always failed when a boolean was supplied (https://github.com/ansible-collections/community.general/pull/2693). + - keycloak_realm - remove warning that ``reset_password_allowed`` needs to be + marked as ``no_log`` (https://github.com/ansible-collections/community.general/pull/2694). + - module_helper module utils - ``CmdMixin`` must also use ``LC_ALL`` to enforce + locale choice (https://github.com/ansible-collections/community.general/pull/2731). + - netcup_dns - use ``str(ex)`` instead of unreliable ``ex.message`` in exception + handling to fix ``AttributeError`` in error cases (https://github.com/ansible-collections/community.general/pull/2590). + - ovir4 inventory script - improve configparser creation to avoid crashes for + options without values (https://github.com/ansible-collections/community.general/issues/674). + - proxmox_kvm - fixed ``vmid`` return value when VM with ``name`` already exists + (https://github.com/ansible-collections/community.general/issues/2648). + - redis cache - improved connection string parsing (https://github.com/ansible-collections/community.general/issues/497). + - rhsm_release - fix the issue that module considers 8, 7Client and 7Workstation + as invalid releases (https://github.com/ansible-collections/community.general/pull/2571). + - ssh_config - reduce stormssh searches based on host (https://github.com/ansible-collections/community.general/pull/2568/). + - stacki_host - when adding a new server, ``rack`` and ``rank`` must be passed, + and network parameters are optional (https://github.com/ansible-collections/community.general/pull/2681). + - terraform - ensure the workspace is set back to its previous value when the + apply fails (https://github.com/ansible-collections/community.general/pull/2634). + - xfconf - also use ``LC_ALL`` to enforce locale choice (https://github.com/ansible-collections/community.general/issues/2715). + - zypper_repository - fix idempotency on adding repository with ``$releasever`` + and ``$basearch`` variables (https://github.com/ansible-collections/community.general/issues/1985). + deprecated_features: + - All inventory and vault scripts will be removed from community.general in + version 4.0.0. If you are referencing them, please update your references + to the new `contrib-scripts GitHub repository `_ + so your workflow will not break once community.general 4.0.0 is released (https://github.com/ansible-collections/community.general/pull/2697). + minor_changes: + - Remove unnecessary ``__init__.py`` files from ``plugins/`` (https://github.com/ansible-collections/community.general/pull/2632). + - archive - added ``exclusion_patterns`` option to exclude files or subdirectories + from archives (https://github.com/ansible-collections/community.general/pull/2616). + - cloud_init_data_facts - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + - composer - add ``composer_executable`` option (https://github.com/ansible-collections/community.general/issues/2649). + - flatpak - add ``no_dependencies`` parameter (https://github.com/ansible/ansible/pull/55452, + https://github.com/ansible-collections/community.general/pull/2751). + - ini_file - opening file with encoding ``utf-8-sig`` (https://github.com/ansible-collections/community.general/issues/2189). + - jira - add comment visibility parameter for comment operation (https://github.com/ansible-collections/community.general/pull/2556). + - maven_artifact - added ``checksum_alg`` option to support SHA1 checksums in + order to support FIPS systems (https://github.com/ansible-collections/community.general/pull/2662). + - module_helper module utils - method ``CmdMixin.run_command()`` now accepts + ``process_output`` specifying a function to process the outcome of the underlying + ``module.run_command()`` (https://github.com/ansible-collections/community.general/pull/2564). + - nmcli - add new options to ignore automatic DNS servers and gateways (https://github.com/ansible-collections/community.general/issues/1087). + - onepassword lookup plugin - add ``domain`` option (https://github.com/ansible-collections/community.general/issues/2734). + - open_iscsi - add ``auto_portal_startup`` parameter to allow ``node.startup`` + setting per portal (https://github.com/ansible-collections/community.general/issues/2685). + - open_iscsi - also consider ``portal`` and ``port`` to check if already logged + in or not (https://github.com/ansible-collections/community.general/issues/2683). + - proxmox_group_info - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + - proxmox_kvm - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + - rhevm - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + - serverless - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + - stacki_host - minor refactoring (https://github.com/ansible-collections/community.general/pull/2681). + - terraform - add option ``overwrite_init`` to skip init if exists (https://github.com/ansible-collections/community.general/pull/2573). + - terraform - minor refactor (https://github.com/ansible-collections/community.general/pull/2557). + release_summary: Regular bugfix and feature release. + fragments: + - 2126-consul_kv-pass-token.yml + - 2461-ovirt4-fix-configparser.yml + - 2510-jenkins_plugin_use_post_method.yml + - 2556-add-comment_visibility-parameter-for-comment-operation-of-jira-module.yml + - 2557-cloud-misc-refactor.yml + - 2560-java_cert-pkcs12-alias-bugfix.yml + - 2564-mh-cmd-process-output.yml + - 2568-ssh_config-reduce-stormssh-searches-based-on-host.yml + - 2571-rhsm_release-fix-release_matcher.yaml + - 2573-terraform-overwrite-init.yml + - 2578-ini-file-utf8-bom.yml + - 2579-redis-cache-ipv6.yml + - 2590-netcup_dns-exception-no-message-attr.yml + - 2614-influxdb_user-fix-issue-introduced-in-PR#2499.yml + - 2616-archive-exclusion_patterns-option.yml + - 2632-cleanup.yml + - 2634-terraform-switch-workspace.yml + - 2635-nmcli-add-ignore-auto-arguments.yml + - 2648-proxmox_kvm-fix-vmid-return-value.yml + - 2650-composer-add_composer_executable.yml + - 2661-maven_artifact-add-sha1-option.yml + - 2671-fix-broken-query-of-async_status-result.yml + - 2681-stacki-host-bugfix.yml + - 2684-open_iscsi-single-target-multiple-portal-overrides.yml + - 2711-fix-iptables_state-2700-async_status-call.yml + - 2722-zypper_repository-fix_idempotency_on_adding_repo_with_releasever.yml + - 2731-mh-cmd-locale.yml + - 2735-onepassword-add_domain_option.yml + - 2751-flatpak-no_dependencies.yml + - 3.2.0.yml + - json_query_more_types.yml + - keycloak-realm-no-log-password-reset.yml + - keycloak_realm_ssl_required.yml + - script-removal.yml + modules: + - description: Execute SQL on HANA + name: hana_query + namespace: database.saphana + - description: Manage pacman's list of trusted keys + name: pacman_key + namespace: packaging.os + - description: Manages SAP SAPCAR archives + name: sapcar_extract + namespace: files + plugins: + lookup: + - description: Generates random string + name: random_string + namespace: null + release_date: '2021-06-08' diff --git a/changelogs/fragments/1475-xfconf-facts.yml b/changelogs/fragments/1475-xfconf-facts.yml deleted file mode 100644 index cffc6f023e..0000000000 --- a/changelogs/fragments/1475-xfconf-facts.yml +++ /dev/null @@ -1,4 +0,0 @@ -minor_changes: - - xfconf - added option ``disable_facts`` to disable facts and its associated deprecation warning (https://github.com/ansible-collections/community.general/issues/1475). -deprecated_features: - - xfconf - returning output as facts is deprecated, this will be removed in community.general 4.0.0. Please register the task output in a variable and use it instead. You can already switch to the new behavior now by using the new ``disable_facts`` option (https://github.com/ansible-collections/community.general/pull/1747). diff --git a/changelogs/fragments/1478-filesystem-fix-1457-resizefs-idempotency.yml b/changelogs/fragments/1478-filesystem-fix-1457-resizefs-idempotency.yml deleted file mode 100644 index a90444308e..0000000000 --- a/changelogs/fragments/1478-filesystem-fix-1457-resizefs-idempotency.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -bugfixes: - - filesystem - do not fail when ``resizefs=yes`` and ``fstype=xfs`` if there is nothing to do, even if - the filesystem is not mounted. This only covers systems supporting access to unmounted XFS filesystems. - Others will still fail (https://github.com/ansible-collections/community.general/issues/1457, https://github.com/ansible-collections/community.general/pull/1478). diff --git a/changelogs/fragments/1596-xfs_quota-feedback_on_projects_not_initialized_has_changed.yml b/changelogs/fragments/1596-xfs_quota-feedback_on_projects_not_initialized_has_changed.yml deleted file mode 100644 index ba75a86a62..0000000000 --- a/changelogs/fragments/1596-xfs_quota-feedback_on_projects_not_initialized_has_changed.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - xfs_quota - the feedback for initializing project quota using xfs_quota binary from ``xfsprogs`` has changed since the version it was written for (https://github.com/ansible-collections/community.general/pull/1596). diff --git a/changelogs/fragments/1661-gitlab-deploy-key-update-pubkey.yml b/changelogs/fragments/1661-gitlab-deploy-key-update-pubkey.yml deleted file mode 100644 index f6edfc6f53..0000000000 --- a/changelogs/fragments/1661-gitlab-deploy-key-update-pubkey.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -minor_changes: - - gitlab_deploy_key - when the given key title already exists but has a different public key, the public key will now be updated to given value (https://github.com/ansible-collections/community.general/pull/1661). -breaking_changes: - - gitlab_deploy_key - if for an already existing key title a different public key was given as parameter nothing happened, now this changed so that the public key is updated to the new value (https://github.com/ansible-collections/community.general/pull/1661). diff --git a/changelogs/fragments/1691-add-name-and-id-props-to-redfish-inventory-output.yml b/changelogs/fragments/1691-add-name-and-id-props-to-redfish-inventory-output.yml deleted file mode 100644 index 1cf8897018..0000000000 --- a/changelogs/fragments/1691-add-name-and-id-props-to-redfish-inventory-output.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - redfish_info module, redfish_utils module utils - add ``Name`` and ``Id`` properties to output of Redfish inventory commands (https://github.com/ansible-collections/community.general/issues/1650). diff --git a/changelogs/fragments/1695-parted-updatedregex.yaml b/changelogs/fragments/1695-parted-updatedregex.yaml deleted file mode 100644 index fb3a5a5eaa..0000000000 --- a/changelogs/fragments/1695-parted-updatedregex.yaml +++ /dev/null @@ -1,4 +0,0 @@ -bugfixes: - - parted - change the regex that decodes the partition size to better support different formats that parted uses. - Change the regex that validates parted's version string - (https://github.com/ansible-collections/community.general/pull/1695). diff --git a/changelogs/fragments/1702_homebrew_tap.yml b/changelogs/fragments/1702_homebrew_tap.yml deleted file mode 100644 index 7eabc45a9b..0000000000 --- a/changelogs/fragments/1702_homebrew_tap.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: -- homebrew_tap - add support to specify search path for ``brew`` executable (https://github.com/ansible-collections/community.general/issues/1702). diff --git a/changelogs/fragments/1703-sensu_silence-fix_json_parsing.yml b/changelogs/fragments/1703-sensu_silence-fix_json_parsing.yml deleted file mode 100644 index 18d39b5674..0000000000 --- a/changelogs/fragments/1703-sensu_silence-fix_json_parsing.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - sensu-silence module - fix json parsing of sensu API responses on Python 3.5 (https://github.com/ansible-collections/community.general/pull/1703). diff --git a/changelogs/fragments/1714-gitlab_runner-required-reg-token.yml b/changelogs/fragments/1714-gitlab_runner-required-reg-token.yml deleted file mode 100644 index ec73bf422c..0000000000 --- a/changelogs/fragments/1714-gitlab_runner-required-reg-token.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - gitlab_runner - parameter ``registration_token`` was required but is used only when ``state`` is ``present`` (https://github.com/ansible-collections/community.general/issues/1714). diff --git a/changelogs/fragments/1715-proxmox_kvm-add-vmid-to-returns.yml b/changelogs/fragments/1715-proxmox_kvm-add-vmid-to-returns.yml deleted file mode 100644 index b4561f5145..0000000000 --- a/changelogs/fragments/1715-proxmox_kvm-add-vmid-to-returns.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - proxmox_kvm module - actually implemented ``vmid`` and ``status`` return values. Updated documentation to reflect current situation (https://github.com/ansible-collections/community.general/issues/1410, https://github.com/ansible-collections/community.general/pull/1715). diff --git a/changelogs/fragments/1721-fix-nomad_job_info-no-jobs-failure.yml b/changelogs/fragments/1721-fix-nomad_job_info-no-jobs-failure.yml deleted file mode 100644 index c3c3d804e3..0000000000 --- a/changelogs/fragments/1721-fix-nomad_job_info-no-jobs-failure.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - nomad_job_info - fix module failure when nomad client returns no jobs (https://github.com/ansible-collections/community.general/pull/1721). diff --git a/changelogs/fragments/1722_timezone.yml b/changelogs/fragments/1722_timezone.yml deleted file mode 100644 index cae337effd..0000000000 --- a/changelogs/fragments/1722_timezone.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: -- timezone - add Gentoo and Alpine Linux support (https://github.com/ansible-collections/community.general/issues/781). diff --git a/changelogs/fragments/1723-datadog_monitor-add-missing-monitor-types.yml b/changelogs/fragments/1723-datadog_monitor-add-missing-monitor-types.yml deleted file mode 100644 index 8b01717897..0000000000 --- a/changelogs/fragments/1723-datadog_monitor-add-missing-monitor-types.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - datadog_monitor - add missing monitor types ``query alert``, ``trace-analytics alert``, ``rum alert`` (https://github.com/ansible-collections/community.general/pull/1723). diff --git a/changelogs/fragments/1724-various-fixes-for-updating-existing-gitlab-user.yml b/changelogs/fragments/1724-various-fixes-for-updating-existing-gitlab-user.yml deleted file mode 100644 index eab67e0f47..0000000000 --- a/changelogs/fragments/1724-various-fixes-for-updating-existing-gitlab-user.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - gitlab_user - make updates to the ``isadmin``, ``password`` and ``confirm`` options of an already existing GitLab user work (https://github.com/ansible-collections/community.general/pull/1724). diff --git a/changelogs/fragments/1735-imc-sessions.yml b/changelogs/fragments/1735-imc-sessions.yml deleted file mode 100644 index 057393d06c..0000000000 --- a/changelogs/fragments/1735-imc-sessions.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - imc_rest - explicitly logging out instead of registering the call in ```atexit``` (https://github.com/ansible-collections/community.general/issues/1735). diff --git a/changelogs/fragments/1740-aerospike_migration.yml b/changelogs/fragments/1740-aerospike_migration.yml deleted file mode 100644 index e66963aae7..0000000000 --- a/changelogs/fragments/1740-aerospike_migration.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "aerospike_migration - fix typo that caused ``migrate_tx_key`` instead of ``migrate_rx_key`` being used (https://github.com/ansible-collections/community.general/pull/1739)." diff --git a/changelogs/fragments/1741-use-path-argspec.yml b/changelogs/fragments/1741-use-path-argspec.yml deleted file mode 100644 index ed05fee16a..0000000000 --- a/changelogs/fragments/1741-use-path-argspec.yml +++ /dev/null @@ -1,4 +0,0 @@ -minor_changes: -- "oci_vcn - ``api_user_key_file`` is now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741)." -- "lxd_container - ``client_key`` and ``client_cert`` are now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741)." -- "lxd_profile - ``client_key`` and ``client_cert`` are now of type ``path`` and no longer ``str``. A side effect is that certain expansions are made, like ``~`` is replaced by the user's home directory, and environment variables like ``$HOME`` or ``$TEMP`` are evaluated (https://github.com/ansible-collections/community.general/pull/1741)." diff --git a/changelogs/fragments/1744-case-insensitive-hostname-fqdn-matching.yml b/changelogs/fragments/1744-case-insensitive-hostname-fqdn-matching.yml deleted file mode 100644 index 0e9c086b96..0000000000 --- a/changelogs/fragments/1744-case-insensitive-hostname-fqdn-matching.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - redfish_config - case insensitive search for situations where the hostname/FQDN case on iLO doesn't match variable's case (https://github.com/ansible-collections/community.general/pull/1744). diff --git a/changelogs/fragments/1753-document-fstypes-supported-by-resizefs.yml b/changelogs/fragments/1753-document-fstypes-supported-by-resizefs.yml deleted file mode 100644 index 9b1329412c..0000000000 --- a/changelogs/fragments/1753-document-fstypes-supported-by-resizefs.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - filesystem - remove ``swap`` from list of FS supported by ``resizefs=yes`` (https://github.com/ansible-collections/community.general/issues/790). diff --git a/changelogs/fragments/1761-redfish-tidy-up-validation.yml b/changelogs/fragments/1761-redfish-tidy-up-validation.yml deleted file mode 100644 index 751c7ca30d..0000000000 --- a/changelogs/fragments/1761-redfish-tidy-up-validation.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - redfish modules - explicitly setting lists' elements to ``str`` (https://github.com/ansible-collections/community.general/pull/1761). diff --git a/changelogs/fragments/1765-proxmox-params.yml b/changelogs/fragments/1765-proxmox-params.yml deleted file mode 100644 index fd6d63c788..0000000000 --- a/changelogs/fragments/1765-proxmox-params.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - proxmox* modules - refactored some parameter validation code into use of ``env_fallback``, ``required_if``, ``required_together``, ``required_one_of`` (https://github.com/ansible-collections/community.general/pull/1765). diff --git a/changelogs/fragments/1766-zfs-fixed-sanity.yml b/changelogs/fragments/1766-zfs-fixed-sanity.yml deleted file mode 100644 index ac31084e2c..0000000000 --- a/changelogs/fragments/1766-zfs-fixed-sanity.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - zfs_delegate_admin - the elements of ``users``, ``groups`` and ``permissions`` are now enforced to be strings (https://github.com/ansible-collections/community.general/pull/1766). diff --git a/changelogs/fragments/1771-centurylink-validation-elements.yml b/changelogs/fragments/1771-centurylink-validation-elements.yml deleted file mode 100644 index 4c7a9bbbe4..0000000000 --- a/changelogs/fragments/1771-centurylink-validation-elements.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - clc_* modules - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1771). diff --git a/changelogs/fragments/1776-git_config-tilde_value.yml b/changelogs/fragments/1776-git_config-tilde_value.yml deleted file mode 100644 index c98912a24d..0000000000 --- a/changelogs/fragments/1776-git_config-tilde_value.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - git_config - prevent ``run_command`` from expanding values (https://github.com/ansible-collections/community.general/issues/1776). diff --git a/changelogs/fragments/1783-proxmox-kvm-fix-args-500-error.yaml b/changelogs/fragments/1783-proxmox-kvm-fix-args-500-error.yaml deleted file mode 100644 index 5e46b066a8..0000000000 --- a/changelogs/fragments/1783-proxmox-kvm-fix-args-500-error.yaml +++ /dev/null @@ -1,3 +0,0 @@ -bugfixes: - - proxmox_kvm - do not add ``args`` if ``proxmox_default_behavior`` is set to no_defaults (https://github.com/ansible-collections/community.general/issues/1641). - - proxmox_kvm - stop implicitly adding ``force`` equal to ``false``. Proxmox API requires not implemented parameters otherwise, and assumes ``force`` to be ``false`` by default anyways (https://github.com/ansible-collections/community.general/pull/1783). diff --git a/changelogs/fragments/1788-ease-nios_host_record-dns-bypass-check.yml b/changelogs/fragments/1788-ease-nios_host_record-dns-bypass-check.yml deleted file mode 100644 index 6b1a43cc25..0000000000 --- a/changelogs/fragments/1788-ease-nios_host_record-dns-bypass-check.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - nios_host_record - allow DNS Bypass for views other than default (https://github.com/ansible-collections/community.general/issues/1786). diff --git a/changelogs/fragments/1795-list-elements-batch1.yml b/changelogs/fragments/1795-list-elements-batch1.yml deleted file mode 100644 index 9b057c7712..0000000000 --- a/changelogs/fragments/1795-list-elements-batch1.yml +++ /dev/null @@ -1,27 +0,0 @@ -minor_changes: - - plugins/module_utils/oracle/oci_utils.py - elements of list parameter ``key_by`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - lxd_container - elements of list parameter ``profiles`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - packet_device - elements of list parameters ``device_ids``, ``hostnames`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - pubnub_blocks - elements of list parameters ``event_handlers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - vmadm - elements of list parameters ``disks``, ``nics``, ``resolvers``, ``filesystems`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - sl_vm - elements of list parameters ``disks``, ``ssh_keys`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - xml - elements of list parameters ``add_children``, ``set_children`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - keycloak_client - elements of list parameters ``default_roles``, ``redirect_uris``, ``web_origins`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - onepassword_info - elements of list parameters ``search_terms`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - librato_annotation - elements of list parameters ``links`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - pagerduty - elements of list parameters ``service`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - statusio_maintenance - elements of list parameters ``components``, ``containers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - dnsimple - elements of list parameters ``record_ids`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - nsupdate - elements of list parameters ``value`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - omapi_host - elements of list parameters ``statements`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - mail - elements of list parameters ``to``, ``cc``, ``bcc``, ``attach``, ``headers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - nexmo - elements of list parameters ``dest`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - rocketchat - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - sendgrid - elements of list parameters ``to_addresses``, ``cc``, ``bcc``, ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - slack - elements of list parameters ``attachments`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - twilio - elements of list parameters ``to_numbers`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - redhat_subscription - elements of list parameters ``pool_ids``, ``addons`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - gitlab_runner - elements of list parameters ``tag_list`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). - - na_ontap_gather_facts - elements of list parameters ``gather_subset`` are now validated (https://github.com/ansible-collections/community.general/pull/1795). -bugfixes: - - redhat_subscription - ``mutually_exclusive`` was referring to parameter alias instead of name (https://github.com/ansible-collections/community.general/pull/1795). diff --git a/changelogs/fragments/1813-lxd_profile-merge-profiles.yml b/changelogs/fragments/1813-lxd_profile-merge-profiles.yml deleted file mode 100644 index d374347a5e..0000000000 --- a/changelogs/fragments/1813-lxd_profile-merge-profiles.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: -- lxd_profile - added ``merge_profile`` parameter to merge configurations from the play to an existing profile (https://github.com/ansible-collections/community.general/pull/1813). diff --git a/changelogs/fragments/1814-dnsimple-add-support-for-caa-records.yml b/changelogs/fragments/1814-dnsimple-add-support-for-caa-records.yml deleted file mode 100644 index bc4915b7b9..0000000000 --- a/changelogs/fragments/1814-dnsimple-add-support-for-caa-records.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - dnsimple - add CAA records to the whitelist of valid record types (https://github.com/ansible-collections/community.general/pull/1814). diff --git a/changelogs/fragments/1819-tidyup-pylint-blacklistnames.yml b/changelogs/fragments/1819-tidyup-pylint-blacklistnames.yml deleted file mode 100644 index fdbc850528..0000000000 --- a/changelogs/fragments/1819-tidyup-pylint-blacklistnames.yml +++ /dev/null @@ -1,17 +0,0 @@ -bugfixes: - - "alternatives - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "beadm - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "cronvar - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "dconf - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "filesystem - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "hipchat - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "interfaces_file - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "java_cert - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "lvg - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "lvol - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "lxc - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "lxc_container - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "parted - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "rundeck_acl_policy - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "statusio_maintenance - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." - - "timezone - internal refactoring: replaced uses of ``_`` with ``dummy`` (https://github.com/ansible-collections/community.general/pull/1819)." diff --git a/changelogs/fragments/1830-valmod_docmissingtype_batch1.yml b/changelogs/fragments/1830-valmod_docmissingtype_batch1.yml deleted file mode 100644 index 83a27f7e77..0000000000 --- a/changelogs/fragments/1830-valmod_docmissingtype_batch1.yml +++ /dev/null @@ -1,7 +0,0 @@ -bugfixes: - - kibana_plugin - ``state`` parameter choices must use ``list()`` in python3 (https://github.com/ansible-collections/community.general/pull/1830). - - elasticsearch_plugin - ``state`` parameter choices must use ``list()`` in python3 (https://github.com/ansible-collections/community.general/pull/1830). - - riak - parameters ``wait_for_handoffs`` and ``wait_for_ring`` are ``int`` but the default value was ``false`` (https://github.com/ansible-collections/community.general/pull/1830). - - logstash_plugin - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` setting (https://github.com/ansible-collections/community.general/pull/1830). - - iso_extract - use proper alias deprecation mechanism for ``thirsty`` alias of ``force`` (https://github.com/ansible-collections/community.general/pull/1830). - - runit - removed unused code, and passing command as ``list`` instead of ``str`` to ``run_command()`` (https://github.com/ansible-collections/community.general/pull/1830). diff --git a/changelogs/fragments/1833-zfs-creation-only-properties.yaml b/changelogs/fragments/1833-zfs-creation-only-properties.yaml deleted file mode 100644 index deb972a6d2..0000000000 --- a/changelogs/fragments/1833-zfs-creation-only-properties.yaml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - zfs - some ZFS properties could be passed when the dataset/volume did not exist, but would fail if the dataset already existed, even if the property matched what was specified in the ansible task (https://github.com/ansible-collections/community.general/issues/868, https://github.com/ansible-collections/community.general/pull/1833). diff --git a/changelogs/fragments/1838-runit-deprecate-param-dist.yml b/changelogs/fragments/1838-runit-deprecate-param-dist.yml deleted file mode 100644 index 5d133c074e..0000000000 --- a/changelogs/fragments/1838-runit-deprecate-param-dist.yml +++ /dev/null @@ -1,2 +0,0 @@ -deprecated_features: - - runit - unused parameter ``dist`` marked for deprecation (https://github.com/ansible-collections/community.general/pull/1830). diff --git a/changelogs/fragments/1847-proxmox-kvm-fix-status.yml b/changelogs/fragments/1847-proxmox-kvm-fix-status.yml deleted file mode 100644 index 0863f1bed2..0000000000 --- a/changelogs/fragments/1847-proxmox-kvm-fix-status.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - proxmox_kvm - fix undefined local variable ``status`` when the parameter ``state`` is either ``stopped``, ``started``, ``restarted`` or ``absent`` (https://github.com/ansible-collections/community.general/pull/1847). diff --git a/changelogs/fragments/1852-deploy-helper-fix-state-is-clean-without-release.yaml b/changelogs/fragments/1852-deploy-helper-fix-state-is-clean-without-release.yaml deleted file mode 100644 index 0946a4f38f..0000000000 --- a/changelogs/fragments/1852-deploy-helper-fix-state-is-clean-without-release.yaml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - deploy_helper - allow ``state=clean`` to be used without defining a ``release`` (https://github.com/ansible-collections/community.general/issues/1852). \ No newline at end of file diff --git a/changelogs/fragments/1861-python3-keys.yml b/changelogs/fragments/1861-python3-keys.yml deleted file mode 100644 index 029ed93575..0000000000 --- a/changelogs/fragments/1861-python3-keys.yml +++ /dev/null @@ -1,22 +0,0 @@ -bugfixes: - - redis cache plugin - wrapped usages of ``keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - memcached cache plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - diy callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - selective callback plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - chef_databag lookup plugin - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - net_tools.nios.api module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - utm_utils module_utils - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - lxc_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - lxd_container - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - oneandone_monitoring_policy - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - oci_vcn - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - spotinst_aws_elastigroup - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - sensu_check - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - redhat_subscription - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - idrac_redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - idrac_redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - idrac_redfish_info - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - redfish_command - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - redfish_config - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - vdo - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). - - nsot inventory script - wrapped usages of ``dict.keys()`` in ``list()`` for Python 3 compatibility (https://github.com/ansible-collections/community.general/pull/1861). diff --git a/changelogs/fragments/1867-modhelper-cmdmixin-dict-params.yml b/changelogs/fragments/1867-modhelper-cmdmixin-dict-params.yml deleted file mode 100644 index 3f757b233a..0000000000 --- a/changelogs/fragments/1867-modhelper-cmdmixin-dict-params.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - module_helper module utils - ``CmdMixin.run_command()`` now accepts ``dict`` command arguments, providing the parameter and its value (https://github.com/ansible-collections/community.general/pull/1867). diff --git a/changelogs/fragments/1871-infoblox-inventory.yml b/changelogs/fragments/1871-infoblox-inventory.yml deleted file mode 100644 index d49d176f1b..0000000000 --- a/changelogs/fragments/1871-infoblox-inventory.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "infoblox inventory script - make sure that the script also works with Ansible 2.9, and returns a more helpful error when community.general is not installed as part of Ansible 2.10/3 (https://github.com/ansible-collections/community.general/pull/1871)." diff --git a/changelogs/fragments/1880-fix_cobbler_system_ssl.yml b/changelogs/fragments/1880-fix_cobbler_system_ssl.yml deleted file mode 100644 index 849f703130..0000000000 --- a/changelogs/fragments/1880-fix_cobbler_system_ssl.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - cobbler_sync, cobbler_system - fix SSL/TLS certificate check when ``validate_certs`` set to ``false`` (https://github.com/ansible-collections/community.general/pull/1880). diff --git a/changelogs/fragments/1882-fix-nmcli-ensure-slave-type-for-bond-slave.yml b/changelogs/fragments/1882-fix-nmcli-ensure-slave-type-for-bond-slave.yml deleted file mode 100644 index 47569b6a24..0000000000 --- a/changelogs/fragments/1882-fix-nmcli-ensure-slave-type-for-bond-slave.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - nmcli - ensure the ``slave-type`` option is passed to ``nmcli`` for type ``bond-slave`` (https://github.com/ansible-collections/community.general/pull/1882). diff --git a/changelogs/fragments/1885-sanity-check-fixes-batch3.yml b/changelogs/fragments/1885-sanity-check-fixes-batch3.yml deleted file mode 100644 index bf819a6e21..0000000000 --- a/changelogs/fragments/1885-sanity-check-fixes-batch3.yml +++ /dev/null @@ -1,18 +0,0 @@ -minor_changes: - - oneandone_firewall_policy - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - oneandone_load_balancer - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - oneandone_monitoring_policy - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - oneandone_private_network - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - oneandone_server - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - profitbricks - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - profitbricks_volume - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - webfaction_domain - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - webfaction_site - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - consul - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - consul_acl - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - consul_session - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - datadog_monitor - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - sensu_check - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - sensu_client - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - sensu_handler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). - - bundler - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1885). diff --git a/changelogs/fragments/1894-feat-nmcli-add-method4-and-method6.yml b/changelogs/fragments/1894-feat-nmcli-add-method4-and-method6.yml deleted file mode 100644 index 05daac483c..0000000000 --- a/changelogs/fragments/1894-feat-nmcli-add-method4-and-method6.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - nmcli - add ``method4`` and ``method6`` options (https://github.com/ansible-collections/community.general/pull/1894). diff --git a/changelogs/fragments/1895-proxmox-kvm-fix-issue-1875.yml b/changelogs/fragments/1895-proxmox-kvm-fix-issue-1875.yml deleted file mode 100644 index 73d908cfa8..0000000000 --- a/changelogs/fragments/1895-proxmox-kvm-fix-issue-1875.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - proxmox_kvm - fix parameter ``vmid`` passed twice to ``exit_json`` while creating a virtual machine without cloning (https://github.com/ansible-collections/community.general/issues/1875, https://github.com/ansible-collections/community.general/pull/1895). diff --git a/changelogs/fragments/1912-yum_versionlock-lock_unlock_concurrently.yml b/changelogs/fragments/1912-yum_versionlock-lock_unlock_concurrently.yml deleted file mode 100644 index 36f40da0fe..0000000000 --- a/changelogs/fragments/1912-yum_versionlock-lock_unlock_concurrently.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: - - yum_versionlock - Do the lock/unlock concurrently to speed up (https://github.com/ansible-collections/community.general/pull/1912). diff --git a/changelogs/fragments/1914-add-sanitization-to-url.yml b/changelogs/fragments/1914-add-sanitization-to-url.yml deleted file mode 100644 index 3b41bcb7af..0000000000 --- a/changelogs/fragments/1914-add-sanitization-to-url.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - proxmox inventory - added handling of extra trailing slashes in the URL (https://github.com/ansible-collections/community.general/pull/1914). diff --git a/changelogs/fragments/1916-add-version-sort-filter.yml b/changelogs/fragments/1916-add-version-sort-filter.yml deleted file mode 100644 index a06b464e55..0000000000 --- a/changelogs/fragments/1916-add-version-sort-filter.yml +++ /dev/null @@ -1,3 +0,0 @@ -add plugin.filter: - - name: version_sort - description: Sort a list according to version order instead of pure alphabetical one diff --git a/changelogs/fragments/1927-removed-parameter-invalid.yml b/changelogs/fragments/1927-removed-parameter-invalid.yml deleted file mode 100644 index 6dbc2e187b..0000000000 --- a/changelogs/fragments/1927-removed-parameter-invalid.yml +++ /dev/null @@ -1,12 +0,0 @@ -deprecated_features: - - composer - deprecated invalid parameter aliases ``working-dir``, ``global-command``, ``prefer-source``, ``prefer-dist``, ``no-dev``, ``no-scripts``, ``no-plugins``, ``optimize-autoloader``, ``classmap-authoritative``, ``apcu-autoloader``, ``ignore-platform-reqs``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - apt_rpm - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - homebrew - deprecated invalid parameter alias ``update-brew``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - homebrew_cask - deprecated invalid parameter alias ``update-brew``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - opkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - pacman - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - slackpkg - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - urmpi - deprecated invalid parameter aliases ``update-cache`` and ``no-recommends``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - xbps - deprecated invalid parameter alias ``update-cache``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - github_deploy_key - deprecated invalid parameter alias ``2fa_token``, will be removed in 5.0.0 (https://github.com/ansible-collections/community.general/pull/1927). - - puppet - deprecated undocumented parameter ``show_diff``, will be removed in 7.0.0. (https://github.com/ansible-collections/community.general/pull/1927). diff --git a/changelogs/fragments/1928-bigpanda-message.yml b/changelogs/fragments/1928-bigpanda-message.yml deleted file mode 100644 index 081b51cc0f..0000000000 --- a/changelogs/fragments/1928-bigpanda-message.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "bigpanda - actually use the ``deployment_message`` option (https://github.com/ansible-collections/community.general/pull/1928)." diff --git a/changelogs/fragments/1929-grove-message.yml b/changelogs/fragments/1929-grove-message.yml deleted file mode 100644 index 402aa24639..0000000000 --- a/changelogs/fragments/1929-grove-message.yml +++ /dev/null @@ -1,4 +0,0 @@ -minor_changes: -- "grove - the option ``message`` has been renamed to ``message_content``. The old name ``message`` is kept as an alias and will be removed for community.general 4.0.0. This was done because ``message`` is used internally by Ansible (https://github.com/ansible-collections/community.general/pull/1929)." -deprecated_features: -- "grove - the option ``message`` will be removed in community.general 4.0.0. Use the new option ``message_content`` instead (https://github.com/ansible-collections/community.general/pull/1929)." diff --git a/changelogs/fragments/1949-proxmox-inventory-tags.yml b/changelogs/fragments/1949-proxmox-inventory-tags.yml deleted file mode 100644 index 073428c2e6..0000000000 --- a/changelogs/fragments/1949-proxmox-inventory-tags.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -bugfixes: -- proxmox inventory plugin - allowed proxomox tag string to contain commas when returned as fact (https://github.com/ansible-collections/community.general/pull/1949). -minor_changes: -- proxmox inventory plugin - added ``tags_parsed`` fact containing tags parsed as a list (https://github.com/ansible-collections/community.general/pull/1949). diff --git a/changelogs/fragments/1970-valmod-batch7.yml b/changelogs/fragments/1970-valmod-batch7.yml deleted file mode 100644 index cd577d4578..0000000000 --- a/changelogs/fragments/1970-valmod-batch7.yml +++ /dev/null @@ -1,18 +0,0 @@ -minor_changes: - - heroku_collaborator - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - linode_v4 - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - one_host - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - one_image_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - one_vm - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - scaleway_compute - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - scaleway_lb - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - manageiq_alert_profiles - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - manageiq_policies - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - manageiq_tags - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - oneview_datacenter_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - oneview_enclosure_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - oneview_ethernet_network_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). - - oneview_network_set_info - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/1970). -bugfixes: - - manageiq_provider - wrapped ``dict.keys()`` with ``list`` for use in ``choices`` setting (https://github.com/ansible-collections/community.general/pull/1970). - - packet_volume_attachment - removed extraneous ``print`` call - old debug? (https://github.com/ansible-collections/community.general/pull/1970). diff --git a/changelogs/fragments/1972-ini_file-empty-str-value.yml b/changelogs/fragments/1972-ini_file-empty-str-value.yml deleted file mode 100644 index 7beba5ac4c..0000000000 --- a/changelogs/fragments/1972-ini_file-empty-str-value.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - ini_file - allows an empty string as a value for an option (https://github.com/ansible-collections/community.general/pull/1972). diff --git a/changelogs/fragments/1977-jenkinsjob-validate-certs.yml b/changelogs/fragments/1977-jenkinsjob-validate-certs.yml deleted file mode 100644 index b4f7b2f938..0000000000 --- a/changelogs/fragments/1977-jenkinsjob-validate-certs.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - jenkins_job - add a ``validate_certs`` parameter that allows disabling TLS/SSL certificate validation (https://github.com/ansible-collections/community.general/issues/255). diff --git a/changelogs/fragments/1978-jira-transition-logic.yml b/changelogs/fragments/1978-jira-transition-logic.yml deleted file mode 100644 index 12b4adc56d..0000000000 --- a/changelogs/fragments/1978-jira-transition-logic.yml +++ /dev/null @@ -1,4 +0,0 @@ -bugfixes: - - jira - fixed fields' update in ticket transitions (https://github.com/ansible-collections/community.general/issues/818). -minor_changes: - - jira - added parameter ``account_id`` for compatibility with recent versions of JIRA (https://github.com/ansible-collections/community.general/issues/818, https://github.com/ansible-collections/community.general/pull/1978). diff --git a/changelogs/fragments/1991-proxmox-inventory-fix-template-in-pool.yml b/changelogs/fragments/1991-proxmox-inventory-fix-template-in-pool.yml deleted file mode 100644 index 90a438dddf..0000000000 --- a/changelogs/fragments/1991-proxmox-inventory-fix-template-in-pool.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - proxmox inventory - exclude qemu templates from inclusion to the inventory via pools (https://github.com/ansible-collections/community.general/issues/1986, https://github.com/ansible-collections/community.general/pull/1991). diff --git a/changelogs/fragments/1993-haproxy-fix-draining.yml b/changelogs/fragments/1993-haproxy-fix-draining.yml deleted file mode 100644 index fd5c77f573..0000000000 --- a/changelogs/fragments/1993-haproxy-fix-draining.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - haproxy - fix a bug preventing haproxy from properly entering ``DRAIN`` mode (https://github.com/ansible-collections/community.general/issues/1913). diff --git a/changelogs/fragments/1999-proxmox-fix-issue-1955.yml b/changelogs/fragments/1999-proxmox-fix-issue-1955.yml deleted file mode 100644 index 274e70fb0f..0000000000 --- a/changelogs/fragments/1999-proxmox-fix-issue-1955.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: -- proxmox - removed requirement that root password is provided when containter state is ``present`` (https://github.com/ansible-collections/community.general/pull/1999). diff --git a/changelogs/fragments/2000-proxmox_kvm-tag-support.yml b/changelogs/fragments/2000-proxmox_kvm-tag-support.yml deleted file mode 100644 index d4084ecd67..0000000000 --- a/changelogs/fragments/2000-proxmox_kvm-tag-support.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- proxmox_kvm - added new module parameter ``tags`` for use with PVE 6+ (https://github.com/ansible-collections/community.general/pull/2000). diff --git a/changelogs/fragments/2001-no_log-false.yml b/changelogs/fragments/2001-no_log-false.yml deleted file mode 100644 index 82d9ba0bb0..0000000000 --- a/changelogs/fragments/2001-no_log-false.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "Mark various module options with ``no_log=False`` which have a name that potentially could leak secrets, but which do not (https://github.com/ansible-collections/community.general/pull/2001)." diff --git a/changelogs/fragments/2006-valmod-batch8.yml b/changelogs/fragments/2006-valmod-batch8.yml deleted file mode 100644 index 30be5e16b2..0000000000 --- a/changelogs/fragments/2006-valmod-batch8.yml +++ /dev/null @@ -1,4 +0,0 @@ -minor_changes: - - rax - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). - - rax_cdb_user - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). - - rax_scaling_group - elements of list parameters are now validated (https://github.com/ansible-collections/community.general/pull/2006). diff --git a/changelogs/fragments/2008-update-java-cert-replace-cert-when-changed.yml b/changelogs/fragments/2008-update-java-cert-replace-cert-when-changed.yml deleted file mode 100644 index 8cfda91016..0000000000 --- a/changelogs/fragments/2008-update-java-cert-replace-cert-when-changed.yml +++ /dev/null @@ -1,7 +0,0 @@ -minor_changes: - - "java_cert - change ``state: present`` to check certificates by hash, not just alias name (https://github.com/ansible/ansible/issues/43249)." -bugfixes: - - "java_cert - allow setting ``state: absent`` by providing just the ``cert_alias`` (https://github.com/ansible/ansible/issues/27982)." - - "java_cert - properly handle proxy arguments when the scheme is provided (https://github.com/ansible/ansible/issues/54481)." -security_fixes: - - "java_cert - remove password from ``run_command`` arguments (https://github.com/ansible-collections/community.general/pull/2008)." diff --git a/changelogs/fragments/2013-proxmox-purge-parameter.yml b/changelogs/fragments/2013-proxmox-purge-parameter.yml deleted file mode 100644 index 6c681e5a19..0000000000 --- a/changelogs/fragments/2013-proxmox-purge-parameter.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- proxmox - added ``purge`` module parameter for use when deleting lxc's with HA options (https://github.com/ansible-collections/community.general/pull/2013). diff --git a/changelogs/fragments/2014-allow-root-for-kibana-plugin.yaml b/changelogs/fragments/2014-allow-root-for-kibana-plugin.yaml deleted file mode 100644 index 6420203888..0000000000 --- a/changelogs/fragments/2014-allow-root-for-kibana-plugin.yaml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - kibana_plugin - add parameter for passing ``--allow-root`` flag to kibana and kibana-plugin commands (https://github.com/ansible-collections/community.general/pull/2014). diff --git a/changelogs/fragments/2020-remove-unused-param-in-rax.yml b/changelogs/fragments/2020-remove-unused-param-in-rax.yml deleted file mode 100644 index 333548f0b9..0000000000 --- a/changelogs/fragments/2020-remove-unused-param-in-rax.yml +++ /dev/null @@ -1,2 +0,0 @@ -removed_features: - - rax - unused parameter ``service`` removed (https://github.com/ansible-collections/community.general/pull/2020). diff --git a/changelogs/fragments/2024-module-helper-fixes.yml b/changelogs/fragments/2024-module-helper-fixes.yml deleted file mode 100644 index 3ce3cc71dc..0000000000 --- a/changelogs/fragments/2024-module-helper-fixes.yml +++ /dev/null @@ -1,4 +0,0 @@ -bugfixes: - - module_helper module utils - actually ignoring formatting of parameters with value ``None`` (https://github.com/ansible-collections/community.general/pull/2024). - - module_helper module utils - handling ``ModuleHelperException`` now properly calls ``fail_json()`` (https://github.com/ansible-collections/community.general/pull/2024). - - module_helper module utils - use the command name as-is in ``CmdMixin`` if it fails ``get_bin_path()`` - allowing full path names to be passed (https://github.com/ansible-collections/community.general/pull/2024). diff --git a/changelogs/fragments/2027-add-redfish-session-create-delete-authenticate.yml b/changelogs/fragments/2027-add-redfish-session-create-delete-authenticate.yml deleted file mode 100644 index b5c22b9502..0000000000 --- a/changelogs/fragments/2027-add-redfish-session-create-delete-authenticate.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - redfish_* modules, redfish_utils module utils - add support for Redfish session create, delete, and authenticate (https://github.com/ansible-collections/community.general/issues/1975). diff --git a/changelogs/fragments/2031-ipa_sudorule_add_runasextusers.yml b/changelogs/fragments/2031-ipa_sudorule_add_runasextusers.yml deleted file mode 100644 index 9e70a16d80..0000000000 --- a/changelogs/fragments/2031-ipa_sudorule_add_runasextusers.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- ipa_sudorule - add support for setting sudo runasuser (https://github.com/ansible-collections/community.general/pull/2031). diff --git a/changelogs/fragments/2032-one_image-pyone.yml b/changelogs/fragments/2032-one_image-pyone.yml deleted file mode 100644 index 4975cb73ad..0000000000 --- a/changelogs/fragments/2032-one_image-pyone.yml +++ /dev/null @@ -1,2 +0,0 @@ -breaking_changes: - - one_image - use pyone instead of python-oca (https://github.com/ansible-collections/community.general/pull/2032). diff --git a/changelogs/fragments/2036-scaleway-inventory.yml b/changelogs/fragments/2036-scaleway-inventory.yml deleted file mode 100644 index 44161306ac..0000000000 --- a/changelogs/fragments/2036-scaleway-inventory.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - scaleway inventory plugin - fix pagination on scaleway inventory plugin (https://github.com/ansible-collections/community.general/pull/2036). diff --git a/changelogs/fragments/2037-add-from-csv-filter.yml b/changelogs/fragments/2037-add-from-csv-filter.yml deleted file mode 100644 index d99c4cd0a8..0000000000 --- a/changelogs/fragments/2037-add-from-csv-filter.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -add plugin.filter: - - name: from_csv - description: Converts CSV text input into list of dicts -minor_changes: - - csv module utils - new module_utils for shared functions between ``from_csv`` filter and ``read_csv`` module (https://github.com/ansible-collections/community.general/pull/2037). - - read_csv - refactored read_csv module to use shared csv functions from csv module_utils (https://github.com/ansible-collections/community.general/pull/2037). diff --git a/changelogs/fragments/2040-fix-index-error-in-redfish-set-manager-nic.yml b/changelogs/fragments/2040-fix-index-error-in-redfish-set-manager-nic.yml deleted file mode 100644 index 04d9a11101..0000000000 --- a/changelogs/fragments/2040-fix-index-error-in-redfish-set-manager-nic.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - redfish_config module, redfish_utils module utils - fix IndexError in ``SetManagerNic`` command (https://github.com/ansible-collections/community.general/issues/1692). diff --git a/changelogs/fragments/2057-nios-devel.yml b/changelogs/fragments/2057-nios-devel.yml deleted file mode 100644 index be9f8a970f..0000000000 --- a/changelogs/fragments/2057-nios-devel.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "nios* modules - fix modules to work with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/2057)." diff --git a/changelogs/fragments/2061-archive-refactor1.yml b/changelogs/fragments/2061-archive-refactor1.yml deleted file mode 100644 index a7189a2f59..0000000000 --- a/changelogs/fragments/2061-archive-refactor1.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - archive - refactored some reused code out into a couple of functions (https://github.com/ansible-collections/community.general/pull/2061). diff --git a/changelogs/fragments/2065-snmp-facts-timeout.yml b/changelogs/fragments/2065-snmp-facts-timeout.yml deleted file mode 100644 index 0e6a4e54fa..0000000000 --- a/changelogs/fragments/2065-snmp-facts-timeout.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - snmp_facts - added parameters ``timeout`` and ``retries`` to module (https://github.com/ansible-collections/community.general/issues/980). diff --git a/changelogs/fragments/2072-stacki-host-params-fallback.yml b/changelogs/fragments/2072-stacki-host-params-fallback.yml deleted file mode 100644 index f586a6eb0c..0000000000 --- a/changelogs/fragments/2072-stacki-host-params-fallback.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - stacki_host - replaced ``default`` to environment variables with ``fallback`` to them (https://github.com/ansible-collections/community.general/pull/2072). diff --git a/changelogs/fragments/2094-bugfix-respect-PATH-env-variable-in-zypper-modules.yaml b/changelogs/fragments/2094-bugfix-respect-PATH-env-variable-in-zypper-modules.yaml deleted file mode 100644 index e0addce2fc..0000000000 --- a/changelogs/fragments/2094-bugfix-respect-PATH-env-variable-in-zypper-modules.yaml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - zypper, zypper_repository - respect ``PATH`` environment variable when resolving zypper executable path (https://github.com/ansible-collections/community.general/pull/2094). diff --git a/changelogs/fragments/2110-vdo-add_force_option.yaml b/changelogs/fragments/2110-vdo-add_force_option.yaml deleted file mode 100644 index 9e93a919a2..0000000000 --- a/changelogs/fragments/2110-vdo-add_force_option.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: - - vdo - add ``force`` option (https://github.com/ansible-collections/community.general/issues/2101). diff --git a/changelogs/fragments/2116-add-fields-to-ipa-config-module.yml b/changelogs/fragments/2116-add-fields-to-ipa-config-module.yml deleted file mode 100644 index d1e1dc3180..0000000000 --- a/changelogs/fragments/2116-add-fields-to-ipa-config-module.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - ipa_config - add new options ``ipaconfigstring``, ``ipadefaultprimarygroup``, ``ipagroupsearchfields``, ``ipahomesrootdir``, ``ipabrkauthzdata``, ``ipamaxusernamelength``, ``ipapwdexpadvnotify``, ``ipasearchrecordslimit``, ``ipasearchtimelimit``, ``ipauserauthtype``, and ``ipausersearchfields`` (https://github.com/ansible-collections/community.general/pull/2116). diff --git a/changelogs/fragments/2125-git-config-scope-file.yml b/changelogs/fragments/2125-git-config-scope-file.yml deleted file mode 100644 index 75862e0333..0000000000 --- a/changelogs/fragments/2125-git-config-scope-file.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - git_config - fixed scope ``file`` behaviour and added integraton test for it (https://github.com/ansible-collections/community.general/issues/2117). diff --git a/changelogs/fragments/2135-vmadm-resolvers-type-fix.yml b/changelogs/fragments/2135-vmadm-resolvers-type-fix.yml deleted file mode 100644 index fcce6e12e1..0000000000 --- a/changelogs/fragments/2135-vmadm-resolvers-type-fix.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - vmadm - correct type of list elements in ``resolvers`` parameter (https://github.com/ansible-collections/community.general/issues/2135). diff --git a/changelogs/fragments/2139-dimensiondata_network-str-format.yml b/changelogs/fragments/2139-dimensiondata_network-str-format.yml deleted file mode 100644 index 115b04f045..0000000000 --- a/changelogs/fragments/2139-dimensiondata_network-str-format.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - dimensiondata_network - bug when formatting message, instead of % a simple comma was used (https://github.com/ansible-collections/community.general/pull/2139). diff --git a/changelogs/fragments/2142-apache2_mod_proxy-cleanup.yml b/changelogs/fragments/2142-apache2_mod_proxy-cleanup.yml deleted file mode 100644 index 6a24f1afc3..0000000000 --- a/changelogs/fragments/2142-apache2_mod_proxy-cleanup.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - apache2_mod_proxy - refactored/cleaned-up part of the code (https://github.com/ansible-collections/community.general/pull/2142). diff --git a/changelogs/fragments/2143-kibana_plugin-fixed-function-calls.yml b/changelogs/fragments/2143-kibana_plugin-fixed-function-calls.yml deleted file mode 100644 index 54a41cd237..0000000000 --- a/changelogs/fragments/2143-kibana_plugin-fixed-function-calls.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - kibana_plugin - added missing parameters to ``remove_plugin`` when using ``state=present force=true``, and fix potential quoting errors when invoking ``kibana`` (https://github.com/ansible-collections/community.general/pull/2143). diff --git a/changelogs/fragments/2144-atomic_get_bin_path.yml b/changelogs/fragments/2144-atomic_get_bin_path.yml deleted file mode 100644 index eeb55114d2..0000000000 --- a/changelogs/fragments/2144-atomic_get_bin_path.yml +++ /dev/null @@ -1,4 +0,0 @@ -minor_changes: - - atomic_container - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). - - atomic_host - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). - - atomic_image - using ``get_bin_path()`` before calling ``run_command()`` (https://github.com/ansible-collections/community.general/pull/2144). diff --git a/changelogs/fragments/2146-npm-add_no_bin_links_option.yaml b/changelogs/fragments/2146-npm-add_no_bin_links_option.yaml deleted file mode 100644 index 651af80186..0000000000 --- a/changelogs/fragments/2146-npm-add_no_bin_links_option.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: - - npm - add ``no_bin_links`` option (https://github.com/ansible-collections/community.general/issues/2128). diff --git a/changelogs/fragments/2148-proxmox-inventory-agent-interfaces.yml b/changelogs/fragments/2148-proxmox-inventory-agent-interfaces.yml deleted file mode 100644 index 0ef97f20ed..0000000000 --- a/changelogs/fragments/2148-proxmox-inventory-agent-interfaces.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- proxmox inventory plugin - added ``proxmox_agent_interfaces`` fact describing network interfaces returned from a QEMU guest agent (https://github.com/ansible-collections/community.general/pull/2148). diff --git a/changelogs/fragments/2157-unreachable-code.yml b/changelogs/fragments/2157-unreachable-code.yml deleted file mode 100644 index 7cb84b4db9..0000000000 --- a/changelogs/fragments/2157-unreachable-code.yml +++ /dev/null @@ -1,4 +0,0 @@ -minor_changes: - - rhevm - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). - - ovh_ip_failover - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). - - bitbucket_pipeline_variable - removed unreachable code (https://github.com/ansible-collections/community.general/pull/2157). diff --git a/changelogs/fragments/2159-ipa-user-sshpubkey-multi-word-comments.yaml b/changelogs/fragments/2159-ipa-user-sshpubkey-multi-word-comments.yaml deleted file mode 100644 index 10547bb71b..0000000000 --- a/changelogs/fragments/2159-ipa-user-sshpubkey-multi-word-comments.yaml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - ipa_user - allow ``sshpubkey`` to permit multiple word comments (https://github.com/ansible-collections/community.general/pull/2159). diff --git a/changelogs/fragments/2160-list-literals.yml b/changelogs/fragments/2160-list-literals.yml deleted file mode 100644 index 661b1e322e..0000000000 --- a/changelogs/fragments/2160-list-literals.yml +++ /dev/null @@ -1,11 +0,0 @@ -minor_changes: - - hiera lookup - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - known_hosts module utils - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - nictagadm - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - smartos_image_info - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - xattr - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - ipwcli_dns - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - svr4pkg - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - zfs_facts - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - zpool_facts - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). - - beadm - minor refactor converting multiple statements to a single list literal (https://github.com/ansible-collections/community.general/pull/2160). diff --git a/changelogs/fragments/2161-pkgutil-list-extend.yml b/changelogs/fragments/2161-pkgutil-list-extend.yml deleted file mode 100644 index 9af970afd8..0000000000 --- a/changelogs/fragments/2161-pkgutil-list-extend.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - pkgutil - fixed calls to ``list.extend()`` (https://github.com/ansible-collections/community.general/pull/2161). diff --git a/changelogs/fragments/2162-modhelper-variables.yml b/changelogs/fragments/2162-modhelper-variables.yml deleted file mode 100644 index 68b0edc37e..0000000000 --- a/changelogs/fragments/2162-modhelper-variables.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - module_helper module utils - added mechanism to manage variables, providing automatic output of variables, change status and diff information (https://github.com/ansible-collections/community.general/pull/2162). diff --git a/changelogs/fragments/2162-proxmox-constructable.yml b/changelogs/fragments/2162-proxmox-constructable.yml deleted file mode 100644 index dfcb1e3495..0000000000 --- a/changelogs/fragments/2162-proxmox-constructable.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- proxmox inventory plugin - added ``Constructable`` class to the inventory to provide options ``strict``, ``keyed_groups``, ``groups``, and ``compose`` (https://github.com/ansible-collections/community.general/pull/2180). diff --git a/changelogs/fragments/2163-java_keystore_1667_improve_temp_files_storage.yml b/changelogs/fragments/2163-java_keystore_1667_improve_temp_files_storage.yml deleted file mode 100644 index 43d183707c..0000000000 --- a/changelogs/fragments/2163-java_keystore_1667_improve_temp_files_storage.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -bugfixes: - - "java_keystore - use tempfile lib to create temporary files with randomized - names, and remove the temporary PKCS#12 keystore as well as other materials - (https://github.com/ansible-collections/community.general/issues/1667)." diff --git a/changelogs/fragments/2174-ipa-user-userauthtype-multiselect.yml b/changelogs/fragments/2174-ipa-user-userauthtype-multiselect.yml deleted file mode 100644 index d162f19b7a..0000000000 --- a/changelogs/fragments/2174-ipa-user-userauthtype-multiselect.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - ipa_user - fix ``userauthtype`` option to take in list of strings for the multi-select field instead of single string (https://github.com/ansible-collections/community.general/pull/2174). diff --git a/changelogs/fragments/2177-java_keystore_1668_dont_expose_secrets_on_cmdline.yml b/changelogs/fragments/2177-java_keystore_1668_dont_expose_secrets_on_cmdline.yml deleted file mode 100644 index 0d961a53ac..0000000000 --- a/changelogs/fragments/2177-java_keystore_1668_dont_expose_secrets_on_cmdline.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -security_fixes: - - "java_keystore - pass secret to keytool through an environment variable to not expose it as a - commandline argument (https://github.com/ansible-collections/community.general/issues/1668)." diff --git a/changelogs/fragments/2183-java_keystore_improve_error_handling.yml b/changelogs/fragments/2183-java_keystore_improve_error_handling.yml deleted file mode 100644 index 5d6ceef511..0000000000 --- a/changelogs/fragments/2183-java_keystore_improve_error_handling.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -bugfixes: - - "java_keystore - improve error handling and return ``cmd`` as documented. - Force ``LANG``, ``LC_ALL`` and ``LC_MESSAGES`` environment variables to ``C`` to rely - on ``keytool`` output parsing. Fix pylint's ``unused-variable`` and ``no-else-return`` - hints (https://github.com/ansible-collections/community.general/pull/2183)." diff --git a/changelogs/fragments/2185-xfconf-absent-check-mode.yml b/changelogs/fragments/2185-xfconf-absent-check-mode.yml deleted file mode 100644 index 059f4acd9a..0000000000 --- a/changelogs/fragments/2185-xfconf-absent-check-mode.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - xfconf - module was not honoring check mode when ``state`` was ``absent`` (https://github.com/ansible-collections/community.general/pull/2185). diff --git a/changelogs/fragments/2188-xfconf-modhelper-variables.yml b/changelogs/fragments/2188-xfconf-modhelper-variables.yml deleted file mode 100644 index 19e94254bd..0000000000 --- a/changelogs/fragments/2188-xfconf-modhelper-variables.yml +++ /dev/null @@ -1,3 +0,0 @@ -minor_changes: - - module_helper module utils - added management of facts and adhoc setting of the initial value for variables (https://github.com/ansible-collections/community.general/pull/2188). - - xfconf - changed implementation to use ``ModuleHelper`` new features (https://github.com/ansible-collections/community.general/pull/2188). diff --git a/changelogs/fragments/2192-add-jira-attach.yml b/changelogs/fragments/2192-add-jira-attach.yml deleted file mode 100644 index 5877250541..0000000000 --- a/changelogs/fragments/2192-add-jira-attach.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - jira - added ``attach`` operation, which allows a user to attach a file to an issue (https://github.com/ansible-collections/community.general/pull/2192). diff --git a/changelogs/fragments/2203-modhelper-cause-changes-deco.yml b/changelogs/fragments/2203-modhelper-cause-changes-deco.yml deleted file mode 100644 index b61f97d6b8..0000000000 --- a/changelogs/fragments/2203-modhelper-cause-changes-deco.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - module_helper module utils - fixed decorator ``cause_changes`` (https://github.com/ansible-collections/community.general/pull/2203). diff --git a/changelogs/fragments/2204-github_repo-fix-baseurl_port.yml b/changelogs/fragments/2204-github_repo-fix-baseurl_port.yml deleted file mode 100644 index 0df3bd8ece..0000000000 --- a/changelogs/fragments/2204-github_repo-fix-baseurl_port.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - github_repo - PyGithub bug does not allow explicit port in ``base_url``. Specifying port is not required (https://github.com/PyGithub/PyGithub/issues/1913). diff --git a/changelogs/fragments/2208-jira-revamp.yml b/changelogs/fragments/2208-jira-revamp.yml deleted file mode 100644 index 32f1650aa0..0000000000 --- a/changelogs/fragments/2208-jira-revamp.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - jira - revamped the module as a class using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2208). diff --git a/changelogs/fragments/2218-cpanm-revamp.yml b/changelogs/fragments/2218-cpanm-revamp.yml deleted file mode 100644 index 668a84f06b..0000000000 --- a/changelogs/fragments/2218-cpanm-revamp.yml +++ /dev/null @@ -1,5 +0,0 @@ -minor_changes: - - cpanm - rewritten using ``ModuleHelper`` (https://github.com/ansible-collections/community.general/pull/2218). - - cpanm - honor and install specified version when running in ``new`` mode; that feature is not available in ``compatibility`` mode (https://github.com/ansible-collections/community.general/issues/208). -deprecated_features: - - cpanm - parameter ``system_lib`` deprecated in favor of using ``become`` (https://github.com/ansible-collections/community.general/pull/2218). diff --git a/changelogs/fragments/2220_nmcli_wifi_support.yaml b/changelogs/fragments/2220_nmcli_wifi_support.yaml deleted file mode 100644 index 224c4dc526..0000000000 --- a/changelogs/fragments/2220_nmcli_wifi_support.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- "nmcli - add ability to connect to a Wifi network and also to attach it to a master (bond) (https://github.com/ansible-collections/community.general/pull/2220)." diff --git a/changelogs/fragments/2223_nmcli_no_IP_config_on_slave.yaml b/changelogs/fragments/2223_nmcli_no_IP_config_on_slave.yaml deleted file mode 100644 index 4d98b62922..0000000000 --- a/changelogs/fragments/2223_nmcli_no_IP_config_on_slave.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- "nmcli - do not set IP configuration on slave connection (https://github.com/ansible-collections/community.general/pull/2223)." diff --git a/changelogs/fragments/2224_nmcli_allow_MAC_overwrite.yaml b/changelogs/fragments/2224_nmcli_allow_MAC_overwrite.yaml deleted file mode 100644 index 98852463d8..0000000000 --- a/changelogs/fragments/2224_nmcli_allow_MAC_overwrite.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- "nmcli - don't restrict the ability to manually set the MAC address to the bridge (https://github.com/ansible-collections/community.general/pull/2224)." diff --git a/changelogs/fragments/2230-java_keystore-1669-ssl-input-files-by-path.yml b/changelogs/fragments/2230-java_keystore-1669-ssl-input-files-by-path.yml deleted file mode 100644 index 0622e93c31..0000000000 --- a/changelogs/fragments/2230-java_keystore-1669-ssl-input-files-by-path.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -minor_changes: - - "java_keystore - add options ``certificate_path`` and ``private_key_path``, - mutually exclusive with ``certificate`` and ``private_key`` respectively, and - targetting files on remote hosts rather than their contents on the controller. - (https://github.com/ansible-collections/community.general/issues/1669)." diff --git a/changelogs/fragments/2236-jira-isinstance.yml b/changelogs/fragments/2236-jira-isinstance.yml deleted file mode 100644 index e80cbacdf9..0000000000 --- a/changelogs/fragments/2236-jira-isinstance.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - jira - fixed calling of ``isinstance`` (https://github.com/ansible-collections/community.general/issues/2234). diff --git a/changelogs/fragments/2244-hashids-filters.yml b/changelogs/fragments/2244-hashids-filters.yml deleted file mode 100644 index 568119e890..0000000000 --- a/changelogs/fragments/2244-hashids-filters.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -add plugin.filter: - - name: hashids_encode - description: Encodes YouTube-like hashes from a sequence of integers - - name: hashids_decode - description: Decodes a sequence of numbers from a YouTube-like hash diff --git a/changelogs/fragments/2245-proxmox_fix_agent_string_handling.yml b/changelogs/fragments/2245-proxmox_fix_agent_string_handling.yml deleted file mode 100644 index 3eae94f4ea..0000000000 --- a/changelogs/fragments/2245-proxmox_fix_agent_string_handling.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - proxmox inventory - added handling of commas in KVM agent configuration string (https://github.com/ansible-collections/community.general/pull/2245). diff --git a/changelogs/fragments/2246-terraform.yaml b/changelogs/fragments/2246-terraform.yaml deleted file mode 100644 index d2dd93e22e..0000000000 --- a/changelogs/fragments/2246-terraform.yaml +++ /dev/null @@ -1,4 +0,0 @@ -bugfixes: - - terraform - fix issue that cause the execution fail because from Terraform 0.15 on, the ``-var`` and ``-var-file`` options are no longer available on ``terraform validate`` (https://github.com/ansible-collections/community.general/pull/2246). - - terraform - fix issue that cause the destroy to fail because from Terraform 0.15 on, the ``terraform destroy -force`` option is replaced with ``terraform destroy -auto-approve`` (https://github.com/ansible-collections/community.general/issues/2247). - - terraform - remove uses of ``use_unsafe_shell=True`` (https://github.com/ansible-collections/community.general/pull/2246). diff --git a/changelogs/fragments/2249-linode_v4-support-private_ip-option.yaml b/changelogs/fragments/2249-linode_v4-support-private_ip-option.yaml deleted file mode 100644 index e5d6ca02d7..0000000000 --- a/changelogs/fragments/2249-linode_v4-support-private_ip-option.yaml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - linode_v4 - add support for ``private_ip`` option (https://github.com/ansible-collections/community.general/pull/2249). diff --git a/changelogs/fragments/2250-allow-keycloak-modules-to-take-token-as-param.yml b/changelogs/fragments/2250-allow-keycloak-modules-to-take-token-as-param.yml deleted file mode 100644 index 5b8deb2a03..0000000000 --- a/changelogs/fragments/2250-allow-keycloak-modules-to-take-token-as-param.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -minor_changes: - - keycloak_* modules - allow the keycloak modules to use a token for the - authentication, the modules can take either a token or the credentials - (https://github.com/ansible-collections/community.general/pull/2250). diff --git a/changelogs/fragments/2257-ldap_entry-params.yml b/changelogs/fragments/2257-ldap_entry-params.yml deleted file mode 100644 index f5c92d0b9c..0000000000 --- a/changelogs/fragments/2257-ldap_entry-params.yml +++ /dev/null @@ -1,2 +0,0 @@ -removed_features: -- "ldap_entry - the ``params`` parameter is now completely removed. Using it already triggered an error since community.general 0.1.2 (https://github.com/ansible-collections/community.general/pull/2257)." diff --git a/changelogs/fragments/2259-proxmox-multi-nic-and-unsupported.yml b/changelogs/fragments/2259-proxmox-multi-nic-and-unsupported.yml deleted file mode 100644 index d8f6f80385..0000000000 --- a/changelogs/fragments/2259-proxmox-multi-nic-and-unsupported.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -bugfixes: - - proxmox inventory plugin - support network interfaces without IP addresses, multiple network interfaces and unsupported/commanddisabled guest error (https://github.com/ansible-collections/community.general/pull/2263). -minor_changes: - - proxmox inventory plugin - allow to select whether ``ansible_host`` should be set for the proxmox nodes (https://github.com/ansible-collections/community.general/pull/2263). diff --git a/changelogs/fragments/2262-java_keystore-passphrase.yml b/changelogs/fragments/2262-java_keystore-passphrase.yml deleted file mode 100644 index 882ada97c3..0000000000 --- a/changelogs/fragments/2262-java_keystore-passphrase.yml +++ /dev/null @@ -1,8 +0,0 @@ -breaking_changes: -- "java_keystore - instead of failing, now overwrites keystore if the alias (name) is changed. - This was originally the intended behavior, but did not work due to a logic error. Make sure - that your playbooks and roles do not depend on the old behavior of failing instead of - overwriting (https://github.com/ansible-collections/community.general/issues/1671)." -- "java_keystore - instead of failing, now overwrites keystore if the passphrase is changed. - Make sure that your playbooks and roles do not depend on the old behavior of failing instead - of overwriting (https://github.com/ansible-collections/community.general/issues/1671)." diff --git a/changelogs/fragments/2267-lvol_size_addition-subtraction_support.yaml b/changelogs/fragments/2267-lvol_size_addition-subtraction_support.yaml deleted file mode 100644 index 25b79f4528..0000000000 --- a/changelogs/fragments/2267-lvol_size_addition-subtraction_support.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -minor_changes: - - lvol - added proper support for ``+-`` options when extending or reducing the logical volume (https://github.com/ansible-collections/community.general/issues/1988). -bugfixes: - - lvol - fixed sizing calculation rounding to match the underlying tools (https://github.com/ansible-collections/community.general/issues/1988). diff --git a/changelogs/fragments/2268-validation-univetion.yml b/changelogs/fragments/2268-validation-univetion.yml deleted file mode 100644 index f245380441..0000000000 --- a/changelogs/fragments/2268-validation-univetion.yml +++ /dev/null @@ -1,4 +0,0 @@ -bugfixes: - - udm_dns_record - fixed default value of parameter ``data`` to match its type (https://github.com/ansible-collections/community.general/pull/2268). -minor_changes: - - udm_dns_zone - elements of list parameters ``nameserver``, ``interfaces``, and ``mx`` are now validated (https://github.com/ansible-collections/community.general/pull/2268). diff --git a/changelogs/fragments/2280-pids-new-pattern-option.yml b/changelogs/fragments/2280-pids-new-pattern-option.yml deleted file mode 100644 index fb9f07e744..0000000000 --- a/changelogs/fragments/2280-pids-new-pattern-option.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: -- pids - new options ``pattern`` and `ignore_case`` for retrieving PIDs of processes matching a supplied pattern (https://github.com/ansible-collections/community.general/pull/2280). diff --git a/changelogs/fragments/2282-nmap-fix-cache-support.yml b/changelogs/fragments/2282-nmap-fix-cache-support.yml deleted file mode 100644 index 62b026eb25..0000000000 --- a/changelogs/fragments/2282-nmap-fix-cache-support.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - nmap inventory plugin - fix cache and constructed group support (https://github.com/ansible-collections/community.general/issues/2242). diff --git a/changelogs/fragments/2284-influxdb_retention_policy-idempotence.yml b/changelogs/fragments/2284-influxdb_retention_policy-idempotence.yml deleted file mode 100644 index 0df25ca462..0000000000 --- a/changelogs/fragments/2284-influxdb_retention_policy-idempotence.yml +++ /dev/null @@ -1,4 +0,0 @@ -bugfixes: - - influxdb_retention_policy - ensure idempotent module execution with different - duration and shard duration parameter values - (https://github.com/ansible-collections/community.general/issues/2281). diff --git a/changelogs/fragments/2308-terraform-add-plugin_paths-parameter.yaml b/changelogs/fragments/2308-terraform-add-plugin_paths-parameter.yaml deleted file mode 100644 index ec389b270c..0000000000 --- a/changelogs/fragments/2308-terraform-add-plugin_paths-parameter.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -minor_changes: - - terraform - add ``plugin_paths`` parameter which allows disabling Terraform from performing plugin discovery and auto-download (https://github.com/ansible-collections/community.general/pull/2308). diff --git a/changelogs/fragments/2329-hiera-lookup-plugin-return-type.yaml b/changelogs/fragments/2329-hiera-lookup-plugin-return-type.yaml deleted file mode 100644 index 4cced727a2..0000000000 --- a/changelogs/fragments/2329-hiera-lookup-plugin-return-type.yaml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - hiera lookup plugin - converts the return type of plugin to unicode string (https://github.com/ansible-collections/community.general/pull/2329). diff --git a/changelogs/fragments/2340-jenkins_plugin-py2.yml b/changelogs/fragments/2340-jenkins_plugin-py2.yml deleted file mode 100644 index f3bcdbd361..0000000000 --- a/changelogs/fragments/2340-jenkins_plugin-py2.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "jenkins_plugin - fixes Python 2 compatibility issue (https://github.com/ansible-collections/community.general/pull/2340)." \ No newline at end of file diff --git a/changelogs/fragments/2349-jira-bugfix-b64decode.yml b/changelogs/fragments/2349-jira-bugfix-b64decode.yml deleted file mode 100644 index 41a1dabb94..0000000000 --- a/changelogs/fragments/2349-jira-bugfix-b64decode.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - jira - fixed error when loading base64-encoded content as attachment (https://github.com/ansible-collections/community.general/pull/2349). diff --git a/changelogs/fragments/620-consul_io-env-variables-conf-based.yml b/changelogs/fragments/620-consul_io-env-variables-conf-based.yml deleted file mode 100644 index e3378428c5..0000000000 --- a/changelogs/fragments/620-consul_io-env-variables-conf-based.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -bugfixes: - - consul_io inventory script - kv_groups - fix byte chain decoding for Python 3 (https://github.com/ansible-collections/community.general/pull/620). -minor_changes: - - consul_io inventory script - conf options - allow custom configuration options via env variables (https://github.com/ansible-collections/community.general/pull/620). diff --git a/changelogs/fragments/719-manageiq-resource_id.yml b/changelogs/fragments/719-manageiq-resource_id.yml deleted file mode 100644 index bbeef5ff82..0000000000 --- a/changelogs/fragments/719-manageiq-resource_id.yml +++ /dev/null @@ -1,2 +0,0 @@ -minor_changes: - - manageiq_tags and manageiq_policies - added new parameter ``resource_id``. This parameter can be used instead of parameter ``resource_name`` (https://github.com/ansible-collections/community.general/pull/719). \ No newline at end of file diff --git a/changelogs/fragments/720-cloudforms_inventory.yml b/changelogs/fragments/720-cloudforms_inventory.yml deleted file mode 100644 index f5675205d1..0000000000 --- a/changelogs/fragments/720-cloudforms_inventory.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - cloudforms inventory - fixed issue that non-existing (archived) VMs were synced (https://github.com/ansible-collections/community.general/pull/720). diff --git a/changelogs/fragments/816-only-invocate-feature-when-variable-is-set.yml b/changelogs/fragments/816-only-invocate-feature-when-variable-is-set.yml deleted file mode 100644 index 7d48c77298..0000000000 --- a/changelogs/fragments/816-only-invocate-feature-when-variable-is-set.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - proxmox lxc - only add the features flag when module parameter ``features`` is set. Before an empty string was send to proxmox in case the parameter was not used, which required to use ``root@pam`` for module execution (https://github.com/ansible-collections/community.general/pull/1763). diff --git a/changelogs/fragments/948-dellemc-migration-removal.yml b/changelogs/fragments/948-dellemc-migration-removal.yml deleted file mode 100644 index c4f64a815f..0000000000 --- a/changelogs/fragments/948-dellemc-migration-removal.yml +++ /dev/null @@ -1,13 +0,0 @@ -removed_features: - - | - The ``ome_device_info``, ``idrac_firmware`` and ``idrac_server_config_profile`` modules have now been migrated from community.general to the `dellemc.openmanage `_ Ansible collection. - If you use ansible-base 2.10 or newer, redirections have been provided. - - If you use Ansible 2.9 and installed this collection, you need to adjust the FQCNs (``community.general.idrac_firmware`` → ``dellemc.openmanage.idrac_firmware``) and make sure to install the dellemc.openmanage collection. -breaking_changes: - - | - If you use Ansible 2.9 and these plugins or modules from this collection, community.general 3.0.0 results in errors when trying to use the DellEMC content by FQCN, like ``community.general.idrac_firmware``. - Since Ansible 2.9 is not able to use redirections, you will have to adjust your playbooks and roles manually to use the new FQCNs (``dellemc.openmanage.idrac_firmware`` for the previous example) and to make sure that you have ``dellemc.openmanage`` installed. - - If you use ansible-base 2.10 or newer and did not install Ansible 4.0.0, but installed (and/or upgraded) community.general manually, you need to make sure to also install the ``dellemc.openmanage`` collection if you are using any of these plugins or modules. - While ansible-base 2.10 or newer can use the redirects that community.general 3.0.0 adds, the collection they point to (such as dellemc.openmanage) must be installed for them to work. diff --git a/changelogs/fragments/CVE-2021-20191_no_log.yml b/changelogs/fragments/CVE-2021-20191_no_log.yml deleted file mode 100644 index a2c8740598..0000000000 --- a/changelogs/fragments/CVE-2021-20191_no_log.yml +++ /dev/null @@ -1,4 +0,0 @@ -security_fixes: - - module_utils/_netapp, na_ontap_gather_facts - enabled ``no_log`` for the options ``api_key`` and ``secret_key`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). - - module_utils/identity/keycloak, keycloak_client, keycloak_clienttemplate, keycloak_group - enabled ``no_log`` for the option ``auth_client_secret`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). - - utm_proxy_auth_profile - enabled ``no_log`` for the option ``frontend_cookie_secret`` to prevent accidental disclosure (CVE-2021-20191, https://github.com/ansible-collections/community.general/pull/1725). diff --git a/changelogs/fragments/allow_funcd_to_load.yml b/changelogs/fragments/allow_funcd_to_load.yml deleted file mode 100644 index 3336b0aaf4..0000000000 --- a/changelogs/fragments/allow_funcd_to_load.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - funcd connection plugin - can now load (https://github.com/ansible-collections/community.general/pull/2235). diff --git a/changelogs/fragments/dict-filter.yml b/changelogs/fragments/dict-filter.yml deleted file mode 100644 index 1e9923e796..0000000000 --- a/changelogs/fragments/dict-filter.yml +++ /dev/null @@ -1,3 +0,0 @@ -add plugin.filter: - - name: dict - description: "The ``dict`` function as a filter: converts a list of tuples to a dictionary" diff --git a/changelogs/fragments/meta-runtime-deprecations.yml b/changelogs/fragments/meta-runtime-deprecations.yml deleted file mode 100644 index 8863f346af..0000000000 --- a/changelogs/fragments/meta-runtime-deprecations.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "meta/runtime.yml - improve deprecation messages (https://github.com/ansible-collections/community.general/pull/1918)." diff --git a/changelogs/fragments/no_log-fixes.yml b/changelogs/fragments/no_log-fixes.yml deleted file mode 100644 index 70afd3229d..0000000000 --- a/changelogs/fragments/no_log-fixes.yml +++ /dev/null @@ -1,25 +0,0 @@ -security_fixes: - - "ovirt - mark the ``instance_rootpw`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "oneandone_firewall_policy, oneandone_load_balancer, oneandone_monitoring_policy, oneandone_private_network, oneandone_public_ip - mark the ``auth_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "rax_clb_ssl - mark the ``private_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "spotinst_aws_elastigroup - mark the ``multai_token`` and ``token`` parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "keycloak_client - mark the ``registration_access_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "librato_annotation - mark the ``api_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "pagerduty_alert - mark the ``api_key``, ``service_key`` and ``integration_key`` parameters as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "nios_nsgroup - mark the ``tsig_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "pulp_repo - mark the ``feed_client_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "gitlab_runner - mark the ``registration_token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "ibm_sa_host - mark the ``iscsi_chap_secret`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "keycloak_* modules - mark the ``auth_client_secret`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "hwc_ecs_instance - mark the ``admin_pass`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "ovirt - mark the ``instance_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "pagerduty_change - mark the ``integration_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "pingdom - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "rollbar_deployment - mark the ``token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "stackdriver - mark the ``key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "dnsmadeeasy - mark the ``account_key`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "logentries_msg - mark the ``token`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "redfish_command - mark the ``update_creds.password`` parameter as ``no_log`` to avoid leakage of secrets (https://github.com/ansible-collections/community.general/pull/1736)." - - "utm_proxy_auth_profile - mark the ``frontend_cookie_secret`` parameter as ``no_log`` to avoid leakage of secrets. This causes the ``utm_proxy_auth_profile`` return value to no longer containing the correct value, but a placeholder (https://github.com/ansible-collections/community.general/pull/1736)." -breaking_changes: - - "utm_proxy_auth_profile - the ``frontend_cookie_secret`` return value now contains a placeholder string instead of the module's ``frontend_cookie_secret`` parameter (https://github.com/ansible-collections/community.general/pull/1736)." diff --git a/changelogs/fragments/path_join-shim-filter.yml b/changelogs/fragments/path_join-shim-filter.yml deleted file mode 100644 index f96922203f..0000000000 --- a/changelogs/fragments/path_join-shim-filter.yml +++ /dev/null @@ -1,3 +0,0 @@ -add plugin.filter: - - name: path_join - description: Redirects to ansible.builtin.path_join for ansible-base 2.10 or newer, and provides a compatible implementation for Ansible 2.9 diff --git a/changelogs/fragments/remove-deprecated-features.yml b/changelogs/fragments/remove-deprecated-features.yml deleted file mode 100644 index e728ce62d3..0000000000 --- a/changelogs/fragments/remove-deprecated-features.yml +++ /dev/null @@ -1,16 +0,0 @@ -removed_features: -- "airbrake_deployment - removed deprecated ``token`` parameter. Use ``project_id`` and ``project_key`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "bigpanda - the alias ``message`` has been removed. Use ``deployment_message`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "cisco_spark, cisco_webex - the alias ``message`` has been removed. Use ``msg`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "clc_aa_policy - the ``wait`` parameter has been removed. It did not have any effect (https://github.com/ansible-collections/community.general/pull/1926)." -- "datadog_monitor - the alias ``message`` has been removed. Use ``notification_message`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "django_manage - the parameter ``liveserver`` has been removed (https://github.com/ansible-collections/community.general/pull/1926)." -- "idrac_redfish_config - the parameters ``manager_attribute_name`` and ``manager_attribute_value`` have been removed. Use ``manager_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "iso_extract - the alias ``thirsty`` has been removed. Use ``force`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "redfish_config - the parameters ``bios_attribute_name`` and ``bios_attribute_value`` have been removed. Use ``bios_attributes`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "syspatch - the ``apply`` parameter has been removed. This is the default mode, so simply removing it will not change the behavior (https://github.com/ansible-collections/community.general/pull/1926)." -- "xbps - the ``force`` parameter has been removed. It did not have any effect (https://github.com/ansible-collections/community.general/pull/1926)." -- "redfish modules - issuing a data modification command without specifying the ID of the target System, Chassis or Manager resource when there is more than one is no longer allowed. Use the ``resource_id`` option to specify the target ID (https://github.com/ansible-collections/community.general/pull/1926)." -- "pulp_repo - the alias ``ca_cert`` has been removed. Use ``feed_ca_cert`` instead (https://github.com/ansible-collections/community.general/pull/1926)." -- "pulp_repo - the ``feed_client_cert`` parameter no longer defaults to the value of the ``client_cert`` parameter (https://github.com/ansible-collections/community.general/pull/1926)." -- "pulp_repo - the ``feed_client_key`` parameter no longer defaults to the value of the ``client_key`` parameter (https://github.com/ansible-collections/community.general/pull/1926)." diff --git a/changelogs/fragments/remove-deprecated-modules.yml b/changelogs/fragments/remove-deprecated-modules.yml deleted file mode 100644 index fa9d9c9eb7..0000000000 --- a/changelogs/fragments/remove-deprecated-modules.yml +++ /dev/null @@ -1,66 +0,0 @@ -removed_features: -- "The deprecated ali_instance_facts module has been removed. Use ali_instance_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated hpilo_facts module has been removed. Use hpilo_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated idrac_redfish_facts module has been removed. Use idrac_redfish_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated jenkins_job_facts module has been removed. Use jenkins_job_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated memset_memstore_facts module has been removed. Use memset_memstore_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated memset_server_facts module has been removed. Use memset_server_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated na_ontap_gather_facts module has been removed. Use netapp.ontap.na_ontap_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated nginx_status_facts module has been removed. Use nginx_status_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated one_image_facts module has been removed. Use one_image_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated onepassword_facts module has been removed. Use onepassword_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_datacenter_facts module has been removed. Use oneview_datacenter_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_enclosure_facts module has been removed. Use oneview_enclosure_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_ethernet_network_facts module has been removed. Use oneview_ethernet_network_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_fc_network_facts module has been removed. Use oneview_fc_network_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_fcoe_network_facts module has been removed. Use oneview_fcoe_network_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_logical_interconnect_group_facts module has been removed. Use oneview_logical_interconnect_group_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_network_set_facts module has been removed. Use oneview_network_set_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated oneview_san_manager_facts module has been removed. Use oneview_san_manager_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated online_server_facts module has been removed. Use online_server_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated online_user_facts module has been removed. Use online_user_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated purefa_facts module has been removed. Use purestorage.flasharray.purefa_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated purefb_facts module has been removed. Use purestorage.flasharray.purefb_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated python_requirements_facts module has been removed. Use python_requirements_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated redfish_facts module has been removed. Use redfish_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_image_facts module has been removed. Use scaleway_image_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_ip_facts module has been removed. Use scaleway_ip_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_organization_facts module has been removed. Use scaleway_organization_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_security_group_facts module has been removed. Use scaleway_security_group_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_server_facts module has been removed. Use scaleway_server_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_snapshot_facts module has been removed. Use scaleway_snapshot_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated scaleway_volume_facts module has been removed. Use scaleway_volume_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated smartos_image_facts module has been removed. Use smartos_image_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated vertica_facts module has been removed. Use vertica_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated xenserver_guest_facts module has been removed. Use xenserver_guest_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt module has been removed. Use ovirt.ovirt.ovirt_vm instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_affinity_label_facts module has been removed. Use ovirt.ovirt.ovirt_affinity_label_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_api_facts module has been removed. Use ovirt.ovirt.ovirt_api_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_cluster_facts module has been removed. Use ovirt.ovirt.ovirt_cluster_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_datacenter_facts module has been removed. Use ovirt.ovirt.ovirt_datacenter_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_disk_facts module has been removed. Use ovirt.ovirt.ovirt_disk_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_event_facts module has been removed. Use ovirt.ovirt.ovirt_event_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_external_provider_facts module has been removed. Use ovirt.ovirt.ovirt_external_provider_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_group_facts module has been removed. Use ovirt.ovirt.ovirt_group_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_host_facts module has been removed. Use ovirt.ovirt.ovirt_host_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_host_storage_facts module has been removed. Use ovirt.ovirt.ovirt_host_storage_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_network_facts module has been removed. Use ovirt.ovirt.ovirt_network_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_nic_facts module has been removed. Use ovirt.ovirt.ovirt_nic_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_permission_facts module has been removed. Use ovirt.ovirt.ovirt_permission_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_quota_facts module has been removed. Use ovirt.ovirt.ovirt_quota_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_scheduling_policy_facts module has been removed. Use ovirt.ovirt.ovirt_scheduling_policy_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_snapshot_facts module has been removed. Use ovirt.ovirt.ovirt_snapshot_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_storage_domain_facts module has been removed. Use ovirt.ovirt.ovirt_storage_domain_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_storage_template_facts module has been removed. Use ovirt.ovirt.ovirt_storage_template_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_storage_vm_facts module has been removed. Use ovirt.ovirt.ovirt_storage_vm_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_tag_facts module has been removed. Use ovirt.ovirt.ovirt_tag_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_template_facts module has been removed. Use ovirt.ovirt.ovirt_template_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_user_facts module has been removed. Use ovirt.ovirt.ovirt_user_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_vm_facts module has been removed. Use ovirt.ovirt.ovirt_vm_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ovirt_vmpool_facts module has been removed. Use ovirt.ovirt.ovirt_vmpool_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The ovirt_facts docs fragment has been removed (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated gluster_heal_info module has been removed. Use gluster.gluster.gluster_heal_info instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated gluster_peer module has been removed. Use gluster.gluster.gluster_peer instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated gluster_volume module has been removed. Use gluster.gluster.gluster_volume instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated helm module has been removed. Use community.kubernetes.helm instead (https://github.com/ansible-collections/community.general/pull/1924)." -- "The deprecated ldap_attr module has been removed. Use ldap_attrs instead (https://github.com/ansible-collections/community.general/pull/1924)." diff --git a/changelogs/fragments/selective-core-2.11.yml b/changelogs/fragments/selective-core-2.11.yml deleted file mode 100644 index 994e555c7c..0000000000 --- a/changelogs/fragments/selective-core-2.11.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- "selective callback plugin - adjust import so that the plugin also works with ansible-core 2.11 (https://github.com/ansible-collections/community.general/pull/1807)." diff --git a/commit-rights.md b/commit-rights.md index d10bea9af7..9b39d47b2c 100644 --- a/commit-rights.md +++ b/commit-rights.md @@ -67,6 +67,8 @@ Individuals who have been asked to become a part of this group have generally be | Name | GitHub ID | IRC Nick | Other | | ------------------- | -------------------- | ------------------ | -------------------- | +| Alexei Znamensky | russoz | russoz | | +| Amin Vakil | aminvakil | aminvakil | | | Andrew Klychkov | andersson007 | andersson007_ | | | Felix Fontein | felixfontein | felixfontein | | | John R Barker | gundalow | gundalow | | diff --git a/docs/docsite/extra-docs.yml b/docs/docsite/extra-docs.yml new file mode 100644 index 0000000000..22ae7b58f5 --- /dev/null +++ b/docs/docsite/extra-docs.yml @@ -0,0 +1,5 @@ +--- +sections: + - title: Guides + toctree: + - filter_guide diff --git a/docs/docsite/rst/filter_guide.rst b/docs/docsite/rst/filter_guide.rst new file mode 100644 index 0000000000..201b275aae --- /dev/null +++ b/docs/docsite/rst/filter_guide.rst @@ -0,0 +1,753 @@ +.. _ansible_collections.community.general.docsite.filter_guide: + +community.general Filter Guide +============================== + +The :ref:`community.general collection ` offers several useful filter plugins. + +.. contents:: Topics + +Paths +----- + +The ``path_join`` filter has been added in ansible-base 2.10. If you want to use this filter, but also need to support Ansible 2.9, you can use ``community.general``'s ``path_join`` shim, ``community.general.path_join``. This filter redirects to ``path_join`` for ansible-base 2.10 and ansible-core 2.11 or newer, and re-implements the filter for Ansible 2.9. + +.. code-block:: yaml+jinja + + # ansible-base 2.10 or newer: + path: {{ ('/etc', path, 'subdir', file) | path_join }} + + # Also works with Ansible 2.9: + path: {{ ('/etc', path, 'subdir', file) | community.general.path_join }} + +.. versionadded:: 3.0.0 + +Abstract transformations +------------------------ + +Dictionaries +^^^^^^^^^^^^ + +You can use the ``dict_kv`` filter to create a single-entry dictionary with ``value | community.general.dict_kv(key)``: + +.. code-block:: yaml+jinja + + - name: Create a single-entry dictionary + debug: + msg: "{{ myvar | community.general.dict_kv('thatsmyvar') }}" + vars: + myvar: myvalue + + - name: Create a list of dictionaries where the 'server' field is taken from a list + debug: + msg: >- + {{ myservers | map('community.general.dict_kv', 'server') + | map('combine', common_config) }} + vars: + common_config: + type: host + database: all + myservers: + - server1 + - server2 + +This produces: + +.. code-block:: ansible-output + + TASK [Create a single-entry dictionary] ************************************************** + ok: [localhost] => { + "msg": { + "thatsmyvar": "myvalue" + } + } + + TASK [Create a list of dictionaries where the 'server' field is taken from a list] ******* + ok: [localhost] => { + "msg": [ + { + "database": "all", + "server": "server1", + "type": "host" + }, + { + "database": "all", + "server": "server2", + "type": "host" + } + ] + } + +.. versionadded:: 2.0.0 + +If you need to convert a list of key-value pairs to a dictionary, you can use the ``dict`` function. Unfortunately, this function cannot be used with ``map``. For this, the ``community.general.dict`` filter can be used: + +.. code-block:: yaml+jinja + + - name: Create a dictionary with the dict function + debug: + msg: "{{ dict([[1, 2], ['a', 'b']]) }}" + + - name: Create a dictionary with the community.general.dict filter + debug: + msg: "{{ [[1, 2], ['a', 'b']] | community.general.dict }}" + + - name: Create a list of dictionaries with map and the community.general.dict filter + debug: + msg: >- + {{ values | map('zip', ['k1', 'k2', 'k3']) + | map('map', 'reverse') + | map('community.general.dict') }} + vars: + values: + - - foo + - 23 + - a + - - bar + - 42 + - b + +This produces: + +.. code-block:: ansible-output + + TASK [Create a dictionary with the dict function] **************************************** + ok: [localhost] => { + "msg": { + "1": 2, + "a": "b" + } + } + + TASK [Create a dictionary with the community.general.dict filter] ************************ + ok: [localhost] => { + "msg": { + "1": 2, + "a": "b" + } + } + + TASK [Create a list of dictionaries with map and the community.general.dict filter] ****** + ok: [localhost] => { + "msg": [ + { + "k1": "foo", + "k2": 23, + "k3": "a" + }, + { + "k1": "bar", + "k2": 42, + "k3": "b" + } + ] + } + +.. versionadded:: 3.0.0 + +Grouping +^^^^^^^^ + +If you have a list of dictionaries, the Jinja2 ``groupby`` filter allows to group the list by an attribute. This results in a list of ``(grouper, list)`` namedtuples, where ``list`` contains all dictionaries where the selected attribute equals ``grouper``. If you know that for every ``grouper``, there will be a most one entry in that list, you can use the ``community.general.groupby_as_dict`` filter to convert the original list into a dictionary which maps ``grouper`` to the corresponding dictionary. + +One example is ``ansible_facts.mounts``, which is a list of dictionaries where each has one ``device`` element to indicate the device which is mounted. Therefore, ``ansible_facts.mounts | community.general.groupby_as_dict('device')`` is a dictionary mapping a device to the mount information: + +.. code-block:: yaml+jinja + + - name: Output mount facts grouped by device name + debug: + var: ansible_facts.mounts | community.general.groupby_as_dict('device') + + - name: Output mount facts grouped by mount point + debug: + var: ansible_facts.mounts | community.general.groupby_as_dict('mount') + +This produces: + +.. code-block:: ansible-output + + TASK [Output mount facts grouped by device name] ****************************************** + ok: [localhost] => { + "ansible_facts.mounts | community.general.groupby_as_dict('device')": { + "/dev/sda1": { + "block_available": 2000, + "block_size": 4096, + "block_total": 2345, + "block_used": 345, + "device": "/dev/sda1", + "fstype": "ext4", + "inode_available": 500, + "inode_total": 512, + "inode_used": 12, + "mount": "/boot", + "options": "rw,relatime,data=ordered", + "size_available": 56821, + "size_total": 543210, + "uuid": "ab31cade-d9c1-484d-8482-8a4cbee5241a" + }, + "/dev/sda2": { + "block_available": 1234, + "block_size": 4096, + "block_total": 12345, + "block_used": 11111, + "device": "/dev/sda2", + "fstype": "ext4", + "inode_available": 1111, + "inode_total": 1234, + "inode_used": 123, + "mount": "/", + "options": "rw,relatime", + "size_available": 42143, + "size_total": 543210, + "uuid": "abcdef01-2345-6789-0abc-def012345678" + } + } + } + + TASK [Output mount facts grouped by mount point] ****************************************** + ok: [localhost] => { + "ansible_facts.mounts | community.general.groupby_as_dict('mount')": { + "/": { + "block_available": 1234, + "block_size": 4096, + "block_total": 12345, + "block_used": 11111, + "device": "/dev/sda2", + "fstype": "ext4", + "inode_available": 1111, + "inode_total": 1234, + "inode_used": 123, + "mount": "/", + "options": "rw,relatime", + "size_available": 42143, + "size_total": 543210, + "uuid": "bdf50b7d-4859-40af-8665-c637ee7a7808" + }, + "/boot": { + "block_available": 2000, + "block_size": 4096, + "block_total": 2345, + "block_used": 345, + "device": "/dev/sda1", + "fstype": "ext4", + "inode_available": 500, + "inode_total": 512, + "inode_used": 12, + "mount": "/boot", + "options": "rw,relatime,data=ordered", + "size_available": 56821, + "size_total": 543210, + "uuid": "ab31cade-d9c1-484d-8482-8a4cbee5241a" + } + } + } + +.. versionadded: 3.0.0 + +Merging lists of dictionaries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you have two lists of dictionaries and want to combine them into a list of merged dictionaries, where two dictionaries are merged if they coincide in one attribute, you can use the ``lists_mergeby`` filter. + +.. code-block:: yaml+jinja + + - name: Merge two lists by common attribute 'name' + debug: + var: list1 | community.general.lists_mergeby(list2, 'name') + vars: + list1: + - name: foo + extra: true + - name: bar + extra: false + - name: meh + extra: true + list2: + - name: foo + path: /foo + - name: baz + path: /bazzz + +This produces: + +.. code-block:: ansible-output + + TASK [Merge two lists by common attribute 'name'] **************************************** + ok: [localhost] => { + "list1 | community.general.lists_mergeby(list2, 'name')": [ + { + "extra": false, + "name": "bar" + }, + { + "name": "baz", + "path": "/bazzz" + }, + { + "extra": true, + "name": "foo", + "path": "/foo" + }, + { + "extra": true, + "name": "meh" + } + ] + } + +.. versionadded: 2.0.0 + +Working with times +------------------ + +The ``to_time_unit`` filter allows to convert times from a human-readable string to a unit. For example, ``'4h 30min 12second' | community.general.to_time_unit('hour')`` gives the number of hours that correspond to 4 hours, 30 minutes and 12 seconds. + +There are shorthands to directly convert to various units, like ``to_hours``, ``to_minutes``, ``to_seconds``, and so on. The following table lists all units that can be used: + +.. list-table:: Units + :widths: 25 25 25 25 + :header-rows: 1 + + * - Unit name + - Unit value in seconds + - Unit strings for filter + - Shorthand filter + * - Millisecond + - 1/1000 second + - ``ms``, ``millisecond``, ``milliseconds``, ``msec``, ``msecs``, ``msecond``, ``mseconds`` + - ``to_milliseconds`` + * - Second + - 1 second + - ``s``, ``sec``, ``secs``, ``second``, ``seconds`` + - ``to_seconds`` + * - Minute + - 60 seconds + - ``m``, ``min``, ``mins``, ``minute``, ``minutes`` + - ``to_minutes`` + * - Hour + - 60*60 seconds + - ``h``, ``hour``, ``hours`` + - ``to_hours`` + * - Day + - 24*60*60 seconds + - ``d``, ``day``, ``days`` + - ``to_days`` + * - Week + - 7*24*60*60 seconds + - ``w``, ``week``, ``weeks`` + - ``to_weeks`` + * - Month + - 30*24*60*60 seconds + - ``mo``, ``month``, ``months`` + - ``to_months`` + * - Year + - 365*24*60*60 seconds + - ``y``, ``year``, ``years`` + - ``to_years`` + +Note that months and years are using a simplified representation: a month is 30 days, and a year is 365 days. If you need different definitions of months or years, you can pass them as keyword arguments. For example, if you want a year to be 365.25 days, and a month to be 30.5 days, you can write ``'11months 4' | community.general.to_years(year=365.25, month=30.5)``. These keyword arguments can be specified to ``to_time_unit`` and to all shorthand filters. + +.. code-block:: yaml+jinja + + - name: Convert string to seconds + debug: + msg: "{{ '30h 20m 10s 123ms' | community.general.to_time_unit('seconds') }}" + + - name: Convert string to hours + debug: + msg: "{{ '30h 20m 10s 123ms' | community.general.to_hours }}" + + - name: Convert string to years (using 365.25 days == 1 year) + debug: + msg: "{{ '400d 15h' | community.general.to_years(year=365.25) }}" + +This produces: + +.. code-block:: ansible-output + + TASK [Convert string to seconds] ********************************************************** + ok: [localhost] => { + "msg": "109210.123" + } + + TASK [Convert string to hours] ************************************************************ + ok: [localhost] => { + "msg": "30.336145277778" + } + + TASK [Convert string to years (using 365.25 days == 1 year)] ****************************** + ok: [localhost] => { + "msg": "1.096851471595" + } + +.. versionadded: 0.2.0 + +Working with versions +--------------------- + +If you need to sort a list of version numbers, the Jinja ``sort`` filter is problematic. Since it sorts lexicographically, ``2.10`` will come before ``2.9``. To treat version numbers correctly, you can use the ``version_sort`` filter: + +.. code-block:: yaml+jinja + + - name: Sort list by version number + debug: + var: ansible_versions | community.general.version_sort + vars: + ansible_versions: + - '2.8.0' + - '2.11.0' + - '2.7.0' + - '2.10.0' + - '2.9.0' + +This produces: + +.. code-block:: ansible-output + + TASK [Sort list by version number] ******************************************************** + ok: [localhost] => { + "ansible_versions | community.general.version_sort": [ + "2.7.0", + "2.8.0", + "2.9.0", + "2.10.0", + "2.11.0" + ] + } + +.. versionadded: 2.2.0 + +Creating identifiers +-------------------- + +The following filters allow to create identifiers. + +Hashids +^^^^^^^ + +`Hashids `_ allow to convert sequences of integers to short unique string identifiers. This filter needs the `hashids Python library `_ installed on the controller. + +.. code-block:: yaml+jinja + + - name: "Create hashid" + debug: + msg: "{{ [1234, 5, 6] | community.general.hashids_encode }}" + + - name: "Decode hashid" + debug: + msg: "{{ 'jm2Cytn' | community.general.hashids_decode }}" + +This produces: + +.. code-block:: ansible-output + + TASK [Create hashid] ********************************************************************** + ok: [localhost] => { + "msg": "jm2Cytn" + } + + TASK [Decode hashid] ********************************************************************** + ok: [localhost] => { + "msg": [ + 1234, + 5, + 6 + ] + } + +The hashids filters accept keyword arguments to allow fine-tuning the hashids generated: + +:salt: String to use as salt when hashing. +:alphabet: String of 16 or more unique characters to produce a hash. +:min_length: Minimum length of hash produced. + +.. versionadded: 3.0.0 + +Random MACs +^^^^^^^^^^^ + +You can use the ``random_mac`` filter to complete a partial `MAC address `_ to a random 6-byte MAC address. + +.. code-block:: yaml+jinja + + - name: "Create a random MAC starting with ff:" + debug: + msg: "{{ 'FF' | community.general.random_mac }}" + + - name: "Create a random MAC starting with 00:11:22:" + debug: + msg: "{{ '00:11:22' | community.general.random_mac }}" + +This produces: + +.. code-block:: ansible-output + + TASK [Create a random MAC starting with ff:] ********************************************** + ok: [localhost] => { + "msg": "ff:69:d3:78:7f:b4" + } + + TASK [Create a random MAC starting with 00:11:22:] **************************************** + ok: [localhost] => { + "msg": "00:11:22:71:5d:3b" + } + +You can also initialize the random number generator from a seed to create random-but-idempotent MAC addresses: + +.. code-block:: yaml+jinja + + "{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}" + +Conversions +----------- + +Parsing CSV files +^^^^^^^^^^^^^^^^^ + +Ansible offers the :ref:`community.general.read_csv module ` to read CSV files. Sometimes you need to convert strings to CSV files instead. For this, the ``from_csv`` filter exists. + +.. code-block:: yaml+jinja + + - name: "Parse CSV from string" + debug: + msg: "{{ csv_string | community.general.from_csv }}" + vars: + csv_string: | + foo,bar,baz + 1,2,3 + you,this,then + +This produces: + +.. code-block:: ansible-output + + TASK [Parse CSV from string] ************************************************************** + ok: [localhost] => { + "msg": [ + { + "bar": "2", + "baz": "3", + "foo": "1" + }, + { + "bar": "this", + "baz": "then", + "foo": "you" + } + ] + } + +The ``from_csv`` filter has several keyword arguments to control its behavior: + +:dialect: Dialect of the CSV file. Default is ``excel``. Other possible choices are ``excel-tab`` and ``unix``. If one of ``delimiter``, ``skipinitialspace`` or ``strict`` is specified, ``dialect`` is ignored. +:fieldnames: A set of column names to use. If not provided, the first line of the CSV is assumed to contain the column names. +:delimiter: Sets the delimiter to use. Default depends on the dialect used. +:skipinitialspace: Set to ``true`` to ignore space directly after the delimiter. Default depends on the dialect used (usually ``false``). +:strict: Set to ``true`` to error out on invalid CSV input. + +.. versionadded: 3.0.0 + +Converting to JSON +^^^^^^^^^^^^^^^^^^ + +`JC `_ is a CLI tool and Python library which allows to interpret output of various CLI programs as JSON. It is also available as a filter in community.general. This filter needs the `jc Python library `_ installed on the controller. + +.. code-block:: yaml+jinja + + - name: Run 'ls' to list files in / + command: ls / + register: result + + - name: Parse the ls output + debug: + msg: "{{ result.stdout | community.general.jc('ls') }}" + +This produces: + +.. code-block:: ansible-output + + TASK [Run 'ls' to list files in /] ******************************************************** + changed: [localhost] + + TASK [Parse the ls output] **************************************************************** + ok: [localhost] => { + "msg": [ + { + "filename": "bin" + }, + { + "filename": "boot" + }, + { + "filename": "dev" + }, + { + "filename": "etc" + }, + { + "filename": "home" + }, + { + "filename": "lib" + }, + { + "filename": "proc" + }, + { + "filename": "root" + }, + { + "filename": "run" + }, + { + "filename": "tmp" + } + ] + } + +.. versionadded: 2.0.0 + +.. _ansible_collections.community.general.docsite.json_query_filter: + +Selecting JSON data: JSON queries +--------------------------------- + +To select a single element or a data subset from a complex data structure in JSON format (for example, Ansible facts), use the ``json_query`` filter. The ``json_query`` filter lets you query a complex JSON structure and iterate over it using a loop structure. + +.. note:: You must manually install the **jmespath** dependency on the Ansible controller before using this filter. This filter is built upon **jmespath**, and you can use the same syntax. For examples, see `jmespath examples `_. + +Consider this data structure: + +.. code-block:: yaml+jinja + + { + "domain_definition": { + "domain": { + "cluster": [ + { + "name": "cluster1" + }, + { + "name": "cluster2" + } + ], + "server": [ + { + "name": "server11", + "cluster": "cluster1", + "port": "8080" + }, + { + "name": "server12", + "cluster": "cluster1", + "port": "8090" + }, + { + "name": "server21", + "cluster": "cluster2", + "port": "9080" + }, + { + "name": "server22", + "cluster": "cluster2", + "port": "9090" + } + ], + "library": [ + { + "name": "lib1", + "target": "cluster1" + }, + { + "name": "lib2", + "target": "cluster2" + } + ] + } + } + } + +To extract all clusters from this structure, you can use the following query: + +.. code-block:: yaml+jinja + + - name: Display all cluster names + ansible.builtin.debug: + var: item + loop: "{{ domain_definition | community.general.json_query('domain.cluster[*].name') }}" + +To extract all server names: + +.. code-block:: yaml+jinja + + - name: Display all server names + ansible.builtin.debug: + var: item + loop: "{{ domain_definition | community.general.json_query('domain.server[*].name') }}" + +To extract ports from cluster1: + +.. code-block:: yaml+jinja + + - name: Display all ports from cluster1 + ansible.builtin.debug: + var: item + loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}" + vars: + server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port" + +.. note:: You can use a variable to make the query more readable. + +To print out the ports from cluster1 in a comma separated string: + +.. code-block:: yaml+jinja + + - name: Display all ports from cluster1 as a string + ansible.builtin.debug: + msg: "{{ domain_definition | community.general.json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}" + +.. note:: In the example above, quoting literals using backticks avoids escaping quotes and maintains readability. + +You can use YAML `single quote escaping `_: + +.. code-block:: yaml+jinja + + - name: Display all ports from cluster1 + ansible.builtin.debug: + var: item + loop: "{{ domain_definition | community.general.json_query('domain.server[?cluster==''cluster1''].port') }}" + +.. note:: Escaping single quotes within single quotes in YAML is done by doubling the single quote. + +To get a hash map with all ports and names of a cluster: + +.. code-block:: yaml+jinja + + - name: Display all server ports and names from cluster1 + ansible.builtin.debug: + var: item + loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}" + vars: + server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}" + +To extract ports from all clusters with name starting with 'server1': + +.. code-block:: yaml+jinja + + - name: Display all ports from cluster1 + ansible.builtin.debug: + msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}" + vars: + server_name_query: "domain.server[?starts_with(name,'server1')].port" + +To extract ports from all clusters with name containing 'server1': + +.. code-block:: yaml+jinja + + - name: Display all ports from cluster1 + ansible.builtin.debug: + msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}" + vars: + server_name_query: "domain.server[?contains(name,'server1')].port" + +.. note:: while using ``starts_with`` and ``contains``, you have to use `` to_json | from_json `` filter for correct parsing of data structure. diff --git a/galaxy.yml b/galaxy.yml index 3676516625..ba1969d712 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,6 +1,6 @@ namespace: community name: general -version: 3.0.0 +version: 3.2.0 readme: README.md authors: - Ansible (https://github.com/ansible) diff --git a/meta/runtime.yml b/meta/runtime.yml index c116029974..8b2a0c0ad6 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,31 +1,5 @@ --- requires_ansible: '>=2.9.10' -action_groups: - ovirt: - - ovirt_affinity_label_facts - - ovirt_api_facts - - ovirt_cluster_facts - - ovirt_datacenter_facts - - ovirt_disk_facts - - ovirt_event_facts - - ovirt_external_provider_facts - - ovirt_group_facts - - ovirt_host_facts - - ovirt_host_storage_facts - - ovirt_network_facts - - ovirt_nic_facts - - ovirt_permission_facts - - ovirt_quota_facts - - ovirt_scheduling_policy_facts - - ovirt_snapshot_facts - - ovirt_storage_domain_facts - - ovirt_storage_template_facts - - ovirt_storage_vm_facts - - ovirt_tag_facts - - ovirt_template_facts - - ovirt_user_facts - - ovirt_vm_facts - - ovirt_vmpool_facts plugin_routing: connection: docker: @@ -37,6 +11,21 @@ plugin_routing: redirect: community.google.gcp_storage_file hashi_vault: redirect: community.hashi_vault.hashi_vault + nios: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios lookup plugin has been deprecated. + Please use infoblox.nios_modules.nios_lookup instead. + nios_next_ip: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_next_ip lookup plugin has been deprecated. + Please use infoblox.nios_modules.nios_next_ip instead. + nios_next_network: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_next_network lookup plugin has been + deprecated. Please use infoblox.nios_modules.nios_next_network instead. modules: ali_instance_facts: tombstone: @@ -141,11 +130,13 @@ plugin_routing: gcp_forwarding_rule: tombstone: removal_version: 2.0.0 - warning_text: Use google.cloud.gcp_compute_forwarding_rule or google.cloud.gcp_compute_global_forwarding_rule instead. + warning_text: Use google.cloud.gcp_compute_forwarding_rule or google.cloud.gcp_compute_global_forwarding_rule + instead. gcp_healthcheck: tombstone: removal_version: 2.0.0 - warning_text: Use google.cloud.gcp_compute_health_check, google.cloud.gcp_compute_http_health_check or google.cloud.gcp_compute_https_health_check instead. + warning_text: Use google.cloud.gcp_compute_health_check, google.cloud.gcp_compute_http_health_check + or google.cloud.gcp_compute_https_health_check instead. gcp_target_proxy: tombstone: removal_version: 2.0.0 @@ -156,37 +147,22 @@ plugin_routing: warning_text: Use google.cloud.gcp_compute_url_map instead. gcpubsub: redirect: community.google.gcpubsub - gcpubsub_info: - redirect: community.google.gcpubsub_info gcpubsub_facts: tombstone: removal_version: 3.0.0 warning_text: Use community.google.gcpubsub_info instead. + gcpubsub_info: + redirect: community.google.gcpubsub_info gcspanner: tombstone: removal_version: 2.0.0 - warning_text: Use google.cloud.gcp_spanner_database and/or google.cloud.gcp_spanner_instance instead. + warning_text: Use google.cloud.gcp_spanner_database and/or google.cloud.gcp_spanner_instance + instead. github_hooks: tombstone: removal_version: 2.0.0 - warning_text: Use community.general.github_webhook and community.general.github_webhook_info instead. - # Adding tombstones burns the old name, so we simply remove the entries: - # gluster_heal_info: - # tombstone: - # removal_version: 3.0.0 - # warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_heal_info instead. - # gluster_peer: - # tombstone: - # removal_version: 3.0.0 - # warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_peer instead. - # gluster_volume: - # tombstone: - # removal_version: 3.0.0 - # warning_text: The gluster modules have migrated to the gluster.gluster collection. Use gluster.gluster.gluster_volume instead. - # helm: - # tombstone: - # removal_version: 3.0.0 - # warning_text: Use community.kubernetes.helm instead. + warning_text: Use community.general.github_webhook and community.general.github_webhook_info + instead. hetzner_failover_ip: redirect: community.hrobot.failover_ip hetzner_failover_ip_info: @@ -234,11 +210,13 @@ plugin_routing: logicmonitor: tombstone: removal_version: 1.0.0 - warning_text: The logicmonitor_facts module is no longer maintained and the API used has been disabled in 2017. + warning_text: The logicmonitor_facts module is no longer maintained and the + API used has been disabled in 2017. logicmonitor_facts: tombstone: removal_version: 1.0.0 - warning_text: The logicmonitor_facts module is no longer maintained and the API used has been disabled in 2017. + warning_text: The logicmonitor_facts module is no longer maintained and the + API used has been disabled in 2017. memset_memstore_facts: tombstone: removal_version: 3.0.0 @@ -287,6 +265,86 @@ plugin_routing: tombstone: removal_version: 3.0.0 warning_text: Use community.general.nginx_status_info instead. + nios_a_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_a_record module has been deprecated. + Please use infoblox.nios_modules.nios_a_record instead. + nios_aaaa_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_aaaa_record module has been deprecated. + Please use infoblox.nios_modules.nios_aaaa_record instead. + nios_cname_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_cname_record module has been deprecated. + Please use infoblox.nios_modules.nios_cname_record instead. + nios_dns_view: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_dns_view module has been deprecated. + Please use infoblox.nios_modules.nios_dns_view instead. + nios_fixed_address: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_fixed_address module has been deprecated. + Please use infoblox.nios_modules.nios_fixed_address instead. + nios_host_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_host_record module has been deprecated. + Please use infoblox.nios_modules.nios_host_record instead. + nios_member: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_member module has been deprecated. + Please use infoblox.nios_modules.nios_member instead. + nios_mx_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_mx_record module has been deprecated. + Please use infoblox.nios_modules.nios_mx_record instead. + nios_naptr_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_naptr_record module has been deprecated. + Please use infoblox.nios_modules.nios_naptr_record instead. + nios_network: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_network module has been deprecated. + Please use infoblox.nios_modules.nios_network instead. + nios_network_view: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_network_view module has been deprecated. + Please use infoblox.nios_modules.nios_network_view instead. + nios_nsgroup: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_nsgroup module has been deprecated. + Please use infoblox.nios_modules.nios_nsgroup instead. + nios_ptr_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_ptr_record module has been deprecated. + Please use infoblox.nios_modules.nios_ptr_record instead. + nios_srv_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_srv_record module has been deprecated. + Please use infoblox.nios_modules.nios_srv_record instead. + nios_txt_record: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_txt_record module has been deprecated. + Please use infoblox.nios_modules.nios_txt_record instead. + nios_zone: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios_zone module has been deprecated. + Please use infoblox.nios_modules.nios_zone instead. ome_device_info: redirect: dellemc.openmanage.ome_device_info one_image_facts: @@ -320,7 +378,8 @@ plugin_routing: oneview_logical_interconnect_group_facts: tombstone: removal_version: 3.0.0 - warning_text: Use community.general.oneview_logical_interconnect_group_info instead. + warning_text: Use community.general.oneview_logical_interconnect_group_info + instead. oneview_network_set_facts: tombstone: removal_version: 3.0.0 @@ -477,10 +536,10 @@ plugin_routing: redirect: community.postgresql.postgresql_table postgresql_tablespace: redirect: community.postgresql.postgresql_tablespace - postgresql_user_obj_stat_info: - redirect: community.postgresql.postgresql_user_obj_stat_info postgresql_user: redirect: community.postgresql.postgresql_user + postgresql_user_obj_stat_info: + redirect: community.postgresql.postgresql_user_obj_stat_info purefa_facts: tombstone: removal_version: 3.0.0 @@ -568,11 +627,14 @@ plugin_routing: redirect: community.kubevirt.kubevirt_common_options kubevirt_vm_options: redirect: community.kubevirt.kubevirt_vm_options + nios: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.nios document fragment has been deprecated. + Please use infoblox.nios_modules.nios instead. postgresql: redirect: community.postgresql.postgresql module_utils: - remote_management.dellemc.dellemc_idrac: - redirect: dellemc.openmanage.dellemc_idrac docker.common: redirect: community.docker.common docker.swarm: @@ -587,23 +649,33 @@ plugin_routing: redirect: community.hrobot.robot kubevirt: redirect: community.kubevirt.kubevirt - remote_management.dellemc.ome: - redirect: dellemc.openmanage.ome + net_tools.nios.api: + deprecation: + removal_version: 5.0.0 + warning_text: The community.general.net_tools.nios.api module_utils has been + deprecated. Please use infoblox.nios_modules.api instead. postgresql: redirect: community.postgresql.postgresql + remote_management.dellemc.dellemc_idrac: + redirect: dellemc.openmanage.dellemc_idrac + remote_management.dellemc.ome: + redirect: dellemc.openmanage.ome callback: actionable: tombstone: removal_version: 2.0.0 - warning_text: Use the 'default' callback plugin with 'display_skipped_hosts = no' and 'display_ok_hosts = no' options. + warning_text: Use the 'default' callback plugin with 'display_skipped_hosts + = no' and 'display_ok_hosts = no' options. full_skip: tombstone: removal_version: 2.0.0 - warning_text: Use the 'default' callback plugin with 'display_skipped_hosts = no' option. + warning_text: Use the 'default' callback plugin with 'display_skipped_hosts + = no' option. stderr: tombstone: removal_version: 2.0.0 - warning_text: Use the 'default' callback plugin with 'display_failed_stderr = yes' option. + warning_text: Use the 'default' callback plugin with 'display_failed_stderr + = yes' option. inventory: docker_machine: redirect: community.docker.docker_machine diff --git a/plugins/action/system/iptables_state.py b/plugins/action/system/iptables_state.py index cc174b3bd7..6884e77713 100644 --- a/plugins/action/system/iptables_state.py +++ b/plugins/action/system/iptables_state.py @@ -7,7 +7,7 @@ __metaclass__ = type import time from ansible.plugins.action import ActionBase -from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleConnectionFailure +from ansible.errors import AnsibleActionFail, AnsibleConnectionFailure from ansible.utils.vars import merge_hash from ansible.utils.display import Display @@ -40,19 +40,27 @@ class ActionModule(ActionBase): "(=%s) to 0, and 'async' (=%s) to a value >2 and not greater than " "'ansible_timeout' (=%s) (recommended).") - def _async_result(self, module_args, task_vars, timeout): + def _async_result(self, async_status_args, task_vars, timeout): ''' Retrieve results of the asynchonous task, and display them in place of the async wrapper results (those with the ansible_job_id key). ''' + async_status = self._task.copy() + async_status.args = async_status_args + async_status.action = 'ansible.builtin.async_status' + async_status.async_val = 0 + async_action = self._shared_loader_obj.action_loader.get( + async_status.action, task=async_status, connection=self._connection, + play_context=self._play_context, loader=self._loader, templar=self._templar, + shared_loader_obj=self._shared_loader_obj) + + if async_status.args['mode'] == 'cleanup': + return async_action.run(task_vars=task_vars) + # At least one iteration is required, even if timeout is 0. - for i in range(max(1, timeout)): - async_result = self._execute_module( - module_name='ansible.builtin.async_status', - module_args=module_args, - task_vars=task_vars, - wrap_async=False) - if async_result['finished'] == 1: + for dummy in range(max(1, timeout)): + async_result = async_action.run(task_vars=task_vars) + if async_result.get('finished', 0) == 1: break time.sleep(min(1, timeout)) @@ -76,7 +84,6 @@ class ActionModule(ActionBase): task_async = self._task.async_val check_mode = self._play_context.check_mode max_timeout = self._connection._play_context.timeout - module_name = self._task.action module_args = self._task.args if module_args.get('state', None) == 'restored': @@ -107,7 +114,7 @@ class ActionModule(ActionBase): # longer on the controller); and set a backup file path. module_args['_timeout'] = task_async module_args['_back'] = '%s/iptables.state' % async_dir - async_status_args = dict(_async_dir=async_dir) + async_status_args = dict(mode='status') confirm_cmd = 'rm -f %s' % module_args['_back'] starter_cmd = 'touch %s.starter' % module_args['_back'] remaining_time = max(task_async, max_timeout) @@ -133,7 +140,7 @@ class ActionModule(ActionBase): # The module is aware to not process the main iptables-restore # command before finding (and deleting) the 'starter' cookie on # the host, so the previous query will not reach ssh timeout. - garbage = self._low_level_execute_command(starter_cmd, sudoable=self.DEFAULT_SUDOABLE) + dummy = self._low_level_execute_command(starter_cmd, sudoable=self.DEFAULT_SUDOABLE) # As the main command is not yet executed on the target, here # 'finished' means 'failed before main command be executed'. @@ -143,7 +150,7 @@ class ActionModule(ActionBase): except AttributeError: pass - for x in range(max_timeout): + for dummy in range(max_timeout): time.sleep(1) remaining_time -= 1 # - AnsibleConnectionFailure covers rejected requests (i.e. @@ -151,7 +158,7 @@ class ActionModule(ActionBase): # - ansible_timeout is able to cover dropped requests (due # to a rule or policy DROP) if not lower than async_val. try: - garbage = self._low_level_execute_command(confirm_cmd, sudoable=self.DEFAULT_SUDOABLE) + dummy = self._low_level_execute_command(confirm_cmd, sudoable=self.DEFAULT_SUDOABLE) break except AnsibleConnectionFailure: continue @@ -164,16 +171,12 @@ class ActionModule(ActionBase): del result[key] if result.get('invocation', {}).get('module_args'): - if '_timeout' in result['invocation']['module_args']: - del result['invocation']['module_args']['_back'] - del result['invocation']['module_args']['_timeout'] + for key in ('_back', '_timeout', '_async_dir', 'jid'): + if result['invocation']['module_args'].get(key): + del result['invocation']['module_args'][key] async_status_args['mode'] = 'cleanup' - garbage = self._execute_module( - module_name='ansible.builtin.async_status', - module_args=async_status_args, - task_vars=task_vars, - wrap_async=False) + dummy = self._async_result(async_status_args, task_vars, 0) if not wrap_async: # remove a temporary path we created diff --git a/plugins/become/sudosu.py b/plugins/become/sudosu.py index e9668e6522..410b881b96 100644 --- a/plugins/become/sudosu.py +++ b/plugins/become/sudosu.py @@ -5,7 +5,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = """ - become: sudosu + name: sudosu short_description: Run tasks using sudo su - description: - This become plugins allows your remote/login user to execute commands as another user via the C(sudo) and C(su) utilities combined. diff --git a/plugins/cache/__init__.py b/plugins/cache/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/cache/redis.py b/plugins/cache/redis.py index 7a376d6d7c..6af7c731e4 100644 --- a/plugins/cache/redis.py +++ b/plugins/cache/redis.py @@ -61,6 +61,7 @@ DOCUMENTATION = ''' type: integer ''' +import re import time import json @@ -91,6 +92,8 @@ class CacheModule(BaseCacheModule): performance. """ _sentinel_service_name = None + re_url_conn = re.compile(r'^([^:]+|\[[^]]+\]):(\d+):(\d+)(?::(.*))?$') + re_sent_conn = re.compile(r'^(.*):(\d+)$') def __init__(self, *args, **kwargs): uri = '' @@ -130,11 +133,18 @@ class CacheModule(BaseCacheModule): self._db = self._get_sentinel_connection(uri, kw) # normal connection else: - connection = uri.split(':') + connection = self._parse_connection(self.re_url_conn, uri) self._db = StrictRedis(*connection, **kw) display.vv('Redis connection: %s' % self._db) + @staticmethod + def _parse_connection(re_patt, uri): + match = re_patt.match(uri) + if not match: + raise AnsibleError("Unable to parse connection string") + return match.groups() + def _get_sentinel_connection(self, uri, kw): """ get sentinel connection details from _uri @@ -158,7 +168,7 @@ class CacheModule(BaseCacheModule): except IndexError: pass # password is optional - sentinels = [tuple(shost.split(':')) for shost in connections] + sentinels = [self._parse_connection(self.re_sent_conn, shost) for shost in connections] display.vv('\nUsing redis sentinels: %s' % sentinels) scon = Sentinel(sentinels, **kw) try: diff --git a/plugins/callback/__init__.py b/plugins/callback/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/callback/loganalytics.py b/plugins/callback/loganalytics.py index 507d6fccd9..ef1ea02f87 100644 --- a/plugins/callback/loganalytics.py +++ b/plugins/callback/loganalytics.py @@ -4,7 +4,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = ''' - callback: loganalytics + name: loganalytics type: aggregate short_description: Posts task results to Azure Log Analytics author: "Cyrus Li (@zhcli) " diff --git a/plugins/connection/__init__.py b/plugins/connection/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/connection/chroot.py b/plugins/connection/chroot.py index ffaea2b198..a18506cb80 100644 --- a/plugins/connection/chroot.py +++ b/plugins/connection/chroot.py @@ -62,7 +62,7 @@ display = Display() class Connection(ConnectionBase): - ''' Local chroot based connections ''' + """ Local chroot based connections """ transport = 'community.general.chroot' has_pipelining = True @@ -95,7 +95,7 @@ class Connection(ConnectionBase): raise AnsibleError("%s does not look like a chrootable dir (/bin/sh missing)" % self.chroot) def _connect(self): - ''' connect to the chroot ''' + """ connect to the chroot """ if os.path.isabs(self.get_option('chroot_exe')): self.chroot_cmd = self.get_option('chroot_exe') else: @@ -110,17 +110,17 @@ class Connection(ConnectionBase): self._connected = True def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE): - ''' run a command on the chroot. This is only needed for implementing + """ run a command on the chroot. This is only needed for implementing put_file() get_file() so that we don't have to read the whole file into memory. compared to exec_command() it looses some niceties like being able to return the process's exit code immediately. - ''' + """ executable = self.get_option('executable') local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd] - display.vvv("EXEC %s" % (local_cmd), host=self.chroot) + display.vvv("EXEC %s" % local_cmd, host=self.chroot) local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd] p = subprocess.Popen(local_cmd, shell=False, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -128,16 +128,17 @@ class Connection(ConnectionBase): return p def exec_command(self, cmd, in_data=None, sudoable=False): - ''' run a command on the chroot ''' + """ run a command on the chroot """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) p = self._buffered_exec_command(cmd) stdout, stderr = p.communicate(in_data) - return (p.returncode, stdout, stderr) + return p.returncode, stdout, stderr - def _prefix_login_path(self, remote_path): - ''' Make sure that we put files into a standard path + @staticmethod + def _prefix_login_path(remote_path): + """ Make sure that we put files into a standard path If a path is relative, then we need to choose where to put it. ssh chooses $HOME but we aren't guaranteed that a home dir will @@ -145,13 +146,13 @@ class Connection(ConnectionBase): This also happens to be the former default. Can revisit using $HOME instead if it's a problem - ''' + """ if not remote_path.startswith(os.path.sep): remote_path = os.path.join(os.path.sep, remote_path) return os.path.normpath(remote_path) def put_file(self, in_path, out_path): - ''' transfer a file from local to chroot ''' + """ transfer a file from local to chroot """ super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.chroot) @@ -177,7 +178,7 @@ class Connection(ConnectionBase): raise AnsibleError("file or module does not exist at: %s" % in_path) def fetch_file(self, in_path, out_path): - ''' fetch a file from chroot to local ''' + """ fetch a file from chroot to local """ super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.chroot) @@ -201,6 +202,6 @@ class Connection(ConnectionBase): raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr)) def close(self): - ''' terminate the connection; nothing to do here ''' + """ terminate the connection; nothing to do here """ super(Connection, self).close() self._connected = False diff --git a/plugins/connection/funcd.py b/plugins/connection/funcd.py index 3aed7145cb..109e251146 100644 --- a/plugins/connection/funcd.py +++ b/plugins/connection/funcd.py @@ -44,7 +44,7 @@ display = Display() class Connection(ConnectionBase): - ''' Func-based connections ''' + """ Func-based connections """ has_pipelining = False @@ -53,6 +53,7 @@ class Connection(ConnectionBase): self.host = host # port is unused, this go on func self.port = port + self.client = None def connect(self, port=None): if not HAVE_FUNC: @@ -62,31 +63,32 @@ class Connection(ConnectionBase): return self def exec_command(self, cmd, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): - ''' run a command on the remote minion ''' + """ run a command on the remote minion """ if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") # totally ignores privlege escalation - display.vvv("EXEC %s" % (cmd), host=self.host) + display.vvv("EXEC %s" % cmd, host=self.host) p = self.client.command.run(cmd)[self.host] - return (p[0], p[1], p[2]) + return p[0], p[1], p[2] - def _normalize_path(self, path, prefix): + @staticmethod + def _normalize_path(path, prefix): if not path.startswith(os.path.sep): path = os.path.join(os.path.sep, path) normpath = os.path.normpath(path) return os.path.join(prefix, normpath[1:]) def put_file(self, in_path, out_path): - ''' transfer a file from local to remote ''' + """ transfer a file from local to remote """ out_path = self._normalize_path(out_path, '/') display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) self.client.local.copyfile.send(in_path, out_path) def fetch_file(self, in_path, out_path): - ''' fetch a file from remote to local ''' + """ fetch a file from remote to local """ in_path = self._normalize_path(in_path, '/') display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) @@ -99,5 +101,5 @@ class Connection(ConnectionBase): shutil.rmtree(tmpdir) def close(self): - ''' terminate the connection; nothing to do here ''' + """ terminate the connection; nothing to do here """ pass diff --git a/plugins/connection/iocage.py b/plugins/connection/iocage.py index 435c789fd2..beb440eae3 100644 --- a/plugins/connection/iocage.py +++ b/plugins/connection/iocage.py @@ -40,7 +40,7 @@ display = Display() class Connection(Jail): - ''' Local iocage based connections ''' + """ Local iocage based connections """ transport = 'community.general.iocage' diff --git a/plugins/connection/jail.py b/plugins/connection/jail.py index 5252e3c4eb..f5d787b62f 100644 --- a/plugins/connection/jail.py +++ b/plugins/connection/jail.py @@ -35,7 +35,6 @@ import os import os.path import subprocess import traceback -import ansible.constants as C from ansible.errors import AnsibleError from ansible.module_utils.six.moves import shlex_quote @@ -47,7 +46,7 @@ display = Display() class Connection(ConnectionBase): - ''' Local BSD Jail based connections ''' + """ Local BSD Jail based connections """ modified_jailname_key = 'conn_jail_name' @@ -90,20 +89,20 @@ class Connection(ConnectionBase): return to_text(stdout, errors='surrogate_or_strict').split() def _connect(self): - ''' connect to the jail; nothing to do here ''' + """ connect to the jail; nothing to do here """ super(Connection, self)._connect() if not self._connected: display.vvv(u"ESTABLISH JAIL CONNECTION FOR USER: {0}".format(self._play_context.remote_user), host=self.jail) self._connected = True def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE): - ''' run a command on the jail. This is only needed for implementing + """ run a command on the jail. This is only needed for implementing put_file() get_file() so that we don't have to read the whole file into memory. compared to exec_command() it looses some niceties like being able to return the process's exit code immediately. - ''' + """ local_cmd = [self.jexec_cmd] set_env = '' @@ -123,16 +122,17 @@ class Connection(ConnectionBase): return p def exec_command(self, cmd, in_data=None, sudoable=False): - ''' run a command on the jail ''' + """ run a command on the jail """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) p = self._buffered_exec_command(cmd) stdout, stderr = p.communicate(in_data) - return (p.returncode, stdout, stderr) + return p.returncode, stdout, stderr - def _prefix_login_path(self, remote_path): - ''' Make sure that we put files into a standard path + @staticmethod + def _prefix_login_path(remote_path): + """ Make sure that we put files into a standard path If a path is relative, then we need to choose where to put it. ssh chooses $HOME but we aren't guaranteed that a home dir will @@ -140,13 +140,13 @@ class Connection(ConnectionBase): This also happens to be the former default. Can revisit using $HOME instead if it's a problem - ''' + """ if not remote_path.startswith(os.path.sep): remote_path = os.path.join(os.path.sep, remote_path) return os.path.normpath(remote_path) def put_file(self, in_path, out_path): - ''' transfer a file from local to jail ''' + """ transfer a file from local to jail """ super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.jail) @@ -172,7 +172,7 @@ class Connection(ConnectionBase): raise AnsibleError("file or module does not exist at: %s" % in_path) def fetch_file(self, in_path, out_path): - ''' fetch a file from jail to local ''' + """ fetch a file from jail to local """ super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.jail) @@ -196,6 +196,6 @@ class Connection(ConnectionBase): raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, to_native(stdout), to_native(stderr))) def close(self): - ''' terminate the connection; nothing to do here ''' + """ terminate the connection; nothing to do here """ super(Connection, self).close() self._connected = False diff --git a/plugins/connection/lxc.py b/plugins/connection/lxc.py index 8de1acc35d..6512a87c6d 100644 --- a/plugins/connection/lxc.py +++ b/plugins/connection/lxc.py @@ -42,14 +42,13 @@ try: except ImportError: pass -from ansible import constants as C from ansible import errors from ansible.module_utils._text import to_bytes, to_native from ansible.plugins.connection import ConnectionBase class Connection(ConnectionBase): - ''' Local lxc based connections ''' + """ Local lxc based connections """ transport = 'community.general.lxc' has_pipelining = True @@ -62,7 +61,7 @@ class Connection(ConnectionBase): self.container = None def _connect(self): - ''' connect to the lxc; nothing to do here ''' + """ connect to the lxc; nothing to do here """ super(Connection, self)._connect() if not HAS_LIBLXC: @@ -77,7 +76,8 @@ class Connection(ConnectionBase): if self.container.state == "STOPPED": raise errors.AnsibleError("%s is not running" % self.container_name) - def _communicate(self, pid, in_data, stdin, stdout, stderr): + @staticmethod + def _communicate(pid, in_data, stdin, stdout, stderr): buf = {stdout: [], stderr: []} read_fds = [stdout, stderr] if in_data: @@ -111,7 +111,7 @@ class Connection(ConnectionBase): return fd def exec_command(self, cmd, in_data=None, sudoable=False): - ''' run a command on the chroot ''' + """ run a command on the chroot """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) # python2-lxc needs bytes. python3-lxc needs text. diff --git a/plugins/connection/qubes.py b/plugins/connection/qubes.py index aa0075b674..d3f934b601 100644 --- a/plugins/connection/qubes.py +++ b/plugins/connection/qubes.py @@ -37,15 +37,9 @@ DOCUMENTATION = ''' # - name: hosts ''' -import shlex -import shutil - -import os -import base64 import subprocess -import ansible.constants as C -from ansible.module_utils._text import to_bytes, to_native +from ansible.module_utils._text import to_bytes from ansible.plugins.connection import ConnectionBase, ensure_connect from ansible.errors import AnsibleConnectionFailure from ansible.utils.display import Display diff --git a/plugins/connection/saltstack.py b/plugins/connection/saltstack.py index 6be7a79949..f8e3680aea 100644 --- a/plugins/connection/saltstack.py +++ b/plugins/connection/saltstack.py @@ -16,14 +16,11 @@ DOCUMENTATION = ''' - This allows you to use existing Saltstack infrastructure to connect to targets. ''' -import re import os -import pty -import codecs -import subprocess +import base64 -from ansible.module_utils._text import to_bytes, to_text -from ansible.module_utils.six.moves import cPickle +from ansible import errors +from ansible.plugins.connection import ConnectionBase HAVE_SALTSTACK = False try: @@ -32,13 +29,9 @@ try: except ImportError: pass -import os -from ansible import errors -from ansible.plugins.connection import ConnectionBase - class Connection(ConnectionBase): - ''' Salt-based connections ''' + """ Salt-based connections """ has_pipelining = False # while the name of the product is salt, naming that module salt cause @@ -58,29 +51,30 @@ class Connection(ConnectionBase): return self def exec_command(self, cmd, sudoable=False, in_data=None): - ''' run a command on the remote minion ''' + """ run a command on the remote minion """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") - self._display.vvv("EXEC %s" % (cmd), host=self.host) + self._display.vvv("EXEC %s" % cmd, host=self.host) # need to add 'true;' to work around https://github.com/saltstack/salt/issues/28077 res = self.client.cmd(self.host, 'cmd.exec_code_all', ['bash', 'true;' + cmd]) if self.host not in res: raise errors.AnsibleError("Minion %s didn't answer, check if salt-minion is running and the name is correct" % self.host) p = res[self.host] - return (p['retcode'], p['stdout'], p['stderr']) + return p['retcode'], p['stdout'], p['stderr'] - def _normalize_path(self, path, prefix): + @staticmethod + def _normalize_path(path, prefix): if not path.startswith(os.path.sep): path = os.path.join(os.path.sep, path) normpath = os.path.normpath(path) return os.path.join(prefix, normpath[1:]) def put_file(self, in_path, out_path): - ''' transfer a file from local to remote ''' + """ transfer a file from local to remote """ super(Connection, self).put_file(in_path, out_path) @@ -88,11 +82,11 @@ class Connection(ConnectionBase): self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) with open(in_path, 'rb') as in_fh: content = in_fh.read() - self.client.cmd(self.host, 'hashutil.base64_decodefile', [codecs.encode(content, 'base64'), out_path]) + self.client.cmd(self.host, 'hashutil.base64_decodefile', [base64.b64encode(content), out_path]) # TODO test it def fetch_file(self, in_path, out_path): - ''' fetch a file from remote to local ''' + """ fetch a file from remote to local """ super(Connection, self).fetch_file(in_path, out_path) @@ -102,5 +96,5 @@ class Connection(ConnectionBase): open(out_path, 'wb').write(content) def close(self): - ''' terminate the connection; nothing to do here ''' + """ terminate the connection; nothing to do here """ pass diff --git a/plugins/connection/zone.py b/plugins/connection/zone.py index 7a7a36331d..b101ec5cf3 100644 --- a/plugins/connection/zone.py +++ b/plugins/connection/zone.py @@ -31,7 +31,6 @@ import os.path import subprocess import traceback -from ansible import constants as C from ansible.errors import AnsibleError from ansible.module_utils.six.moves import shlex_quote from ansible.module_utils._text import to_bytes @@ -42,7 +41,7 @@ display = Display() class Connection(ConnectionBase): - ''' Local zone based connections ''' + """ Local zone based connections """ transport = 'community.general.zone' has_pipelining = True @@ -75,9 +74,9 @@ class Connection(ConnectionBase): stdout=subprocess.PIPE, stderr=subprocess.PIPE) zones = [] - for l in process.stdout.readlines(): + for line in process.stdout.readlines(): # 1:work:running:/zones/work:3126dc59-9a07-4829-cde9-a816e4c5040e:native:shared - s = l.split(':') + s = line.split(':') if s[1] != 'global': zones.append(s[1]) @@ -95,20 +94,20 @@ class Connection(ConnectionBase): return path + '/root' def _connect(self): - ''' connect to the zone; nothing to do here ''' + """ connect to the zone; nothing to do here """ super(Connection, self)._connect() if not self._connected: display.vvv("THIS IS A LOCAL ZONE DIR", host=self.zone) self._connected = True def _buffered_exec_command(self, cmd, stdin=subprocess.PIPE): - ''' run a command on the zone. This is only needed for implementing + """ run a command on the zone. This is only needed for implementing put_file() get_file() so that we don't have to read the whole file into memory. compared to exec_command() it looses some niceties like being able to return the process's exit code immediately. - ''' + """ # NOTE: zlogin invokes a shell (just like ssh does) so we do not pass # this through /bin/sh -c here. Instead it goes through the shell # that zlogin selects. @@ -122,16 +121,16 @@ class Connection(ConnectionBase): return p def exec_command(self, cmd, in_data=None, sudoable=False): - ''' run a command on the zone ''' + """ run a command on the zone """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) p = self._buffered_exec_command(cmd) stdout, stderr = p.communicate(in_data) - return (p.returncode, stdout, stderr) + return p.returncode, stdout, stderr def _prefix_login_path(self, remote_path): - ''' Make sure that we put files into a standard path + """ Make sure that we put files into a standard path If a path is relative, then we need to choose where to put it. ssh chooses $HOME but we aren't guaranteed that a home dir will @@ -139,13 +138,13 @@ class Connection(ConnectionBase): This also happens to be the former default. Can revisit using $HOME instead if it's a problem - ''' + """ if not remote_path.startswith(os.path.sep): remote_path = os.path.join(os.path.sep, remote_path) return os.path.normpath(remote_path) def put_file(self, in_path, out_path): - ''' transfer a file from local to zone ''' + """ transfer a file from local to zone """ super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.zone) @@ -171,7 +170,7 @@ class Connection(ConnectionBase): raise AnsibleError("file or module does not exist at: %s" % in_path) def fetch_file(self, in_path, out_path): - ''' fetch a file from zone to local ''' + """ fetch a file from zone to local """ super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.zone) @@ -195,6 +194,6 @@ class Connection(ConnectionBase): raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr)) def close(self): - ''' terminate the connection; nothing to do here ''' + """ terminate the connection; nothing to do here """ super(Connection, self).close() self._connected = False diff --git a/plugins/doc_fragments/__init__.py b/plugins/doc_fragments/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/filter/__init__.py b/plugins/filter/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/filter/groupby.py b/plugins/filter/groupby.py new file mode 100644 index 0000000000..a2a85aa905 --- /dev/null +++ b/plugins/filter/groupby.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Felix Fontein +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.errors import AnsibleFilterError +from ansible.module_utils.common._collections_compat import Mapping, Sequence + + +def groupby_as_dict(sequence, attribute): + ''' + Given a sequence of dictionaries and an attribute name, returns a dictionary mapping + the value of this attribute to the dictionary. + + If multiple dictionaries in the sequence have the same value for this attribute, + the filter will fail. + ''' + if not isinstance(sequence, Sequence): + raise AnsibleFilterError('Input is not a sequence') + + result = dict() + for list_index, element in enumerate(sequence): + if not isinstance(element, Mapping): + raise AnsibleFilterError('Sequence element #{0} is not a mapping'.format(list_index)) + if attribute not in element: + raise AnsibleFilterError('Attribute not contained in element #{0} of sequence'.format(list_index)) + result_index = element[attribute] + if result_index in result: + raise AnsibleFilterError('Multiple sequence entries have attribute value {0!r}'.format(result_index)) + result[result_index] = element + return result + + +class FilterModule(object): + ''' Ansible list filters ''' + + def filters(self): + return { + 'groupby_as_dict': groupby_as_dict, + } diff --git a/plugins/filter/json_query.py b/plugins/filter/json_query.py index 972109a045..673cafa587 100644 --- a/plugins/filter/json_query.py +++ b/plugins/filter/json_query.py @@ -35,9 +35,11 @@ def json_query(data, expr): raise AnsibleError('You need to install "jmespath" prior to running ' 'json_query filter') - # Hack to handle Ansible String Types + # Hack to handle Ansible Unsafe text, AnsibleMapping and AnsibleSequence # See issue: https://github.com/ansible-collections/community.general/issues/320 jmespath.functions.REVERSE_TYPES_MAP['string'] = jmespath.functions.REVERSE_TYPES_MAP['string'] + ('AnsibleUnicode', 'AnsibleUnsafeText', ) + jmespath.functions.REVERSE_TYPES_MAP['array'] = jmespath.functions.REVERSE_TYPES_MAP['array'] + ('AnsibleSequence', ) + jmespath.functions.REVERSE_TYPES_MAP['object'] = jmespath.functions.REVERSE_TYPES_MAP['object'] + ('AnsibleMapping', ) try: return jmespath.search(expr, data) except jmespath.exceptions.JMESPathError as e: diff --git a/plugins/inventory/__init__.py b/plugins/inventory/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/inventory/lxd.py b/plugins/inventory/lxd.py index c48818d595..d1e47b0505 100644 --- a/plugins/inventory/lxd.py +++ b/plugins/inventory/lxd.py @@ -6,7 +6,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = r''' - name: community.general.lxd + name: lxd short_description: Returns Ansible inventory from lxd host description: - Get inventory from the lxd. @@ -68,7 +68,7 @@ DOCUMENTATION = r''' description: - Create groups by the following keywords C(location), C(pattern), C(network_range), C(os), C(release), C(profile), C(vlanid). - See example for syntax. - type: json + type: dict ''' EXAMPLES = ''' diff --git a/plugins/inventory/nmap.py b/plugins/inventory/nmap.py index 687317abfa..39a6ff3a67 100644 --- a/plugins/inventory/nmap.py +++ b/plugins/inventory/nmap.py @@ -130,7 +130,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): # This occurs if the cache_key is not in the cache or if the cache_key expired, so the cache needs to be updated cache_needs_update = True - if cache_needs_update: + if not user_cache_setting or cache_needs_update: # setup command cmd = [self._nmap] if not self._options['ports']: @@ -207,6 +207,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): except Exception as e: raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e))) + if cache_needs_update: self._cache[cache_key] = results self._populate(results) diff --git a/plugins/inventory/stackpath_compute.py b/plugins/inventory/stackpath_compute.py index 393edac384..8e6b5bf953 100644 --- a/plugins/inventory/stackpath_compute.py +++ b/plugins/inventory/stackpath_compute.py @@ -10,6 +10,8 @@ DOCUMENTATION = ''' name: stackpath_compute short_description: StackPath Edge Computing inventory source version_added: 1.2.0 + author: + - UNKNOWN (@shayrybak) extends_documentation_fragment: - inventory_cache - constructed @@ -102,13 +104,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): raise AnsibleError("plugin doesn't match this plugin") try: client_id = config['client_id'] - if client_id != 32: + if len(client_id) != 32: raise AnsibleError("client_id must be 32 characters long") except KeyError: raise AnsibleError("config missing client_id, a required option") try: client_secret = config['client_secret'] - if client_secret != 64: + if len(client_secret) != 64: raise AnsibleError("client_secret must be 64 characters long") except KeyError: raise AnsibleError("config missing client_id, a required option") diff --git a/plugins/lookup/__init__.py b/plugins/lookup/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/lookup/consul_kv.py b/plugins/lookup/consul_kv.py index 7ba7e5ac90..d567b7f687 100644 --- a/plugins/lookup/consul_kv.py +++ b/plugins/lookup/consul_kv.py @@ -171,10 +171,10 @@ class LookupModule(LookupBase): paramvals = { 'key': params[0], - 'token': None, - 'recurse': False, - 'index': None, - 'datacenter': None + 'token': self.get_option('token'), + 'recurse': self.get_option('recurse'), + 'index': self.get_option('index'), + 'datacenter': self.get_option('datacenter') } # parameters specified? diff --git a/plugins/lookup/dependent.py b/plugins/lookup/dependent.py new file mode 100644 index 0000000000..a22a98476c --- /dev/null +++ b/plugins/lookup/dependent.py @@ -0,0 +1,208 @@ +# (c) 2015-2021, Felix Fontein +# (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = """ +name: dependent +short_description: Composes a list with nested elements of other lists or dicts which can depend on previous loop variables +version_added: 3.1.0 +description: + - "Takes the input lists and returns a list with elements that are lists, dictionaries, + or template expressions which evaluate to lists or dicts, composed of the elements of + the input evaluated lists and dictionaries." +options: + _raw: + description: + - A list where the elements are one-element dictionaries, mapping a name to a string, list, or dictionary. + The name is the index that is used in the result object. The value is iterated over as described below. + - If the value is a list, it is simply iterated over. + - If the value is a dictionary, it is iterated over and returned as if they would be processed by the + R(ansible.builtin.dict2items filter,ansible_collections.ansible.builtin.dict2items_filter). + - If the value is a string, it is evaluated as Jinja2 expressions which can access the previously chosen + elements with C(item.). The result must be a list or a dictionary. + type: list + elements: dict + required: true +""" + +EXAMPLES = """ +- name: Install/remove public keys for active admin users + ansible.posix.authorized_key: + user: "{{ item.admin.key }}" + key: "{{ lookup('file', item.key.public_key) }}" + state: "{{ 'present' if item.key.active else 'absent' }}" + when: item.admin.value.active + with_community.general.dependent: + - admin: admin_user_data + - key: admin_ssh_keys[item.admin.key] + loop_control: + # Makes the output readable, so that it doesn't contain the whole subdictionaries and lists + label: "{{ [item.admin.key, 'active' if item.key.active else 'inactive', item.key.public_key] }}" + vars: + admin_user_data: + admin1: + name: Alice + active: true + admin2: + name: Bob + active: true + admin_ssh_keys: + admin1: + - private_key: keys/private_key_admin1.pem + public_key: keys/private_key_admin1.pub + active: true + admin2: + - private_key: keys/private_key_admin2.pem + public_key: keys/private_key_admin2.pub + active: true + - private_key: keys/private_key_admin2-old.pem + public_key: keys/private_key_admin2-old.pub + active: false + +- name: Update DNS records + community.aws.route53: + zone: "{{ item.zone.key }}" + record: "{{ item.prefix.key ~ '.' if item.prefix.key else '' }}{{ item.zone.key }}" + type: "{{ item.entry.key }}" + ttl: "{{ item.entry.value.ttl | default(3600) }}" + value: "{{ item.entry.value.value }}" + state: "{{ 'absent' if (item.entry.value.absent | default(False)) else 'present' }}" + overwrite: true + loop_control: + # Makes the output readable, so that it doesn't contain the whole subdictionaries and lists + label: |- + {{ [item.zone.key, item.prefix.key, item.entry.key, + item.entry.value.ttl | default(3600), + item.entry.value.absent | default(False), item.entry.value.value] }} + with_community.general.dependent: + - zone: dns_setup + - prefix: item.zone.value + - entry: item.prefix.value + vars: + dns_setup: + example.com: + '': + A: + value: + - 1.2.3.4 + AAAA: + value: + - "2a01:1:2:3::1" + 'test._domainkey': + TXT: + ttl: 300 + value: + - '"k=rsa; t=s; p=MIGfMA..."' + example.org: + 'www': + A: + value: + - 1.2.3.4 + - 5.6.7.8 +""" + +RETURN = """ + _list: + description: + - A list composed of dictionaries whose keys are the variable names from the input list. + type: list + elements: dict + sample: + - key1: a + key2: test + - key1: a + key2: foo + - key1: b + key2: bar +""" + +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.template import Templar + + +class LookupModule(LookupBase): + def __evaluate(self, expression, templar, variables): + """Evaluate expression with templar. + + ``expression`` is the expression to evaluate. + ``variables`` are the variables to use. + """ + templar.available_variables = variables or {} + return templar.template("{0}{1}{2}".format("{{", expression, "}}"), cache=False) + + def __process(self, result, terms, index, current, templar, variables): + """Fills ``result`` list with evaluated items. + + ``result`` is a list where the resulting items are placed. + ``terms`` is the parsed list of terms + ``index`` is the current index to be processed in the list. + ``current`` is a dictionary where the first ``index`` values are filled in. + ``variables`` are the variables currently available. + """ + # If we are done, add to result list: + if index == len(terms): + result.append(current.copy()) + return + + key, expression, values = terms[index] + + if expression is not None: + # Evaluate expression in current context + vars = variables.copy() + vars['item'] = current.copy() + try: + values = self.__evaluate(expression, templar, variables=vars) + except Exception as e: + raise AnsibleLookupError( + 'Caught "{error}" while evaluating {key!r} with item == {item!r}'.format( + error=e, key=key, item=current)) + + if isinstance(values, Mapping): + for idx, val in sorted(values.items()): + current[key] = dict([('key', idx), ('value', val)]) + self.__process(result, terms, index + 1, current, templar, variables) + elif isinstance(values, Sequence): + for elt in values: + current[key] = elt + self.__process(result, terms, index + 1, current, templar, variables) + else: + raise AnsibleLookupError( + 'Did not obtain dictionary or list while evaluating {key!r} with item == {item!r}, but {type}'.format( + key=key, item=current, type=type(values))) + + def run(self, terms, variables=None, **kwargs): + """Generate list.""" + result = [] + if len(terms) > 0: + templar = Templar(loader=self._templar._loader) + data = [] + vars_so_far = set() + for index, term in enumerate(terms): + if not isinstance(term, Mapping): + raise AnsibleLookupError( + 'Parameter {index} must be a dictionary, got {type}'.format( + index=index, type=type(term))) + if len(term) != 1: + raise AnsibleLookupError( + 'Parameter {index} must be a one-element dictionary, got {count} elements'.format( + index=index, count=len(term))) + k, v = list(term.items())[0] + if k in vars_so_far: + raise AnsibleLookupError( + 'The variable {key!r} appears more than once'.format(key=k)) + vars_so_far.add(k) + if isinstance(v, string_types): + data.append((k, v, None)) + elif isinstance(v, (Sequence, Mapping)): + data.append((k, None, v)) + else: + raise AnsibleLookupError( + 'Parameter {key!r} (index {index}) must have a value of type string, dictionary or list, got type {type}'.format( + index=index, key=k, type=type(v))) + self.__process(result, data, 0, {}, templar, variables) + return result diff --git a/plugins/lookup/nios.py b/plugins/lookup/nios.py index 4b606e78ba..819d8077e6 100644 --- a/plugins/lookup/nios.py +++ b/plugins/lookup/nios.py @@ -25,6 +25,10 @@ DOCUMENTATION = ''' author: Unknown (!UNKNOWN) name: nios short_description: Query Infoblox NIOS objects +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding lookup from it. + alternative: infoblox.nios_modules.nios_lookup + removed_in: 5.0.0 description: - Uses the Infoblox WAPI API to fetch NIOS specified objects. This lookup supports adding additional keywords to filter the return data and specify diff --git a/plugins/lookup/nios_next_ip.py b/plugins/lookup/nios_next_ip.py index 5b979b8d07..21773cb53e 100644 --- a/plugins/lookup/nios_next_ip.py +++ b/plugins/lookup/nios_next_ip.py @@ -25,6 +25,10 @@ DOCUMENTATION = ''' author: Unknown (!UNKNOWN) name: nios_next_ip short_description: Return the next available IP address for a network +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding lookup from it. + alternative: infoblox.nios_modules.nios_next_ip + removed_in: 5.0.0 description: - Uses the Infoblox WAPI API to return the next available IP addresses for a given network CIDR diff --git a/plugins/lookup/nios_next_network.py b/plugins/lookup/nios_next_network.py index 84b230d1fe..2aa22ab704 100644 --- a/plugins/lookup/nios_next_network.py +++ b/plugins/lookup/nios_next_network.py @@ -25,6 +25,10 @@ DOCUMENTATION = ''' author: Unknown (!UNKNOWN) name: nios_next_network short_description: Return the next available network range for a network-container +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding lookup from it. + alternative: infoblox.nios_modules.nios_next_network + removed_in: 5.0.0 description: - Uses the Infoblox WAPI API to return the next available network addresses for a given network CIDR diff --git a/plugins/lookup/onepassword.py b/plugins/lookup/onepassword.py index a2346ed072..715c337ffd 100644 --- a/plugins/lookup/onepassword.py +++ b/plugins/lookup/onepassword.py @@ -30,6 +30,11 @@ DOCUMENTATION = ''' aliases: ['vault_password'] 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). + version_added: 3.2.0 + default: '1password.com' + type: str subdomain: description: The 1Password subdomain to authenticate against. username: @@ -109,6 +114,7 @@ class OnePass(object): self.logged_in = False self.token = None self.subdomain = None + self.domain = None self.username = None self.secret_key = None self.master_password = None @@ -168,7 +174,7 @@ class OnePass(object): args = [ 'signin', - '{0}.1password.com'.format(self.subdomain), + '{0}.{1}'.format(self.subdomain, self.domain), to_bytes(self.username), to_bytes(self.secret_key), '--output=raw', @@ -265,6 +271,7 @@ class LookupModule(LookupBase): section = kwargs.get('section') vault = kwargs.get('vault') op.subdomain = kwargs.get('subdomain') + op.domain = kwargs.get('domain', '1password.com') op.username = kwargs.get('username') op.secret_key = kwargs.get('secret_key') op.master_password = kwargs.get('master_password', kwargs.get('vault_password')) diff --git a/plugins/lookup/passwordstore.py b/plugins/lookup/passwordstore.py index 79c69ed962..976dfb837e 100644 --- a/plugins/lookup/passwordstore.py +++ b/plugins/lookup/passwordstore.py @@ -25,9 +25,9 @@ DOCUMENTATION = ''' env: - name: PASSWORD_STORE_DIR create: - description: Create the password if it does not already exist. + description: Create the password if it does not already exist. Takes precedence over C(missing). type: bool - default: 'no' + default: false overwrite: description: Overwrite the password if it does already exist. type: bool @@ -60,6 +60,22 @@ DOCUMENTATION = ''' description: use alphanumeric characters. type: bool default: 'no' + missing: + description: + - List of preference about what to do if the password file is missing. + - If I(create=true), the value for this option is ignored and assumed to be C(create). + - If set to C(error), the lookup will error out if the passname does not exist. + - If set to C(create), the passname will be created with the provided length I(length) if it does not exist. + - If set to C(empty) or C(warn), will return a C(none) in case the passname does not exist. + When using C(lookup) and not C(query), this will be translated to an empty string. + version_added: 3.1.0 + type: str + default: error + choices: + - error + - warn + - empty + - create ''' EXAMPLES = """ # Debug is used for examples, BAD IDEA to show passwords on screen @@ -67,12 +83,28 @@ EXAMPLES = """ ansible.builtin.debug: msg: "{{ lookup('community.general.passwordstore', 'example/test')}}" +- name: Basic lookup. Warns if example/test does not exist and returns empty string + ansible.builtin.debug: + msg: "{{ lookup('community.general.passwordstore', 'example/test missing=warn')}}" + - name: Create pass with random 16 character password. If password exists just give the password ansible.builtin.debug: var: mypassword vars: mypassword: "{{ lookup('community.general.passwordstore', 'example/test create=true')}}" +- name: Create pass with random 16 character password. If password exists just give the password + ansible.builtin.debug: + var: mypassword + vars: + mypassword: "{{ lookup('community.general.passwordstore', 'example/test missing=create')}}" + +- name: Prints 'abc' if example/test does not exist, just give the password otherwise + ansible.builtin.debug: + var: mypassword + vars: + mypassword: "{{ lookup('community.general.passwordstore', 'example/test missing=empty') | default('abc', true) }}" + - name: Different size password ansible.builtin.debug: msg: "{{ lookup('community.general.passwordstore', 'example/test create=true length=42')}}" @@ -111,10 +143,13 @@ import yaml from distutils import util from ansible.errors import AnsibleError, AnsibleAssertionError from ansible.module_utils._text import to_bytes, to_native, to_text +from ansible.utils.display import Display from ansible.utils.encrypt import random_password from ansible.plugins.lookup import LookupBase from ansible import constants as C +display = Display() + # backhacked check_output with input for python 2.7 # http://stackoverflow.com/questions/10103551/passing-data-to-subprocess-check-output @@ -178,12 +213,17 @@ class LookupModule(LookupBase): self.paramvals[key] = util.strtobool(self.paramvals[key]) except (ValueError, AssertionError) as e: raise AnsibleError(e) + if self.paramvals['missing'] not in ['error', 'warn', 'create', 'empty']: + raise AnsibleError("{0} is not a valid option for missing".format(self.paramvals['missing'])) if not isinstance(self.paramvals['length'], int): if self.paramvals['length'].isdigit(): self.paramvals['length'] = int(self.paramvals['length']) else: raise AnsibleError("{0} is not a correct value for length".format(self.paramvals['length'])) + if self.paramvals['create']: + self.paramvals['missing'] = 'create' + # Collect pass environment variables from the plugin's parameters. self.env = os.environ.copy() @@ -224,9 +264,11 @@ class LookupModule(LookupBase): if e.returncode != 0 and 'not in the password store' in e.output: # if pass returns 1 and return string contains 'is not in the password store.' # We need to determine if this is valid or Error. - if not self.paramvals['create']: - raise AnsibleError('passname: {0} not found, use create=True'.format(self.passname)) + if self.paramvals['missing'] == 'error': + raise AnsibleError('passwordstore: passname {0} not found and missing=error is set'.format(self.passname)) else: + if self.paramvals['missing'] == 'warn': + display.warning('passwordstore: passname {0} not found'.format(self.passname)) return False else: raise AnsibleError(e) @@ -294,6 +336,7 @@ class LookupModule(LookupBase): 'userpass': '', 'length': 16, 'backup': False, + 'missing': 'error', } for term in terms: @@ -304,6 +347,9 @@ class LookupModule(LookupBase): else: result.append(self.get_passresult()) else: # password does not exist - if self.paramvals['create']: + if self.paramvals['missing'] == 'create': result.append(self.generate_password()) + else: + result.append(None) + return result diff --git a/plugins/lookup/random_pet.py b/plugins/lookup/random_pet.py new file mode 100644 index 0000000000..6caf178e4b --- /dev/null +++ b/plugins/lookup/random_pet.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2021, Abhijeet Kasurde +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +DOCUMENTATION = r''' + name: random_pet + author: + - Abhijeet Kasurde (@Akasurde) + short_description: Generates random pet names + version_added: '3.1.0' + requirements: + - petname U(https://github.com/dustinkirkland/python-petname) + description: + - Generates random pet names that can be used as unique identifiers for the resources. + options: + words: + description: + - The number of words in the pet name. + default: 2 + type: int + length: + description: + - The maximal length of every component of the pet name. + - Values below 3 will be set to 3 by petname. + default: 6 + type: int + prefix: + description: A string to prefix with the name. + type: str + separator: + description: The character to separate words in the pet name. + default: "-" + type: str +''' + +EXAMPLES = r''' +- name: Generate pet name + ansible.builtin.debug: + var: lookup('community.general.random_pet') + # Example result: 'loving-raptor' + +- name: Generate pet name with 3 words + ansible.builtin.debug: + var: lookup('community.general.random_pet', words=3) + # Example result: 'fully-fresh-macaw' + +- name: Generate pet name with separator + ansible.builtin.debug: + var: lookup('community.general.random_pet', separator="_") + # Example result: 'causal_snipe' + +- name: Generate pet name with length + ansible.builtin.debug: + var: lookup('community.general.random_pet', length=7) + # Example result: 'natural-peacock' +''' + +RETURN = r''' + _raw: + description: A one-element list containing a random pet name + type: list + elements: str +''' + +try: + import petname + + HAS_PETNAME = True +except ImportError: + HAS_PETNAME = False + +from ansible.errors import AnsibleError +from ansible.plugins.lookup import LookupBase + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + + if not HAS_PETNAME: + raise AnsibleError('Python petname library is required. ' + 'Please install using "pip install petname"') + + self.set_options(var_options=variables, direct=kwargs) + words = self.get_option('words') + length = self.get_option('length') + prefix = self.get_option('prefix') + separator = self.get_option('separator') + + values = petname.Generate(words=words, separator=separator, letters=length) + if prefix: + values = "%s%s%s" % (prefix, separator, values) + + return [values] diff --git a/plugins/lookup/random_string.py b/plugins/lookup/random_string.py new file mode 100644 index 0000000000..6a05cfd041 --- /dev/null +++ b/plugins/lookup/random_string.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2021, Abhijeet Kasurde +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" + name: random_string + author: + - Abhijeet Kasurde (@Akasurde) + short_description: Generates random string + version_added: '3.2.0' + description: + - Generates random string based upon the given constraints. + options: + length: + description: The length of the string. + default: 8 + type: int + upper: + description: + - Include uppercase letters in the string. + default: true + type: bool + lower: + description: + - Include lowercase letters in the string. + default: true + type: bool + numbers: + description: + - Include numbers in the string. + default: true + type: bool + special: + description: + - Include special characters in the string. + - Special characters are taken from Python standard library C(string). + See L(the documentation of string.punctuation,https://docs.python.org/3/library/string.html#string.punctuation) + for which characters will be used. + - The choice of special characters can be changed to setting I(override_special). + default: true + type: bool + min_numeric: + description: + - Minimum number of numeric characters in the string. + - If set, overrides I(numbers=false). + default: 0 + type: int + min_upper: + description: + - Minimum number of uppercase alphabets in the string. + - If set, overrides I(upper=false). + default: 0 + type: int + min_lower: + description: + - Minimum number of lowercase alphabets in the string. + - If set, overrides I(lower=false). + default: 0 + type: int + min_special: + description: + - Minimum number of special character in the string. + default: 0 + type: int + override_special: + description: + - Overide a list of special characters to use in the string. + - If set I(min_special) should be set to a non-default value. + type: str + override_all: + description: + - Override all values of I(numbers), I(upper), I(lower), and I(special) with + the given list of characters. + type: str + base64: + description: + - Returns base64 encoded string. + type: bool + default: false +""" + +EXAMPLES = r""" +- name: Generate random string + ansible.builtin.debug: + var: lookup('community.general.random_string') + # Example result: ['DeadBeeF'] + +- name: Generate random string with length 12 + ansible.builtin.debug: + var: lookup('community.general.random_string', length=12) + # Example result: ['Uan0hUiX5kVG'] + +- name: Generate base64 encoded random string + ansible.builtin.debug: + var: lookup('community.general.random_string', base64=True) + # Example result: ['NHZ6eWN5Qk0='] + +- name: Generate a random string with 1 lower, 1 upper, 1 number and 1 special char (atleast) + ansible.builtin.debug: + var: lookup('community.general.random_string', min_lower=1, min_upper=1, min_special=1, min_numeric=1) + # Example result: ['&Qw2|E[-'] + +- name: Generate a random string with all lower case characters + debug: + var: query('community.general.random_string', upper=false, numbers=false, special=false) + # Example result: ['exolxzyz'] + +- name: Generate random hexadecimal string + debug: + var: query('community.general.random_string', upper=false, lower=false, override_special=hex_chars, numbers=false) + vars: + hex_chars: '0123456789ABCDEF' + # Example result: ['D2A40737'] + +- name: Generate random hexadecimal string with override_all + debug: + var: query('community.general.random_string', override_all=hex_chars) + vars: + hex_chars: '0123456789ABCDEF' + # Example result: ['D2A40737'] +""" + +RETURN = r""" + _raw: + description: A one-element list containing a random string + type: list + elements: str +""" + +import base64 +import random +import string + +from ansible.errors import AnsibleLookupError +from ansible.plugins.lookup import LookupBase +from ansible.module_utils._text import to_bytes, to_text + + +class LookupModule(LookupBase): + @staticmethod + def get_random(random_generator, chars, length): + if not chars: + raise AnsibleLookupError( + "Available characters cannot be None, please change constraints" + ) + return "".join(random_generator.choice(chars) for dummy in range(length)) + + @staticmethod + def b64encode(string_value, encoding="utf-8"): + return to_text( + base64.b64encode( + to_bytes(string_value, encoding=encoding, errors="surrogate_or_strict") + ) + ) + + def run(self, terms, variables=None, **kwargs): + number_chars = string.digits + lower_chars = string.ascii_lowercase + upper_chars = string.ascii_uppercase + special_chars = string.punctuation + random_generator = random.SystemRandom() + + self.set_options(var_options=variables, direct=kwargs) + + length = self.get_option("length") + base64_flag = self.get_option("base64") + override_all = self.get_option("override_all") + values = "" + available_chars_set = "" + + if override_all: + # Override all the values + available_chars_set = override_all + else: + upper = self.get_option("upper") + lower = self.get_option("lower") + numbers = self.get_option("numbers") + special = self.get_option("special") + override_special = self.get_option("override_special") + + if override_special: + special_chars = override_special + + if upper: + available_chars_set += upper_chars + if lower: + available_chars_set += lower_chars + if numbers: + available_chars_set += number_chars + if special: + available_chars_set += special_chars + + mapping = { + "min_numeric": number_chars, + "min_lower": lower_chars, + "min_upper": upper_chars, + "min_special": special_chars, + } + + for m in mapping: + if self.get_option(m): + values += self.get_random(random_generator, mapping[m], self.get_option(m)) + + remaining_pass_len = length - len(values) + values += self.get_random(random_generator, available_chars_set, remaining_pass_len) + + # Get pseudo randomization + shuffled_values = list(values) + # Randomize the order + random.shuffle(shuffled_values) + + if base64_flag: + return [self.b64encode("".join(shuffled_values))] + + return ["".join(shuffled_values)] diff --git a/plugins/lookup/tss.py b/plugins/lookup/tss.py index 2c25532699..b7b7cd85e0 100644 --- a/plugins/lookup/tss.py +++ b/plugins/lookup/tss.py @@ -103,6 +103,14 @@ EXAMPLES = r""" | items2dict(key_name='slug', value_name='itemValue'))['password'] }} + +- hosts: localhost + vars: + secret_password: >- + {{ ((lookup('community.general.tss', 1) | from_json).get('items') | items2dict(key_name='slug', value_name='itemValue'))['password'] }}" + tasks: + - ansible.builtin.debug: + msg: the password is {{ secret_password }} """ from ansible.errors import AnsibleError, AnsibleOptionsError diff --git a/plugins/module_utils/__init__.py b/plugins/module_utils/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/identity/__init__.py b/plugins/module_utils/identity/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/identity/keycloak/__init__.py b/plugins/module_utils/identity/keycloak/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/mh/base.py b/plugins/module_utils/mh/base.py new file mode 100644 index 0000000000..e0de7f2fdd --- /dev/null +++ b/plugins/module_utils/mh/base.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException as _MHE +from ansible_collections.community.general.plugins.module_utils.mh.deco import module_fails_on_exception + + +class ModuleHelperBase(object): + module = None + ModuleHelperException = _MHE + + def __init__(self, module=None): + self._changed = False + + if module: + self.module = module + + if not isinstance(self.module, AnsibleModule): + self.module = AnsibleModule(**self.module) + + def __init_module__(self): + pass + + def __run__(self): + raise NotImplementedError() + + def __quit_module__(self): + pass + + def __changed__(self): + raise NotImplementedError() + + @property + def changed(self): + try: + return self.__changed__() + except NotImplementedError: + return self._changed + + @changed.setter + def changed(self, value): + self._changed = value + + def has_changed(self): + raise NotImplementedError() + + @property + def output(self): + raise NotImplementedError() + + @module_fails_on_exception + def run(self): + self.__init_module__() + self.__run__() + self.__quit_module__() + self.module.exit_json(changed=self.has_changed(), **self.output) diff --git a/plugins/module_utils/mh/deco.py b/plugins/module_utils/mh/deco.py new file mode 100644 index 0000000000..91f0d97744 --- /dev/null +++ b/plugins/module_utils/mh/deco.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import traceback +from functools import wraps + +from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException + + +def cause_changes(on_success=None, on_failure=None): + + def deco(func): + if on_success is None and on_failure is None: + return func + + @wraps(func) + def wrapper(*args, **kwargs): + try: + self = args[0] + func(*args, **kwargs) + if on_success is not None: + self.changed = on_success + except Exception: + if on_failure is not None: + self.changed = on_failure + raise + + return wrapper + + return deco + + +def module_fails_on_exception(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + try: + func(self, *args, **kwargs) + except SystemExit: + raise + except ModuleHelperException as e: + if e.update_output: + self.update_output(e.update_output) + self.module.fail_json(msg=e.msg, exception=traceback.format_exc(), + output=self.output, vars=self.vars.output(), **self.output) + except Exception as e: + msg = "Module failed with exception: {0}".format(str(e).strip()) + self.module.fail_json(msg=msg, exception=traceback.format_exc(), + output=self.output, vars=self.vars.output(), **self.output) + return wrapper diff --git a/plugins/module_utils/mh/exceptions.py b/plugins/module_utils/mh/exceptions.py new file mode 100644 index 0000000000..558dcca05f --- /dev/null +++ b/plugins/module_utils/mh/exceptions.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class ModuleHelperException(Exception): + @staticmethod + def _get_remove(key, kwargs): + if key in kwargs: + result = kwargs[key] + del kwargs[key] + return result + return None + + def __init__(self, *args, **kwargs): + self.msg = self._get_remove('msg', kwargs) or "Module failed with exception: {0}".format(self) + self.update_output = self._get_remove('update_output', kwargs) or {} + super(ModuleHelperException, self).__init__(*args) diff --git a/plugins/module_utils/mh/mixins/cmd.py b/plugins/module_utils/mh/mixins/cmd.py new file mode 100644 index 0000000000..0367b6173c --- /dev/null +++ b/plugins/module_utils/mh/mixins/cmd.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from functools import partial + + +class ArgFormat(object): + """ + Argument formatter for use as a command line parameter. Used in CmdMixin. + """ + BOOLEAN = 0 + PRINTF = 1 + FORMAT = 2 + + @staticmethod + def stars_deco(num): + if num == 1: + def deco(f): + return lambda v: f(*v) + return deco + elif num == 2: + def deco(f): + return lambda v: f(**v) + return deco + + return lambda f: f + + def __init__(self, name, fmt=None, style=FORMAT, stars=0): + """ + Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for + the CLI command execution. + :param name: Name of the argument to be formatted + :param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that + :param style: Whether arg_format (as str) should use printf-style formatting. + Ignored if arg_format is None or not a str (should be callable). + :param stars: A int with 0, 1 or 2 value, indicating to formatting the value as: value, *value or **value + """ + def printf_fmt(_fmt, v): + try: + return [_fmt % v] + except TypeError as e: + if e.args[0] != 'not all arguments converted during string formatting': + raise + return [_fmt] + + _fmts = { + ArgFormat.BOOLEAN: lambda _fmt, v: ([_fmt] if bool(v) else []), + ArgFormat.PRINTF: printf_fmt, + ArgFormat.FORMAT: lambda _fmt, v: [_fmt.format(v)], + } + + self.name = name + self.stars = stars + + if fmt is None: + fmt = "{0}" + style = ArgFormat.FORMAT + + if isinstance(fmt, str): + func = _fmts[style] + self.arg_format = partial(func, fmt) + elif isinstance(fmt, list) or isinstance(fmt, tuple): + self.arg_format = lambda v: [_fmts[style](f, v)[0] for f in fmt] + elif hasattr(fmt, '__call__'): + self.arg_format = fmt + else: + raise TypeError('Parameter fmt must be either: a string, a list/tuple of ' + 'strings or a function: type={0}, value={1}'.format(type(fmt), fmt)) + + if stars: + self.arg_format = (self.stars_deco(stars))(self.arg_format) + + def to_text(self, value): + if value is None: + return [] + func = self.arg_format + return [str(p) for p in func(value)] + + +class CmdMixin(object): + """ + Mixin for mapping module options to running a CLI command with its arguments. + """ + command = None + command_args_formats = {} + run_command_fixed_options = {} + check_rc = False + force_lang = "C" + + @property + def module_formats(self): + result = {} + for param in self.module.params.keys(): + result[param] = ArgFormat(param) + return result + + @property + def custom_formats(self): + result = {} + for param, fmt_spec in self.command_args_formats.items(): + result[param] = ArgFormat(param, **fmt_spec) + return result + + def _calculate_args(self, extra_params=None, params=None): + def add_arg_formatted_param(_cmd_args, arg_format, _value): + args = list(arg_format.to_text(_value)) + return _cmd_args + args + + def find_format(_param): + return self.custom_formats.get(_param, self.module_formats.get(_param)) + + extra_params = extra_params or dict() + cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command) + try: + cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True) + except ValueError: + pass + param_list = params if params else self.vars.keys() + + for param in param_list: + if isinstance(param, dict): + if len(param) != 1: + raise self.ModuleHelperException("run_command parameter as a dict must " + "contain only one key: {0}".format(param)) + _param = list(param.keys())[0] + fmt = find_format(_param) + value = param[_param] + elif isinstance(param, str): + if param in self.vars.keys(): + fmt = find_format(param) + value = self.vars[param] + elif param in extra_params: + fmt = find_format(param) + value = extra_params[param] + else: + self.module.deprecate("Cannot determine value for parameter: {0}. " + "From version 4.0.0 onwards this will generate an exception".format(param), + version="4.0.0", collection_name="community.general") + continue + + else: + raise self.ModuleHelperException("run_command parameter must be either a str or a dict: {0}".format(param)) + cmd_args = add_arg_formatted_param(cmd_args, fmt, value) + + return cmd_args + + def process_command_output(self, rc, out, err): + return rc, out, err + + def run_command(self, extra_params=None, params=None, process_output=None, *args, **kwargs): + self.vars.cmd_args = self._calculate_args(extra_params, params) + options = dict(self.run_command_fixed_options) + options['check_rc'] = options.get('check_rc', self.check_rc) + options.update(kwargs) + env_update = dict(options.get('environ_update', {})) + if self.force_lang: + env_update.update({ + 'LANGUAGE': self.force_lang, + 'LC_ALL': self.force_lang, + }) + self.update_output(force_lang=self.force_lang) + options['environ_update'] = env_update + rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options) + self.update_output(rc=rc, stdout=out, stderr=err) + if process_output is None: + _process = self.process_command_output + else: + _process = process_output + + return _process(rc, out, err) diff --git a/plugins/module_utils/mh/mixins/deps.py b/plugins/module_utils/mh/mixins/deps.py new file mode 100644 index 0000000000..1c6c9ae484 --- /dev/null +++ b/plugins/module_utils/mh/mixins/deps.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import traceback + +from ansible_collections.community.general.plugins.module_utils.mh.base import ModuleHelperBase +from ansible_collections.community.general.plugins.module_utils.mh.deco import module_fails_on_exception + + +class DependencyCtxMgr(object): + def __init__(self, name, msg=None): + self.name = name + self.msg = msg + self.has_it = False + self.exc_type = None + self.exc_val = None + self.exc_tb = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.has_it = exc_type is None + self.exc_type = exc_type + self.exc_val = exc_val + self.exc_tb = exc_tb + return not self.has_it + + @property + def text(self): + return self.msg or str(self.exc_val) + + +class DependencyMixin(ModuleHelperBase): + _dependencies = [] + + @classmethod + def dependency(cls, name, msg): + cls._dependencies.append(DependencyCtxMgr(name, msg)) + return cls._dependencies[-1] + + def fail_on_missing_deps(self): + for d in self._dependencies: + if not d.has_it: + self.module.fail_json(changed=False, + exception="\n".join(traceback.format_exception(d.exc_type, d.exc_val, d.exc_tb)), + msg=d.text, + **self.output) + + @module_fails_on_exception + def run(self): + self.fail_on_missing_deps() + super(DependencyMixin, self).run() diff --git a/plugins/module_utils/mh/mixins/state.py b/plugins/module_utils/mh/mixins/state.py new file mode 100644 index 0000000000..b946090ac9 --- /dev/null +++ b/plugins/module_utils/mh/mixins/state.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class StateMixin(object): + state_param = 'state' + default_state = None + + def _state(self): + state = self.module.params.get(self.state_param) + return self.default_state if state is None else state + + def _method(self, state): + return "{0}_{1}".format(self.state_param, state) + + def __run__(self): + state = self._state() + self.vars.state = state + + # resolve aliases + if state not in self.module.params: + aliased = [name for name, param in self.module.argument_spec.items() if state in param.get('aliases', [])] + if aliased: + state = aliased[0] + self.vars.effective_state = state + + method = self._method(state) + if not hasattr(self, method): + return self.__state_fallback__() + func = getattr(self, method) + return func() + + def __state_fallback__(self): + raise ValueError("Cannot find method: {0}".format(self._method(self._state()))) diff --git a/plugins/module_utils/mh/mixins/vars.py b/plugins/module_utils/mh/mixins/vars.py new file mode 100644 index 0000000000..7c936e04ac --- /dev/null +++ b/plugins/module_utils/mh/mixins/vars.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class VarMeta(object): + NOTHING = object() + + def __init__(self, diff=False, output=True, change=None, fact=False): + self.init = False + self.initial_value = None + self.value = None + + self.diff = diff + self.change = diff if change is None else change + self.output = output + self.fact = fact + + def set(self, diff=None, output=None, change=None, fact=None, initial_value=NOTHING): + if diff is not None: + self.diff = diff + if output is not None: + self.output = output + if change is not None: + self.change = change + if fact is not None: + self.fact = fact + if initial_value is not self.NOTHING: + self.initial_value = initial_value + + def set_value(self, value): + if not self.init: + self.initial_value = value + self.init = True + self.value = value + return self + + @property + def has_changed(self): + return self.change and (self.initial_value != self.value) + + @property + def diff_result(self): + return None if not (self.diff and self.has_changed) else { + 'before': self.initial_value, + 'after': self.value, + } + + def __str__(self): + return "".format( + self.value, self.initial_value, self.diff, self.output, self.change + ) + + +class VarDict(object): + def __init__(self): + self._data = dict() + self._meta = dict() + + def __getitem__(self, item): + return self._data[item] + + def __setitem__(self, key, value): + self.set(key, value) + + def __getattr__(self, item): + try: + return self._data[item] + except KeyError: + return getattr(self._data, item) + + def __setattr__(self, key, value): + if key in ('_data', '_meta'): + super(VarDict, self).__setattr__(key, value) + else: + self.set(key, value) + + def meta(self, name): + return self._meta[name] + + def set_meta(self, name, **kwargs): + self.meta(name).set(**kwargs) + + def set(self, name, value, **kwargs): + if name in ('_data', '_meta'): + raise ValueError("Names _data and _meta are reserved for use by ModuleHelper") + self._data[name] = value + if name in self._meta: + meta = self.meta(name) + else: + meta = VarMeta(**kwargs) + meta.set_value(value) + self._meta[name] = meta + + def output(self): + return dict((k, v) for k, v in self._data.items() if self.meta(k).output) + + def diff(self): + diff_results = [(k, self.meta(k).diff_result) for k in self._data] + diff_results = [dr for dr in diff_results if dr[1] is not None] + if diff_results: + before = dict((dr[0], dr[1]['before']) for dr in diff_results) + after = dict((dr[0], dr[1]['after']) for dr in diff_results) + return {'before': before, 'after': after} + return None + + def facts(self): + facts_result = dict((k, v) for k, v in self._data.items() if self._meta[k].fact) + return facts_result if facts_result else None + + def change_vars(self): + return [v for v in self._data if self.meta(v).change] + + def has_changed(self, v): + return self._meta[v].has_changed + + +class VarsMixin(object): + + def __init__(self, module=None): + self.vars = VarDict() + super(VarsMixin, self).__init__(module) + + def update_vars(self, meta=None, **kwargs): + if meta is None: + meta = {} + for k, v in kwargs.items(): + self.vars.set(k, v, **meta) diff --git a/plugins/module_utils/mh/module_helper.py b/plugins/module_utils/mh/module_helper.py new file mode 100644 index 0000000000..b27b60df9a --- /dev/null +++ b/plugins/module_utils/mh/module_helper.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright: (c) 2020, Ansible Project +# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.common.dict_transformations import dict_merge + +from ansible_collections.community.general.plugins.module_utils.mh.base import ModuleHelperBase, AnsibleModule +from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin +from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin +from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyMixin +from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarsMixin, VarDict as _VD + + +class ModuleHelper(VarsMixin, DependencyMixin, ModuleHelperBase): + _output_conflict_list = ('msg', 'exception', 'output', 'vars', 'changed') + facts_name = None + output_params = () + diff_params = () + change_params = () + facts_params = () + + VarDict = _VD # for backward compatibility, will be deprecated at some point + + def __init__(self, module=None): + super(ModuleHelper, self).__init__(module) + for name, value in self.module.params.items(): + self.vars.set( + name, value, + diff=name in self.diff_params, + output=name in self.output_params, + change=None if not self.change_params else name in self.change_params, + fact=name in self.facts_params, + ) + + def update_output(self, **kwargs): + self.update_vars(meta={"output": True}, **kwargs) + + def update_facts(self, **kwargs): + self.update_vars(meta={"fact": True}, **kwargs) + + def _vars_changed(self): + return any(self.vars.has_changed(v) for v in self.vars.change_vars()) + + def has_changed(self): + return self.changed or self._vars_changed() + + @property + def output(self): + result = dict(self.vars.output()) + if self.facts_name: + facts = self.vars.facts() + if facts is not None: + result['ansible_facts'] = {self.facts_name: facts} + if self.module._diff: + diff = result.get('diff', {}) + vars_diff = self.vars.diff() or {} + result['diff'] = dict_merge(dict(diff), vars_diff) + + for varname in result: + if varname in self._output_conflict_list: + result["_" + varname] = result[varname] + del result[varname] + return result + + +class StateModuleHelper(StateMixin, ModuleHelper): + pass + + +class CmdModuleHelper(CmdMixin, ModuleHelper): + pass + + +class CmdStateModuleHelper(CmdMixin, StateMixin, ModuleHelper): + pass diff --git a/plugins/module_utils/module_helper.py b/plugins/module_utils/module_helper.py index d241eba5af..a6b35bdd33 100644 --- a/plugins/module_utils/module_helper.py +++ b/plugins/module_utils/module_helper.py @@ -6,506 +6,13 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from functools import partial, wraps -import traceback -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.common.dict_transformations import dict_merge - - -class ModuleHelperException(Exception): - @staticmethod - def _get_remove(key, kwargs): - if key in kwargs: - result = kwargs[key] - del kwargs[key] - return result - return None - - def __init__(self, *args, **kwargs): - self.msg = self._get_remove('msg', kwargs) or "Module failed with exception: {0}".format(self) - self.update_output = self._get_remove('update_output', kwargs) or {} - super(ModuleHelperException, self).__init__(*args) - - -class ArgFormat(object): - """ - Argument formatter for use as a command line parameter. Used in CmdMixin. - """ - BOOLEAN = 0 - PRINTF = 1 - FORMAT = 2 - - @staticmethod - def stars_deco(num): - if num == 1: - def deco(f): - return lambda v: f(*v) - return deco - elif num == 2: - def deco(f): - return lambda v: f(**v) - return deco - - return lambda f: f - - def __init__(self, name, fmt=None, style=FORMAT, stars=0): - """ - Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for - the CLI command execution. - :param name: Name of the argument to be formatted - :param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that - :param style: Whether arg_format (as str) should use printf-style formatting. - Ignored if arg_format is None or not a str (should be callable). - :param stars: A int with 0, 1 or 2 value, indicating to formatting the value as: value, *value or **value - """ - def printf_fmt(_fmt, v): - try: - return [_fmt % v] - except TypeError as e: - if e.args[0] != 'not all arguments converted during string formatting': - raise - return [_fmt] - - _fmts = { - ArgFormat.BOOLEAN: lambda _fmt, v: ([_fmt] if bool(v) else []), - ArgFormat.PRINTF: printf_fmt, - ArgFormat.FORMAT: lambda _fmt, v: [_fmt.format(v)], - } - - self.name = name - self.stars = stars - - if fmt is None: - fmt = "{0}" - style = ArgFormat.FORMAT - - if isinstance(fmt, str): - func = _fmts[style] - self.arg_format = partial(func, fmt) - elif isinstance(fmt, list) or isinstance(fmt, tuple): - self.arg_format = lambda v: [_fmts[style](f, v)[0] for f in fmt] - elif hasattr(fmt, '__call__'): - self.arg_format = fmt - else: - raise TypeError('Parameter fmt must be either: a string, a list/tuple of ' - 'strings or a function: type={0}, value={1}'.format(type(fmt), fmt)) - - if stars: - self.arg_format = (self.stars_deco(stars))(self.arg_format) - - def to_text(self, value): - if value is None: - return [] - func = self.arg_format - return [str(p) for p in func(value)] - - -def cause_changes(on_success=None, on_failure=None): - - def deco(func): - if on_success is None and on_failure is None: - return func - - @wraps(func) - def wrapper(*args, **kwargs): - try: - self = args[0] - func(*args, **kwargs) - if on_success is not None: - self.changed = on_success - except Exception: - if on_failure is not None: - self.changed = on_failure - raise - - return wrapper - - return deco - - -def module_fails_on_exception(func): - @wraps(func) - def wrapper(self, *args, **kwargs): - try: - func(self, *args, **kwargs) - except SystemExit: - raise - except ModuleHelperException as e: - if e.update_output: - self.update_output(e.update_output) - self.module.fail_json(msg=e.msg, exception=traceback.format_exc(), - output=self.output, vars=self.vars.output(), **self.output) - except Exception as e: - msg = "Module failed with exception: {0}".format(str(e).strip()) - self.module.fail_json(msg=msg, exception=traceback.format_exc(), - output=self.output, vars=self.vars.output(), **self.output) - return wrapper - - -class DependencyCtxMgr(object): - def __init__(self, name, msg=None): - self.name = name - self.msg = msg - self.has_it = False - self.exc_type = None - self.exc_val = None - self.exc_tb = None - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.has_it = exc_type is None - self.exc_type = exc_type - self.exc_val = exc_val - self.exc_tb = exc_tb - return not self.has_it - - @property - def text(self): - return self.msg or str(self.exc_val) - - -class VarMeta(object): - NOTHING = object() - - def __init__(self, diff=False, output=True, change=None, fact=False): - self.init = False - self.initial_value = None - self.value = None - - self.diff = diff - self.change = diff if change is None else change - self.output = output - self.fact = fact - - def set(self, diff=None, output=None, change=None, fact=None, initial_value=NOTHING): - if diff is not None: - self.diff = diff - if output is not None: - self.output = output - if change is not None: - self.change = change - if fact is not None: - self.fact = fact - if initial_value is not self.NOTHING: - self.initial_value = initial_value - - def set_value(self, value): - if not self.init: - self.initial_value = value - self.init = True - self.value = value - return self - - @property - def has_changed(self): - return self.change and (self.initial_value != self.value) - - @property - def diff_result(self): - return None if not (self.diff and self.has_changed) else { - 'before': self.initial_value, - 'after': self.value, - } - - def __str__(self): - return "".format( - self.value, self.initial_value, self.diff, self.output, self.change - ) - - -class ModuleHelper(object): - _output_conflict_list = ('msg', 'exception', 'output', 'vars', 'changed') - _dependencies = [] - module = None - facts_name = None - output_params = () - diff_params = () - change_params = () - facts_params = () - - class VarDict(object): - def __init__(self): - self._data = dict() - self._meta = dict() - - def __getitem__(self, item): - return self._data[item] - - def __setitem__(self, key, value): - self.set(key, value) - - def __getattr__(self, item): - try: - return self._data[item] - except KeyError: - return getattr(self._data, item) - - def __setattr__(self, key, value): - if key in ('_data', '_meta'): - super(ModuleHelper.VarDict, self).__setattr__(key, value) - else: - self.set(key, value) - - def meta(self, name): - return self._meta[name] - - def set_meta(self, name, **kwargs): - self.meta(name).set(**kwargs) - - def set(self, name, value, **kwargs): - if name in ('_data', '_meta'): - raise ValueError("Names _data and _meta are reserved for use by ModuleHelper") - self._data[name] = value - if name in self._meta: - meta = self.meta(name) - else: - meta = VarMeta(**kwargs) - meta.set_value(value) - self._meta[name] = meta - - def output(self): - return dict((k, v) for k, v in self._data.items() if self.meta(k).output) - - def diff(self): - diff_results = [(k, self.meta(k).diff_result) for k in self._data] - diff_results = [dr for dr in diff_results if dr[1] is not None] - if diff_results: - before = dict((dr[0], dr[1]['before']) for dr in diff_results) - after = dict((dr[0], dr[1]['after']) for dr in diff_results) - return {'before': before, 'after': after} - return None - - def facts(self): - facts_result = dict((k, v) for k, v in self._data.items() if self._meta[k].fact) - return facts_result if facts_result else None - - def change_vars(self): - return [v for v in self._data if self.meta(v).change] - - def has_changed(self, v): - return self._meta[v].has_changed - - def __init__(self, module=None): - self.vars = ModuleHelper.VarDict() - self._changed = False - - if module: - self.module = module - - if not isinstance(self.module, AnsibleModule): - self.module = AnsibleModule(**self.module) - - for name, value in self.module.params.items(): - self.vars.set( - name, value, - diff=name in self.diff_params, - output=name in self.output_params, - change=None if not self.change_params else name in self.change_params, - fact=name in self.facts_params, - ) - - def update_vars(self, meta=None, **kwargs): - if meta is None: - meta = {} - for k, v in kwargs.items(): - self.vars.set(k, v, **meta) - - def update_output(self, **kwargs): - self.update_vars(meta={"output": True}, **kwargs) - - def update_facts(self, **kwargs): - self.update_vars(meta={"fact": True}, **kwargs) - - def __init_module__(self): - pass - - def __run__(self): - raise NotImplementedError() - - def __quit_module__(self): - pass - - def _vars_changed(self): - return any(self.vars.has_changed(v) for v in self.vars.change_vars()) - - @property - def changed(self): - return self._changed - - @changed.setter - def changed(self, value): - self._changed = value - - def has_changed(self): - return self.changed or self._vars_changed() - - @property - def output(self): - result = dict(self.vars.output()) - if self.facts_name: - facts = self.vars.facts() - if facts is not None: - result['ansible_facts'] = {self.facts_name: facts} - if self.module._diff: - diff = result.get('diff', {}) - vars_diff = self.vars.diff() or {} - result['diff'] = dict_merge(dict(diff), vars_diff) - - for varname in result: - if varname in self._output_conflict_list: - result["_" + varname] = result[varname] - del result[varname] - return result - - @module_fails_on_exception - def run(self): - self.fail_on_missing_deps() - self.__init_module__() - self.__run__() - self.__quit_module__() - self.module.exit_json(changed=self.has_changed(), **self.output) - - @classmethod - def dependency(cls, name, msg): - cls._dependencies.append(DependencyCtxMgr(name, msg)) - return cls._dependencies[-1] - - def fail_on_missing_deps(self): - for d in self._dependencies: - if not d.has_it: - self.module.fail_json(changed=False, - exception="\n".join(traceback.format_exception(d.exc_type, d.exc_val, d.exc_tb)), - msg=d.text, - **self.output) - - -class StateMixin(object): - state_param = 'state' - default_state = None - - def _state(self): - state = self.module.params.get(self.state_param) - return self.default_state if state is None else state - - def _method(self, state): - return "{0}_{1}".format(self.state_param, state) - - def __run__(self): - state = self._state() - self.vars.state = state - - # resolve aliases - if state not in self.module.params: - aliased = [name for name, param in self.module.argument_spec.items() if state in param.get('aliases', [])] - if aliased: - state = aliased[0] - self.vars.effective_state = state - - method = self._method(state) - if not hasattr(self, method): - return self.__state_fallback__() - func = getattr(self, method) - return func() - - def __state_fallback__(self): - raise ValueError("Cannot find method: {0}".format(self._method(self._state()))) - - -class CmdMixin(object): - """ - Mixin for mapping module options to running a CLI command with its arguments. - """ - command = None - command_args_formats = {} - run_command_fixed_options = {} - check_rc = False - force_lang = "C" - - @property - def module_formats(self): - result = {} - for param in self.module.params.keys(): - result[param] = ArgFormat(param) - return result - - @property - def custom_formats(self): - result = {} - for param, fmt_spec in self.command_args_formats.items(): - result[param] = ArgFormat(param, **fmt_spec) - return result - - def _calculate_args(self, extra_params=None, params=None): - def add_arg_formatted_param(_cmd_args, arg_format, _value): - args = list(arg_format.to_text(_value)) - return _cmd_args + args - - def find_format(_param): - return self.custom_formats.get(_param, self.module_formats.get(_param)) - - extra_params = extra_params or dict() - cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command) - try: - cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True) - except ValueError: - pass - param_list = params if params else self.module.params.keys() - - for param in param_list: - if isinstance(param, dict): - if len(param) != 1: - raise ModuleHelperException("run_command parameter as a dict must " - "contain only one key: {0}".format(param)) - _param = list(param.keys())[0] - fmt = find_format(_param) - value = param[_param] - elif isinstance(param, str): - if param in self.module.argument_spec: - fmt = find_format(param) - value = self.module.params[param] - elif param in extra_params: - fmt = find_format(param) - value = extra_params[param] - else: - self.module.deprecate("Cannot determine value for parameter: {0}. " - "From version 4.0.0 onwards this will generate an exception".format(param), - version="4.0.0", collection_name="community.general") - continue - - else: - raise ModuleHelperException("run_command parameter must be either a str or a dict: {0}".format(param)) - cmd_args = add_arg_formatted_param(cmd_args, fmt, value) - - return cmd_args - - def process_command_output(self, rc, out, err): - return rc, out, err - - def run_command(self, extra_params=None, params=None, *args, **kwargs): - self.vars.cmd_args = self._calculate_args(extra_params, params) - options = dict(self.run_command_fixed_options) - env_update = dict(options.get('environ_update', {})) - options['check_rc'] = options.get('check_rc', self.check_rc) - if self.force_lang: - env_update.update({'LANGUAGE': self.force_lang}) - self.update_output(force_lang=self.force_lang) - options['environ_update'] = env_update - options.update(kwargs) - rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options) - self.update_output(rc=rc, stdout=out, stderr=err) - return self.process_command_output(rc, out, err) - - -class StateModuleHelper(StateMixin, ModuleHelper): - pass - - -class CmdModuleHelper(CmdMixin, ModuleHelper): - pass - - -class CmdStateModuleHelper(CmdMixin, StateMixin, ModuleHelper): - pass +from ansible_collections.community.general.plugins.module_utils.mh.module_helper import ( + ModuleHelper, StateModuleHelper, CmdModuleHelper, CmdStateModuleHelper, AnsibleModule +) +from ansible_collections.community.general.plugins.module_utils.mh.mixins.cmd import CmdMixin, ArgFormat +from ansible_collections.community.general.plugins.module_utils.mh.mixins.state import StateMixin +from ansible_collections.community.general.plugins.module_utils.mh.mixins.deps import DependencyCtxMgr +from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException +from ansible_collections.community.general.plugins.module_utils.mh.deco import cause_changes, module_fails_on_exception +from ansible_collections.community.general.plugins.module_utils.mh.mixins.vars import VarMeta, VarDict diff --git a/plugins/module_utils/net_tools/__init__.py b/plugins/module_utils/net_tools/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/net_tools/nios/__init__.py b/plugins/module_utils/net_tools/nios/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/net_tools/pritunl/__init__.py b/plugins/module_utils/net_tools/pritunl/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/oracle/__init__.py b/plugins/module_utils/oracle/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/redfish_utils.py b/plugins/module_utils/redfish_utils.py index d8cc4061f8..df7011a0b4 100644 --- a/plugins/module_utils/redfish_utils.py +++ b/plugins/module_utils/redfish_utils.py @@ -1671,19 +1671,31 @@ class RedfishUtils(object): # Make a copy of the attributes dict attrs_to_patch = dict(attributes) + # List to hold attributes not found + attrs_bad = {} # Check the attributes - for attr in attributes: - if attr not in data[u'Attributes']: - return {'ret': False, 'msg': "BIOS attribute %s not found" % attr} + for attr_name, attr_value in attributes.items(): + # Check if attribute exists + if attr_name not in data[u'Attributes']: + # Remove and proceed to next attribute if this isn't valid + attrs_bad.update({attr_name: attr_value}) + del attrs_to_patch[attr_name] + continue + # If already set to requested value, remove it from PATCH payload - if data[u'Attributes'][attr] == attributes[attr]: - del attrs_to_patch[attr] + if data[u'Attributes'][attr_name] == attributes[attr_name]: + del attrs_to_patch[attr_name] + + warning = "" + if attrs_bad: + warning = "Incorrect attributes %s" % (attrs_bad) # Return success w/ changed=False if no attrs need to be changed if not attrs_to_patch: return {'ret': True, 'changed': False, - 'msg': "BIOS attributes already set"} + 'msg': "BIOS attributes already set", + 'warning': warning} # Get the SettingsObject URI set_bios_attr_uri = data["@Redfish.Settings"]["SettingsObject"]["@odata.id"] @@ -1693,7 +1705,9 @@ class RedfishUtils(object): response = self.patch_request(self.root_uri + set_bios_attr_uri, payload) if response['ret'] is False: return response - return {'ret': True, 'changed': True, 'msg': "Modified BIOS attribute"} + return {'ret': True, 'changed': True, + 'msg': "Modified BIOS attributes %s" % (attrs_to_patch), + 'warning': warning} def set_boot_order(self, boot_list): if not boot_list: diff --git a/plugins/module_utils/remote_management/__init__.py b/plugins/module_utils/remote_management/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/remote_management/lxca/__init__.py b/plugins/module_utils/remote_management/lxca/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/source_control/__init__.py b/plugins/module_utils/source_control/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/storage/__init__.py b/plugins/module_utils/storage/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/storage/emc/__init__.py b/plugins/module_utils/storage/emc/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/module_utils/storage/hpe3par/__init__.py b/plugins/module_utils/storage/hpe3par/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/modules/__init__.py b/plugins/modules/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/modules/cloud/linode/linode.py b/plugins/modules/cloud/linode/linode.py index a35b25b6c7..c9ee0e61ed 100644 --- a/plugins/modules/cloud/linode/linode.py +++ b/plugins/modules/cloud/linode/linode.py @@ -21,8 +21,10 @@ options: type: str api_key: description: - - Linode API key + - Linode API key. + - C(LINODE_API_KEY) env variable can be used instead. type: str + required: yes name: description: - Name to give the instance (alphanumeric, dashes, underscore). @@ -46,6 +48,7 @@ options: - List of dictionaries for creating additional disks that are added to the Linode configuration settings. - Dictionary takes Size, Label, Type. Size is in MB. type: list + elements: dict alert_bwin_enabled: description: - Set status of bandwidth in alerts. @@ -86,9 +89,18 @@ options: description: - Set threshold for average IO ops/sec over 2 hour period. type: int + backupsenabled: + description: + - Deprecated parameter, it will be removed in community.general C(5.0.0). + - To enable backups pass values to either I(backupweeklyday) or I(backupwindow). + type: int backupweeklyday: description: - - Integer value for what day of the week to store weekly backups. + - Day of the week to take backups. + type: int + backupwindow: + description: + - The time window in which backups will be taken. type: int plan: description: @@ -153,7 +165,6 @@ author: notes: - Please note, linode-python does not have python 3 support. - This module uses the now deprecated v3 of the Linode API. - - C(LINODE_API_KEY) env variable can be used instead. - Please review U(https://www.linode.com/api/linode) for determining the required parameters. ''' @@ -262,7 +273,6 @@ EXAMPLES = ''' delegate_to: localhost ''' -import os import time import traceback @@ -274,7 +284,7 @@ except ImportError: LINODE_IMP_ERR = traceback.format_exc() HAS_LINODE = False -from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible.module_utils.basic import AnsibleModule, missing_required_lib, env_fallback def randompass(): @@ -358,7 +368,7 @@ def linodeServers(module, api, state, name, if not servers: for arg in (name, plan, distribution, datacenter): if not arg: - module.fail_json(msg='%s is required for %s state' % (arg, state)) # @TODO use required_if instead + module.fail_json(msg='%s is required for %s state' % (arg, state)) # Create linode entity new_server = True @@ -383,7 +393,7 @@ def linodeServers(module, api, state, name, try: res = api.linode_ip_addprivate(LinodeID=linode_id) except Exception as e: - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) if not disks: for arg in (name, linode_id, distribution): @@ -428,7 +438,7 @@ def linodeServers(module, api, state, name, jobs.append(res['JobID']) except Exception as e: # TODO: destroy linode ? - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) if not configs: for arg in (name, linode_id, distribution): @@ -471,7 +481,7 @@ def linodeServers(module, api, state, name, Disklist=disks_list, Label='%s config' % name) configs = api.linode_config_list(LinodeId=linode_id) except Exception as e: - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) # Start / Ensure servers are running for server in servers: @@ -517,10 +527,7 @@ def linodeServers(module, api, state, name, instance['password'] = password instances.append(instance) - elif state in ('stopped'): - if not linode_id: - module.fail_json(msg='linode_id is required for stopped state') - + elif state in ('stopped',): if not servers: module.fail_json(msg='Server (lid: %s) not found' % (linode_id)) @@ -530,17 +537,14 @@ def linodeServers(module, api, state, name, try: res = api.linode_shutdown(LinodeId=linode_id) except Exception as e: - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) instance['status'] = 'Stopping' changed = True else: instance['status'] = 'Stopped' instances.append(instance) - elif state in ('restarted'): - if not linode_id: - module.fail_json(msg='linode_id is required for restarted state') - + elif state in ('restarted',): if not servers: module.fail_json(msg='Server (lid: %s) not found' % (linode_id)) @@ -549,7 +553,7 @@ def linodeServers(module, api, state, name, try: res = api.linode_reboot(LinodeId=server['LINODEID']) except Exception as e: - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) instance['status'] = 'Restarting' changed = True instances.append(instance) @@ -560,7 +564,7 @@ def linodeServers(module, api, state, name, try: api.linode_delete(LinodeId=server['LINODEID'], skipChecks=True) except Exception as e: - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) instance['status'] = 'Deleting' changed = True instances.append(instance) @@ -577,7 +581,7 @@ def main(): argument_spec=dict( state=dict(type='str', default='present', choices=['absent', 'active', 'deleted', 'present', 'restarted', 'started', 'stopped']), - api_key=dict(type='str', no_log=True), + api_key=dict(type='str', no_log=True, required=True, fallback=(env_fallback, ['LINODE_API_KEY'])), name=dict(type='str', required=True), alert_bwin_enabled=dict(type='bool'), alert_bwin_threshold=dict(type='int'), @@ -589,12 +593,12 @@ def main(): alert_cpu_threshold=dict(type='int'), alert_diskio_enabled=dict(type='bool'), alert_diskio_threshold=dict(type='int'), - backupsenabled=dict(type='int'), + backupsenabled=dict(type='int', removed_in_version='5.0.0', removed_from_collection='community.general'), backupweeklyday=dict(type='int'), backupwindow=dict(type='int'), displaygroup=dict(type='str', default=''), plan=dict(type='int'), - additional_disks=dict(type='list'), + additional_disks=dict(type='list', elements='dict'), distribution=dict(type='int'), datacenter=dict(type='int'), kernel_id=dict(type='int'), @@ -608,6 +612,10 @@ def main(): wait_timeout=dict(type='int', default=300), watchdog=dict(type='bool', default=True), ), + required_if=[ + ('state', 'restarted', ['linode_id']), + ('state', 'stopped', ['linode_id']), + ] ) if not HAS_LINODE: @@ -626,7 +634,6 @@ def main(): alert_cpu_threshold = module.params.get('alert_cpu_threshold') alert_diskio_enabled = module.params.get('alert_diskio_enabled') alert_diskio_threshold = module.params.get('alert_diskio_threshold') - backupsenabled = module.params.get('backupsenabled') backupweeklyday = module.params.get('backupweeklyday') backupwindow = module.params.get('backupwindow') displaygroup = module.params.get('displaygroup') @@ -642,10 +649,9 @@ def main(): ssh_pub_key = module.params.get('ssh_pub_key') swap = module.params.get('swap') wait = module.params.get('wait') - wait_timeout = int(module.params.get('wait_timeout')) + wait_timeout = module.params.get('wait_timeout') watchdog = int(module.params.get('watchdog')) - kwargs = dict() check_items = dict( alert_bwin_enabled=alert_bwin_enabled, alert_bwin_threshold=alert_bwin_threshold, @@ -661,23 +667,14 @@ def main(): backupwindow=backupwindow, ) - for key, value in check_items.items(): - if value is not None: - kwargs[key] = value - - # Setup the api_key - if not api_key: - try: - api_key = os.environ['LINODE_API_KEY'] - except KeyError as e: - module.fail_json(msg='Unable to load %s' % e.message) + kwargs = dict((k, v) for k, v in check_items.items() if v is not None) # setup the auth try: api = linode_api.Api(api_key) api.test_echo() except Exception as e: - module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE']) + module.fail_json(msg='%s' % e.value[0]['ERRORMESSAGE'], exception=traceback.format_exc()) linodeServers(module, api, state, name, displaygroup, plan, diff --git a/plugins/modules/cloud/linode/linode_v4.py b/plugins/modules/cloud/linode/linode_v4.py index 0f1133bac0..fcf3725bfc 100644 --- a/plugins/modules/cloud/linode/linode_v4.py +++ b/plugins/modules/cloud/linode/linode_v4.py @@ -208,9 +208,8 @@ def create_linode(module, client, **kwargs): else: return response._raw_json except TypeError: - module.fail_json(msg='Unable to parse Linode instance creation' - ' response. Please raise a bug against this' - ' module on https://github.com/ansible/ansible/issues' + module.fail_json(msg='Unable to parse Linode instance creation response. Please raise a bug against this' + ' module on https://github.com/ansible-collections/community.general/issues' ) diff --git a/plugins/modules/cloud/misc/cloud_init_data_facts.py b/plugins/modules/cloud/misc/cloud_init_data_facts.py index 2efb90cfeb..5774fa6f39 100644 --- a/plugins/modules/cloud/misc/cloud_init_data_facts.py +++ b/plugins/modules/cloud/misc/cloud_init_data_facts.py @@ -88,7 +88,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_text -CLOUD_INIT_PATH = "/var/lib/cloud/data/" +CLOUD_INIT_PATH = "/var/lib/cloud/data" def gather_cloud_init_data_facts(module): @@ -100,7 +100,7 @@ def gather_cloud_init_data_facts(module): filter = module.params.get('filter') if filter is None or filter == i: res['cloud_init_data_facts'][i] = dict() - json_file = CLOUD_INIT_PATH + i + '.json' + json_file = os.path.join(CLOUD_INIT_PATH, i + '.json') if os.path.exists(json_file): f = open(json_file, 'rb') diff --git a/plugins/modules/cloud/misc/proxmox_group_info.py b/plugins/modules/cloud/misc/proxmox_group_info.py index bf88659656..3d60e7e214 100644 --- a/plugins/modules/cloud/misc/proxmox_group_info.py +++ b/plugins/modules/cloud/misc/proxmox_group_info.py @@ -95,7 +95,7 @@ class ProxmoxGroup: self.group = dict() # Data representation is not the same depending on API calls for k, v in group.items(): - if k == 'users' and type(v) == str: + if k == 'users' and isinstance(v, str): self.group['users'] = v.split(',') elif k == 'members': self.group['users'] = group['members'] diff --git a/plugins/modules/cloud/misc/proxmox_kvm.py b/plugins/modules/cloud/misc/proxmox_kvm.py index 2dcb1ab573..a664279e57 100644 --- a/plugins/modules/cloud/misc/proxmox_kvm.py +++ b/plugins/modules/cloud/misc/proxmox_kvm.py @@ -808,23 +808,23 @@ def get_vminfo(module, proxmox, node, vmid, **kwargs): # Sanitize kwargs. Remove not defined args and ensure True and False converted to int. kwargs = dict((k, v) for k, v in kwargs.items() if v is not None) - # Convert all dict in kwargs to elements. For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n] + # Convert all dict in kwargs to elements. + # For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n] for k in list(kwargs.keys()): if isinstance(kwargs[k], dict): kwargs.update(kwargs[k]) del kwargs[k] # Split information by type + re_net = re.compile(r'net[0-9]') + re_dev = re.compile(r'(virtio|ide|scsi|sata)[0-9]') for k, v in kwargs.items(): - if re.match(r'net[0-9]', k) is not None: + if re_net.match(k): interface = k k = vm[k] k = re.search('=(.*?),', k).group(1) mac[interface] = k - if (re.match(r'virtio[0-9]', k) is not None or - re.match(r'ide[0-9]', k) is not None or - re.match(r'scsi[0-9]', k) is not None or - re.match(r'sata[0-9]', k) is not None): + elif re_dev.match(k): device = k k = vm[k] k = re.search('(.*?),', k).group(1) @@ -835,16 +835,13 @@ def get_vminfo(module, proxmox, node, vmid, **kwargs): results['vmid'] = int(vmid) -def settings(module, proxmox, vmid, node, name, **kwargs): +def settings(proxmox, vmid, node, **kwargs): proxmox_node = proxmox.nodes(node) # Sanitize kwargs. Remove not defined args and ensure True and False converted to int. kwargs = dict((k, v) for k, v in kwargs.items() if v is not None) - if proxmox_node.qemu(vmid).config.set(**kwargs) is None: - return True - else: - return False + return proxmox_node.qemu(vmid).config.set(**kwargs) is None def wait_for_task(module, proxmox, node, taskid): @@ -915,7 +912,8 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock if 'pool' in kwargs: del kwargs['pool'] - # Convert all dict in kwargs to elements. For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n], ipconfig[n] + # Convert all dict in kwargs to elements. + # For hostpci[n], ide[n], net[n], numa[n], parallel[n], sata[n], scsi[n], serial[n], virtio[n], ipconfig[n] for k in list(kwargs.keys()): if isinstance(kwargs[k], dict): kwargs.update(kwargs[k]) @@ -938,8 +936,9 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock # VM 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.match(r'^[a-z0-9_][a-z0-9_\-\+\.]*$', tag): + if not re_tag.match(tag): module.fail_json(msg='%s is not a valid tag' % tag) kwargs['tags'] = ",".join(kwargs['tags']) @@ -971,7 +970,7 @@ def create_vm(module, proxmox, vmid, newid, node, name, memory, cpu, cores, sock if not wait_for_task(module, proxmox, node, taskid): module.fail_json(msg='Reached timeout while waiting for creating VM. Last line in task before timeout: %s' % - proxmox_node.tasks(taskid).log.get()[:1]) + proxmox_node.tasks(taskid).log.get()[:1]) return False return True @@ -1209,14 +1208,14 @@ def main(): if delete is not None: try: - settings(module, proxmox, vmid, node, name, delete=delete) + settings(proxmox, vmid, node, delete=delete) module.exit_json(changed=True, vmid=vmid, msg="Settings has deleted on VM {0} with vmid {1}".format(name, vmid)) except Exception as e: module.fail_json(vmid=vmid, msg='Unable to delete settings on VM {0} with vmid {1}: '.format(name, vmid) + str(e)) if revert is not None: try: - settings(module, proxmox, vmid, node, name, revert=revert) + settings(proxmox, vmid, node, revert=revert) module.exit_json(changed=True, vmid=vmid, msg="Settings has reverted on VM {0} with vmid {1}".format(name, vmid)) except Exception as e: module.fail_json(vmid=vmid, msg='Unable to revert settings on VM {0} with vmid {1}: Maybe is not a pending task... '.format(name, vmid) + str(e)) @@ -1226,7 +1225,7 @@ def main(): if get_vm(proxmox, vmid) and not (update or clone): module.exit_json(changed=False, vmid=vmid, msg="VM with vmid <%s> already exists" % vmid) elif get_vmid(proxmox, name) and not (update or clone): - module.exit_json(changed=False, vmid=vmid, msg="VM with name <%s> already exists" % name) + module.exit_json(changed=False, vmid=get_vmid(proxmox, name)[0], msg="VM with name <%s> already exists" % name) elif not (node, name): module.fail_json(msg='node, name is mandatory for creating/updating vm') elif not node_check(proxmox, node): diff --git a/plugins/modules/cloud/misc/proxmox_nic.py b/plugins/modules/cloud/misc/proxmox_nic.py new file mode 100644 index 0000000000..23be9473eb --- /dev/null +++ b/plugins/modules/cloud/misc/proxmox_nic.py @@ -0,0 +1,348 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Lammert Hellinga (@Kogelvis) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: proxmox_nic +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. +author: "Lammert Hellinga (@Kogelvis) " +options: + bridge: + description: + - Add this interface to the specified bridge device. The Proxmox VE default bridge is called C(vmbr0). + type: str + firewall: + description: + - Whether this interface should be protected by the firewall. + type: bool + default: false + interface: + description: + - Name of the interface, should be C(net[n]) where C(1 ≤ n ≤ 31). + type: str + required: true + link_down: + description: + - Whether this interface should be disconnected (like pulling the plug). + type: bool + default: false + mac: + description: + - C(XX:XX:XX:XX:XX:XX) should be a unique MAC address. This is automatically generated if not specified. + - When not specified this module will keep the MAC address the same when changing an existing interface. + type: str + model: + description: + - The NIC emulator model. + type: str + choices: ['e1000', 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em', 'i82551', 'i82557b', 'i82559er', 'ne2k_isa', 'ne2k_pci', 'pcnet', + 'rtl8139', 'virtio', 'vmxnet3'] + default: virtio + mtu: + description: + - Force MTU, for C(virtio) model only, setting will be ignored otherwise. + - Set to C(1) to use the bridge MTU. + - Value should be C(1 ≤ n ≤ 65520). + type: int + name: + description: + - Specifies the VM name. Only used on the configuration web interface. + - Required only for I(state=present). + type: str + queues: + description: + - Number of packet queues to be used on the device. + - Value should be C(0 ≤ n ≤ 16). + type: int + rate: + description: + - Rate limit in MBps (MegaBytes per second) as floating point number. + type: float + state: + description: + - Indicates desired state of the NIC. + type: str + choices: ['present', 'absent'] + default: present + tag: + description: + - VLAN tag to apply to packets on this interface. + - Value should be C(1 ≤ n ≤ 4094). + type: int + trunks: + description: + - List of VLAN trunks to pass through this interface. + type: list + elements: int + vmid: + description: + - Specifies the instance ID. + type: int +extends_documentation_fragment: + - community.general.proxmox.documentation +''' + +EXAMPLES = ''' +- name: Create NIC net0 targeting the vm by name + community.general.proxmox_nic: + api_user: root@pam + api_password: secret + api_host: proxmoxhost + name: my_vm + interface: net0 + bridge: vmbr0 + tag: 3 + +- name: Create NIC net0 targeting the vm by id + community.general.proxmox_nic: + api_user: root@pam + api_password: secret + api_host: proxmoxhost + vmid: 103 + interface: net0 + bridge: vmbr0 + mac: "12:34:56:C0:FF:EE" + firewall: true + +- name: Delete NIC net0 targeting the vm by name + community.general.proxmox_nic: + api_user: root@pam + api_password: secret + api_host: proxmoxhost + name: my_vm + interface: net0 + state: absent +''' + +RETURN = ''' +vmid: + description: The VM vmid. + returned: success + type: int + sample: 115 +msg: + description: A short message + returned: always + type: str + sample: "Nic net0 unchanged on VM with vmid 103" +''' + +try: + from proxmoxer import ProxmoxAPI + HAS_PROXMOXER = True +except ImportError: + HAS_PROXMOXER = False + +from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible_collections.community.general.plugins.module_utils.proxmox import proxmox_auth_argument_spec + + +def get_vmid(module, proxmox, name): + try: + vms = [vm['vmid'] for vm in proxmox.cluster.resources.get(type='vm') if vm.get('name') == name] + except Exception as e: + module.fail_json(msg='Error: %s occurred while retrieving VM with name = %s' % (e, name)) + + if not vms: + module.fail_json(msg='No VM found with name: %s' % name) + elif len(vms) > 1: + module.fail_json(msg='Multiple VMs found with name: %s, provide vmid instead' % name) + + return vms[0] + + +def get_vm(proxmox, vmid): + return [vm for vm in proxmox.cluster.resources.get(type='vm') if vm['vmid'] == int(vmid)] + + +def update_nic(module, proxmox, vmid, interface, model, **kwargs): + vm = get_vm(proxmox, vmid) + + try: + vminfo = proxmox.nodes(vm[0]['node']).qemu(vmid).config.get() + except Exception as e: + module.fail_json(msg='Getting information for VM with vmid = %s failed with exception: %s' % (vmid, e)) + + if interface in vminfo: + # Convert the current config to a dictionary + config = vminfo[interface].split(',') + config.sort() + + config_current = {} + + for i in config: + kv = i.split('=') + try: + config_current[kv[0]] = kv[1] + except IndexError: + config_current[kv[0]] = '' + + # determine the current model nic and mac-address + models = ['e1000', 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em', 'i82551', 'i82557b', + 'i82559er', 'ne2k_isa', 'ne2k_pci', 'pcnet', 'rtl8139', 'virtio', 'vmxnet3'] + current_model = set(models) & set(config_current.keys()) + current_model = current_model.pop() + current_mac = config_current[current_model] + + # build nic config string + config_provided = "{0}={1}".format(model, current_mac) + else: + config_provided = model + + if kwargs['mac']: + config_provided = "{0}={1}".format(model, kwargs['mac']) + + if kwargs['bridge']: + config_provided += ",bridge={0}".format(kwargs['bridge']) + + if kwargs['firewall']: + config_provided += ",firewall=1" + + if kwargs['link_down']: + config_provided += ',link_down=1' + + if kwargs['mtu']: + config_provided += ",mtu={0}".format(kwargs['mtu']) + if model != 'virtio': + module.warn( + 'Ignoring MTU for nic {0} on VM with vmid {1}, ' + 'model should be set to \'virtio\': '.format(interface, vmid)) + + if kwargs['queues']: + config_provided += ",queues={0}".format(kwargs['queues']) + + if kwargs['rate']: + config_provided += ",rate={0}".format(kwargs['rate']) + + if kwargs['tag']: + config_provided += ",tag={0}".format(kwargs['tag']) + + if kwargs['trunks']: + config_provided += ",trunks={0}".format(';'.join(str(x) for x in kwargs['trunks'])) + + net = {interface: config_provided} + vm = get_vm(proxmox, vmid) + + if ((interface not in vminfo) or (vminfo[interface] != config_provided)): + if not module.check_mode: + proxmox.nodes(vm[0]['node']).qemu(vmid).config.set(**net) + return True + + return False + + +def delete_nic(module, proxmox, vmid, interface): + vm = get_vm(proxmox, vmid) + vminfo = proxmox.nodes(vm[0]['node']).qemu(vmid).config.get() + + if interface in vminfo: + if not module.check_mode: + proxmox.nodes(vm[0]['node']).qemu(vmid).config.set(vmid=vmid, delete=interface) + return True + + return False + + +def main(): + module_args = proxmox_auth_argument_spec() + nic_args = dict( + bridge=dict(type='str'), + firewall=dict(type='bool', default=False), + interface=dict(type='str', required=True), + link_down=dict(type='bool', default=False), + mac=dict(type='str'), + model=dict(choices=['e1000', 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em', + 'i82551', 'i82557b', 'i82559er', 'ne2k_isa', 'ne2k_pci', 'pcnet', + 'rtl8139', 'virtio', 'vmxnet3'], default='virtio'), + mtu=dict(type='int'), + name=dict(type='str'), + queues=dict(type='int'), + rate=dict(type='float'), + state=dict(default='present', choices=['present', 'absent']), + tag=dict(type='int'), + trunks=dict(type='list', elements='int'), + vmid=dict(type='int'), + ) + module_args.update(nic_args) + + module = AnsibleModule( + argument_spec=module_args, + required_together=[('api_token_id', 'api_token_secret')], + required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')], + supports_check_mode=True, + ) + + if not HAS_PROXMOXER: + module.fail_json(msg='proxmoxer required for this module') + + api_host = module.params['api_host'] + api_password = module.params['api_password'] + api_token_id = module.params['api_token_id'] + api_token_secret = module.params['api_token_secret'] + api_user = module.params['api_user'] + interface = module.params['interface'] + model = module.params['model'] + name = module.params['name'] + state = module.params['state'] + validate_certs = module.params['validate_certs'] + vmid = module.params['vmid'] + + auth_args = {'user': api_user} + if not (api_token_id and api_token_secret): + auth_args['password'] = api_password + else: + auth_args['token_name'] = api_token_id + auth_args['token_value'] = api_token_secret + + try: + proxmox = ProxmoxAPI(api_host, verify_ssl=validate_certs, **auth_args) + except Exception as e: + module.fail_json(msg='authorization on proxmox cluster failed with exception: %s' % e) + + # If vmid is not defined then retrieve its value from the vm name, + if not vmid: + vmid = get_vmid(module, proxmox, name) + + # Ensure VM id exists + if not get_vm(proxmox, vmid): + module.fail_json(vmid=vmid, msg='VM with vmid = %s does not exist in cluster' % vmid) + + if state == 'present': + try: + if update_nic(module, proxmox, vmid, interface, model, + bridge=module.params['bridge'], + firewall=module.params['firewall'], + link_down=module.params['link_down'], + mac=module.params['mac'], + mtu=module.params['mtu'], + queues=module.params['queues'], + rate=module.params['rate'], + tag=module.params['tag'], + trunks=module.params['trunks']): + module.exit_json(changed=True, vmid=vmid, msg="Nic {0} updated on VM with vmid {1}".format(interface, vmid)) + else: + module.exit_json(vmid=vmid, msg="Nic {0} unchanged on VM with vmid {1}".format(interface, vmid)) + except Exception as e: + module.fail_json(vmid=vmid, msg='Unable to change nic {0} on VM with vmid {1}: '.format(interface, vmid) + str(e)) + + elif state == 'absent': + try: + if delete_nic(module, proxmox, vmid, interface): + module.exit_json(changed=True, vmid=vmid, msg="Nic {0} deleted on VM with vmid {1}".format(interface, vmid)) + else: + module.exit_json(vmid=vmid, msg="Nic {0} does not exist on VM with vmid {1}".format(interface, vmid)) + except Exception as e: + module.fail_json(vmid=vmid, msg='Unable to delete nic {0} on VM with vmid {1}: '.format(interface, vmid) + str(e)) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/cloud/misc/rhevm.py b/plugins/modules/cloud/misc/rhevm.py index cc6c1252bf..77b40248b3 100644 --- a/plugins/modules/cloud/misc/rhevm.py +++ b/plugins/modules/cloud/misc/rhevm.py @@ -547,7 +547,7 @@ class RHEVConn(object): def set_Memory_Policy(self, name, memory_policy): VM = self.get_VM(name) - VM.memory_policy.guaranteed = int(int(memory_policy) * 1024 * 1024 * 1024) + VM.memory_policy.guaranteed = int(memory_policy) * 1024 * 1024 * 1024 try: VM.update() setMsg("The memory policy has been updated.") @@ -1260,7 +1260,7 @@ def core(module): r = RHEV(module) - state = module.params.get('state', 'present') + state = module.params.get('state') if state == 'ping': r.test() diff --git a/plugins/modules/cloud/misc/serverless.py b/plugins/modules/cloud/misc/serverless.py index 912d4226a8..1b2f8b62a6 100644 --- a/plugins/modules/cloud/misc/serverless.py +++ b/plugins/modules/cloud/misc/serverless.py @@ -139,16 +139,14 @@ from ansible.module_utils.basic import AnsibleModule def read_serverless_config(module): path = module.params.get('service_path') + full_path = os.path.join(path, 'serverless.yml') try: - with open(os.path.join(path, 'serverless.yml')) as sls_config: + with open(full_path) as sls_config: config = yaml.safe_load(sls_config.read()) return config except IOError as e: - module.fail_json(msg="Could not open serverless.yml in {0}. err: {1}".format(path, str(e))) - - module.fail_json(msg="Failed to open serverless config at {0}".format( - os.path.join(path, 'serverless.yml'))) + module.fail_json(msg="Could not open serverless.yml in {0}. err: {1}".format(full_path, str(e))) def get_service_name(module, stage): @@ -182,7 +180,6 @@ def main(): service_path = module.params.get('service_path') state = module.params.get('state') - functions = module.params.get('functions') region = module.params.get('region') stage = module.params.get('stage') deploy = module.params.get('deploy', True) @@ -193,7 +190,7 @@ def main(): if serverless_bin_path is not None: command = serverless_bin_path + " " else: - command = "serverless " + command = module.get_bin_path("serverless") + " " if state == 'present': command += 'deploy ' diff --git a/plugins/modules/cloud/misc/terraform.py b/plugins/modules/cloud/misc/terraform.py index 0a4e41b5f0..86521ed264 100644 --- a/plugins/modules/cloud/misc/terraform.py +++ b/plugins/modules/cloud/misc/terraform.py @@ -107,6 +107,12 @@ options: you intend to provision an entirely new Terraform deployment. default: false type: bool + overwrite_init: + description: + - Run init even if C(.terraform/terraform.tfstate) already exists in I(project_path). + default: true + type: bool + version_added: '3.2.0' backend_config: description: - A group of key-values to provide at init stage to the -backend-config parameter. @@ -227,7 +233,7 @@ def get_version(bin_path): def preflight_validation(bin_path, project_path, version, variables_args=None, plan_file=None): - if project_path in [None, ''] or '/' not in project_path: + if project_path is None or '/' not in project_path: module.fail_json(msg="Path for Terraform project can not be None or ''.") if not os.path.exists(bin_path): module.fail_json(msg="Path for Terraform binary '{0}' doesn't exist on this host - check the path and try again please.".format(bin_path)) @@ -348,6 +354,7 @@ def main(): backend_config=dict(type='dict', default=None), backend_config_files=dict(type='list', elements='path', default=None), init_reconfigure=dict(required=False, type='bool', default=False), + overwrite_init=dict(type='bool', default=True), ), required_if=[('state', 'planned', ['plan_file'])], supports_check_mode=True, @@ -367,6 +374,7 @@ def main(): backend_config = module.params.get('backend_config') backend_config_files = module.params.get('backend_config_files') init_reconfigure = module.params.get('init_reconfigure') + overwrite_init = module.params.get('overwrite_init') if bin_path is not None: command = [bin_path] @@ -383,7 +391,8 @@ def main(): APPLY_ARGS = ('apply', '-no-color', '-input=false', '-auto-approve') if force_init: - init_plugins(command[0], project_path, backend_config, backend_config_files, init_reconfigure, plugin_paths) + if overwrite_init or not os.path.isfile(os.path.join(project_path, ".terraform", "terraform.tfstate")): + init_plugins(command[0], project_path, backend_config, backend_config_files, init_reconfigure, plugin_paths) workspace_ctx = get_workspace_context(command[0], project_path) if workspace_ctx["current"] != workspace: @@ -438,7 +447,14 @@ def main(): command.append(plan_file) if needs_application and not module.check_mode and not state == 'planned': - rc, out, err = module.run_command(command, check_rc=True, cwd=project_path) + rc, out, err = module.run_command(command, check_rc=False, cwd=project_path) + if rc != 0: + if workspace_ctx["current"] != workspace: + select_workspace(command[0], project_path, workspace_ctx["current"]) + module.fail_json(msg=err.rstrip(), rc=rc, stdout=out, + stdout_lines=out.splitlines(), stderr=err, + stderr_lines=err.splitlines(), + cmd=' '.join(command)) # checks out to decide if changes were made during execution if ' 0 added, 0 changed' not in out and not state == "absent" or ' 0 destroyed' not in out: changed = True diff --git a/plugins/modules/cloud/online/online_server_info.py b/plugins/modules/cloud/online/online_server_info.py index f0e73aea16..f33a44d30f 100644 --- a/plugins/modules/cloud/online/online_server_info.py +++ b/plugins/modules/cloud/online/online_server_info.py @@ -32,11 +32,13 @@ EXAMPLES = r''' ''' RETURN = r''' ---- online_server_info: - description: Response from Online API + description: + - Response from Online API. + - "For more details please refer to: U(https://console.online.net/en/api/)." returned: success - type: complex + type: list + elements: dict sample: "online_server_info": [ { diff --git a/plugins/modules/cloud/online/online_user_info.py b/plugins/modules/cloud/online/online_user_info.py index 093a2c687f..4125ccb63d 100644 --- a/plugins/modules/cloud/online/online_user_info.py +++ b/plugins/modules/cloud/online/online_user_info.py @@ -7,7 +7,6 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = r''' ---- module: online_user_info short_description: Gather information about Online user. description: @@ -16,7 +15,6 @@ author: - "Remy Leone (@sieben)" extends_documentation_fragment: - community.general.online - ''' EXAMPLES = r''' @@ -29,11 +27,12 @@ EXAMPLES = r''' ''' RETURN = r''' ---- online_user_info: - description: Response from Online API + description: + - Response from Online API. + - "For more details please refer to: U(https://console.online.net/en/api/)." returned: success - type: complex + type: dict sample: "online_user_info": { "company": "foobar LLC", diff --git a/plugins/modules/cloud/opennebula/one_vm.py b/plugins/modules/cloud/opennebula/one_vm.py index 425a1c464a..fa3d4abaab 100644 --- a/plugins/modules/cloud/opennebula/one_vm.py +++ b/plugins/modules/cloud/opennebula/one_vm.py @@ -752,11 +752,20 @@ def get_vm_info(client, vm): if 'NIC' in vm.TEMPLATE: if isinstance(vm.TEMPLATE['NIC'], list): for nic in vm.TEMPLATE['NIC']: - networks_info.append({'ip': nic['IP'], 'mac': nic['MAC'], 'name': nic['NETWORK'], 'security_groups': nic['SECURITY_GROUPS']}) + networks_info.append({ + 'ip': nic.get('IP', ''), + 'mac': nic.get('MAC', ''), + 'name': nic.get('NETWORK', ''), + 'security_groups': nic.get('SECURITY_GROUPS', '') + }) else: - networks_info.append( - {'ip': vm.TEMPLATE['NIC']['IP'], 'mac': vm.TEMPLATE['NIC']['MAC'], - 'name': vm.TEMPLATE['NIC']['NETWORK'], 'security_groups': vm.TEMPLATE['NIC']['SECURITY_GROUPS']}) + networks_info.append({ + 'ip': vm.TEMPLATE['NIC'].get('IP', ''), + 'mac': vm.TEMPLATE['NIC'].get('MAC', ''), + 'name': vm.TEMPLATE['NIC'].get('NETWORK', ''), + 'security_groups': + vm.TEMPLATE['NIC'].get('SECURITY_GROUPS', '') + }) import time current_time = time.localtime() diff --git a/plugins/modules/cloud/scaleway/scaleway_image_info.py b/plugins/modules/cloud/scaleway/scaleway_image_info.py index 3fad216ee5..609ba3d1e8 100644 --- a/plugins/modules/cloud/scaleway/scaleway_image_info.py +++ b/plugins/modules/cloud/scaleway/scaleway_image_info.py @@ -19,9 +19,7 @@ author: extends_documentation_fragment: - community.general.scaleway - options: - region: type: str description: @@ -51,9 +49,12 @@ EXAMPLES = r''' RETURN = r''' --- scaleway_image_info: - description: Response from Scaleway API + description: + - Response from Scaleway API. + - "For more details please refer to: U(https://developers.scaleway.com/en/products/instance/api/)." returned: success - type: complex + type: list + elements: dict sample: "scaleway_image_info": [ { diff --git a/plugins/modules/cloud/scaleway/scaleway_ip_info.py b/plugins/modules/cloud/scaleway/scaleway_ip_info.py index 145fb20338..e2e49557cc 100644 --- a/plugins/modules/cloud/scaleway/scaleway_ip_info.py +++ b/plugins/modules/cloud/scaleway/scaleway_ip_info.py @@ -49,9 +49,12 @@ EXAMPLES = r''' RETURN = r''' --- scaleway_ip_info: - description: Response from Scaleway API + description: + - Response from Scaleway API. + - "For more details please refer to: U(https://developers.scaleway.com/en/products/instance/api/)." returned: success - type: complex + type: list + elements: dict sample: "scaleway_ip_info": [ { diff --git a/plugins/modules/cloud/scaleway/scaleway_security_group_info.py b/plugins/modules/cloud/scaleway/scaleway_security_group_info.py index d3488f0c8b..1f5af7da53 100644 --- a/plugins/modules/cloud/scaleway/scaleway_security_group_info.py +++ b/plugins/modules/cloud/scaleway/scaleway_security_group_info.py @@ -49,9 +49,12 @@ EXAMPLES = r''' RETURN = r''' --- scaleway_security_group_info: - description: Response from Scaleway API + description: + - Response from Scaleway API. + - "For more details please refer to: U(https://developers.scaleway.com/en/products/instance/api/)." returned: success - type: complex + type: list + elements: dict sample: "scaleway_security_group_info": [ { diff --git a/plugins/modules/cloud/scaleway/scaleway_server_info.py b/plugins/modules/cloud/scaleway/scaleway_server_info.py index 43b0badc14..61bd9de41b 100644 --- a/plugins/modules/cloud/scaleway/scaleway_server_info.py +++ b/plugins/modules/cloud/scaleway/scaleway_server_info.py @@ -49,9 +49,12 @@ EXAMPLES = r''' RETURN = r''' --- scaleway_server_info: - description: Response from Scaleway API + description: + - Response from Scaleway API. + - "For more details please refer to: U(https://developers.scaleway.com/en/products/instance/api/)." returned: success - type: complex + type: list + elements: dict sample: "scaleway_server_info": [ { diff --git a/plugins/modules/cloud/scaleway/scaleway_snapshot_info.py b/plugins/modules/cloud/scaleway/scaleway_snapshot_info.py index f31b74b00e..95ec04d16f 100644 --- a/plugins/modules/cloud/scaleway/scaleway_snapshot_info.py +++ b/plugins/modules/cloud/scaleway/scaleway_snapshot_info.py @@ -49,9 +49,12 @@ EXAMPLES = r''' RETURN = r''' --- scaleway_snapshot_info: - description: Response from Scaleway API + description: + - Response from Scaleway API. + - "For more details please refer to: U(https://developers.scaleway.com/en/products/instance/api/)." returned: success - type: complex + type: list + elements: dict sample: "scaleway_snapshot_info": [ { diff --git a/plugins/modules/cloud/scaleway/scaleway_volume_info.py b/plugins/modules/cloud/scaleway/scaleway_volume_info.py index ff6093e830..0042146795 100644 --- a/plugins/modules/cloud/scaleway/scaleway_volume_info.py +++ b/plugins/modules/cloud/scaleway/scaleway_volume_info.py @@ -49,9 +49,12 @@ EXAMPLES = r''' RETURN = r''' --- scaleway_volume_info: - description: Response from Scaleway API + description: + - Response from Scaleway API. + - "For more details please refer to: U(https://developers.scaleway.com/en/products/instance/api/)." returned: success - type: complex + type: list + elements: dict sample: "scaleway_volume_info": [ { diff --git a/plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py b/plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py index 1a0ddb9fef..5ed8028e37 100644 --- a/plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py +++ b/plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py @@ -23,26 +23,26 @@ options: credentials_path: description: - - (Path) Optional parameter that allows to set a non-default credentials path. + - Optional parameter that allows to set a non-default credentials path. default: ~/.spotinst/credentials type: path account_id: description: - - (String) Optional parameter that allows to set an account-id inside the module configuration - By default this is retrieved from the credentials path + - Optional parameter that allows to set an account-id inside the module configuration. + By default this is retrieved from the credentials path. type: str availability_vs_cost: description: - - (String) The strategy orientation. + - The strategy orientation. - "The choices available are: C(availabilityOriented), C(costOriented), C(balanced)." required: true type: str availability_zones: description: - - (List of Objects) a list of hash/dictionaries of Availability Zones that are configured in the elastigroup; + - A list of hash/dictionaries of Availability Zones that are configured in the elastigroup; '[{"key":"value", "key":"value"}]'; keys allowed are name (String), @@ -50,10 +50,11 @@ options: placement_group_name (String), required: true type: list + elements: dict block_device_mappings: description: - - (List of Objects) a list of hash/dictionaries of Block Device Mappings for elastigroup instances; + - A list of hash/dictionaries of Block Device Mappings for elastigroup instances; You can specify virtual devices and EBS volumes.; '[{"key":"value", "key":"value"}]'; keys allowed are @@ -68,10 +69,11 @@ options: volume_type(String), volume_size(Integer)) type: list + elements: dict chef: description: - - (Object) The Chef integration configuration.; + - The Chef integration configuration.; Expects the following keys - chef_server (String), organization (String), user (String), @@ -81,92 +83,94 @@ options: draining_timeout: description: - - (Integer) Time for instance to be drained from incoming requests and deregistered from ELB before termination. + - Time for instance to be drained from incoming requests and deregistered from ELB before termination. type: int ebs_optimized: description: - - (Boolean) Enable EBS optimization for supported instances which are not enabled by default.; + - Enable EBS optimization for supported instances which are not enabled by default.; Note - additional charges will be applied. type: bool ebs_volume_pool: description: - - (List of Objects) a list of hash/dictionaries of EBS devices to reattach to the elastigroup when available; + - A list of hash/dictionaries of EBS devices to reattach to the elastigroup when available; '[{"key":"value", "key":"value"}]'; keys allowed are - volume_ids (List of Strings), device_name (String) type: list + elements: dict ecs: description: - - (Object) The ECS integration configuration.; + - The ECS integration configuration.; Expects the following key - cluster_name (String) type: dict elastic_ips: description: - - (List of Strings) List of ElasticIps Allocation Ids (Example C(eipalloc-9d4e16f8)) to associate to the group instances + - List of ElasticIps Allocation Ids (Example C(eipalloc-9d4e16f8)) to associate to the group instances type: list + elements: str fallback_to_od: description: - - (Boolean) In case of no spots available, Elastigroup will launch an On-demand instance instead + - In case of no spots available, Elastigroup will launch an On-demand instance instead type: bool health_check_grace_period: description: - - (Integer) The amount of time, in seconds, after the instance has launched to start and check its health. + - The amount of time, in seconds, after the instance has launched to start and check its health. - If not specified, it defaults to C(300). type: int health_check_unhealthy_duration_before_replacement: description: - - (Integer) Minimal mount of time instance should be unhealthy for us to consider it unhealthy. + - Minimal mount of time instance should be unhealthy for us to consider it unhealthy. type: int health_check_type: description: - - (String) The service to use for the health check. + - The service to use for the health check. - "The choices available are: C(ELB), C(HCS), C(TARGET_GROUP), C(MLB), C(EC2)." type: str iam_role_name: description: - - (String) The instance profile iamRole name + - The instance profile iamRole name - Only use iam_role_arn, or iam_role_name type: str iam_role_arn: description: - - (String) The instance profile iamRole arn + - The instance profile iamRole arn - Only use iam_role_arn, or iam_role_name type: str id: description: - - (String) The group id if it already exists and you want to update, or delete it. + - The group id if it already exists and you want to update, or delete it. This will not work unless the uniqueness_by field is set to id. When this is set, and the uniqueness_by field is set, the group will either be updated or deleted, but not created. type: str image_id: description: - - (String) The image Id used to launch the instance.; + - The image Id used to launch the instance.; In case of conflict between Instance type and image type, an error will be returned required: true type: str key_pair: description: - - (String) Specify a Key Pair to attach to the instances + - Specify a Key Pair to attach to the instances type: str kubernetes: description: - - (Object) The Kubernetes integration configuration. + - The Kubernetes integration configuration. Expects the following keys - api_server (String), token (String) @@ -174,47 +178,48 @@ options: lifetime_period: description: - - (Integer) lifetime period + - Lifetime period type: int load_balancers: description: - - (List of Strings) List of classic ELB names + - List of classic ELB names type: list + elements: str max_size: description: - - (Integer) The upper limit number of instances that you can scale up to + - The upper limit number of instances that you can scale up to required: true type: int mesosphere: description: - - (Object) The Mesosphere integration configuration. + - The Mesosphere integration configuration. Expects the following key - api_server (String) type: dict min_size: description: - - (Integer) The lower limit number of instances that you can scale down to + - The lower limit number of instances that you can scale down to required: true type: int monitoring: description: - - (String) Describes whether instance Enhanced Monitoring is enabled + - Describes whether instance Enhanced Monitoring is enabled type: str name: description: - - (String) Unique name for elastigroup to be created, updated or deleted + - Unique name for elastigroup to be created, updated or deleted required: true type: str network_interfaces: description: - - (List of Objects) a list of hash/dictionaries of network interfaces to add to the elastigroup; + - A list of hash/dictionaries of network interfaces to add to the elastigroup; '[{"key":"value", "key":"value"}]'; keys allowed are - description (String), @@ -229,29 +234,30 @@ options: associate_ipv6_address (Boolean), private_ip_addresses (List of Objects, Keys are privateIpAddress (String, required) and primary (Boolean)) type: list + elements: dict on_demand_count: description: - - (Integer) Required if risk is not set + - Required if risk is not set - Number of on demand instances to launch. All other instances will be spot instances.; Either set this parameter or the risk parameter type: int on_demand_instance_type: description: - - (String) On-demand instance type that will be provisioned + - On-demand instance type that will be provisioned type: str opsworks: description: - - (Object) The elastigroup OpsWorks integration configration.; + - The elastigroup OpsWorks integration configration.; Expects the following key - layer_id (String) type: dict persistence: description: - - (Object) The Stateful elastigroup configration.; + - The Stateful elastigroup configration.; Accepts the following keys - should_persist_root_device (Boolean), should_persist_block_devices (Boolean), @@ -260,14 +266,14 @@ options: product: description: - - (String) Operation system type. + - Operation system type. - "Available choices are: C(Linux/UNIX), C(SUSE Linux), C(Windows), C(Linux/UNIX (Amazon VPC)), C(SUSE Linux (Amazon VPC))." required: true type: str rancher: description: - - (Object) The Rancher integration configuration.; + - The Rancher integration configuration.; Expects the following keys - version (String), access_key (String), @@ -277,7 +283,7 @@ options: right_scale: description: - - (Object) The Rightscale integration configuration.; + - The Rightscale integration configuration.; Expects the following keys - account_id (String), refresh_token (String) @@ -285,12 +291,12 @@ options: risk: description: - - (Integer) required if on demand is not set. The percentage of Spot instances to launch (0 - 100). + - Required if on demand is not set. The percentage of Spot instances to launch (0 - 100). type: int roll_config: description: - - (Object) Roll configuration.; + - Roll configuration.; If you would like the group to roll after updating, please use this feature. Accepts the following keys - batch_size_percentage(Integer, Required), @@ -300,7 +306,7 @@ options: scheduled_tasks: description: - - (List of Objects) a list of hash/dictionaries of scheduled tasks to configure in the elastigroup; + - A list of hash/dictionaries of scheduled tasks to configure in the elastigroup; '[{"key":"value", "key":"value"}]'; keys allowed are - adjustment (Integer), @@ -315,84 +321,90 @@ options: task_type (String, required), is_enabled (Boolean) type: list + elements: dict security_group_ids: description: - - (List of Strings) One or more security group IDs. ; + - One or more security group IDs. ; In case of update it will override the existing Security Group with the new given array required: true type: list + elements: str shutdown_script: description: - - (String) The Base64-encoded shutdown script that executes prior to instance termination. + - The Base64-encoded shutdown script that executes prior to instance termination. Encode before setting. type: str signals: description: - - (List of Objects) a list of hash/dictionaries of signals to configure in the elastigroup; + - A list of hash/dictionaries of signals to configure in the elastigroup; keys allowed are - name (String, required), timeout (Integer) type: list + elements: dict spin_up_time: description: - - (Integer) spin up time, in seconds, for the instance + - Spin up time, in seconds, for the instance type: int spot_instance_types: description: - - (List of Strings) Spot instance type that will be provisioned. + - Spot instance type that will be provisioned. required: true type: list + elements: str state: choices: - present - absent description: - - (String) create or delete the elastigroup + - Create or delete the elastigroup default: present type: str tags: description: - - (List of tagKey:tagValue pairs) a list of tags to configure in the elastigroup. Please specify list of keys and values (key colon value); + - A list of tags to configure in the elastigroup. Please specify list of keys and values (key colon value); type: list + elements: dict target: description: - - (Integer) The number of instances to launch + - The number of instances to launch required: true type: int target_group_arns: description: - - (List of Strings) List of target group arns instances should be registered to + - List of target group arns instances should be registered to type: list + elements: str tenancy: description: - - (String) dedicated vs shared tenancy. + - Dedicated vs shared tenancy. - "The available choices are: C(default), C(dedicated)." type: str terminate_at_end_of_billing_hour: description: - - (Boolean) terminate at the end of billing hour + - Terminate at the end of billing hour type: bool unit: description: - - (String) The capacity unit to launch instances by. + - The capacity unit to launch instances by. - "The available choices are: C(instance), C(weight)." type: str up_scaling_policies: description: - - (List of Objects) a list of hash/dictionaries of scaling policies to configure in the elastigroup; + - A list of hash/dictionaries of scaling policies to configure in the elastigroup; '[{"key":"value", "key":"value"}]'; keys allowed are - policy_name (String, required), @@ -413,10 +425,11 @@ options: maximum (String), minimum (String) type: list + elements: dict down_scaling_policies: description: - - (List of Objects) a list of hash/dictionaries of scaling policies to configure in the elastigroup; + - A list of hash/dictionaries of scaling policies to configure in the elastigroup; '[{"key":"value", "key":"value"}]'; keys allowed are - policy_name (String, required), @@ -437,10 +450,11 @@ options: maximum (String), minimum (String) type: list + elements: dict target_tracking_policies: description: - - (List of Objects) a list of hash/dictionaries of target tracking policies to configure in the elastigroup; + - A list of hash/dictionaries of target tracking policies to configure in the elastigroup; '[{"key":"value", "key":"value"}]'; keys allowed are - policy_name (String, required), @@ -452,37 +466,38 @@ options: cooldown (String, required), target (String, required) type: list + elements: dict uniqueness_by: choices: - id - name description: - - (String) If your group names are not unique, you may use this feature to update or delete a specific group. + - If your group names are not unique, you may use this feature to update or delete a specific group. Whenever this property is set, you must set a group_id in order to update or delete a group, otherwise a group will be created. default: name type: str user_data: description: - - (String) Base64-encoded MIME user data. Encode before setting the value. + - Base64-encoded MIME user data. Encode before setting the value. type: str utilize_reserved_instances: description: - - (Boolean) In case of any available Reserved Instances, + - In case of any available Reserved Instances, Elastigroup will utilize your reservations before purchasing Spot instances. type: bool wait_for_instances: description: - - (Boolean) Whether or not the elastigroup creation / update actions should wait for the instances to spin + - Whether or not the elastigroup creation / update actions should wait for the instances to spin type: bool default: false wait_timeout: description: - - (Integer) How long the module should wait for instances before failing the action.; + - How long the module should wait for instances before failing the action.; Only works if wait_for_instances is True. type: int @@ -1428,18 +1443,18 @@ def main(): fields = dict( account_id=dict(type='str'), availability_vs_cost=dict(type='str', required=True), - availability_zones=dict(type='list', required=True), - block_device_mappings=dict(type='list'), + availability_zones=dict(type='list', elements='dict', required=True), + 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'), - down_scaling_policies=dict(type='list'), + down_scaling_policies=dict(type='list', elements='dict'), draining_timeout=dict(type='int'), ebs_optimized=dict(type='bool'), - ebs_volume_pool=dict(type='list'), + ebs_volume_pool=dict(type='list', elements='dict'), ecs=dict(type='dict'), elastic_beanstalk=dict(type='dict'), - elastic_ips=dict(type='list'), + elastic_ips=dict(type='list', elements='str'), fallback_to_od=dict(type='bool'), id=dict(type='str'), health_check_grace_period=dict(type='int'), @@ -1451,7 +1466,7 @@ def main(): key_pair=dict(type='str', no_log=False), kubernetes=dict(type='dict'), lifetime_period=dict(type='int'), - load_balancers=dict(type='list'), + load_balancers=dict(type='list', elements='str'), max_size=dict(type='int', required=True), mesosphere=dict(type='dict'), min_size=dict(type='int', required=True), @@ -1459,7 +1474,7 @@ def main(): multai_load_balancers=dict(type='list'), multai_token=dict(type='str', no_log=True), name=dict(type='str', required=True), - network_interfaces=dict(type='list'), + network_interfaces=dict(type='list', elements='dict'), on_demand_count=dict(type='int'), on_demand_instance_type=dict(type='str'), opsworks=dict(type='dict'), @@ -1469,16 +1484,16 @@ def main(): right_scale=dict(type='dict'), risk=dict(type='int'), roll_config=dict(type='dict'), - scheduled_tasks=dict(type='list'), - security_group_ids=dict(type='list', required=True), + scheduled_tasks=dict(type='list', elements='dict'), + security_group_ids=dict(type='list', elements='str', required=True), shutdown_script=dict(type='str'), - signals=dict(type='list'), + signals=dict(type='list', elements='dict'), spin_up_time=dict(type='int'), - spot_instance_types=dict(type='list', required=True), + spot_instance_types=dict(type='list', elements='str', required=True), state=dict(default='present', choices=['present', 'absent']), - tags=dict(type='list'), + tags=dict(type='list', elements='dict'), target=dict(type='int', required=True), - target_group_arns=dict(type='list'), + target_group_arns=dict(type='list', elements='str'), tenancy=dict(type='str'), terminate_at_end_of_billing_hour=dict(type='bool'), token=dict(type='str', no_log=True), @@ -1486,8 +1501,8 @@ def main(): user_data=dict(type='str'), utilize_reserved_instances=dict(type='bool'), uniqueness_by=dict(default='name', choices=['name', 'id']), - up_scaling_policies=dict(type='list'), - target_tracking_policies=dict(type='list'), + up_scaling_policies=dict(type='list', elements='dict'), + target_tracking_policies=dict(type='list', elements='dict'), wait_for_instances=dict(type='bool', default=False), wait_timeout=dict(type='int') ) diff --git a/plugins/modules/clustering/consul/consul_acl.py b/plugins/modules/clustering/consul/consul_acl.py index cb5395ed31..5a37ca0eb9 100644 --- a/plugins/modules/clustering/consul/consul_acl.py +++ b/plugins/modules/clustering/consul/consul_acl.py @@ -189,7 +189,24 @@ from collections import defaultdict from ansible.module_utils.basic import to_text, AnsibleModule -RULE_SCOPES = ["agent", "event", "key", "keyring", "node", "operator", "query", "service", "session"] +RULE_SCOPES = [ + "agent", + "agent_prefix", + "event", + "event_prefix", + "key", + "key_prefix", + "keyring", + "node", + "node_prefix", + "operator", + "query", + "query_prefix", + "service", + "service_prefix", + "session", + "session_prefix", +] MANAGEMENT_PARAMETER_NAME = "mgmt_token" HOST_PARAMETER_NAME = "host" diff --git a/plugins/modules/database/influxdb/influxdb_retention_policy.py b/plugins/modules/database/influxdb/influxdb_retention_policy.py index 2c2f9674b7..3ff48cbad0 100644 --- a/plugins/modules/database/influxdb/influxdb_retention_policy.py +++ b/plugins/modules/database/influxdb/influxdb_retention_policy.py @@ -29,17 +29,24 @@ options: - Name of the retention policy. required: true type: str + state: + description: + - State of the retention policy. + choices: [ absent, present ] + default: present + type: str + version_added: 3.1.0 duration: description: - Determines how long InfluxDB should keep the data. If specified, it should be C(INF) or at least one hour. If not specified, C(INF) is assumed. Supports complex duration expressions with multiple units. - required: true + - Required only if I(state) is set to C(present). type: str replication: description: - Determines how many independent copies of each point are stored in the cluster. - required: true + - Required only if I(state) is set to C(present). type: int default: description: @@ -63,53 +70,65 @@ EXAMPLES = r''' # Example influxdb_retention_policy command from Ansible Playbooks - name: Create 1 hour retention policy community.general.influxdb_retention_policy: - hostname: "{{influxdb_ip_address}}" - database_name: "{{influxdb_database_name}}" + hostname: "{{ influxdb_ip_address }}" + database_name: "{{ influxdb_database_name }}" policy_name: test duration: 1h replication: 1 ssl: yes validate_certs: yes + state: present - name: Create 1 day retention policy with 1 hour shard group duration community.general.influxdb_retention_policy: - hostname: "{{influxdb_ip_address}}" - database_name: "{{influxdb_database_name}}" + hostname: "{{ influxdb_ip_address }}" + database_name: "{{ influxdb_database_name }}" policy_name: test duration: 1d replication: 1 shard_group_duration: 1h + state: present - name: Create 1 week retention policy with 1 day shard group duration community.general.influxdb_retention_policy: - hostname: "{{influxdb_ip_address}}" - database_name: "{{influxdb_database_name}}" + hostname: "{{ influxdb_ip_address }}" + database_name: "{{ influxdb_database_name }}" policy_name: test duration: 1w replication: 1 shard_group_duration: 1d + state: present - name: Create infinite retention policy with 1 week of shard group duration community.general.influxdb_retention_policy: - hostname: "{{influxdb_ip_address}}" - database_name: "{{influxdb_database_name}}" + hostname: "{{ influxdb_ip_address }}" + database_name: "{{ influxdb_database_name }}" policy_name: test duration: INF replication: 1 ssl: no validate_certs: no shard_group_duration: 1w + state: present - name: Create retention policy with complex durations community.general.influxdb_retention_policy: - hostname: "{{influxdb_ip_address}}" - database_name: "{{influxdb_database_name}}" + hostname: "{{ influxdb_ip_address }}" + database_name: "{{ influxdb_database_name }}" policy_name: test duration: 5d1h30m replication: 1 ssl: no validate_certs: no shard_group_duration: 1d10h30m + state: present + +- name: Drop retention policy + community.general.influxdb_retention_policy: + hostname: "{{ influxdb_ip_address }}" + database_name: "{{ influxdb_database_name }}" + policy_name: test + state: absent ''' RETURN = r''' @@ -129,11 +148,26 @@ from ansible_collections.community.general.plugins.module_utils.influxdb import from ansible.module_utils._text import to_native -VALID_DURATION_REGEX = re.compile(r'^(\d+(ns|u|µ|ms|s|m|h|d|w))+$') +VALID_DURATION_REGEX = re.compile(r'^(INF|(\d+(ns|u|µ|ms|s|m|h|d|w)))+$') DURATION_REGEX = re.compile(r'(\d+)(ns|u|µ|ms|s|m|h|d|w)') EXTENDED_DURATION_REGEX = re.compile(r'(?:(\d+)(ns|u|µ|ms|m|h|d|w)|(\d+(?:\.\d+)?)(s))') +DURATION_UNIT_NANOSECS = { + 'ns': 1, + 'u': 1000, + 'µ': 1000, + 'ms': 1000 * 1000, + 's': 1000 * 1000 * 1000, + 'm': 1000 * 1000 * 1000 * 60, + 'h': 1000 * 1000 * 1000 * 60 * 60, + 'd': 1000 * 1000 * 1000 * 60 * 60 * 24, + 'w': 1000 * 1000 * 1000 * 60 * 60 * 24 * 7, +} + +MINIMUM_VALID_DURATION = 1 * DURATION_UNIT_NANOSECS['h'] +MINIMUM_VALID_SHARD_GROUP_DURATION = 1 * DURATION_UNIT_NANOSECS['h'] + def check_duration_literal(value): return VALID_DURATION_REGEX.search(value) is not None @@ -148,28 +182,9 @@ def parse_duration_literal(value, extended=False): lookup = (EXTENDED_DURATION_REGEX if extended else DURATION_REGEX).findall(value) for duration_literal in lookup: - if extended and duration_literal[3] == 's': - duration_val = float(duration_literal[2]) - duration += duration_val * 1000 * 1000 * 1000 - else: - duration_val = int(duration_literal[0]) - - if duration_literal[1] == 'ns': - duration += duration_val - elif duration_literal[1] == 'u' or duration_literal[1] == 'µ': - duration += duration_val * 1000 - elif duration_literal[1] == 'ms': - duration += duration_val * 1000 * 1000 - elif duration_literal[1] == 's': - duration += duration_val * 1000 * 1000 * 1000 - elif duration_literal[1] == 'm': - duration += duration_val * 1000 * 1000 * 1000 * 60 - elif duration_literal[1] == 'h': - duration += duration_val * 1000 * 1000 * 1000 * 60 * 60 - elif duration_literal[1] == 'd': - duration += duration_val * 1000 * 1000 * 1000 * 60 * 60 * 24 - elif duration_literal[1] == 'w': - duration += duration_val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7 + filtered_literal = list(filter(None, duration_literal)) + duration_val = float(filtered_literal[0]) + duration += duration_val * DURATION_UNIT_NANOSECS[filtered_literal[1]] return duration @@ -208,7 +223,7 @@ def create_retention_policy(module, client): module.fail_json(msg="Failed to parse value of duration") influxdb_duration_format = parse_duration_literal(duration) - if influxdb_duration_format != 0 and influxdb_duration_format < 3600000000000: + if influxdb_duration_format != 0 and influxdb_duration_format < MINIMUM_VALID_DURATION: module.fail_json(msg="duration value must be at least 1h") if shard_group_duration is not None: @@ -216,8 +231,8 @@ def create_retention_policy(module, client): module.fail_json(msg="Failed to parse value of shard_group_duration") influxdb_shard_group_duration_format = parse_duration_literal(shard_group_duration) - if influxdb_shard_group_duration_format < 3600000000000: - module.fail_json(msg="shard_group_duration value must be at least 1h") + if influxdb_shard_group_duration_format < MINIMUM_VALID_SHARD_GROUP_DURATION: + module.fail_json(msg="shard_group_duration value must be finite and at least 1h") if not module.check_mode: try: @@ -245,7 +260,7 @@ def alter_retention_policy(module, client, retention_policy): module.fail_json(msg="Failed to parse value of duration") influxdb_duration_format = parse_duration_literal(duration) - if influxdb_duration_format != 0 and influxdb_duration_format < 3600000000000: + if influxdb_duration_format != 0 and influxdb_duration_format < MINIMUM_VALID_DURATION: module.fail_json(msg="duration value must be at least 1h") if shard_group_duration is None: @@ -255,8 +270,8 @@ def alter_retention_policy(module, client, retention_policy): module.fail_json(msg="Failed to parse value of shard_group_duration") influxdb_shard_group_duration_format = parse_duration_literal(shard_group_duration) - if influxdb_shard_group_duration_format < 3600000000000: - module.fail_json(msg="shard_group_duration value must be at least 1h") + if influxdb_shard_group_duration_format < MINIMUM_VALID_SHARD_GROUP_DURATION: + module.fail_json(msg="shard_group_duration value must be finite and at least 1h") if (retention_policy['duration'] != influxdb_duration_format or retention_policy['shardGroupDuration'] != influxdb_shard_group_duration_format or @@ -272,30 +287,55 @@ def alter_retention_policy(module, client, retention_policy): module.exit_json(changed=changed) +def drop_retention_policy(module, client): + database_name = module.params['database_name'] + policy_name = module.params['policy_name'] + + if not module.check_mode: + try: + client.drop_retention_policy(policy_name, database_name) + except exceptions.InfluxDBClientError as e: + module.fail_json(msg=e.content) + module.exit_json(changed=True) + + def main(): argument_spec = InfluxDb.influxdb_argument_spec() argument_spec.update( + state=dict(default='present', type='str', choices=['present', 'absent']), database_name=dict(required=True, type='str'), policy_name=dict(required=True, type='str'), - duration=dict(required=True, type='str'), - replication=dict(required=True, type='int'), + duration=dict(type='str'), + replication=dict(type='int'), default=dict(default=False, type='bool'), - shard_group_duration=dict(required=False, type='str'), + shard_group_duration=dict(type='str'), ) module = AnsibleModule( argument_spec=argument_spec, - supports_check_mode=True + supports_check_mode=True, + required_if=( + ('state', 'present', ['duration', 'replication']), + ), ) + state = module.params['state'] + influxdb = InfluxDb(module) client = influxdb.connect_to_influxdb() retention_policy = find_retention_policy(module, client) - if retention_policy: - alter_retention_policy(module, client, retention_policy) - else: - create_retention_policy(module, client) + if state == 'present': + if retention_policy: + alter_retention_policy(module, client, retention_policy) + else: + create_retention_policy(module, client) + + if state == 'absent': + if retention_policy: + drop_retention_policy(module, client) + else: + module.exit_json(changed=False) if __name__ == '__main__': diff --git a/plugins/modules/database/influxdb/influxdb_user.py b/plugins/modules/database/influxdb/influxdb_user.py index e17e3753f2..cb35ea7ce6 100644 --- a/plugins/modules/database/influxdb/influxdb_user.py +++ b/plugins/modules/database/influxdb/influxdb_user.py @@ -100,6 +100,8 @@ RETURN = r''' #only defaults ''' +import json + from ansible.module_utils.urls import ConnectionError from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native @@ -115,7 +117,7 @@ def find_user(module, client, user_name): if user['user'] == user_name: user_result = user break - except (ConnectionError, influx.exceptions.InfluxDBClientError) as e: + except ConnectionError as e: module.fail_json(msg=to_native(e)) return user_result @@ -166,16 +168,16 @@ def set_user_grants(module, client, user_name, grants): try: current_grants = client.get_list_privileges(user_name) + parsed_grants = [] # Fix privileges wording for i, v in enumerate(current_grants): - if v['privilege'] == 'ALL PRIVILEGES': - v['privilege'] = 'ALL' - current_grants[i] = v - elif v['privilege'] == 'NO PRIVILEGES': - del(current_grants[i]) + if v['privilege'] != 'NO PRIVILEGES': + if v['privilege'] == 'ALL PRIVILEGES': + v['privilege'] = 'ALL' + parsed_grants.append(v) # check if the current grants are included in the desired ones - for current_grant in current_grants: + for current_grant in parsed_grants: if current_grant not in grants: if not module.check_mode: client.revoke_privilege(current_grant['privilege'], @@ -185,7 +187,7 @@ def set_user_grants(module, client, user_name, grants): # check if the desired grants are included in the current ones for grant in grants: - if grant not in current_grants: + if grant not in parsed_grants: if not module.check_mode: client.grant_privilege(grant['privilege'], grant['database'], @@ -198,6 +200,9 @@ def set_user_grants(module, client, user_name, grants): return changed +INFLUX_AUTH_FIRST_USER_REQUIRED = "error authorizing query: create admin user first or disable authentication" + + def main(): argument_spec = influx.InfluxDb.influxdb_argument_spec() argument_spec.update( @@ -219,7 +224,23 @@ def main(): grants = module.params['grants'] influxdb = influx.InfluxDb(module) client = influxdb.connect_to_influxdb() - user = find_user(module, client, user_name) + + user = None + try: + user = find_user(module, client, user_name) + except influx.exceptions.InfluxDBClientError as e: + if e.code == 403: + reason = None + try: + msg = json.loads(e.content) + reason = msg["error"] + except (KeyError, ValueError): + module.fail_json(msg=to_native(e)) + + if reason != INFLUX_AUTH_FIRST_USER_REQUIRED: + module.fail_json(msg=to_native(e)) + else: + module.fail_json(msg=to_native(e)) changed = False diff --git a/plugins/modules/database/saphana/hana_query.py b/plugins/modules/database/saphana/hana_query.py new file mode 100644 index 0000000000..ab147ef3fe --- /dev/null +++ b/plugins/modules/database/saphana/hana_query.py @@ -0,0 +1,187 @@ +#!/usr/bin/python + +# Copyright: (c) 2021, Rainer Leber +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: hana_query +short_description: Execute SQL on HANA +version_added: 3.2.0 +description: This module executes SQL statements on HANA with hdbsql. +options: + sid: + description: The system ID. + type: str + required: true + instance: + description: The instance number. + type: str + required: true + user: + description: A dedicated username. Defaults to C(SYSTEM). + type: str + default: SYSTEM + password: + description: The password to connect to the database. + type: str + required: true + autocommit: + description: Autocommit the statement. + type: bool + default: true + host: + description: The Host IP address. The port can be defined as well. + type: str + database: + description: Define the database on which to connect. + type: str + encrypted: + description: Use encrypted connection. Defaults to C(false). + type: bool + default: false + filepath: + description: + - One or more files each containing one SQL query to run. + - Must be a string or list containing strings. + type: list + elements: path + query: + description: + - SQL query to run. + - Must be a string or list containing strings. Please note that if you supply a string, it will be split by commas (C(,)) to a list. + It is better to supply a one-element list instead to avoid mangled input. + type: list + elements: str +notes: + - Does not support C(check_mode). +author: + - Rainer Leber (@rainerleber) +''' + +EXAMPLES = r''' +- name: Simple select query + community.general.hana_query: + sid: "hdb" + instance: "01" + password: "Test123" + query: "select user_name from users" + +- name: Run several queries + community.general.hana_query: + sid: "hdb" + instance: "01" + password: "Test123" + query: + - "select user_name from users;" + - select * from SYSTEM; + host: "localhost" + autocommit: False + +- name: Run several queries from file + community.general.hana_query: + sid: "hdb" + instance: "01" + password: "Test123" + filepath: + - /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt + - /tmp/HANA.txt + host: "localhost" +''' + +RETURN = r''' +query_result: + description: List containing results of all queries executed (one sublist for every query). + returned: on success + type: list + elements: list + sample: [[{"Column": "Value1"}, {"Column": "Value2"}], [{"Column": "Value1"}, {"Column": "Value2"}]] +''' + +import csv +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import StringIO +from ansible.module_utils._text import to_native + + +def csv_to_list(rawcsv): + reader_raw = csv.DictReader(StringIO(rawcsv)) + reader = [dict((k, v.strip()) for k, v in row.items()) for row in reader_raw] + return list(reader) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + sid=dict(type='str', required=True), + instance=dict(type='str', required=True), + encrypted=dict(type='bool', required=False, default=False), + host=dict(type='str', required=False), + user=dict(type='str', required=False, default="SYSTEM"), + password=dict(type='str', required=True, no_log=True), + database=dict(type='str', required=False), + query=dict(type='list', elements='str', required=False), + filepath=dict(type='list', elements='path', required=False), + autocommit=dict(type='bool', required=False, default=True), + ), + required_one_of=[('query', 'filepath')], + supports_check_mode=False, + ) + rc, out, err, out_raw = [0, [], "", ""] + + params = module.params + + sid = (params['sid']).upper() + instance = params['instance'] + user = params['user'] + password = params['password'] + autocommit = params['autocommit'] + host = params['host'] + database = params['database'] + encrypted = params['encrypted'] + + filepath = params['filepath'] + query = params['query'] + + bin_path = "/usr/sap/{sid}/HDB{instance}/exe/hdbsql".format(sid=sid, instance=instance) + + try: + command = [module.get_bin_path(bin_path, required=True)] + except Exception as e: + module.fail_json(msg='Failed to find hdbsql at the expected path "{0}". Please check SID and instance number: "{1}"'.format(bin_path, to_native(e))) + + if encrypted is True: + command.extend(['-attemptencrypt']) + if autocommit is False: + command.extend(['-z']) + if host is not None: + command.extend(['-n', host]) + if database is not None: + command.extend(['-d', database]) + # -x Suppresses additional output, such as the number of selected rows in a result set. + command.extend(['-x', '-i', instance, '-u', user, '-p', password]) + + if filepath is not None: + command.extend(['-I']) + for p in filepath: + # makes a command like hdbsql -i 01 -u SYSTEM -p secret123# -I /tmp/HANA_CPU_UtilizationPerCore_2.00.020+.txt, + # iterates through files and append the output to var out. + query_command = command + [p] + (rc, out_raw, err) = module.run_command(query_command) + out.append(csv_to_list(out_raw)) + if query is not None: + for q in query: + # makes a command like hdbsql -i 01 -u SYSTEM -p secret123# "select user_name from users", + # iterates through multiple commands and append the output to var out. + query_command = command + [q] + (rc, out_raw, err) = module.run_command(query_command) + out.append(csv_to_list(out_raw)) + changed = True + + module.exit_json(changed=changed, rc=rc, query_result=out, stderr=err) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/discord.py b/plugins/modules/discord.py new file mode 120000 index 0000000000..1acf222f94 --- /dev/null +++ b/plugins/modules/discord.py @@ -0,0 +1 @@ +./notification/discord.py \ No newline at end of file diff --git a/plugins/modules/files/archive.py b/plugins/modules/files/archive.py index 8b8088dae1..8d4afa58a5 100644 --- a/plugins/modules/files/archive.py +++ b/plugins/modules/files/archive.py @@ -41,8 +41,16 @@ options: exclude_path: description: - Remote absolute path, glob, or list of paths or globs for the file or files to exclude from I(path) list and glob expansion. + - Use I(exclusion_patterns) to instead exclude files or subdirectories below any of the paths from the I(path) list. type: list elements: path + exclusion_patterns: + description: + - Glob style patterns to exclude files or directories from the resulting archive. + - This differs from I(exclude_path) which applies only to the source paths from I(path). + type: list + elements: path + version_added: 3.2.0 force_archive: description: - Allows you to force the module to treat this as an archive even if only a single file is specified. @@ -163,6 +171,8 @@ import re import shutil import tarfile import zipfile +from fnmatch import fnmatch +from sys import version_info from traceback import format_exc from ansible.module_utils.basic import AnsibleModule, missing_required_lib @@ -186,6 +196,8 @@ else: LZMA_IMP_ERR = format_exc() HAS_LZMA = False +PY27 = version_info[0:2] >= (2, 7) + def to_b(s): return to_bytes(s, errors='surrogate_or_strict') @@ -214,6 +226,59 @@ def expand_paths(paths): return expanded_path, is_globby +def matches_exclusion_patterns(path, exclusion_patterns): + return any(fnmatch(path, p) for p in exclusion_patterns) + + +def get_filter(exclusion_patterns, format): + def zip_filter(path): + return matches_exclusion_patterns(path, exclusion_patterns) + + def tar_filter(tarinfo): + return None if matches_exclusion_patterns(tarinfo.name, exclusion_patterns) else tarinfo + + return zip_filter if format == 'zip' or not PY27 else tar_filter + + +def get_archive_contains(format): + def archive_contains(archive, name): + try: + if format == 'zip': + archive.getinfo(name) + else: + archive.getmember(name) + except KeyError: + return False + + return True + + return archive_contains + + +def get_add_to_archive(format, filter): + def add_to_zip_archive(archive_file, path, archive_name): + try: + if not filter(path): + archive_file.write(path, archive_name) + except Exception as e: + return e + + return None + + def add_to_tar_archive(archive_file, path, archive_name): + try: + if PY27: + archive_file.add(path, archive_name, recursive=False, filter=filter) + else: + archive_file.add(path, archive_name, recursive=False, exclude=filter) + except Exception as e: + return e + + return None + + return add_to_zip_archive if format == 'zip' else add_to_tar_archive + + def main(): module = AnsibleModule( argument_spec=dict( @@ -221,6 +286,7 @@ def main(): format=dict(type='str', default='gz', choices=['bz2', 'gz', 'tar', 'xz', 'zip']), dest=dict(type='path'), exclude_path=dict(type='list', elements='path'), + exclusion_patterns=dict(type='list', elements='path'), force_archive=dict(type='bool', default=False), remove=dict(type='bool', default=False), ), @@ -242,6 +308,8 @@ def main(): changed = False state = 'absent' + exclusion_patterns = params['exclusion_patterns'] or [] + # Simple or archive file compression (inapplicable with 'zip' since it's always an archive) b_successes = [] @@ -262,6 +330,10 @@ def main(): # Only attempt to expand the exclude paths if it exists b_expanded_exclude_paths = expand_paths(exclude_paths)[0] if exclude_paths else [] + filter = get_filter(exclusion_patterns, fmt) + archive_contains = get_archive_contains(fmt) + add_to_archive = get_add_to_archive(fmt, filter) + # Only try to determine if we are working with an archive or not if we haven't set archive to true if not force_archive: # If we actually matched multiple files or TRIED to, then @@ -384,38 +456,31 @@ def main(): n_fullpath = to_na(b_fullpath) n_arcname = to_native(b_match_root.sub(b'', b_fullpath), errors='surrogate_or_strict') - try: - if fmt == 'zip': - arcfile.write(n_fullpath, n_arcname) - else: - arcfile.add(n_fullpath, n_arcname, recursive=False) - - except Exception as e: - errors.append('%s: %s' % (n_fullpath, to_native(e))) + err = add_to_archive(arcfile, n_fullpath, n_arcname) + if err: + errors.append('%s: %s' % (n_fullpath, to_native(err))) for b_filename in b_filenames: b_fullpath = b_dirpath + b_filename n_fullpath = to_na(b_fullpath) n_arcname = to_n(b_match_root.sub(b'', b_fullpath)) - try: - if fmt == 'zip': - arcfile.write(n_fullpath, n_arcname) - else: - arcfile.add(n_fullpath, n_arcname, recursive=False) + err = add_to_archive(arcfile, n_fullpath, n_arcname) + if err: + errors.append('Adding %s: %s' % (to_native(b_path), to_native(err))) + if archive_contains(arcfile, n_arcname): b_successes.append(b_fullpath) - except Exception as e: - errors.append('Adding %s: %s' % (to_native(b_path), to_native(e))) else: path = to_na(b_path) arcname = to_n(b_match_root.sub(b'', b_path)) - if fmt == 'zip': - arcfile.write(path, arcname) - else: - arcfile.add(path, arcname, recursive=False) - b_successes.append(b_path) + err = add_to_archive(arcfile, path, arcname) + if err: + errors.append('Adding %s: %s' % (to_native(b_path), to_native(err))) + + if archive_contains(arcfile, arcname): + b_successes.append(b_path) except Exception as e: expanded_fmt = 'zip' if fmt == 'zip' else ('tar.' + fmt) diff --git a/plugins/modules/files/ini_file.py b/plugins/modules/files/ini_file.py index ac4c6d0cf3..d318d04d57 100644 --- a/plugins/modules/files/ini_file.py +++ b/plugins/modules/files/ini_file.py @@ -79,6 +79,7 @@ options: notes: - While it is possible to add an I(option) without specifying a I(value), this makes no sense. - As of Ansible 2.3, the I(dest) option has been changed to I(path) as default, but I(dest) still works as well. + - As of community.general 3.2.0, UTF-8 BOM markers are discarded when reading files. author: - Jan-Piet Mens (@jpmens) - Ales Nosek (@noseka1) @@ -104,6 +105,7 @@ EXAMPLES = r''' backup: yes ''' +import io import os import re import tempfile @@ -141,7 +143,7 @@ def do_ini(module, filename, section=None, option=None, value=None, os.makedirs(destpath) ini_lines = [] else: - with open(filename, 'r') as ini_file: + with io.open(filename, 'r', encoding="utf-8-sig") as ini_file: ini_lines = ini_file.readlines() if module._diff: diff --git a/plugins/modules/files/sapcar_extract.py b/plugins/modules/files/sapcar_extract.py new file mode 100644 index 0000000000..db0f5f9ea8 --- /dev/null +++ b/plugins/modules/files/sapcar_extract.py @@ -0,0 +1,219 @@ +#!/usr/bin/python + +# Copyright: (c) 2021, Rainer Leber +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: sapcar_extract +short_description: Manages SAP SAPCAR archives +version_added: "3.2.0" +description: + - Provides support for unpacking C(sar)/C(car) files with the SAPCAR binary from SAP and pulling + information back into Ansible. +options: + path: + description: The path to the SAR/CAR file. + type: path + required: true + dest: + description: + - The destination where SAPCAR extracts the SAR file. Missing folders will be created. + If this parameter is not provided it will unpack in the same folder as the SAR file. + type: path + binary_path: + description: + - The path to the SAPCAR binary, for example, C(/home/dummy/sapcar) or C(https://myserver/SAPCAR). + If this parameter is not provided the module will look in C(PATH). + type: path + signature: + description: + - If C(true) the signature will be extracted. + default: false + type: bool + security_library: + description: + - The path to the security library, for example, C(/usr/sap/hostctrl/exe/libsapcrytp.so), for signature operations. + type: path + manifest: + description: + - The name of the manifest. + default: "SIGNATURE.SMF" + type: str + remove: + description: + - If C(true) the SAR/CAR file will be removed. B(This should be used with caution!) + default: false + type: bool +author: + - Rainer Leber (@RainerLeber) +notes: + - Always returns C(changed=true) in C(check_mode). +''' + +EXAMPLES = """ +- name: Extract SAR file + community.general.sapcar_extract: + path: "~/source/hana.sar" + +- name: Extract SAR file with destination + community.general.sapcar_extract: + path: "~/source/hana.sar" + dest: "~/test/" + +- name: Extract SAR file with destination and download from webserver can be a fileshare as well + community.general.sapcar_extract: + path: "~/source/hana.sar" + dest: "~/dest/" + binary_path: "https://myserver/SAPCAR" + +- name: Extract SAR file and delete SAR after extract + community.general.sapcar_extract: + path: "~/source/hana.sar" + remove: true + +- name: Extract SAR file with manifest + community.general.sapcar_extract: + path: "~/source/hana.sar" + signature: true + +- name: Extract SAR file with manifest and rename it + community.general.sapcar_extract: + path: "~/source/hana.sar" + manifest: "MyNewSignature.SMF" + signature: true +""" + +import os +from tempfile import NamedTemporaryFile +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils._text import to_native + + +def get_list_of_files(dir_name): + # create a list of file and directories + # names in the given directory + list_of_file = os.listdir(dir_name) + allFiles = list() + # Iterate over all the entries + for entry in list_of_file: + # Create full path + fullPath = os.path.join(dir_name, entry) + # If entry is a directory then get the list of files in this directory + if os.path.isdir(fullPath): + allFiles = allFiles + [fullPath] + allFiles = allFiles + get_list_of_files(fullPath) + else: + allFiles.append(fullPath) + return allFiles + + +def download_SAPCAR(binary_path, module): + bin_path = None + # download sapcar binary if url is provided otherwise path is returned + if binary_path is not None: + if binary_path.startswith('https://') or binary_path.startswith('http://'): + random_file = NamedTemporaryFile(delete=False) + with open_url(binary_path) as response: + with random_file as out_file: + data = response.read() + out_file.write(data) + os.chmod(out_file.name, 0o700) + bin_path = out_file.name + module.add_cleanup_file(bin_path) + else: + bin_path = binary_path + return bin_path + + +def check_if_present(command, path, dest, signature, manifest, module): + # manipuliating output from SAR file for compare with already extracted files + iter_command = [command, '-tvf', path] + sar_out = module.run_command(iter_command)[1] + sar_raw = sar_out.split("\n")[1:] + if dest[-1] != "/": + dest = dest + "/" + sar_files = [dest + x.split(" ")[-1] for x in sar_raw if x] + # remove any SIGNATURE.SMF from list because it will not unpacked if signature is false + if not signature: + sar_files = [item for item in sar_files if '.SMF' not in item] + # if signature is renamed manipulate files in list of sar file for compare. + if manifest != "SIGNATURE.SMF": + sar_files = [item for item in sar_files if '.SMF' not in item] + sar_files = sar_files + [manifest] + # get extracted files if present + files_extracted = get_list_of_files(dest) + # compare extracted files with files in sar file + present = all(elem in files_extracted for elem in sar_files) + return present + + +def main(): + module = AnsibleModule( + argument_spec=dict( + path=dict(type='path', required=True), + dest=dict(type='path'), + binary_path=dict(type='path'), + signature=dict(type='bool', default=False), + security_library=dict(type='path'), + manifest=dict(type='str', default="SIGNATURE.SMF"), + remove=dict(type='bool', default=False), + ), + supports_check_mode=True, + ) + rc, out, err = [0, "", ""] + params = module.params + check_mode = module.check_mode + + path = params['path'] + dest = params['dest'] + signature = params['signature'] + security_library = params['security_library'] + manifest = params['manifest'] + remove = params['remove'] + + bin_path = download_SAPCAR(params['binary_path'], module) + + if dest is None: + dest_head_tail = os.path.split(path) + dest = dest_head_tail[0] + '/' + else: + if not os.path.exists(dest): + os.makedirs(dest, 0o755) + + if bin_path is not None: + command = [module.get_bin_path(bin_path, required=True)] + else: + try: + command = [module.get_bin_path('sapcar', required=True)] + except Exception as e: + module.fail_json(msg='Failed to find SAPCAR at the expected path or URL "{0}". Please check whether it is available: {1}' + .format(bin_path, to_native(e))) + + present = check_if_present(command[0], path, dest, signature, manifest, module) + + if not present: + command.extend(['-xvf', path, '-R', dest]) + if security_library: + command.extend(['-L', security_library]) + if signature: + command.extend(['-manifest', manifest]) + if not check_mode: + (rc, out, err) = module.run_command(command, check_rc=True) + changed = True + else: + changed = False + out = "allready unpacked" + + if remove: + os.remove(path) + + module.exit_json(changed=changed, message=rc, stdout=out, + stderr=err, command=' '.join(command)) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/files/xml.py b/plugins/modules/files/xml.py index df3562df8c..e7c6ca3f1e 100644 --- a/plugins/modules/files/xml.py +++ b/plugins/modules/files/xml.py @@ -285,6 +285,39 @@ EXAMPLES = r''' z: http://z.test attribute: z:my_namespaced_attribute value: 'false' + +- name: Adding building nodes with floor subnodes from a YAML variable + community.general.xml: + path: /foo/bar.xml + xpath: /business + add_children: + - building: + # Attributes + name: Scumm bar + location: Monkey island + # Subnodes + _: + - floor: Pirate hall + - floor: Grog storage + - construction_date: "1990" # Only strings are valid + - building: Grog factory + +# Consider this XML for following example - +# +# +# +# part to remove +# +# +# part to keep +# +# + +- name: Delete element node based upon attribute + community.general.xml: + path: bar.xml + xpath: /config/element[@name='test1'] + state: absent ''' RETURN = r''' diff --git a/plugins/modules/hana_query.py b/plugins/modules/hana_query.py new file mode 120000 index 0000000000..ea869eb7a4 --- /dev/null +++ b/plugins/modules/hana_query.py @@ -0,0 +1 @@ +./database/saphana/hana_query.py \ No newline at end of file diff --git a/plugins/modules/identity/keycloak/keycloak_realm.py b/plugins/modules/identity/keycloak/keycloak_realm.py index 7e80bd3d3d..95f79704ef 100644 --- a/plugins/modules/identity/keycloak/keycloak_realm.py +++ b/plugins/modules/identity/keycloak/keycloak_realm.py @@ -439,9 +439,10 @@ options: ssl_required: description: - The realm ssl required option. + choices: ['all', 'external', 'none'] aliases: - sslRequired - type: bool + type: str sso_session_idle_timeout: description: - The realm sso session idle timeout. @@ -654,10 +655,10 @@ def main(): registration_flow=dict(type='str', aliases=['registrationFlow']), remember_me=dict(type='bool', aliases=['rememberMe']), reset_credentials_flow=dict(type='str', aliases=['resetCredentialsFlow']), - reset_password_allowed=dict(type='bool', aliases=['resetPasswordAllowed']), + reset_password_allowed=dict(type='bool', aliases=['resetPasswordAllowed'], no_log=False), revoke_refresh_token=dict(type='bool', aliases=['revokeRefreshToken']), smtp_server=dict(type='dict', aliases=['smtpServer']), - ssl_required=dict(type='bool', aliases=['sslRequired']), + ssl_required=dict(choices=["external", "all", "none"], aliases=['sslRequired']), sso_session_idle_timeout=dict(type='int', aliases=['ssoSessionIdleTimeout']), sso_session_idle_timeout_remember_me=dict(type='int', aliases=['ssoSessionIdleTimeoutRememberMe']), sso_session_max_lifespan=dict(type='int', aliases=['ssoSessionMaxLifespan']), diff --git a/plugins/modules/net_tools/haproxy.py b/plugins/modules/net_tools/haproxy.py index 8efb59ed2e..a3320b45c5 100644 --- a/plugins/modules/net_tools/haproxy.py +++ b/plugins/modules/net_tools/haproxy.py @@ -150,7 +150,7 @@ EXAMPLES = r''' backend: www wait: yes drain: yes - wait_interval: 1 + wait_interval: 60 wait_retries: 60 - name: Disable backend server in 'www' backend pool and drop open sessions to it diff --git a/plugins/modules/net_tools/netcup_dns.py b/plugins/modules/net_tools/netcup_dns.py index 5d63a5b38e..5ec5cbb246 100644 --- a/plugins/modules/net_tools/netcup_dns.py +++ b/plugins/modules/net_tools/netcup_dns.py @@ -255,7 +255,7 @@ def main(): has_changed = True except Exception as ex: - module.fail_json(msg=ex.message) + module.fail_json(msg=str(ex)) module.exit_json(changed=has_changed, result={"records": [record_data(r) for r in all_records]}) diff --git a/plugins/modules/net_tools/nios/nios_a_record.py b/plugins/modules/net_tools/nios/nios_a_record.py index 7e8b273024..b4adfe0103 100644 --- a/plugins/modules/net_tools/nios/nios_a_record.py +++ b/plugins/modules/net_tools/nios/nios_a_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_a_record author: "Blair Rampling (@brampling)" short_description: Configure Infoblox NIOS A records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_a_record + removed_in: 5.0.0 description: - Adds and/or removes instances of A record objects from Infoblox NIOS servers. This module manages NIOS C(record:a) objects diff --git a/plugins/modules/net_tools/nios/nios_aaaa_record.py b/plugins/modules/net_tools/nios/nios_aaaa_record.py index d35b779f10..9b22f86948 100644 --- a/plugins/modules/net_tools/nios/nios_aaaa_record.py +++ b/plugins/modules/net_tools/nios/nios_aaaa_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_aaaa_record author: "Blair Rampling (@brampling)" short_description: Configure Infoblox NIOS AAAA records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_aaaa_record + removed_in: 5.0.0 description: - Adds and/or removes instances of AAAA record objects from Infoblox NIOS servers. This module manages NIOS C(record:aaaa) objects diff --git a/plugins/modules/net_tools/nios/nios_cname_record.py b/plugins/modules/net_tools/nios/nios_cname_record.py index 2ab38473f3..099cb02572 100644 --- a/plugins/modules/net_tools/nios/nios_cname_record.py +++ b/plugins/modules/net_tools/nios/nios_cname_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_cname_record author: "Blair Rampling (@brampling)" short_description: Configure Infoblox NIOS CNAME records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_cname_record + removed_in: 5.0.0 description: - Adds and/or removes instances of CNAME record objects from Infoblox NIOS servers. This module manages NIOS C(record:cname) objects diff --git a/plugins/modules/net_tools/nios/nios_dns_view.py b/plugins/modules/net_tools/nios/nios_dns_view.py index af5d56d4ca..46c56fc7bb 100644 --- a/plugins/modules/net_tools/nios/nios_dns_view.py +++ b/plugins/modules/net_tools/nios/nios_dns_view.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_dns_view author: "Peter Sprygada (@privateip)" short_description: Configure Infoblox NIOS DNS views +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_dns_view + removed_in: 5.0.0 description: - Adds and/or removes instances of DNS view objects from Infoblox NIOS servers. This module manages NIOS C(view) objects diff --git a/plugins/modules/net_tools/nios/nios_fixed_address.py b/plugins/modules/net_tools/nios/nios_fixed_address.py index cab3b5e1b5..bc2969bbe5 100644 --- a/plugins/modules/net_tools/nios/nios_fixed_address.py +++ b/plugins/modules/net_tools/nios/nios_fixed_address.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_fixed_address author: "Sumit Jaiswal (@sjaiswal)" short_description: Configure Infoblox NIOS DHCP Fixed Address +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_fixed_address + removed_in: 5.0.0 description: - A fixed address is a specific IP address that a DHCP server always assigns when a lease request comes from a particular diff --git a/plugins/modules/net_tools/nios/nios_host_record.py b/plugins/modules/net_tools/nios/nios_host_record.py index d3e9d3de95..6fed663657 100644 --- a/plugins/modules/net_tools/nios/nios_host_record.py +++ b/plugins/modules/net_tools/nios/nios_host_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_host_record author: "Peter Sprygada (@privateip)" short_description: Configure Infoblox NIOS host records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_host_record + removed_in: 5.0.0 description: - Adds and/or removes instances of host record objects from Infoblox NIOS servers. This module manages NIOS C(record:host) objects diff --git a/plugins/modules/net_tools/nios/nios_member.py b/plugins/modules/net_tools/nios/nios_member.py index f8bf3e2595..186933864a 100644 --- a/plugins/modules/net_tools/nios/nios_member.py +++ b/plugins/modules/net_tools/nios/nios_member.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_member author: "Krishna Vasudevan (@krisvasudevan)" short_description: Configure Infoblox NIOS members +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_member + removed_in: 5.0.0 description: - Adds and/or removes Infoblox NIOS servers. This module manages NIOS C(member) objects using the Infoblox WAPI interface over REST. requirements: diff --git a/plugins/modules/net_tools/nios/nios_mx_record.py b/plugins/modules/net_tools/nios/nios_mx_record.py index a5c93b92bf..6e54ff2bda 100644 --- a/plugins/modules/net_tools/nios/nios_mx_record.py +++ b/plugins/modules/net_tools/nios/nios_mx_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_mx_record author: "Blair Rampling (@brampling)" short_description: Configure Infoblox NIOS MX records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_mx_record + removed_in: 5.0.0 description: - Adds and/or removes instances of MX record objects from Infoblox NIOS servers. This module manages NIOS C(record:mx) objects diff --git a/plugins/modules/net_tools/nios/nios_naptr_record.py b/plugins/modules/net_tools/nios/nios_naptr_record.py index 387dd1dd98..f943d3d6d9 100644 --- a/plugins/modules/net_tools/nios/nios_naptr_record.py +++ b/plugins/modules/net_tools/nios/nios_naptr_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_naptr_record author: "Blair Rampling (@brampling)" short_description: Configure Infoblox NIOS NAPTR records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_naptr_record + removed_in: 5.0.0 description: - Adds and/or removes instances of NAPTR record objects from Infoblox NIOS servers. This module manages NIOS C(record:naptr) objects diff --git a/plugins/modules/net_tools/nios/nios_network.py b/plugins/modules/net_tools/nios/nios_network.py index 98d06a2ede..6a7decb894 100644 --- a/plugins/modules/net_tools/nios/nios_network.py +++ b/plugins/modules/net_tools/nios/nios_network.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_network author: "Peter Sprygada (@privateip)" short_description: Configure Infoblox NIOS network object +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_network + removed_in: 5.0.0 description: - Adds and/or removes instances of network objects from Infoblox NIOS servers. This module manages NIOS C(network) objects diff --git a/plugins/modules/net_tools/nios/nios_network_view.py b/plugins/modules/net_tools/nios/nios_network_view.py index c8925adcfb..a27f8519a0 100644 --- a/plugins/modules/net_tools/nios/nios_network_view.py +++ b/plugins/modules/net_tools/nios/nios_network_view.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_network_view author: "Peter Sprygada (@privateip)" short_description: Configure Infoblox NIOS network views +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_network_view + removed_in: 5.0.0 description: - Adds and/or removes instances of network view objects from Infoblox NIOS servers. This module manages NIOS C(networkview) objects diff --git a/plugins/modules/net_tools/nios/nios_nsgroup.py b/plugins/modules/net_tools/nios/nios_nsgroup.py index b56c3f0b8d..8e8cde399c 100644 --- a/plugins/modules/net_tools/nios/nios_nsgroup.py +++ b/plugins/modules/net_tools/nios/nios_nsgroup.py @@ -11,6 +11,10 @@ DOCUMENTATION = ''' --- module: nios_nsgroup short_description: Configure InfoBlox DNS Nameserver Groups +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_nsgroup + removed_in: 5.0.0 extends_documentation_fragment: - community.general.nios diff --git a/plugins/modules/net_tools/nios/nios_ptr_record.py b/plugins/modules/net_tools/nios/nios_ptr_record.py index 04c1370920..22550f129a 100644 --- a/plugins/modules/net_tools/nios/nios_ptr_record.py +++ b/plugins/modules/net_tools/nios/nios_ptr_record.py @@ -11,6 +11,10 @@ DOCUMENTATION = ''' module: nios_ptr_record author: "Trebuchet Clement (@clementtrebuchet)" short_description: Configure Infoblox NIOS PTR records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_ptr_record + removed_in: 5.0.0 description: - Adds and/or removes instances of PTR record objects from Infoblox NIOS servers. This module manages NIOS C(record:ptr) objects diff --git a/plugins/modules/net_tools/nios/nios_srv_record.py b/plugins/modules/net_tools/nios/nios_srv_record.py index 8a12aa7fd3..574a5fcf8b 100644 --- a/plugins/modules/net_tools/nios/nios_srv_record.py +++ b/plugins/modules/net_tools/nios/nios_srv_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_srv_record author: "Blair Rampling (@brampling)" short_description: Configure Infoblox NIOS SRV records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_srv_record + removed_in: 5.0.0 description: - Adds and/or removes instances of SRV record objects from Infoblox NIOS servers. This module manages NIOS C(record:srv) objects diff --git a/plugins/modules/net_tools/nios/nios_txt_record.py b/plugins/modules/net_tools/nios/nios_txt_record.py index 761a895052..b3267af41f 100644 --- a/plugins/modules/net_tools/nios/nios_txt_record.py +++ b/plugins/modules/net_tools/nios/nios_txt_record.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_txt_record author: "Corey Wanless (@coreywan)" short_description: Configure Infoblox NIOS txt records +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_txt_record + removed_in: 5.0.0 description: - Adds and/or removes instances of txt record objects from Infoblox NIOS servers. This module manages NIOS C(record:txt) objects diff --git a/plugins/modules/net_tools/nios/nios_zone.py b/plugins/modules/net_tools/nios/nios_zone.py index 3c59aab298..f97098351b 100644 --- a/plugins/modules/net_tools/nios/nios_zone.py +++ b/plugins/modules/net_tools/nios/nios_zone.py @@ -10,6 +10,10 @@ DOCUMENTATION = ''' module: nios_zone author: "Peter Sprygada (@privateip)" short_description: Configure Infoblox NIOS DNS zones +deprecated: + why: Please install the infoblox.nios_modules collection and use the corresponding module from it. + alternative: infoblox.nios_modules.nios_zone + removed_in: 5.0.0 description: - Adds and/or removes instances of DNS zone objects from Infoblox NIOS servers. This module manages NIOS C(zone_auth) objects diff --git a/plugins/modules/net_tools/nmcli.py b/plugins/modules/net_tools/nmcli.py index 4ae5a1dac9..399d15267a 100644 --- a/plugins/modules/net_tools/nmcli.py +++ b/plugins/modules/net_tools/nmcli.py @@ -77,6 +77,12 @@ options: - Use the format C(192.0.2.1). - This parameter is mutually_exclusive with never_default4 parameter. type: str + gw4_ignore_auto: + description: + - Ignore automatically configured IPv4 routes. + type: bool + default: false + version_added: 3.2.0 routes4: description: - The list of ipv4 routes. @@ -107,6 +113,12 @@ options: - A list of DNS search domains. elements: str type: list + dns4_ignore_auto: + description: + - Ignore automatically configured IPv4 name servers. + type: bool + default: false + version_added: 3.2.0 method4: description: - Configuration method to be used for IPv4. @@ -125,6 +137,12 @@ options: - The IPv6 gateway for this interface. - Use the format C(2001:db8::1). type: str + gw6_ignore_auto: + description: + - Ignore automatically configured IPv6 routes. + type: bool + default: false + version_added: 3.2.0 dns6: description: - A list of up to 3 dns servers. @@ -136,6 +154,12 @@ options: - A list of DNS search domains. elements: str type: list + dns6_ignore_auto: + description: + - Ignore automatically configured IPv6 name servers. + type: bool + default: false + version_added: 3.2.0 method6: description: - Configuration method to be used for IPv6 @@ -648,16 +672,20 @@ class Nmcli(object): self.type = module.params['type'] self.ip4 = module.params['ip4'] self.gw4 = module.params['gw4'] + self.gw4_ignore_auto = module.params['gw4_ignore_auto'] self.routes4 = module.params['routes4'] self.route_metric4 = module.params['route_metric4'] self.never_default4 = module.params['never_default4'] self.dns4 = module.params['dns4'] self.dns4_search = module.params['dns4_search'] + self.dns4_ignore_auto = module.params['dns4_ignore_auto'] self.method4 = module.params['method4'] self.ip6 = module.params['ip6'] self.gw6 = module.params['gw6'] + self.gw6_ignore_auto = module.params['gw6_ignore_auto'] self.dns6 = module.params['dns6'] self.dns6_search = module.params['dns6_search'] + self.dns6_ignore_auto = module.params['dns6_ignore_auto'] self.method6 = module.params['method6'] self.mtu = module.params['mtu'] self.stp = module.params['stp'] @@ -729,7 +757,9 @@ class Nmcli(object): 'ipv4.dhcp-client-id': self.dhcp_client_id, 'ipv4.dns': self.dns4, 'ipv4.dns-search': self.dns4_search, + 'ipv4.ignore-auto-dns': self.dns4_ignore_auto, 'ipv4.gateway': self.gw4, + 'ipv4.ignore-auto-routes': self.gw4_ignore_auto, 'ipv4.routes': self.routes4, 'ipv4.route-metric': self.route_metric4, 'ipv4.never-default': self.never_default4, @@ -737,7 +767,9 @@ class Nmcli(object): 'ipv6.addresses': self.ip6, 'ipv6.dns': self.dns6, 'ipv6.dns-search': self.dns6_search, + 'ipv6.ignore-auto-dns': self.dns6_ignore_auto, 'ipv6.gateway': self.gw6, + 'ipv6.ignore-auto-routes': self.gw6_ignore_auto, 'ipv6.method': self.ipv6_method, }) @@ -780,6 +812,7 @@ class Nmcli(object): }) elif self.type == 'bridge-slave': options.update({ + 'connection.slave-type': 'bridge', 'bridge-port.path-cost': self.path_cost, 'bridge-port.hairpin-mode': self.hairpin, 'bridge-port.priority': self.slavepriority, @@ -899,7 +932,11 @@ class Nmcli(object): if setting in ('bridge.stp', 'bridge-port.hairpin-mode', 'connection.autoconnect', - 'ipv4.never-default'): + 'ipv4.never-default', + 'ipv4.ignore-auto-dns', + 'ipv4.ignore-auto-routes', + 'ipv6.ignore-auto-dns', + 'ipv6.ignore-auto-routes'): return bool elif setting in ('ipv4.dns', 'ipv4.dns-search', @@ -1035,18 +1072,6 @@ class Nmcli(object): return conn_info def _compare_conn_params(self, conn_info, options): - # See nmcli(1) for details - param_alias = { - 'type': 'connection.type', - 'con-name': 'connection.id', - 'autoconnect': 'connection.autoconnect', - 'ifname': 'connection.interface-name', - 'mac': self.mac_setting, - 'master': 'connection.master', - 'slave-type': 'connection.slave-type', - 'zone': 'connection.zone', - } - changed = False diff_before = dict() diff_after = dict() @@ -1065,13 +1090,11 @@ class Nmcli(object): current_value = [re.sub(r'^{\s*ip\s*=\s*([^, ]+),\s*nh\s*=\s*([^} ]+),\s*mt\s*=\s*([^} ]+)\s*}', r'\1 \2 \3', route) for route in current_value] current_value = [re.sub(r'^{\s*ip\s*=\s*([^, ]+),\s*nh\s*=\s*([^} ]+)\s*}', r'\1 \2', route) for route in current_value] - elif key in param_alias: - real_key = param_alias[key] - if real_key in conn_info: - current_value = conn_info[real_key] - else: - # alias parameter does not exist - current_value = None + if key == self.mac_setting: + # MAC addresses are case insensitive, nmcli always reports them in uppercase + value = value.upper() + # ensure current_value is also converted to uppercase in case nmcli changes behaviour + current_value = current_value.upper() else: # parameter does not exist current_value = None @@ -1129,17 +1152,21 @@ def main(): ]), ip4=dict(type='str'), gw4=dict(type='str'), + gw4_ignore_auto=dict(type='bool', default=False), routes4=dict(type='list', elements='str'), route_metric4=dict(type='int'), never_default4=dict(type='bool', default=False), dns4=dict(type='list', elements='str'), dns4_search=dict(type='list', elements='str'), + dns4_ignore_auto=dict(type='bool', default=False), method4=dict(type='str', choices=['auto', 'link-local', 'manual', 'shared', 'disabled']), dhcp_client_id=dict(type='str'), ip6=dict(type='str'), gw6=dict(type='str'), + gw6_ignore_auto=dict(type='bool', default=False), dns6=dict(type='list', elements='str'), dns6_search=dict(type='list', elements='str'), + dns6_ignore_auto=dict(type='bool', default=False), method6=dict(type='str', choices=['ignore', 'auto', 'dhcp', 'link-local', 'manual', 'shared']), # Bond Specific vars mode=dict(type='str', default='balance-rr', diff --git a/plugins/modules/net_tools/pritunl/__init__.py b/plugins/modules/net_tools/pritunl/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/modules/notification/discord.py b/plugins/modules/notification/discord.py new file mode 100644 index 0000000000..27dc6fc85c --- /dev/null +++ b/plugins/modules/notification/discord.py @@ -0,0 +1,215 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Christian Wollinger +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: discord +short_description: Send Discord messages +version_added: 3.1.0 +description: + - Sends a message to a Discord channel using the Discord webhook API. +author: Christian Wollinger (@cwollinger) +seealso: + - name: API documentation + description: Documentation for Discord API + link: https://discord.com/developers/docs/resources/webhook#execute-webhook +options: + webhook_id: + description: + - The webhook ID. + - "Format from Discord webhook URL: C(/webhooks/{webhook.id}/{webhook.token})." + required: yes + type: str + webhook_token: + description: + - The webhook token. + - "Format from Discord webhook URL: C(/webhooks/{webhook.id}/{webhook.token})." + required: yes + type: str + content: + description: + - Content of the message to the Discord channel. + - At least one of I(content) and I(embeds) must be specified. + type: str + username: + description: + - Overrides the default username of the webhook. + type: str + avatar_url: + description: + - Overrides the default avatar of the webhook. + type: str + tts: + description: + - Set this to C(true) if this is a TTS (Text to Speech) message. + type: bool + default: false + embeds: + description: + - Send messages as Embeds to the Discord channel. + - Embeds can have a colored border, embedded images, text fields and more. + - "Allowed parameters are described in the Discord Docs: U(https://discord.com/developers/docs/resources/channel#embed-object)" + - At least one of I(content) and I(embeds) must be specified. + type: list + elements: dict +''' + +EXAMPLES = """ +- name: Send a message to the Discord channel + community.general.discord: + webhook_id: "00000" + webhook_token: "XXXYYY" + content: "This is a message from ansible" + +- name: Send a message to the Discord channel with specific username and avatar + community.general.discord: + webhook_id: "00000" + webhook_token: "XXXYYY" + content: "This is a message from ansible" + username: Ansible + avatar_url: "https://docs.ansible.com/ansible/latest/_static/images/logo_invert.png" + +- name: Send a embedded message to the Discord channel + community.general.discord: + webhook_id: "00000" + webhook_token: "XXXYYY" + embeds: + - title: "Embedded message" + description: "This is an embedded message" + footer: + text: "Author: Ansible" + image: + url: "https://docs.ansible.com/ansible/latest/_static/images/logo_invert.png" + +- name: Send two embedded messages + community.general.discord: + webhook_id: "00000" + webhook_token: "XXXYYY" + embeds: + - title: "First message" + description: "This is my first embedded message" + footer: + text: "Author: Ansible" + image: + url: "https://docs.ansible.com/ansible/latest/_static/images/logo_invert.png" + - title: "Second message" + description: "This is my first second message" + footer: + text: "Author: Ansible" + icon_url: "https://docs.ansible.com/ansible/latest/_static/images/logo_invert.png" + fields: + - name: "Field 1" + value: "Value of my first field" + - name: "Field 2" + value: "Value of my second field" + timestamp: "{{ ansible_date_time.iso8601 }}" +""" + +RETURN = """ +http_code: + description: + - Response Code returned by Discord API. + returned: always + type: int + sample: 204 +""" + +from ansible.module_utils.urls import fetch_url +from ansible.module_utils.basic import AnsibleModule + + +def discord_check_mode(module): + + webhook_id = module.params['webhook_id'] + webhook_token = module.params['webhook_token'] + + headers = { + 'content-type': 'application/json' + } + + url = "https://discord.com/api/webhooks/%s/%s" % ( + webhook_id, webhook_token) + + response, info = fetch_url(module, url, method='GET', headers=headers) + return response, info + + +def discord_text_msg(module): + + webhook_id = module.params['webhook_id'] + webhook_token = module.params['webhook_token'] + content = module.params['content'] + user = module.params['username'] + avatar_url = module.params['avatar_url'] + tts = module.params['tts'] + embeds = module.params['embeds'] + + headers = { + 'content-type': 'application/json' + } + + url = "https://discord.com/api/webhooks/%s/%s" % ( + webhook_id, webhook_token) + + payload = { + 'content': content, + 'username': user, + 'avatar_url': avatar_url, + 'tts': tts, + 'embeds': embeds, + } + + payload = module.jsonify(payload) + + response, info = fetch_url(module, url, data=payload, headers=headers, method='POST') + return response, info + + +def main(): + module = AnsibleModule( + argument_spec=dict( + webhook_id=dict(type='str', required=True), + webhook_token=dict(type='str', required=True, no_log=True), + content=dict(type='str'), + username=dict(type='str'), + avatar_url=dict(type='str'), + tts=dict(type='bool', default=False), + embeds=dict(type='list', elements='dict'), + ), + required_one_of=[['content', 'embeds']], + supports_check_mode=True + ) + + result = dict( + changed=False, + http_code='', + ) + + if module.check_mode: + response, info = discord_check_mode(module) + if info['status'] != 200: + try: + module.fail_json(http_code=info['status'], msg=info['msg'], response=module.from_json(info['body']), info=info) + except Exception: + module.fail_json(http_code=info['status'], msg=info['msg'], info=info) + else: + module.exit_json(msg=info['msg'], changed=False, http_code=info['status'], response=module.from_json(response.read())) + else: + response, info = discord_text_msg(module) + if info['status'] != 204: + try: + module.fail_json(http_code=info['status'], msg=info['msg'], response=module.from_json(info['body']), info=info) + except Exception: + module.fail_json(http_code=info['status'], msg=info['msg'], info=info) + else: + module.exit_json(msg=info['msg'], changed=True, http_code=info['status']) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/packaging/language/composer.py b/plugins/modules/packaging/language/composer.py index c792098b04..86fe7bdea3 100644 --- a/plugins/modules/packaging/language/composer.py +++ b/plugins/modules/packaging/language/composer.py @@ -117,9 +117,14 @@ options: default: false type: bool aliases: [ ignore-platform-reqs ] + composer_executable: + type: path + description: + - Path to composer executable on the remote host, if composer is not in C(PATH) or a custom composer is needed. + version_added: 3.2.0 requirements: - php - - composer installed in bin path (recommended /usr/local/bin) + - composer installed in bin path (recommended /usr/local/bin) or specified in I(composer_executable) notes: - Default options that are always appended in each execution are --no-ansi, --no-interaction and --no-progress if available. - We received reports about issues on macOS if composer was installed by Homebrew. Please use the official install method to avoid issues. @@ -169,7 +174,7 @@ def has_changed(string): def get_available_options(module, command='install'): # get all available options from a composer command using composer help to json - rc, out, err = composer_command(module, "help %s --format=json" % command) + rc, out, err = composer_command(module, "help %s" % command, arguments="--no-interaction --format=json") if rc != 0: output = parse_out(err) module.fail_json(msg=output) @@ -187,7 +192,11 @@ def composer_command(module, command, arguments="", options=None, global_command else: php_path = module.params['executable'] - composer_path = module.get_bin_path("composer", True, ["/usr/local/bin"]) + if module.params['composer_executable'] is None: + composer_path = module.get_bin_path("composer", True, ["/usr/local/bin"]) + else: + composer_path = module.params['composer_executable'] + cmd = "%s %s %s %s %s %s" % (php_path, composer_path, "global" if global_command else "", command, " ".join(options), arguments) return module.run_command(cmd) @@ -231,6 +240,7 @@ def main(): ignore_platform_reqs=dict( default=False, type="bool", aliases=["ignore-platform-reqs"], deprecated_aliases=[dict(name='ignore-platform-reqs', version='5.0.0', collection_name='community.general')]), + composer_executable=dict(type="path"), ), required_if=[('global_command', False, ['working_dir'])], supports_check_mode=True diff --git a/plugins/modules/packaging/language/maven_artifact.py b/plugins/modules/packaging/language/maven_artifact.py index 50b808f57a..83833b0480 100644 --- a/plugins/modules/packaging/language/maven_artifact.py +++ b/plugins/modules/packaging/language/maven_artifact.py @@ -129,10 +129,10 @@ options: verify_checksum: type: str description: - - If C(never), the md5 checksum will never be downloaded and verified. - - If C(download), the md5 checksum will be downloaded and verified only after artifact download. This is the default. - - If C(change), the md5 checksum will be downloaded and verified if the destination already exist, - to verify if they are identical. This was the behaviour before 2.6. Since it downloads the md5 before (maybe) + - If C(never), the MD5/SHA1 checksum will never be downloaded and verified. + - If C(download), the MD5/SHA1 checksum will be downloaded and verified only after artifact download. This is the default. + - If C(change), the MD5/SHA1 checksum will be downloaded and verified if the destination already exist, + to verify if they are identical. This was the behaviour before 2.6. Since it downloads the checksum before (maybe) downloading the artifact, and since some repository software, when acting as a proxy/cache, return a 404 error if the artifact has not been cached yet, it may fail unexpectedly. If you still need it, you should consider using C(always) instead - if you deal with a checksum, it is better to @@ -141,6 +141,15 @@ options: required: false default: 'download' choices: ['never', 'download', 'change', 'always'] + checksum_alg: + type: str + description: + - If C(md5), checksums will use the MD5 algorithm. This is the default. + - If C(sha1), checksums will use the SHA1 algorithm. This can be used on systems configured to use + FIPS-compliant algorithms, since MD5 will be blocked on such systems. + default: 'md5' + choices: ['md5', 'sha1'] + version_added: 3.2.0 directory_mode: type: str description: @@ -507,7 +516,7 @@ class MavenDownloader: raise ValueError(failmsg + " because of " + info['msg'] + "for URL " + url_to_use) return None - def download(self, tmpdir, artifact, verify_download, filename=None): + def download(self, tmpdir, artifact, verify_download, filename=None, checksum_alg='md5'): if (not artifact.version and not artifact.version_by_spec) or artifact.version == "latest": artifact = Artifact(artifact.group_id, artifact.artifact_id, self.find_latest_version_available(artifact), None, artifact.classifier, artifact.extension) @@ -528,11 +537,11 @@ class MavenDownloader: shutil.copyfileobj(response, f) if verify_download: - invalid_md5 = self.is_invalid_md5(tempname, url) - if invalid_md5: + invalid_checksum = self.is_invalid_checksum(tempname, url, checksum_alg) + if invalid_checksum: # if verify_change was set, the previous file would be deleted os.remove(tempname) - return invalid_md5 + return invalid_checksum except Exception as e: os.remove(tempname) raise e @@ -541,40 +550,45 @@ class MavenDownloader: shutil.move(tempname, artifact.get_filename(filename)) return None - def is_invalid_md5(self, file, remote_url): + def is_invalid_checksum(self, file, remote_url, checksum_alg='md5'): if os.path.exists(file): - local_md5 = self._local_md5(file) + local_checksum = self._local_checksum(checksum_alg, file) if self.local: parsed_url = urlparse(remote_url) - remote_md5 = self._local_md5(parsed_url.path) + remote_checksum = self._local_checksum(checksum_alg, parsed_url.path) else: try: - remote_md5 = to_text(self._getContent(remote_url + '.md5', "Failed to retrieve MD5", False), errors='strict') + remote_checksum = to_text(self._getContent(remote_url + '.' + checksum_alg, "Failed to retrieve checksum", False), errors='strict') except UnicodeError as e: - return "Cannot retrieve a valid md5 from %s: %s" % (remote_url, to_native(e)) - if(not remote_md5): - return "Cannot find md5 from " + remote_url + return "Cannot retrieve a valid %s checksum from %s: %s" % (checksum_alg, remote_url, to_native(e)) + if not remote_checksum: + return "Cannot find %s checksum from %s" % (checksum_alg, remote_url) try: - # Check if remote md5 only contains md5 or md5 + filename - _remote_md5 = remote_md5.split(None)[0] - remote_md5 = _remote_md5 - # remote_md5 is empty so we continue and keep original md5 string - # This should not happen since we check for remote_md5 before + # Check if remote checksum only contains md5/sha1 or md5/sha1 + filename + _remote_checksum = remote_checksum.split(None)[0] + remote_checksum = _remote_checksum + # remote_checksum is empty so we continue and keep original checksum string + # This should not happen since we check for remote_checksum before except IndexError: pass - if local_md5.lower() == remote_md5.lower(): + if local_checksum.lower() == remote_checksum.lower(): return None else: - return "Checksum does not match: we computed " + local_md5 + " but the repository states " + remote_md5 + return "Checksum does not match: we computed " + local_checksum + " but the repository states " + remote_checksum return "Path does not exist: " + file - def _local_md5(self, file): - md5 = hashlib.md5() + def _local_checksum(self, checksum_alg, file): + if checksum_alg.lower() == 'md5': + hash = hashlib.md5() + elif checksum_alg.lower() == 'sha1': + hash = hashlib.sha1() + else: + raise ValueError("Unknown checksum_alg %s" % checksum_alg) with io.open(file, 'rb') as f: for chunk in iter(lambda: f.read(8192), b''): - md5.update(chunk) - return md5.hexdigest() + hash.update(chunk) + return hash.hexdigest() def main(): @@ -599,6 +613,7 @@ def main(): client_key=dict(type="path", required=False), keep_name=dict(required=False, default=False, type='bool'), verify_checksum=dict(required=False, default='download', choices=['never', 'download', 'change', 'always']), + checksum_alg=dict(required=False, default='md5', choices=['md5', 'sha1']), directory_mode=dict(type='str'), ), add_file_common_args=True, @@ -639,6 +654,7 @@ def main(): verify_checksum = module.params["verify_checksum"] verify_download = verify_checksum in ['download', 'always'] verify_change = verify_checksum in ['change', 'always'] + checksum_alg = module.params["checksum_alg"] downloader = MavenDownloader(module, repository_url, local, headers) @@ -683,12 +699,12 @@ def main(): b_dest = to_bytes(dest, errors='surrogate_or_strict') - if os.path.lexists(b_dest) and ((not verify_change) or not downloader.is_invalid_md5(dest, downloader.find_uri_for_artifact(artifact))): + if os.path.lexists(b_dest) and ((not verify_change) or not downloader.is_invalid_checksum(dest, downloader.find_uri_for_artifact(artifact), checksum_alg)): prev_state = "present" if prev_state == "absent": try: - download_error = downloader.download(module.tmpdir, artifact, verify_download, b_dest) + download_error = downloader.download(module.tmpdir, artifact, verify_download, b_dest, checksum_alg) if download_error is None: changed = True else: diff --git a/plugins/modules/packaging/os/flatpak.py b/plugins/modules/packaging/os/flatpak.py index 1be1a72243..4a9e214fde 100644 --- a/plugins/modules/packaging/os/flatpak.py +++ b/plugins/modules/packaging/os/flatpak.py @@ -6,27 +6,6 @@ # Copyright: (c) 2017 Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# ATTENTION CONTRIBUTORS! -# -# TL;DR: Run this module's integration tests manually before opening a pull request -# -# Long explanation: -# The integration tests for this module are currently NOT run on the Ansible project's continuous -# delivery pipeline. So please: When you make changes to this module, make sure that you run the -# included integration tests manually for both Python 2 and Python 3: -# -# Python 2: -# ansible-test integration -v --docker fedora28 --docker-privileged --allow-unsupported --python 2.7 flatpak -# Python 3: -# ansible-test integration -v --docker fedora28 --docker-privileged --allow-unsupported --python 3.6 flatpak -# -# Because of external dependencies, the current integration tests are somewhat too slow and brittle -# to be included right now. I have plans to rewrite the integration tests based on a local flatpak -# repository so that they can be included into the normal CI pipeline. -# //oolongbrothers - - from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -60,18 +39,28 @@ options: name: description: - The name of the flatpak to manage. - - When used with I(state=present), I(name) can be specified as an C(http(s)) URL to a + - When used with I(state=present), I(name) can be specified as a URL to a C(flatpakref) file or the unique reverse DNS name that identifies a flatpak. + - Both C(https://) and C(http://) URLs are supported. - When supplying a reverse DNS name, you can use the I(remote) option to specify on what remote to look for the flatpak. An example for a reverse DNS name is C(org.gnome.gedit). - When used with I(state=absent), it is recommended to specify the name in the reverse DNS format. - - When supplying an C(http(s)) URL with I(state=absent), the module will try to match the + - When supplying a URL with I(state=absent), the module will try to match the installed flatpak based on the name of the flatpakref to remove it. However, there is no guarantee that the names of the flatpakref file and the reverse DNS name of the installed flatpak do match. type: str required: true + no_dependencies: + description: + - If installing runtime dependencies should be omitted or not + - This parameter is primarily implemented for integration testing this module. + There might however be some use cases where you would want to have this, like when you are + packaging your own flatpaks. + type: bool + default: false + version_added: 3.2.0 remote: description: - The flatpak remote (repository) to install the flatpak from. @@ -94,10 +83,11 @@ EXAMPLES = r''' name: https://s3.amazonaws.com/alexlarsson/spotify-repo/spotify.flatpakref state: present -- name: Install the gedit flatpak package +- name: Install the gedit flatpak package without dependencies (not recommended) community.general.flatpak: name: https://git.gnome.org/browse/gnome-apps-nightly/plain/gedit.flatpakref state: present + no_dependencies: true - name: Install the gedit package from flathub for current user community.general.flatpak: @@ -153,18 +143,21 @@ from ansible.module_utils.basic import AnsibleModule OUTDATED_FLATPAK_VERSION_ERROR_MESSAGE = "Unknown option --columns=application" -def install_flat(module, binary, remote, name, method): +def install_flat(module, binary, remote, name, method, no_dependencies): """Add a new flatpak.""" global result flatpak_version = _flatpak_version(module, binary) + command = [binary, "install", "--{0}".format(method)] if StrictVersion(flatpak_version) < StrictVersion('1.1.3'): - noninteractive_arg = "-y" + command += ["-y"] else: - noninteractive_arg = "--noninteractive" + command += ["--noninteractive"] + if no_dependencies: + command += ["--no-deps"] if name.startswith('http://') or name.startswith('https://'): - command = [binary, "install", "--{0}".format(method), noninteractive_arg, name] + command += [name] else: - command = [binary, "install", "--{0}".format(method), noninteractive_arg, remote, name] + command += [remote, name] _flatpak_command(module, module.check_mode, command) result['changed'] = True @@ -279,6 +272,7 @@ def main(): choices=['user', 'system']), state=dict(type='str', default='present', choices=['absent', 'present']), + no_dependencies=dict(type='bool', default=False), executable=dict(type='path', default='flatpak') ), supports_check_mode=True, @@ -287,6 +281,7 @@ def main(): name = module.params['name'] state = module.params['state'] remote = module.params['remote'] + no_dependencies = module.params['no_dependencies'] method = module.params['method'] executable = module.params['executable'] binary = module.get_bin_path(executable, None) @@ -301,7 +296,7 @@ def main(): module.fail_json(msg="Executable '%s' was not found on the system." % executable, **result) if state == 'present' and not flatpak_exists(module, binary, name, method): - install_flat(module, binary, remote, name, method) + install_flat(module, binary, remote, name, method, no_dependencies) elif state == 'absent' and flatpak_exists(module, binary, name, method): uninstall_flat(module, binary, name, method) diff --git a/plugins/modules/packaging/os/flatpak_remote.py b/plugins/modules/packaging/os/flatpak_remote.py index dbb211c2fb..a7767621d7 100644 --- a/plugins/modules/packaging/os/flatpak_remote.py +++ b/plugins/modules/packaging/os/flatpak_remote.py @@ -6,27 +6,6 @@ # Copyright: (c) 2017 Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# ATTENTION CONTRIBUTORS! -# -# TL;DR: Run this module's integration tests manually before opening a pull request -# -# Long explanation: -# The integration tests for this module are currently NOT run on the Ansible project's continuous -# delivery pipeline. So please: When you make changes to this module, make sure that you run the -# included integration tests manually for both Python 2 and Python 3: -# -# Python 2: -# ansible-test integration -v --docker fedora28 --docker-privileged --allow-unsupported --python 2.7 flatpak_remote -# Python 3: -# ansible-test integration -v --docker fedora28 --docker-privileged --allow-unsupported --python 3.6 flatpak_remote -# -# Because of external dependencies, the current integration tests are somewhat too slow and brittle -# to be included right now. I have plans to rewrite the integration tests based on a local flatpak -# repository so that they can be included into the normal CI pipeline. -# //oolongbrothers - - from __future__ import (absolute_import, division, print_function) __metaclass__ = type diff --git a/plugins/modules/packaging/os/homebrew.py b/plugins/modules/packaging/os/homebrew.py index 9a41370c3d..47ec930a2c 100644 --- a/plugins/modules/packaging/os/homebrew.py +++ b/plugins/modules/packaging/os/homebrew.py @@ -127,6 +127,11 @@ EXAMPLES = ''' state: present install_options: with-baz,enable-debug +- name: Install formula foo with 'brew' from cask + community.general.homebrew: + name: homebrew/cask/foo + state: present + - name: Use ignored-pinned option while upgrading all community.general.homebrew: upgrade_all: yes diff --git a/plugins/modules/packaging/os/pacman.py b/plugins/modules/packaging/os/pacman.py index b19528ba9e..859c90a6c4 100644 --- a/plugins/modules/packaging/os/pacman.py +++ b/plugins/modules/packaging/os/pacman.py @@ -44,6 +44,14 @@ options: default: no type: bool + executable: + description: + - Name of binary to use. This can either be C(pacman) or a pacman compatible AUR helper. + - Beware that AUR helpers might behave unexpectedly and are therefore not recommended. + default: pacman + type: str + version_added: 3.1.0 + extra_args: description: - Additional option to pass to pacman when enforcing C(state). @@ -79,8 +87,10 @@ options: type: str notes: - - When used with a `loop:` each package will be processed individually, - it is much more efficient to pass the list directly to the `name` option. + - When used with a C(loop:) each package will be processed individually, + it is much more efficient to pass the list directly to the I(name) option. + - To use an AUR helper (I(executable) option), a few extra setup steps might be required beforehand. + For example, a dedicated build user with permissions to install packages could be necessary. ''' RETURN = ''' @@ -109,6 +119,13 @@ EXAMPLES = ''' - ~/bar-1.0-1-any.pkg.tar.xz state: present +- name: Install package from AUR using a Pacman compatible AUR helper + community.general.pacman: + name: foo + state: present + executable: yay + extra_args: --builddir /var/cache/yay + - name: Upgrade package foo community.general.pacman: name: foo @@ -419,6 +436,7 @@ def main(): name=dict(type='list', elements='str', aliases=['pkg', 'package']), state=dict(type='str', default='present', choices=['present', 'installed', 'latest', 'absent', 'removed']), force=dict(type='bool', default=False), + executable=dict(type='str', default='pacman'), extra_args=dict(type='str', default=''), upgrade=dict(type='bool', default=False), upgrade_extra_args=dict(type='str', default=''), @@ -432,11 +450,13 @@ def main(): supports_check_mode=True, ) - pacman_path = module.get_bin_path('pacman', True) module.run_command_environ_update = dict(LC_ALL='C') p = module.params + # find pacman binary + pacman_path = module.get_bin_path(p['executable'], True) + # normalize the state parameter if p['state'] in ['present', 'installed']: p['state'] = 'present' diff --git a/plugins/modules/packaging/os/pacman_key.py b/plugins/modules/packaging/os/pacman_key.py new file mode 100644 index 0000000000..85896c211d --- /dev/null +++ b/plugins/modules/packaging/os/pacman_key.py @@ -0,0 +1,314 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, George Rawlinson +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: pacman_key +author: +- George Rawlinson (@grawlinson) +version_added: "3.2.0" +short_description: Manage pacman's list of trusted keys +description: +- Add or remove gpg keys from the pacman keyring. +notes: +- Use full-length key ID (40 characters). +- Keys will be verified when using I(data), I(file), or I(url) unless I(verify) is overridden. +- Keys will be locally signed after being imported into the keyring. +- If the key ID exists in the keyring, the key will not be added unless I(force_update) is specified. +- I(data), I(file), I(url), and I(keyserver) are mutually exclusive. +- Supports C(check_mode). +requirements: +- gpg +- pacman-key +options: + id: + description: + - The 40 character identifier of the key. + - Including this allows check mode to correctly report the changed state. + - Do not specify a subkey ID, instead specify the primary key ID. + required: true + type: str + data: + description: + - The keyfile contents to add to the keyring. + - Must be of C(PGP PUBLIC KEY BLOCK) type. + type: str + file: + description: + - The path to a keyfile on the remote server to add to the keyring. + - Remote file must be of C(PGP PUBLIC KEY BLOCK) type. + type: path + url: + description: + - The URL to retrieve keyfile from. + - Remote file must be of C(PGP PUBLIC KEY BLOCK) type. + type: str + keyserver: + description: + - The keyserver used to retrieve key from. + type: str + verify: + description: + - Whether or not to verify the keyfile's key ID against specified key ID. + type: bool + default: true + force_update: + description: + - This forces the key to be updated if it already exists in the keyring. + type: bool + default: false + keyring: + description: + - The full path to the keyring folder on the remote server. + - If not specified, module will use pacman's default (C(/etc/pacman.d/gnupg)). + - Useful if the remote system requires an alternative gnupg directory. + type: path + default: /etc/pacman.d/gnupg + state: + description: + - Ensures that the key is present (added) or absent (revoked). + default: present + choices: [ absent, present ] + type: str +''' + +EXAMPLES = ''' +- name: Import a key via local file + community.general.pacman_key: + data: "{{ lookup('file', 'keyfile.asc') }}" + state: present + +- name: Import a key via remote file + community.general.pacman_key: + file: /tmp/keyfile.asc + state: present + +- name: Import a key via url + community.general.pacman_key: + id: 01234567890ABCDE01234567890ABCDE12345678 + url: https://domain.tld/keys/keyfile.asc + state: present + +- name: Import a key via keyserver + community.general.pacman_key: + id: 01234567890ABCDE01234567890ABCDE12345678 + keyserver: keyserver.domain.tld + +- name: Import a key into an alternative keyring + community.general.pacman_key: + id: 01234567890ABCDE01234567890ABCDE12345678 + file: /tmp/keyfile.asc + keyring: /etc/pacman.d/gnupg-alternative + +- name: Remove a key from the keyring + community.general.pacman_key: + id: 01234567890ABCDE01234567890ABCDE12345678 + state: absent +''' + +RETURN = r''' # ''' + +import os.path +import tempfile +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import fetch_url +from ansible.module_utils._text import to_native + + +class PacmanKey(object): + def __init__(self, module): + self.module = module + # obtain binary paths for gpg & pacman-key + self.gpg = module.get_bin_path('gpg', required=True) + self.pacman_key = module.get_bin_path('pacman-key', required=True) + + # obtain module parameters + keyid = module.params['id'] + url = module.params['url'] + data = module.params['data'] + file = module.params['file'] + keyserver = module.params['keyserver'] + verify = module.params['verify'] + force_update = module.params['force_update'] + keyring = module.params['keyring'] + state = module.params['state'] + self.keylength = 40 + + # sanitise key ID & check if key exists in the keyring + keyid = self.sanitise_keyid(keyid) + key_present = self.key_in_keyring(keyring, keyid) + + # check mode + if module.check_mode: + if state == "present": + changed = (key_present and force_update) or not key_present + module.exit_json(changed=changed) + elif state == "absent": + if key_present: + module.exit_json(changed=True) + module.exit_json(changed=False) + + if state == "present": + if key_present and not force_update: + module.exit_json(changed=False) + + if data: + file = self.save_key(data) + self.add_key(keyring, file, keyid, verify) + module.exit_json(changed=True) + elif file: + self.add_key(keyring, file, keyid, verify) + module.exit_json(changed=True) + elif url: + data = self.fetch_key(url) + file = self.save_key(data) + self.add_key(keyring, file, keyid, verify) + module.exit_json(changed=True) + elif keyserver: + self.recv_key(keyring, keyid, keyserver) + module.exit_json(changed=True) + elif state == "absent": + if key_present: + self.remove_key(keyring, keyid) + module.exit_json(changed=True) + module.exit_json(changed=False) + + def is_hexadecimal(self, string): + """Check if a given string is valid hexadecimal""" + try: + int(string, 16) + except ValueError: + return False + return True + + def sanitise_keyid(self, keyid): + """Sanitise given key ID. + + Strips whitespace, uppercases all characters, and strips leading `0X`. + """ + sanitised_keyid = keyid.strip().upper().replace(' ', '').replace('0X', '') + if len(sanitised_keyid) != self.keylength: + self.module.fail_json(msg="key ID is not full-length: %s" % sanitised_keyid) + if not self.is_hexadecimal(sanitised_keyid): + self.module.fail_json(msg="key ID is not hexadecimal: %s" % sanitised_keyid) + return sanitised_keyid + + def fetch_key(self, url): + """Downloads a key from url""" + response, info = fetch_url(self.module, url) + if info['status'] != 200: + self.module.fail_json(msg="failed to fetch key at %s, error was %s" % (url, info['msg'])) + return to_native(response.read()) + + def recv_key(self, keyring, keyid, keyserver): + """Receives key via keyserver""" + cmd = [self.pacman_key, '--gpgdir', keyring, '--keyserver', keyserver, '--recv-keys', keyid] + self.module.run_command(cmd, check_rc=True) + self.lsign_key(keyring, keyid) + + def lsign_key(self, keyring, keyid): + """Locally sign key""" + cmd = [self.pacman_key, '--gpgdir', keyring] + self.module.run_command(cmd + ['--lsign-key', keyid], check_rc=True) + + def save_key(self, data): + "Saves key data to a temporary file" + tmpfd, tmpname = tempfile.mkstemp() + self.module.add_cleanup_file(tmpname) + tmpfile = os.fdopen(tmpfd, "w") + tmpfile.write(data) + tmpfile.close() + return tmpname + + def add_key(self, keyring, keyfile, keyid, verify): + """Add key to pacman's keyring""" + if verify: + self.verify_keyfile(keyfile, keyid) + cmd = [self.pacman_key, '--gpgdir', keyring, '--add', keyfile] + self.module.run_command(cmd, check_rc=True) + self.lsign_key(keyring, keyid) + + def remove_key(self, keyring, keyid): + """Remove key from pacman's keyring""" + cmd = [self.pacman_key, '--gpgdir', keyring, '--delete', keyid] + self.module.run_command(cmd, check_rc=True) + + def verify_keyfile(self, keyfile, keyid): + """Verify that keyfile matches the specified key ID""" + if keyfile is None: + self.module.fail_json(msg="expected a key, got none") + elif keyid is None: + self.module.fail_json(msg="expected a key ID, got none") + + rc, stdout, stderr = self.module.run_command( + [ + self.gpg, + '--with-colons', + '--with-fingerprint', + '--batch', + '--no-tty', + '--show-keys', + keyfile + ], + check_rc=True, + ) + + extracted_keyid = None + for line in stdout.splitlines(): + if line.startswith('fpr:'): + extracted_keyid = line.split(':')[9] + break + + if extracted_keyid != keyid: + self.module.fail_json(msg="key ID does not match. expected %s, got %s" % (keyid, extracted_keyid)) + + def key_in_keyring(self, keyring, keyid): + "Check if the key ID is in pacman's keyring" + rc, stdout, stderr = self.module.run_command( + [ + self.gpg, + '--with-colons', + '--batch', + '--no-tty', + '--no-default-keyring', + '--keyring=%s/pubring.gpg' % keyring, + '--list-keys', keyid + ], + check_rc=False, + ) + if rc != 0: + if stderr.find("No public key") >= 0: + return False + else: + self.module.fail_json(msg="gpg returned an error: %s" % stderr) + return True + + +def main(): + module = AnsibleModule( + argument_spec=dict( + id=dict(type='str', required=True), + data=dict(type='str'), + file=dict(type='path'), + url=dict(type='str'), + keyserver=dict(type='str'), + verify=dict(type='bool', default=True), + force_update=dict(type='bool', default=False), + keyring=dict(type='path', default='/etc/pacman.d/gnupg'), + state=dict(type='str', default='present', choices=['absent', 'present']), + ), + supports_check_mode=True, + mutually_exclusive=(('data', 'file', 'url', 'keyserver'),), + required_if=[('state', 'present', ('data', 'file', 'url', 'keyserver'), True)], + ) + PacmanKey(module) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/packaging/os/rhsm_release.py b/plugins/modules/packaging/os/rhsm_release.py index 22b280f1fc..a4d8f71197 100644 --- a/plugins/modules/packaging/os/rhsm_release.py +++ b/plugins/modules/packaging/os/rhsm_release.py @@ -56,9 +56,9 @@ from ansible.module_utils.basic import AnsibleModule import re -# Matches release-like values such as 7.2, 6.10, 10Server, -# but rejects unlikely values, like 100Server, 100.0, 1.100, etc. -release_matcher = re.compile(r'\b\d{1,2}(?:\.\d{1,2}|Server)\b') +# Matches release-like values such as 7.2, 5.10, 6Server, 8 +# but rejects unlikely values, like 100Server, 1.100, 7server etc. +release_matcher = re.compile(r'\b\d{1,2}(?:\.\d{1,2}|Server|Client|Workstation|)\b') def _sm_release(module, *args): diff --git a/plugins/modules/packaging/os/svr4pkg.py b/plugins/modules/packaging/os/svr4pkg.py index ea3cd7d468..aa7a5c2e52 100644 --- a/plugins/modules/packaging/os/svr4pkg.py +++ b/plugins/modules/packaging/os/svr4pkg.py @@ -121,7 +121,7 @@ def package_installed(module, name, category): def create_admin_file(): (desc, filename) = tempfile.mkstemp(prefix='ansible_svr4pkg', text=True) - fullauto = ''' + fullauto = b''' mail= instance=unique partial=nocheck diff --git a/plugins/modules/packaging/os/zypper_repository.py b/plugins/modules/packaging/os/zypper_repository.py index f1d85376f5..608675528d 100644 --- a/plugins/modules/packaging/os/zypper_repository.py +++ b/plugins/modules/packaging/os/zypper_repository.py @@ -175,7 +175,7 @@ def _parse_repos(module): module.fail_json(msg='Failed to execute "%s"' % " ".join(cmd), rc=rc, stdout=stdout, stderr=stderr) -def _repo_changes(realrepo, repocmp): +def _repo_changes(module, realrepo, repocmp): "Check whether the 2 given repos have different settings." for k in repocmp: if repocmp[k] and k not in realrepo: @@ -186,6 +186,16 @@ def _repo_changes(realrepo, repocmp): valold = str(repocmp[k] or "") valnew = v or "" if k == "url": + if '$releasever' in valold or '$releasever' in valnew: + cmd = ['rpm', '-q', '--qf', '%{version}', '-f', '/etc/os-release'] + rc, stdout, stderr = module.run_command(cmd, check_rc=True) + valnew = valnew.replace('$releasever', stdout) + valold = valold.replace('$releasever', stdout) + if '$basearch' in valold or '$basearch' in valnew: + cmd = ['rpm', '-q', '--qf', '%{arch}', '-f', '/etc/os-release'] + rc, stdout, stderr = module.run_command(cmd, check_rc=True) + valnew = valnew.replace('$basearch', stdout) + valold = valold.replace('$basearch', stdout) valold, valnew = valold.rstrip("/"), valnew.rstrip("/") if valold != valnew: return True @@ -215,7 +225,7 @@ def repo_exists(module, repodata, overwrite_multiple): return (False, False, None) elif len(repos) == 1: # Found an existing repo, look for changes - has_changes = _repo_changes(repos[0], repodata) + has_changes = _repo_changes(module, repos[0], repodata) return (True, has_changes, repos) elif len(repos) >= 2: if overwrite_multiple: diff --git a/plugins/modules/pacman_key.py b/plugins/modules/pacman_key.py new file mode 120000 index 0000000000..ac0f448232 --- /dev/null +++ b/plugins/modules/pacman_key.py @@ -0,0 +1 @@ +packaging/os/pacman_key.py \ No newline at end of file diff --git a/plugins/modules/proxmox_nic.py b/plugins/modules/proxmox_nic.py new file mode 120000 index 0000000000..88756ab636 --- /dev/null +++ b/plugins/modules/proxmox_nic.py @@ -0,0 +1 @@ +cloud/misc/proxmox_nic.py \ No newline at end of file diff --git a/plugins/modules/remote_management/redfish/idrac_redfish_config.py b/plugins/modules/remote_management/redfish/idrac_redfish_config.py index e27ef6a2a6..b16401311b 100644 --- a/plugins/modules/remote_management/redfish/idrac_redfish_config.py +++ b/plugins/modules/remote_management/redfish/idrac_redfish_config.py @@ -179,6 +179,7 @@ class IdracRedfishUtils(RedfishUtils): attrs_to_patch = {} attrs_skipped = {} + attrs_bad = {} # Store attrs which were not found in the system # Search for key entry and extract URI from it response = self.get_request(self.root_uri + manager_uri + "/" + key) @@ -189,13 +190,15 @@ class IdracRedfishUtils(RedfishUtils): if key not in data: return {'ret': False, - 'msg': "%s: Key %s not found" % (command, key)} + 'msg': "%s: Key %s not found" % (command, key), + 'warning': ""} for attr_name, attr_value in attributes.items(): # Check if attribute exists if attr_name not in data[u'Attributes']: - return {'ret': False, - 'msg': "%s: Manager attribute %s not found" % (command, attr_name)} + # Skip and proceed to next attribute if this isn't valid + attrs_bad.update({attr_name: attr_value}) + continue # Find out if value is already set to what we want. If yes, exclude # those attributes @@ -204,16 +207,23 @@ class IdracRedfishUtils(RedfishUtils): else: attrs_to_patch.update({attr_name: attr_value}) + warning = "" + if attrs_bad: + warning = "Incorrect attributes %s" % (attrs_bad) + if not attrs_to_patch: return {'ret': True, 'changed': False, - 'msg': "Manager attributes already set"} + 'msg': "No changes made. Manager attributes already set.", + 'warning': warning} payload = {"Attributes": attrs_to_patch} response = self.patch_request(self.root_uri + manager_uri + "/" + key, payload) if response['ret'] is False: return response + return {'ret': True, 'changed': True, - 'msg': "%s: Modified Manager attributes %s" % (command, attrs_to_patch)} + 'msg': "%s: Modified Manager attributes %s" % (command, attrs_to_patch), + 'warning': warning} CATEGORY_COMMANDS_ALL = { @@ -221,6 +231,7 @@ CATEGORY_COMMANDS_ALL = { "SetSystemAttributes"] } + # list of mutually exclusive commands for a category CATEGORY_COMMANDS_MUTUALLY_EXCLUSIVE = { "Manager": [["SetManagerAttributes", "SetLifecycleControllerAttributes", @@ -308,6 +319,9 @@ def main(): # Return data back or fail with proper message if result['ret'] is True: + if result.get('warning'): + module.warn(to_native(result['warning'])) + module.exit_json(changed=result['changed'], msg=to_native(result['msg'])) else: module.fail_json(msg=to_native(result['msg'])) diff --git a/plugins/modules/remote_management/redfish/redfish_config.py b/plugins/modules/remote_management/redfish/redfish_config.py index 5c1df16c4e..e084c670f4 100644 --- a/plugins/modules/remote_management/redfish/redfish_config.py +++ b/plugins/modules/remote_management/redfish/redfish_config.py @@ -321,6 +321,9 @@ def main(): # Return data back or fail with proper message if result['ret'] is True: + if result.get('warning'): + module.warn(to_native(result['warning'])) + module.exit_json(changed=result['changed'], msg=to_native(result['msg'])) else: module.fail_json(msg=to_native(result['msg'])) diff --git a/plugins/modules/remote_management/stacki/stacki_host.py b/plugins/modules/remote_management/stacki/stacki_host.py index 8bdc0f82f6..fda0c5d318 100644 --- a/plugins/modules/remote_management/stacki/stacki_host.py +++ b/plugins/modules/remote_management/stacki/stacki_host.py @@ -12,46 +12,48 @@ DOCUMENTATION = ''' module: stacki_host short_description: Add or remove host to stacki front-end description: - - Use this module to add or remove hosts to a stacki front-end via API. - - U(https://github.com/StackIQ/stacki) + - Use this module to add or remove hosts to a stacki front-end via API. + - Information on stacki can be found at U(https://github.com/StackIQ/stacki). options: name: description: - - Name of the host to be added to Stacki. + - Name of the host to be added to Stacki. required: True type: str stacki_user: description: - - Username for authenticating with Stacki API, but if not - specified, the environment variable C(stacki_user) is used instead. + - Username for authenticating with Stacki API, but if not specified, the environment variable C(stacki_user) is used instead. required: True type: str stacki_password: description: - - Password for authenticating with Stacki API, but if not + - Password for authenticating with Stacki API, but if not specified, the environment variable C(stacki_password) is used instead. required: True type: str stacki_endpoint: description: - - URL for the Stacki API Endpoint. + - URL for the Stacki API Endpoint. required: True type: str prim_intf_mac: description: - - MAC Address for the primary PXE boot network interface. + - MAC Address for the primary PXE boot network interface. + - Currently not used by the module. type: str prim_intf_ip: description: - - IP Address for the primary network interface. + - IP Address for the primary network interface. + - Currently not used by the module. type: str prim_intf: description: - - Name of the primary network interface. + - Name of the primary network interface. + - Currently not used by the module. type: str force_install: description: - - Set value to True to force node into install state if it already exists in stacki. + - Set value to C(true) to force node into install state if it already exists in stacki. type: bool default: no state: @@ -59,6 +61,30 @@ options: - Set value to the desired state for the specified host. type: str choices: [ absent, present ] + default: present + appliance: + description: + - Applicance to be used in host creation. + - Required if I(state) is C(present) and host does not yet exist. + type: str + default: backend + rack: + description: + - Rack to be used in host creation. + - Required if I(state) is C(present) and host does not yet exist. + type: int + rank: + description: + - Rank to be used in host creation. + - In Stacki terminology, the rank is the position of the machine in a rack. + - Required if I(state) is C(present) and host does not yet exist. + type: int + network: + description: + - Network to be configured in the host. + - Currently not used by the module. + type: str + default: private author: - Hugh Ma (@bbyhuy) ''' @@ -128,7 +154,7 @@ class StackiHost(object): 'PASSWORD': module.params['stacki_password']} # Get Initial CSRF - cred_a = self.do_request(self.module, self.endpoint, method="GET") + cred_a = self.do_request(self.endpoint, method="GET") cookie_a = cred_a.headers.get('Set-Cookie').split(';') init_csrftoken = None for c in cookie_a: @@ -145,8 +171,7 @@ class StackiHost(object): login_endpoint = self.endpoint + "/login" # Get Final CSRF and Session ID - login_req = self.do_request(self.module, login_endpoint, headers=header, - payload=urlencode(auth_creds), method='POST') + login_req = self.do_request(login_endpoint, headers=header, payload=urlencode(auth_creds), method='POST') cookie_f = login_req.headers.get('Set-Cookie').split(';') csrftoken = None @@ -163,8 +188,8 @@ class StackiHost(object): 'Content-type': 'application/json', 'Cookie': login_req.headers.get('Set-Cookie')} - def do_request(self, module, url, payload=None, headers=None, method=None): - res, info = fetch_url(module, url, data=payload, headers=headers, method=method) + def do_request(self, url, payload=None, headers=None, method=None): + res, info = fetch_url(self.module, url, data=payload, headers=headers, method=method) if info['status'] != 200: self.module.fail_json(changed=False, msg=info['msg']) @@ -172,24 +197,16 @@ class StackiHost(object): return res def stack_check_host(self): - res = self.do_request(self.module, self.endpoint, payload=json.dumps({"cmd": "list host"}), headers=self.header, method="POST") - - if self.hostname in res.read(): - return True - else: - return False + res = self.do_request(self.endpoint, payload=json.dumps({"cmd": "list host"}), headers=self.header, method="POST") + return self.hostname in res.read() def stack_sync(self): - self.do_request(self.module, self.endpoint, payload=json.dumps({"cmd": "sync config"}), headers=self.header, method="POST") - self.do_request(self.module, self.endpoint, payload=json.dumps({"cmd": "sync host config"}), headers=self.header, method="POST") + self.do_request(self.endpoint, payload=json.dumps({"cmd": "sync config"}), headers=self.header, method="POST") + self.do_request(self.endpoint, payload=json.dumps({"cmd": "sync host config"}), headers=self.header, method="POST") def stack_force_install(self, result): - data = dict() - changed = False - - data['cmd'] = "set host boot {0} action=install" \ - .format(self.hostname) - self.do_request(self.module, self.endpoint, payload=json.dumps(data), headers=self.header, method="POST") + data = {'cmd': "set host boot {0} action=install".format(self.hostname)} + self.do_request(self.endpoint, payload=json.dumps(data), headers=self.header, method="POST") changed = True self.stack_sync() @@ -203,7 +220,7 @@ class StackiHost(object): data['cmd'] = "add host {0} rack={1} rank={2} appliance={3}"\ .format(self.hostname, self.rack, self.rank, self.appliance) - self.do_request(self.module, self.endpoint, payload=json.dumps(data), headers=self.header, method="POST") + self.do_request(self.endpoint, payload=json.dumps(data), headers=self.header, method="POST") self.stack_sync() @@ -215,7 +232,7 @@ class StackiHost(object): data['cmd'] = "remove host {0}"\ .format(self.hostname) - self.do_request(self.module, self.endpoint, payload=json.dumps(data), headers=self.header, method="POST") + self.do_request(self.endpoint, payload=json.dumps(data), headers=self.header, method="POST") self.stack_sync() @@ -258,8 +275,7 @@ def main(): .format(module.params['name']) # Otherwise, state is present, but host doesn't exists, require more params to add host elif module.params['state'] == 'present' and not host_exists: - for param in ['appliance', 'prim_intf', - 'prim_intf_ip', 'network', 'prim_intf_mac']: + for param in ['appliance', 'rack', 'rank', 'prim_intf', 'prim_intf_ip', 'network', 'prim_intf_mac']: if not module.params[param]: missing_params.append(param) if len(missing_params) > 0: # @FIXME replace with required_if diff --git a/plugins/modules/sapcar_extract.py b/plugins/modules/sapcar_extract.py new file mode 120000 index 0000000000..7bb47b10c1 --- /dev/null +++ b/plugins/modules/sapcar_extract.py @@ -0,0 +1 @@ +./files/sapcar_extract.py \ No newline at end of file diff --git a/plugins/modules/source_control/gitlab/gitlab_user.py b/plugins/modules/source_control/gitlab/gitlab_user.py index 9fefe1aff9..4d300ea842 100644 --- a/plugins/modules/source_control/gitlab/gitlab_user.py +++ b/plugins/modules/source_control/gitlab/gitlab_user.py @@ -57,16 +57,22 @@ options: type: str sshkey_name: description: - - The name of the sshkey + - The name of the SSH public key. type: str sshkey_file: description: - - The ssh key itself. + - The SSH public key itself. type: str + sshkey_expires_at: + description: + - The expiration date of the SSH public key in ISO 8601 format C(YYYY-MM-DDTHH:MM:SSZ). + - This is only used when adding new SSH public keys. + type: str + version_added: 3.1.0 group: description: - Id or Full path of parent group in the form of group/name. - - Add user as an member to this group. + - Add user as a member to this group. type: str access_level: description: @@ -254,7 +260,8 @@ class GitLabUser(object): if options['sshkey_name'] and options['sshkey_file']: key_changed = self.addSshKeyToUser(user, { 'name': options['sshkey_name'], - 'file': options['sshkey_file']}) + 'file': options['sshkey_file'], + 'expires_at': options['sshkey_expires_at']}) changed = changed or key_changed # Assign group @@ -295,7 +302,7 @@ class GitLabUser(object): ''' @param user User object - @param sshkey Dict containing sshkey infos {"name": "", "file": ""} + @param sshkey Dict containing sshkey infos {"name": "", "file": "", "expires_at": ""} ''' def addSshKeyToUser(self, user, sshkey): if not self.sshKeyExists(user, sshkey['name']): @@ -303,9 +310,13 @@ class GitLabUser(object): return True try: - user.keys.create({ + parameter = { 'title': sshkey['name'], - 'key': sshkey['file']}) + 'key': sshkey['file'], + } + if sshkey['expires_at'] is not None: + parameter['expires_at'] = sshkey['expires_at'] + user.keys.create(parameter) except gitlab.exceptions.GitlabCreateError as e: self._module.fail_json(msg="Failed to assign sshkey to user: %s" % to_native(e)) return True @@ -471,6 +482,7 @@ def main(): email=dict(type='str'), sshkey_name=dict(type='str'), sshkey_file=dict(type='str', no_log=False), + sshkey_expires_at=dict(type='str', no_log=False), group=dict(type='str'), access_level=dict(type='str', default="guest", choices=["developer", "guest", "maintainer", "master", "owner", "reporter"]), confirm=dict(type='bool', default=True), @@ -503,6 +515,7 @@ def main(): user_email = module.params['email'] user_sshkey_name = module.params['sshkey_name'] user_sshkey_file = module.params['sshkey_file'] + user_sshkey_expires_at = module.params['sshkey_expires_at'] group_path = module.params['group'] access_level = module.params['access_level'] confirm = module.params['confirm'] @@ -549,6 +562,7 @@ def main(): "email": user_email, "sshkey_name": user_sshkey_name, "sshkey_file": user_sshkey_file, + "sshkey_expires_at": user_sshkey_expires_at, "group_path": group_path, "access_level": access_level, "confirm": confirm, diff --git a/plugins/modules/storage/zfs/zfs.py b/plugins/modules/storage/zfs/zfs.py index fe693a5045..2d5d4487dd 100644 --- a/plugins/modules/storage/zfs/zfs.py +++ b/plugins/modules/storage/zfs/zfs.py @@ -37,6 +37,12 @@ options: - A dictionary of zfs properties to be set. - See the zfs(8) man page for more information. type: dict +notes: + - C(check_mode) is supported, but in certain situations it may report a task + as changed that will not be reported as changed when C(check_mode) is disabled. + For example, this might occur when the zpool C(altroot) option is set or when + a size is written using human-readable notation, such as C(1M) or C(1024K), + instead of as an unqualified byte count, such as C(1048576). author: - Johan Wiren (@johanwiren) ''' @@ -184,9 +190,7 @@ class Zfs(object): return cmd = [self.zfs_cmd, 'set', prop + '=' + str(value), self.name] (rc, out, err) = self.module.run_command(cmd) - if rc == 0: - self.changed = True - else: + if rc != 0: self.module.fail_json(msg=err) def set_properties_if_changed(self): @@ -194,15 +198,25 @@ class Zfs(object): for prop, value in self.properties.items(): if current_properties.get(prop, None) != value: self.set_property(prop, value) + if self.module.check_mode: + return + updated_properties = self.get_current_properties() + for prop in self.properties: + value = updated_properties.get(prop, None) + if value is None: + self.module.fail_json(msg="zfsprop was not present after being successfully set: %s" % prop) + if current_properties.get(prop, None) != value: + self.changed = True def get_current_properties(self): - cmd = [self.zfs_cmd, 'get', '-H'] + cmd = [self.zfs_cmd, 'get', '-H', '-p', '-o', "property,value,source"] if self.enhanced_sharing: cmd += ['-e'] cmd += ['all', self.name] rc, out, err = self.module.run_command(" ".join(cmd)) properties = dict() - for prop, value, source in [l.split('\t')[1:4] for l in out.splitlines()]: + for line in out.splitlines(): + prop, value, source = line.split('\t') # include source '-' so that creation-only properties are not removed # to avoids errors when the dataset already exists and the property is not changed # this scenario is most likely when the same playbook is run more than once diff --git a/plugins/modules/storage/zfs/zfs_delegate_admin.py b/plugins/modules/storage/zfs/zfs_delegate_admin.py index 71225fa155..ead4041150 100644 --- a/plugins/modules/storage/zfs/zfs_delegate_admin.py +++ b/plugins/modules/storage/zfs/zfs_delegate_admin.py @@ -51,8 +51,9 @@ options: permissions: description: - The list of permission(s) to delegate (required if C(state) is C(present)). + - Supported permissions depend on the ZFS version in use. See for example + U(https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html) for OpenZFS. type: list - choices: [ allow, clone, create, destroy, diff, hold, mount, promote, readonly, receive, release, rename, rollback, send, share, snapshot, unallow ] elements: str local: description: @@ -248,10 +249,7 @@ def main(): users=dict(type='list', elements='str'), groups=dict(type='list', elements='str'), everyone=dict(type='bool', default=False), - permissions=dict(type='list', elements='str', - choices=['allow', 'clone', 'create', 'destroy', 'diff', 'hold', 'mount', 'promote', - 'readonly', 'receive', 'release', 'rename', 'rollback', 'send', 'share', - 'snapshot', 'unallow']), + permissions=dict(type='list', elements='str'), local=dict(type='bool'), descendents=dict(type='bool'), recursive=dict(type='bool', default=False), diff --git a/plugins/modules/system/filesystem.py b/plugins/modules/system/filesystem.py index 6944178da1..97fe2dc1ab 100644 --- a/plugins/modules/system/filesystem.py +++ b/plugins/modules/system/filesystem.py @@ -7,10 +7,11 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type + DOCUMENTATION = ''' --- author: -- Alexander Bulimov (@abulimov) + - Alexander Bulimov (@abulimov) module: filesystem short_description: Makes a filesystem description: @@ -18,13 +19,12 @@ description: options: state: description: - - If C(state=present), the filesystem is created if it doesn't already - exist, that is the default behaviour if I(state) is omitted. - - If C(state=absent), filesystem signatures on I(dev) are wiped if it - contains a filesystem (as known by C(blkid)). - - When C(state=absent), all other options but I(dev) are ignored, and the - module doesn't fail if the device I(dev) doesn't actually exist. - - C(state=absent) is not supported and will fail on FreeBSD systems. + - If C(state=present), the filesystem is created if it doesn't already + exist, that is the default behaviour if I(state) is omitted. + - If C(state=absent), filesystem signatures on I(dev) are wiped if it + contains a filesystem (as known by C(blkid)). + - When C(state=absent), all other options but I(dev) are ignored, and the + module doesn't fail if the device I(dev) doesn't actually exist. type: str choices: [ present, absent ] default: present @@ -32,48 +32,56 @@ options: fstype: choices: [ btrfs, ext2, ext3, ext4, ext4dev, f2fs, lvm, ocfs2, reiserfs, xfs, vfat, swap ] description: - - Filesystem type to be created. This option is required with - C(state=present) (or if I(state) is omitted). - - reiserfs support was added in 2.2. - - lvm support was added in 2.5. - - since 2.5, I(dev) can be an image file. - - vfat support was added in 2.5 - - ocfs2 support was added in 2.6 - - f2fs support was added in 2.7 - - swap support was added in 2.8 + - Filesystem type to be created. This option is required with + C(state=present) (or if I(state) is omitted). + - reiserfs support was added in 2.2. + - lvm support was added in 2.5. + - since 2.5, I(dev) can be an image file. + - vfat support was added in 2.5 + - ocfs2 support was added in 2.6 + - f2fs support was added in 2.7 + - swap support was added in 2.8 type: str aliases: [type] dev: description: - - Target path to device or image file. + - Target path to block device or regular file. + - On systems not using block devices but character devices instead (as + FreeBSD), this module only works when applying to regular files, aka + disk images. type: path required: yes aliases: [device] force: description: - - If C(yes), allows to create new filesystem on devices that already has filesystem. + - If C(yes), allows to create new filesystem on devices that already has filesystem. type: bool default: 'no' resizefs: description: - - If C(yes), if the block device and filesystem size differ, grow the filesystem into the space. - - Supported for C(ext2), C(ext3), C(ext4), C(ext4dev), C(f2fs), C(lvm), C(xfs) and C(vfat) filesystems. - Attempts to resize other filesystem types will fail. - - XFS Will only grow if mounted. Currently, the module is based on commands - from C(util-linux) package to perform operations, so resizing of XFS is - not supported on FreeBSD systems. - - vFAT will likely fail if fatresize < 1.04. + - If C(yes), if the block device and filesystem size differ, grow the filesystem into the space. + - Supported for C(ext2), C(ext3), C(ext4), C(ext4dev), C(f2fs), C(lvm), C(xfs) and C(vfat) filesystems. + Attempts to resize other filesystem types will fail. + - XFS Will only grow if mounted. Currently, the module is based on commands + from C(util-linux) package to perform operations, so resizing of XFS is + not supported on FreeBSD systems. + - vFAT will likely fail if fatresize < 1.04. type: bool default: 'no' opts: description: - - List of options to be passed to mkfs command. + - List of options to be passed to mkfs command. type: str requirements: - - Uses tools related to the I(fstype) (C(mkfs)) and C(blkid) command. When I(resizefs) is enabled, C(blockdev) command is required too. + - Uses tools related to the I(fstype) (C(mkfs)) and the C(blkid) command. + - When I(resizefs) is enabled, C(blockdev) command is required too. notes: - - Potential filesystem on I(dev) are checked using C(blkid), in case C(blkid) isn't able to detect an existing filesystem, - this filesystem is overwritten even if I(force) is C(no). + - Potential filesystem on I(dev) are checked using C(blkid). In case C(blkid) + isn't able to detect an existing filesystem, this filesystem is overwritten + even if I(force) is C(no). + - On FreeBSD systems, either C(e2fsprogs) or C(util-linux) packages provide + a C(blkid) command that is compatible with this module, when applied to + regular files. - This module supports I(check_mode). ''' @@ -102,6 +110,7 @@ import re import stat from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native class Device(object): @@ -114,13 +123,15 @@ class Device(object): statinfo = os.stat(self.path) if stat.S_ISBLK(statinfo.st_mode): blockdev_cmd = self.module.get_bin_path("blockdev", required=True) - dummy, devsize_in_bytes, dummy = self.module.run_command([blockdev_cmd, "--getsize64", self.path], check_rc=True) - return int(devsize_in_bytes) + dummy, out, dummy = self.module.run_command([blockdev_cmd, "--getsize64", self.path], check_rc=True) + devsize_in_bytes = int(out) elif os.path.isfile(self.path): - return os.path.getsize(self.path) + devsize_in_bytes = os.path.getsize(self.path) else: self.module.fail_json(changed=False, msg="Target device not supported: %s" % self) + return devsize_in_bytes + def get_mountpoint(self): """Return (first) mountpoint of device. Returns None when not mounted.""" cmd_findmnt = self.module.get_bin_path("findmnt", required=True) @@ -141,9 +152,12 @@ class Device(object): class Filesystem(object): - GROW = None MKFS = None - MKFS_FORCE_FLAGS = '' + MKFS_FORCE_FLAGS = [] + INFO = None + GROW = None + GROW_MAX_SPACE_FLAGS = [] + GROW_MOUNTPOINT_ONLY = False LANG_ENV = {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'} @@ -155,7 +169,11 @@ class Filesystem(object): return type(self).__name__ def get_fs_size(self, dev): - """ Return size in bytes of filesystem on device. Returns int """ + """Return size in bytes of filesystem on device (integer). + Should query the info with a per-fstype command that can access the + device whenever it is mounted or not, and parse the command output. + Parser must ensure to return an integer, or raise a ValueError. + """ raise NotImplementedError() def create(self, opts, dev): @@ -163,31 +181,27 @@ class Filesystem(object): return mkfs = self.module.get_bin_path(self.MKFS, required=True) - if opts is None: - cmd = "%s %s '%s'" % (mkfs, self.MKFS_FORCE_FLAGS, dev) - else: - cmd = "%s %s %s '%s'" % (mkfs, self.MKFS_FORCE_FLAGS, opts, dev) + cmd = [mkfs] + self.MKFS_FORCE_FLAGS + opts + [str(dev)] self.module.run_command(cmd, check_rc=True) def wipefs(self, dev): - if platform.system() == 'FreeBSD': - msg = "module param state=absent is currently not supported on this OS (FreeBSD)." - self.module.fail_json(msg=msg) - if self.module.check_mode: return # wipefs comes with util-linux package (as 'blockdev' & 'findmnt' above) - # so it is not supported on FreeBSD. Even the use of dd as a fallback is + # that is ported to FreeBSD. The use of dd as a portable fallback is # not doable here if it needs get_mountpoint() (to prevent corruption of - # a mounted filesystem), since 'findmnt' is not available on FreeBSD. + # a mounted filesystem), since 'findmnt' is not available on FreeBSD, + # even in util-linux port for this OS. wipefs = self.module.get_bin_path('wipefs', required=True) - cmd = [wipefs, "--all", dev.__str__()] + cmd = [wipefs, "--all", str(dev)] self.module.run_command(cmd, check_rc=True) - def grow_cmd(self, dev): - cmd = self.module.get_bin_path(self.GROW, required=True) - return [cmd, str(dev)] + def grow_cmd(self, target): + """Build and return the resizefs commandline as list.""" + cmdline = [self.module.get_bin_path(self.GROW, required=True)] + cmdline += self.GROW_MAX_SPACE_FLAGS + [target] + return cmdline def grow(self, dev): """Get dev and fs size and compare. Returns stdout of used command.""" @@ -196,31 +210,50 @@ class Filesystem(object): try: fssize_in_bytes = self.get_fs_size(dev) except NotImplementedError: - self.module.fail_json(changed=False, msg="module does not support resizing %s filesystem yet." % self.fstype) + self.module.fail_json(msg="module does not support resizing %s filesystem yet" % self.fstype) + except ValueError as err: + self.module.warn("unable to process %s output '%s'" % (self.INFO, to_native(err))) + self.module.fail_json(msg="unable to process %s output for %s" % (self.INFO, dev)) if not fssize_in_bytes < devsize_in_bytes: self.module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (self.fstype, dev)) elif self.module.check_mode: - self.module.exit_json(changed=True, msg="Resizing filesystem %s on device %s" % (self.fstype, dev)) + self.module.exit_json(changed=True, msg="resizing filesystem %s on device %s" % (self.fstype, dev)) + + if self.GROW_MOUNTPOINT_ONLY: + mountpoint = dev.get_mountpoint() + if not mountpoint: + self.module.fail_json(msg="%s needs to be mounted for %s operations" % (dev, self.fstype)) + grow_target = mountpoint else: - dummy, out, dummy = self.module.run_command(self.grow_cmd(dev), check_rc=True) - return out + grow_target = str(dev) + + dummy, out, dummy = self.module.run_command(self.grow_cmd(grow_target), check_rc=True) + return out class Ext(Filesystem): - MKFS_FORCE_FLAGS = '-F' + MKFS_FORCE_FLAGS = ['-F'] + INFO = 'tune2fs' GROW = 'resize2fs' def get_fs_size(self, dev): - cmd = self.module.get_bin_path('tune2fs', required=True) - # Get Block count and Block size - dummy, size, dummy = self.module.run_command([cmd, '-l', str(dev)], check_rc=True, environ_update=self.LANG_ENV) - for line in size.splitlines(): + """Get Block count and Block size and return their product.""" + cmd = self.module.get_bin_path(self.INFO, required=True) + dummy, out, dummy = self.module.run_command([cmd, '-l', str(dev)], check_rc=True, environ_update=self.LANG_ENV) + + block_count = block_size = None + for line in out.splitlines(): if 'Block count:' in line: block_count = int(line.split(':')[1].strip()) elif 'Block size:' in line: block_size = int(line.split(':')[1].strip()) - return block_size * block_count + if None not in (block_size, block_count): + break + else: + raise ValueError(out) + + return block_size * block_count class Ext2(Ext): @@ -237,52 +270,46 @@ class Ext4(Ext): class XFS(Filesystem): MKFS = 'mkfs.xfs' - MKFS_FORCE_FLAGS = '-f' + MKFS_FORCE_FLAGS = ['-f'] + INFO = 'xfs_info' GROW = 'xfs_growfs' + GROW_MOUNTPOINT_ONLY = True def get_fs_size(self, dev): - cmd = self.module.get_bin_path('xfs_info', required=True) + """Get bsize and blocks and return their product.""" + cmdline = [self.module.get_bin_path(self.INFO, required=True)] + # Depending on the versions, xfs_info is able to get info from the + # device, whenever it is mounted or not, or only if unmounted, or + # only if mounted, or not at all. For any version until now, it is + # able to query info from the mountpoint. So try it first, and use + # device as the last resort: it may or may not work. mountpoint = dev.get_mountpoint() if mountpoint: - rc, out, err = self.module.run_command([cmd, str(mountpoint)], environ_update=self.LANG_ENV) + cmdline += [mountpoint] else: - # Recent GNU/Linux distros support access to unmounted XFS filesystems - rc, out, err = self.module.run_command([cmd, str(dev)], environ_update=self.LANG_ENV) - if rc != 0: - self.module.fail_json(msg="Error while attempting to query size of XFS filesystem: %s" % err) + cmdline += [str(dev)] + dummy, out, dummy = self.module.run_command(cmdline, check_rc=True, environ_update=self.LANG_ENV) + block_size = block_count = None for line in out.splitlines(): col = line.split('=') if col[0].strip() == 'data': - if col[1].strip() != 'bsize': - self.module.fail_json(msg='Unexpected output format from xfs_info (could not locate "bsize")') - if col[2].split()[1] != 'blocks': - self.module.fail_json(msg='Unexpected output format from xfs_info (could not locate "blocks")') - block_size = int(col[2].split()[0]) - block_count = int(col[3].split(',')[0]) - return block_size * block_count + if col[1].strip() == 'bsize': + block_size = int(col[2].split()[0]) + if col[2].split()[1] == 'blocks': + block_count = int(col[3].split(',')[0]) + if None not in (block_size, block_count): + break + else: + raise ValueError(out) - def grow_cmd(self, dev): - # Check first if growing is needed, and then if it is doable or not. - devsize_in_bytes = dev.size() - fssize_in_bytes = self.get_fs_size(dev) - if not fssize_in_bytes < devsize_in_bytes: - self.module.exit_json(changed=False, msg="%s filesystem is using the whole device %s" % (self.fstype, dev)) - - mountpoint = dev.get_mountpoint() - if not mountpoint: - # xfs filesystem needs to be mounted - self.module.fail_json(msg="%s needs to be mounted for xfs operations" % dev) - - cmd = self.module.get_bin_path(self.GROW, required=True) - - return [cmd, str(mountpoint)] + return block_size * block_count class Reiserfs(Filesystem): MKFS = 'mkfs.reiserfs' - MKFS_FORCE_FLAGS = '-f' + MKFS_FORCE_FLAGS = ['-q'] class Btrfs(Filesystem): @@ -290,7 +317,8 @@ class Btrfs(Filesystem): def __init__(self, module): super(Btrfs, self).__init__(module) - dummy, stdout, stderr = self.module.run_command('%s --version' % self.MKFS, check_rc=True) + mkfs = self.module.get_bin_path(self.MKFS, required=True) + dummy, stdout, stderr = self.module.run_command([mkfs, '--version'], check_rc=True) match = re.search(r" v([0-9.]+)", stdout) if not match: # v0.20-rc1 use stderr @@ -298,29 +326,27 @@ class Btrfs(Filesystem): if match: # v0.20-rc1 doesn't have --force parameter added in following version v3.12 if LooseVersion(match.group(1)) >= LooseVersion('3.12'): - self.MKFS_FORCE_FLAGS = '-f' - else: - self.MKFS_FORCE_FLAGS = '' + self.MKFS_FORCE_FLAGS = ['-f'] else: # assume version is greater or equal to 3.12 - self.MKFS_FORCE_FLAGS = '-f' + self.MKFS_FORCE_FLAGS = ['-f'] self.module.warn('Unable to identify mkfs.btrfs version (%r, %r)' % (stdout, stderr)) class Ocfs2(Filesystem): MKFS = 'mkfs.ocfs2' - MKFS_FORCE_FLAGS = '-Fx' + MKFS_FORCE_FLAGS = ['-Fx'] class F2fs(Filesystem): MKFS = 'mkfs.f2fs' + INFO = 'dump.f2fs' GROW = 'resize.f2fs' - @property - def MKFS_FORCE_FLAGS(self): + def __init__(self, module): + super(F2fs, self).__init__(module) mkfs = self.module.get_bin_path(self.MKFS, required=True) - cmd = "%s %s" % (mkfs, os.devnull) - dummy, out, dummy = self.module.run_command(cmd, check_rc=False, environ_update=self.LANG_ENV) + dummy, out, dummy = self.module.run_command([mkfs, os.devnull], check_rc=False, environ_update=self.LANG_ENV) # Looking for " F2FS-tools: mkfs.f2fs Ver: 1.10.0 (2018-01-30)" # mkfs.f2fs displays version since v1.2.0 match = re.search(r"F2FS-tools: mkfs.f2fs Ver: ([0-9.]+) \(", out) @@ -328,69 +354,73 @@ class F2fs(Filesystem): # Since 1.9.0, mkfs.f2fs check overwrite before make filesystem # before that version -f switch wasn't used if LooseVersion(match.group(1)) >= LooseVersion('1.9.0'): - return '-f' - - return '' + self.MKFS_FORCE_FLAGS = ['-f'] def get_fs_size(self, dev): - cmd = self.module.get_bin_path('dump.f2fs', required=True) - # Get sector count and sector size - dummy, dump, dummy = self.module.run_command([cmd, str(dev)], check_rc=True, environ_update=self.LANG_ENV) - sector_size = None - sector_count = None - for line in dump.splitlines(): + """Get sector size and total FS sectors and return their product.""" + cmd = self.module.get_bin_path(self.INFO, required=True) + dummy, out, dummy = self.module.run_command([cmd, str(dev)], check_rc=True, environ_update=self.LANG_ENV) + sector_size = sector_count = None + for line in out.splitlines(): if 'Info: sector size = ' in line: # expected: 'Info: sector size = 512' sector_size = int(line.split()[4]) elif 'Info: total FS sectors = ' in line: # expected: 'Info: total FS sectors = 102400 (50 MB)' sector_count = int(line.split()[5]) - if None not in (sector_size, sector_count): break else: - self.module.warn("Unable to process dump.f2fs output '%s'", '\n'.join(dump)) - self.module.fail_json(msg="Unable to process dump.f2fs output for %s" % dev) + raise ValueError(out) return sector_size * sector_count class VFAT(Filesystem): - if platform.system() == 'FreeBSD': - MKFS = "newfs_msdos" - else: - MKFS = 'mkfs.vfat' + INFO = 'fatresize' GROW = 'fatresize' + GROW_MAX_SPACE_FLAGS = ['-s', 'max'] + + def __init__(self, module): + super(VFAT, self).__init__(module) + if platform.system() == 'FreeBSD': + self.MKFS = 'newfs_msdos' + else: + self.MKFS = 'mkfs.vfat' def get_fs_size(self, dev): - cmd = self.module.get_bin_path(self.GROW, required=True) - dummy, output, dummy = self.module.run_command([cmd, '--info', str(dev)], check_rc=True, environ_update=self.LANG_ENV) - for line in output.splitlines()[1:]: + """Get and return size of filesystem, in bytes.""" + cmd = self.module.get_bin_path(self.INFO, required=True) + dummy, out, dummy = self.module.run_command([cmd, '--info', str(dev)], check_rc=True, environ_update=self.LANG_ENV) + fssize = None + for line in out.splitlines()[1:]: param, value = line.split(':', 1) if param.strip() == 'Size': - return int(value.strip()) - self.module.fail_json(msg="fatresize failed to provide filesystem size for %s" % dev) + fssize = int(value.strip()) + break + else: + raise ValueError(out) - def grow_cmd(self, dev): - cmd = self.module.get_bin_path(self.GROW) - return [cmd, "-s", str(dev.size()), str(dev.path)] + return fssize class LVM(Filesystem): MKFS = 'pvcreate' - MKFS_FORCE_FLAGS = '-f' + MKFS_FORCE_FLAGS = ['-f'] + INFO = 'pvs' GROW = 'pvresize' def get_fs_size(self, dev): - cmd = self.module.get_bin_path('pvs', required=True) + """Get and return PV size, in bytes.""" + cmd = self.module.get_bin_path(self.INFO, required=True) dummy, size, dummy = self.module.run_command([cmd, '--noheadings', '-o', 'pv_size', '--units', 'b', '--nosuffix', str(dev)], check_rc=True) - block_count = int(size) - return block_count + pv_size = int(size) + return pv_size class Swap(Filesystem): MKFS = 'mkswap' - MKFS_FORCE_FLAGS = '-f' + MKFS_FORCE_FLAGS = ['-f'] FILESYSTEMS = { @@ -439,6 +469,10 @@ def main(): force = module.params['force'] resizefs = module.params['resizefs'] + mkfs_opts = [] + if opts is not None: + mkfs_opts = opts.split() + changed = False if not os.path.exists(dev): @@ -451,7 +485,7 @@ def main(): dev = Device(module, dev) cmd = module.get_bin_path('blkid', required=True) - rc, raw_fs, err = module.run_command("%s -c /dev/null -o value -s TYPE %s" % (cmd, dev)) + rc, raw_fs, err = module.run_command([cmd, '-c', os.devnull, '-o', 'value', '-s', 'TYPE', str(dev)]) # In case blkid isn't able to identify an existing filesystem, device is considered as empty, # then this existing filesystem would be overwritten even if force isn't enabled. fs = raw_fs.strip() @@ -481,7 +515,7 @@ def main(): module.fail_json(msg="'%s' is already used as %s, use force=yes to overwrite" % (dev, fs), rc=rc, err=err) # create fs - filesystem.create(opts, dev) + filesystem.create(mkfs_opts, dev) changed = True elif fs: diff --git a/plugins/modules/system/iptables_state.py b/plugins/modules/system/iptables_state.py index 5647526819..66ba2c9b20 100644 --- a/plugins/modules/system/iptables_state.py +++ b/plugins/modules/system/iptables_state.py @@ -232,7 +232,7 @@ import filecmp import shutil from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils._text import to_bytes, to_native, to_text +from ansible.module_utils._text import to_bytes, to_native IPTABLES = dict( @@ -262,7 +262,7 @@ def read_state(b_path): lines = text.splitlines() while '' in lines: lines.remove('') - return (lines) + return lines def write_state(b_path, lines, changed): @@ -282,9 +282,9 @@ def write_state(b_path, lines, changed): if b_destdir and not os.path.exists(b_destdir) and not module.check_mode: try: os.makedirs(b_destdir) - except Exception as e: + except Exception as err: module.fail_json( - msg='Error creating %s. Error code: %s. Error description: %s' % (destdir, e[0], e[1]), + msg='Error creating %s: %s' % (destdir, to_native(err)), initial_state=lines) changed = True @@ -295,16 +295,16 @@ def write_state(b_path, lines, changed): if changed and not module.check_mode: try: shutil.copyfile(tmpfile, b_path) - except Exception as e: + except Exception as err: path = to_native(b_path, errors='surrogate_or_strict') module.fail_json( - msg='Error saving state into %s. Error code: %s. Error description: %s' % (path, e[0], e[1]), + msg='Error saving state into %s: %s' % (path, to_native(err)), initial_state=lines) return changed -def initialize_from_null_state(initializer, initcommand, table): +def initialize_from_null_state(initializer, initcommand, fallbackcmd, table): ''' This ensures iptables-state output is suitable for iptables-restore to roll back to it, i.e. iptables-save output is not empty. This also works for the @@ -313,14 +313,17 @@ def initialize_from_null_state(initializer, initcommand, table): if table is None: table = 'filter' - tmpfd, tmpfile = tempfile.mkstemp() - with os.fdopen(tmpfd, 'w') as f: - f.write('*%s\nCOMMIT\n' % table) - - initializer.append(tmpfile) - (rc, out, err) = module.run_command(initializer, check_rc=True) + commandline = list(initializer) + commandline += ['-t', table] + dummy = module.run_command(commandline, check_rc=True) (rc, out, err) = module.run_command(initcommand, check_rc=True) - return (rc, out, err) + if '*%s' % table not in out.splitlines(): + # The last resort. + iptables_input = '*%s\n:OUTPUT ACCEPT\nCOMMIT\n' % table + dummy = module.run_command(fallbackcmd, data=iptables_input, check_rc=True) + (rc, out, err) = module.run_command(initcommand, check_rc=True) + + return rc, out, err def filter_and_format_state(string): @@ -328,13 +331,13 @@ def filter_and_format_state(string): Remove timestamps to ensure idempotence between runs. Also remove counters by default. And return the result as a list. ''' - string = re.sub('((^|\n)# (Generated|Completed)[^\n]*) on [^\n]*', '\\1', string) + string = re.sub(r'((^|\n)# (Generated|Completed)[^\n]*) on [^\n]*', r'\1', string) if not module.params['counters']: - string = re.sub('[[][0-9]+:[0-9]+[]]', '[0:0]', string) + string = re.sub(r'\[[0-9]+:[0-9]+\]', r'[0:0]', string) lines = string.splitlines() while '' in lines: lines.remove('') - return (lines) + return lines def per_table_state(command, state): @@ -347,14 +350,14 @@ def per_table_state(command, state): COMMAND = list(command) if '*%s' % t in state.splitlines(): COMMAND.extend(['--table', t]) - (rc, out, err) = module.run_command(COMMAND, check_rc=True) - out = re.sub('(^|\n)(# Generated|# Completed|[*]%s|COMMIT)[^\n]*' % t, '', out) - out = re.sub(' *[[][0-9]+:[0-9]+[]] *', '', out) + dummy, out, dummy = module.run_command(COMMAND, check_rc=True) + out = re.sub(r'(^|\n)(# Generated|# Completed|[*]%s|COMMIT)[^\n]*' % t, r'', out) + out = re.sub(r' *\[[0-9]+:[0-9]+\] *', r'', out) table = out.splitlines() while '' in table: table.remove('') tables[t] = table - return (tables) + return tables def main(): @@ -402,8 +405,9 @@ def main(): changed = False COMMANDARGS = [] INITCOMMAND = [bin_iptables_save] - INITIALIZER = [bin_iptables_restore] + INITIALIZER = [bin_iptables, '-L', '-n'] TESTCOMMAND = [bin_iptables_restore, '--test'] + FALLBACKCMD = [bin_iptables_restore] if counters: COMMANDARGS.append('--counters') @@ -428,6 +432,7 @@ def main(): INITIALIZER.extend(['--modprobe', modprobe]) INITCOMMAND.extend(['--modprobe', modprobe]) TESTCOMMAND.extend(['--modprobe', modprobe]) + FALLBACKCMD.extend(['--modprobe', modprobe]) SAVECOMMAND = list(COMMANDARGS) SAVECOMMAND.insert(0, bin_iptables_save) @@ -461,15 +466,15 @@ def main(): for t in TABLES: if '*%s' % t in state_to_restore: if len(stdout) == 0 or '*%s' % t not in stdout.splitlines(): - (rc, stdout, stderr) = initialize_from_null_state(INITIALIZER, INITCOMMAND, t) + (rc, stdout, stderr) = initialize_from_null_state(INITIALIZER, INITCOMMAND, FALLBACKCMD, t) elif len(stdout) == 0: - (rc, stdout, stderr) = initialize_from_null_state(INITIALIZER, INITCOMMAND, 'filter') + (rc, stdout, stderr) = initialize_from_null_state(INITIALIZER, INITCOMMAND, FALLBACKCMD, 'filter') elif state == 'restored' and '*%s' % table not in state_to_restore: module.fail_json(msg="Table %s to restore not defined in %s" % (table, path)) elif len(stdout) == 0 or '*%s' % table not in stdout.splitlines(): - (rc, stdout, stderr) = initialize_from_null_state(INITIALIZER, INITCOMMAND, table) + (rc, stdout, stderr) = initialize_from_null_state(INITIALIZER, INITCOMMAND, FALLBACKCMD, table) initial_state = filter_and_format_state(stdout) if initial_state is None: @@ -502,7 +507,7 @@ def main(): if _back is not None: b_back = to_bytes(_back, errors='surrogate_or_strict') - garbage = write_state(b_back, initref_state, changed) + dummy = write_state(b_back, initref_state, changed) BACKCOMMAND = list(MAINCOMMAND) BACKCOMMAND.append(_back) @@ -559,9 +564,7 @@ def main(): if os.path.exists(b_starter): os.remove(b_starter) break - else: - time.sleep(0.01) - continue + time.sleep(0.01) (rc, stdout, stderr) = module.run_command(MAINCOMMAND) if 'Another app is currently holding the xtables lock' in stderr: @@ -579,7 +582,7 @@ def main(): (rc, stdout, stderr) = module.run_command(SAVECOMMAND, check_rc=True) restored_state = filter_and_format_state(stdout) - if restored_state != initref_state and restored_state != initial_state: + if restored_state not in (initref_state, initial_state): if module.check_mode: changed = True else: @@ -609,7 +612,7 @@ def main(): # timeout # * task attribute 'poll' equals 0 # - for x in range(_timeout): + for dummy in range(_timeout): if os.path.exists(b_back): time.sleep(1) continue diff --git a/plugins/modules/system/java_cert.py b/plugins/modules/system/java_cert.py index ad56358034..1c507f9277 100644 --- a/plugins/modules/system/java_cert.py +++ b/plugins/modules/system/java_cert.py @@ -278,7 +278,7 @@ 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_err, + module.fail_json(msg="Internal module failure, cannot extract public certificate from pkcs12, error: %s" % export_stdout, rc=export_rc) with open(dest, 'w') as f: @@ -498,7 +498,7 @@ def main(): if pkcs12_path: # Extracting certificate with openssl - _export_public_cert_from_pkcs12(module, executable, pkcs12_path, cert_alias, pkcs12_pass, new_certificate) + _export_public_cert_from_pkcs12(module, executable, pkcs12_path, pkcs12_alias, pkcs12_pass, new_certificate) elif path: # Extracting the X509 digest is a bit easier. Keytool will print the PEM diff --git a/plugins/modules/system/java_keystore.py b/plugins/modules/system/java_keystore.py index ebfe6abdd7..8293801f1b 100644 --- a/plugins/modules/system/java_keystore.py +++ b/plugins/modules/system/java_keystore.py @@ -88,9 +88,19 @@ options: description: - Mode the file should be. required: false + ssl_backend: + description: + - Backend for loading private keys and certificates. + type: str + default: openssl + choices: + - openssl + - cryptography + version_added: 3.1.0 requirements: - - openssl in PATH + - openssl in PATH (when I(ssl_backend=openssl)) - keytool in PATH + - cryptography >= 3.0 (when I(ssl_backend=cryptography)) author: - Guillaume Grossetie (@Mogztter) - quidame (@quidame) @@ -164,55 +174,281 @@ import os import re import tempfile -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import PY2 +from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text + +try: + from cryptography.hazmat.primitives.serialization.pkcs12 import serialize_key_and_certificates + from cryptography.hazmat.primitives.serialization import ( + BestAvailableEncryption, + NoEncryption, + load_pem_private_key, + load_der_private_key, + ) + from cryptography.x509 import ( + load_pem_x509_certificate, + load_der_x509_certificate, + ) + from cryptography.hazmat.primitives import hashes + from cryptography.exceptions import UnsupportedAlgorithm + from cryptography.hazmat.backends.openssl import backend + HAS_CRYPTOGRAPHY_PKCS12 = True +except ImportError: + HAS_CRYPTOGRAPHY_PKCS12 = False -def read_certificate_fingerprint(module, openssl_bin, certificate_path): - current_certificate_fingerprint_cmd = [openssl_bin, "x509", "-noout", "-in", certificate_path, "-fingerprint", "-sha256"] - (rc, current_certificate_fingerprint_out, current_certificate_fingerprint_err) = run_commands(module, current_certificate_fingerprint_cmd) - if rc != 0: - return module.fail_json(msg=current_certificate_fingerprint_out, - err=current_certificate_fingerprint_err, - cmd=current_certificate_fingerprint_cmd, - rc=rc) +class JavaKeystore: + def __init__(self, module): + self.module = module - current_certificate_match = re.search(r"=([\w:]+)", current_certificate_fingerprint_out) - if not current_certificate_match: - return module.fail_json(msg="Unable to find the current certificate fingerprint in %s" % current_certificate_fingerprint_out, - cmd=current_certificate_fingerprint_cmd, - rc=rc) + self.keytool_bin = module.get_bin_path('keytool', True) - return current_certificate_match.group(1) - - -def read_stored_certificate_fingerprint(module, keytool_bin, alias, keystore_path, keystore_password): - stored_certificate_fingerprint_cmd = [keytool_bin, "-list", "-alias", alias, "-keystore", keystore_path, "-storepass:env", "STOREPASS", "-v"] - (rc, stored_certificate_fingerprint_out, stored_certificate_fingerprint_err) = run_commands( - module, stored_certificate_fingerprint_cmd, environ_update=dict(STOREPASS=keystore_password)) - if rc != 0: - if "keytool error: java.lang.Exception: Alias <%s> does not exist" % alias in stored_certificate_fingerprint_out: - return "alias mismatch" - if re.match(r'keytool error: java\.io\.IOException: [Kk]eystore( was tampered with, or)? password was incorrect', - stored_certificate_fingerprint_out): - return "password mismatch" - return module.fail_json(msg=stored_certificate_fingerprint_out, - err=stored_certificate_fingerprint_err, - cmd=stored_certificate_fingerprint_cmd, - rc=rc) - - stored_certificate_match = re.search(r"SHA256: ([\w:]+)", stored_certificate_fingerprint_out) - if not stored_certificate_match: - return module.fail_json(msg="Unable to find the stored certificate fingerprint in %s" % stored_certificate_fingerprint_out, - cmd=stored_certificate_fingerprint_cmd, - rc=rc) - - return stored_certificate_match.group(1) - - -def run_commands(module, cmd, data=None, environ_update=None, check_rc=False): - return module.run_command(cmd, check_rc=check_rc, data=data, environ_update=environ_update) + self.certificate = module.params['certificate'] + self.keypass = module.params['private_key_passphrase'] + self.keystore_path = module.params['dest'] + self.name = module.params['name'] + self.password = module.params['password'] + self.private_key = module.params['private_key'] + self.ssl_backend = module.params['ssl_backend'] + + if self.ssl_backend == 'openssl': + self.openssl_bin = module.get_bin_path('openssl', True) + else: + if not HAS_CRYPTOGRAPHY_PKCS12: + self.module.fail_json(msg=missing_required_lib('cryptography >= 3.0')) + + if module.params['certificate_path'] is None: + self.certificate_path = create_file(self.certificate) + self.module.add_cleanup_file(self.certificate_path) + else: + self.certificate_path = module.params['certificate_path'] + + if module.params['private_key_path'] is None: + self.private_key_path = create_file(self.private_key) + self.module.add_cleanup_file(self.private_key_path) + else: + self.private_key_path = module.params['private_key_path'] + + def update_permissions(self): + try: + file_args = self.module.load_file_common_arguments(self.module.params, path=self.keystore_path) + except TypeError: + # The path argument is only supported in Ansible-base 2.10+. Fall back to + # pre-2.10 behavior for older Ansible versions. + self.module.params['path'] = self.keystore_path + file_args = self.module.load_file_common_arguments(self.module.params) + return self.module.set_fs_attributes_if_different(file_args, False) + + def read_certificate_fingerprint(self, cert_format='PEM'): + if self.ssl_backend == 'cryptography': + if cert_format == 'PEM': + cert_loader = load_pem_x509_certificate + else: + cert_loader = load_der_x509_certificate + + try: + with open(self.certificate_path, 'rb') as cert_file: + cert = cert_loader( + cert_file.read(), + backend=backend + ) + except (OSError, ValueError) as e: + self.module.fail_json(msg="Unable to read the provided certificate: %s" % to_native(e)) + + fp = hex_decode(cert.fingerprint(hashes.SHA256())).upper() + fingerprint = ':'.join([fp[i:i + 2] for i in range(0, len(fp), 2)]) + else: + current_certificate_fingerprint_cmd = [ + self.openssl_bin, "x509", "-noout", "-in", self.certificate_path, "-fingerprint", "-sha256" + ] + (rc, current_certificate_fingerprint_out, current_certificate_fingerprint_err) = self.module.run_command( + current_certificate_fingerprint_cmd, + environ_update=None, + check_rc=False + ) + if rc != 0: + return self.module.fail_json( + msg=current_certificate_fingerprint_out, + err=current_certificate_fingerprint_err, + cmd=current_certificate_fingerprint_cmd, + rc=rc + ) + + current_certificate_match = re.search(r"=([\w:]+)", current_certificate_fingerprint_out) + if not current_certificate_match: + return self.module.fail_json( + msg="Unable to find the current certificate fingerprint in %s" % ( + current_certificate_fingerprint_out + ), + cmd=current_certificate_fingerprint_cmd, + rc=rc + ) + + fingerprint = current_certificate_match.group(1) + return fingerprint + + def read_stored_certificate_fingerprint(self): + stored_certificate_fingerprint_cmd = [ + self.keytool_bin, "-list", "-alias", self.name, + "-keystore", self.keystore_path, "-v" + ] + (rc, stored_certificate_fingerprint_out, stored_certificate_fingerprint_err) = self.module.run_command( + stored_certificate_fingerprint_cmd, data=self.password, check_rc=False) + if rc != 0: + if "keytool error: java.lang.Exception: Alias <%s> does not exist" % self.name \ + in stored_certificate_fingerprint_out: + return "alias mismatch" + if re.match( + r'keytool error: java\.io\.IOException: ' + + '[Kk]eystore( was tampered with, or)? password was incorrect', + stored_certificate_fingerprint_out + ): + return "password mismatch" + return self.module.fail_json( + msg=stored_certificate_fingerprint_out, + err=stored_certificate_fingerprint_err, + cmd=stored_certificate_fingerprint_cmd, + rc=rc + ) + + stored_certificate_match = re.search(r"SHA256: ([\w:]+)", stored_certificate_fingerprint_out) + if not stored_certificate_match: + return self.module.fail_json( + msg="Unable to find the stored certificate fingerprint in %s" % stored_certificate_fingerprint_out, + cmd=stored_certificate_fingerprint_cmd, + rc=rc + ) + + return stored_certificate_match.group(1) + + def cert_changed(self): + current_certificate_fingerprint = self.read_certificate_fingerprint() + stored_certificate_fingerprint = self.read_stored_certificate_fingerprint() + return current_certificate_fingerprint != stored_certificate_fingerprint + + def cryptography_create_pkcs12_bundle(self, keystore_p12_path, key_format='PEM', cert_format='PEM'): + if key_format == 'PEM': + key_loader = load_pem_private_key + else: + key_loader = load_der_private_key + + if cert_format == 'PEM': + cert_loader = load_pem_x509_certificate + else: + cert_loader = load_der_x509_certificate + + try: + with open(self.private_key_path, 'rb') as key_file: + private_key = key_loader( + key_file.read(), + password=to_bytes(self.keypass), + backend=backend + ) + except TypeError: + # Re-attempt with no password to match existing behavior + try: + with open(self.private_key_path, 'rb') as key_file: + private_key = key_loader( + key_file.read(), + password=None, + backend=backend + ) + except (OSError, TypeError, ValueError, UnsupportedAlgorithm) as e: + self.module.fail_json( + msg="The following error occurred while loading the provided private_key: %s" % to_native(e) + ) + except (OSError, ValueError, UnsupportedAlgorithm) as e: + self.module.fail_json( + msg="The following error occurred while loading the provided private_key: %s" % to_native(e) + ) + try: + with open(self.certificate_path, 'rb') as cert_file: + cert = cert_loader( + cert_file.read(), + backend=backend + ) + except (OSError, ValueError, UnsupportedAlgorithm) as e: + self.module.fail_json( + msg="The following error occurred while loading the provided certificate: %s" % to_native(e) + ) + + if self.password: + encryption = BestAvailableEncryption(to_bytes(self.password)) + else: + encryption = NoEncryption() + + pkcs12_bundle = serialize_key_and_certificates( + name=to_bytes(self.name), + key=private_key, + cert=cert, + cas=None, + encryption_algorithm=encryption + ) + + with open(keystore_p12_path, 'wb') as p12_file: + p12_file.write(pkcs12_bundle) + + def openssl_create_pkcs12_bundle(self, keystore_p12_path): + export_p12_cmd = [self.openssl_bin, "pkcs12", "-export", "-name", self.name, "-in", self.certificate_path, + "-inkey", self.private_key_path, "-out", keystore_p12_path, "-passout", "stdin"] + + # when keypass is provided, add -passin + cmd_stdin = "" + if self.keypass: + export_p12_cmd.append("-passin") + export_p12_cmd.append("stdin") + cmd_stdin = "%s\n" % self.keypass + cmd_stdin += "%s\n%s" % (self.password, self.password) + + (rc, export_p12_out, dummy) = self.module.run_command( + export_p12_cmd, data=cmd_stdin, environ_update=None, check_rc=False + ) + + if rc != 0: + self.module.fail_json(msg=export_p12_out, cmd=export_p12_cmd, rc=rc) + + def create(self): + if self.module.check_mode: + return {'changed': True} + + if os.path.exists(self.keystore_path): + os.remove(self.keystore_path) + + keystore_p12_path = create_path() + self.module.add_cleanup_file(keystore_p12_path) + + if self.ssl_backend == 'cryptography': + self.cryptography_create_pkcs12_bundle(keystore_p12_path) + else: + self.openssl_create_pkcs12_bundle(keystore_p12_path) + + import_keystore_cmd = [self.keytool_bin, "-importkeystore", + "-destkeystore", self.keystore_path, + "-srckeystore", keystore_p12_path, + "-srcstoretype", "pkcs12", + "-alias", self.name, + "-noprompt"] + + (rc, import_keystore_out, dummy) = self.module.run_command( + import_keystore_cmd, data='%s\n%s\n%s' % (self.password, self.password, self.password), check_rc=False + ) + if rc != 0: + return self.module.fail_json(msg=import_keystore_out, cmd=import_keystore_cmd, rc=rc) + + self.update_permissions() + return { + 'changed': True, + 'msg': import_keystore_out, + 'cmd': import_keystore_cmd, + 'rc': rc + } + + def exists(self): + return os.path.exists(self.keystore_path) +# Utility functions def create_path(): dummy, tmpfile = tempfile.mkstemp() os.remove(tmpfile) @@ -226,123 +462,11 @@ def create_file(content): return tmpfile -def create_tmp_certificate(module): - return create_file(module.params['certificate']) - - -def create_tmp_private_key(module): - return create_file(module.params['private_key']) - - -def cert_changed(module, openssl_bin, keytool_bin, keystore_path, keystore_pass, alias): - certificate_path = module.params['certificate_path'] - if certificate_path is None: - certificate_path = create_tmp_certificate(module) - try: - current_certificate_fingerprint = read_certificate_fingerprint(module, openssl_bin, certificate_path) - stored_certificate_fingerprint = read_stored_certificate_fingerprint(module, keytool_bin, alias, keystore_path, keystore_pass) - return current_certificate_fingerprint != stored_certificate_fingerprint - finally: - if module.params['certificate_path'] is None: - os.remove(certificate_path) - - -def create_jks(module, name, openssl_bin, keytool_bin, keystore_path, password, keypass): - if module.check_mode: - return module.exit_json(changed=True) - - certificate_path = module.params['certificate_path'] - if certificate_path is None: - certificate_path = create_tmp_certificate(module) - - private_key_path = module.params['private_key_path'] - if private_key_path is None: - private_key_path = create_tmp_private_key(module) - - keystore_p12_path = create_path() - - try: - if os.path.exists(keystore_path): - os.remove(keystore_path) - - export_p12_cmd = [openssl_bin, "pkcs12", "-export", "-name", name, "-in", certificate_path, - "-inkey", private_key_path, "-out", keystore_p12_path, "-passout", "stdin"] - - # when keypass is provided, add -passin - cmd_stdin = "" - if keypass: - export_p12_cmd.append("-passin") - export_p12_cmd.append("stdin") - cmd_stdin = "%s\n" % keypass - cmd_stdin += "%s\n%s" % (password, password) - - (rc, export_p12_out, dummy) = run_commands(module, export_p12_cmd, data=cmd_stdin) - if rc != 0: - return module.fail_json(msg=export_p12_out, - cmd=export_p12_cmd, - rc=rc) - - import_keystore_cmd = [keytool_bin, "-importkeystore", - "-destkeystore", keystore_path, - "-srckeystore", keystore_p12_path, - "-srcstoretype", "pkcs12", - "-alias", name, - "-deststorepass:env", "STOREPASS", - "-srcstorepass:env", "STOREPASS", - "-noprompt"] - - (rc, import_keystore_out, dummy) = run_commands(module, import_keystore_cmd, data=None, - environ_update=dict(STOREPASS=password)) - if rc != 0: - return module.fail_json(msg=import_keystore_out, - cmd=import_keystore_cmd, - rc=rc) - - update_jks_perm(module, keystore_path) - return module.exit_json(changed=True, - msg=import_keystore_out, - cmd=import_keystore_cmd, - rc=rc) - finally: - if module.params['certificate_path'] is None: - os.remove(certificate_path) - if module.params['private_key_path'] is None: - os.remove(private_key_path) - os.remove(keystore_p12_path) - - -def update_jks_perm(module, keystore_path): - try: - file_args = module.load_file_common_arguments(module.params, path=keystore_path) - except TypeError: - # The path argument is only supported in Ansible-base 2.10+. Fall back to - # pre-2.10 behavior for older Ansible versions. - module.params['path'] = keystore_path - file_args = module.load_file_common_arguments(module.params) - module.set_fs_attributes_if_different(file_args, False) - - -def process_jks(module): - name = module.params['name'] - password = module.params['password'] - keypass = module.params['private_key_passphrase'] - keystore_path = module.params['dest'] - force = module.params['force'] - openssl_bin = module.get_bin_path('openssl', True) - keytool_bin = module.get_bin_path('keytool', True) - - if os.path.exists(keystore_path): - if force: - create_jks(module, name, openssl_bin, keytool_bin, keystore_path, password, keypass) - else: - if cert_changed(module, openssl_bin, keytool_bin, keystore_path, password, name): - create_jks(module, name, openssl_bin, keytool_bin, keystore_path, password, keypass) - else: - if not module.check_mode: - update_jks_perm(module, keystore_path) - module.exit_json(changed=False) +def hex_decode(s): + if PY2: + return s.decode('hex') else: - create_jks(module, name, openssl_bin, keytool_bin, keystore_path, password, keypass) + return s.hex() class ArgumentSpec(object): @@ -358,6 +482,7 @@ class ArgumentSpec(object): private_key_path=dict(type='path', no_log=False), private_key_passphrase=dict(type='str', no_log=True), password=dict(type='str', required=True, no_log=True), + ssl_backend=dict(type='str', default='openssl', choices=['openssl', 'cryptography']), force=dict(type='bool', default=False), ) choose_between = ( @@ -379,7 +504,19 @@ def main(): add_file_common_args=spec.add_file_common_args, ) module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C') - process_jks(module) + + result = dict() + jks = JavaKeystore(module) + + if jks.exists(): + if module.params['force'] or jks.cert_changed(): + result = jks.create() + else: + result['changed'] = jks.update_permissions() + else: + result = jks.create() + + module.exit_json(**result) if __name__ == '__main__': diff --git a/plugins/modules/system/lvol.py b/plugins/modules/system/lvol.py index 8dc3fac7f5..fafa7db38a 100644 --- a/plugins/modules/system/lvol.py +++ b/plugins/modules/system/lvol.py @@ -389,7 +389,7 @@ def main(): # Get information on volume group requested vgs_cmd = module.get_bin_path("vgs", required=True) rc, current_vgs, err = module.run_command( - "%s --noheadings --nosuffix -o vg_name,size,free,vg_extent_size --units %s --separator ';' %s" % (vgs_cmd, unit, vg)) + "%s --noheadings --nosuffix -o vg_name,size,free,vg_extent_size --units %s --separator ';' %s" % (vgs_cmd, unit.lower(), vg)) if rc != 0: if state == 'absent': @@ -403,7 +403,7 @@ def main(): # Get information on logical volume requested lvs_cmd = module.get_bin_path("lvs", required=True) rc, current_lvs, err = module.run_command( - "%s -a --noheadings --nosuffix -o lv_name,size,lv_attr --units %s --separator ';' %s" % (lvs_cmd, unit, vg)) + "%s -a --noheadings --nosuffix -o lv_name,size,lv_attr --units %s --separator ';' %s" % (lvs_cmd, unit.lower(), vg)) if rc != 0: if state == 'absent': @@ -505,16 +505,13 @@ def main(): else: # size_whole == 'FREE': size_requested = size_percent * this_vg['free'] / 100 - # from LVEXTEND(8) - The resulting value is rounded upward. - # from LVREDUCE(8) - The resulting value for the substraction is rounded downward, for the absolute size it is rounded upward. if size_operator == '+': size_requested += this_lv['size'] - size_requested += this_vg['ext_size'] - (size_requested % this_vg['ext_size']) elif size_operator == '-': size_requested = this_lv['size'] - size_requested - size_requested -= (size_requested % this_vg['ext_size']) - else: - size_requested += this_vg['ext_size'] - (size_requested % this_vg['ext_size']) + + # According to latest documentation (LVM2-2.03.11) all tools round down + size_requested -= (size_requested % this_vg['ext_size']) if this_lv['size'] < size_requested: if (size_free > 0) and (size_free >= (size_requested - this_lv['size'])): diff --git a/plugins/modules/system/open_iscsi.py b/plugins/modules/system/open_iscsi.py index 222bb82f3d..570925f6a4 100644 --- a/plugins/modules/system/open_iscsi.py +++ b/plugins/modules/system/open_iscsi.py @@ -57,6 +57,11 @@ options: - Whether the target node should be automatically connected at startup. type: bool aliases: [ automatic ] + auto_portal_startup: + description: + - Whether the target node portal should be automatically connected at startup. + type: bool + version_added: 3.2.0 discover: description: - Whether the list of target nodes on the portal should be @@ -102,10 +107,18 @@ EXAMPLES = r''' community.general.open_iscsi: login: no target: iqn.1986-03.com.sun:02:f8c1f9e0-c3ec-ec84-c9c9-8bfb0cd5de3d + +- name: Override and disable automatic portal login on specific portal + community.general.open_iscsi: + login: false + portal: 10.1.1.250 + auto_portal_startup: false + target: iqn.1986-03.com.sun:02:f8c1f9e0-c3ec-ec84-c9c9-8bfb0cd5de3d ''' import glob import os +import re import socket import time @@ -158,12 +171,18 @@ def iscsi_discover(module, portal, port): module.fail_json(cmd=cmd, rc=rc, msg=err) -def target_loggedon(module, target): +def target_loggedon(module, target, portal=None, port=None): cmd = '%s --mode session' % iscsiadm_cmd (rc, out, err) = module.run_command(cmd) + if portal is None: + portal = "" + if port is None: + port = "" + if rc == 0: - return target in out + search_re = "%s:%s.*%s" % (re.escape(portal), port, re.escape(target)) + return re.search(search_re, out) is not None elif rc == 21: return False else: @@ -219,8 +238,14 @@ def target_device_node(module, target): return devdisks -def target_isauto(module, target): +def target_isauto(module, target, portal=None, port=None): cmd = '%s --mode node --targetname %s' % (iscsiadm_cmd, target) + + if portal is not None: + if port is not None: + portal = '%s:%s' % (portal, port) + cmd = '%s --portal %s' % (cmd, portal) + (rc, out, err) = module.run_command(cmd) if rc == 0: @@ -233,16 +258,28 @@ def target_isauto(module, target): module.fail_json(cmd=cmd, rc=rc, msg=err) -def target_setauto(module, target): +def target_setauto(module, target, portal=None, port=None): cmd = '%s --mode node --targetname %s --op=update --name node.startup --value automatic' % (iscsiadm_cmd, target) + + if portal is not None: + if port is not None: + portal = '%s:%s' % (portal, port) + cmd = '%s --portal %s' % (cmd, portal) + (rc, out, err) = module.run_command(cmd) if rc > 0: module.fail_json(cmd=cmd, rc=rc, msg=err) -def target_setmanual(module, target): +def target_setmanual(module, target, portal=None, port=None): cmd = '%s --mode node --targetname %s --op=update --name node.startup --value manual' % (iscsiadm_cmd, target) + + if portal is not None: + if port is not None: + portal = '%s:%s' % (portal, port) + cmd = '%s --portal %s' % (cmd, portal) + (rc, out, err) = module.run_command(cmd) if rc > 0: @@ -265,6 +302,7 @@ def main(): # actions login=dict(type='bool', aliases=['state']), auto_node_startup=dict(type='bool', aliases=['automatic']), + auto_portal_startup=dict(type='bool'), discover=dict(type='bool', default=False), show_nodes=dict(type='bool', default=False), ), @@ -288,6 +326,7 @@ def main(): port = module.params['port'] login = module.params['login'] automatic = module.params['auto_node_startup'] + automatic_portal = module.params['auto_portal_startup'] discover = module.params['discover'] show_nodes = module.params['show_nodes'] @@ -333,7 +372,7 @@ def main(): result['nodes'] = nodes if login is not None: - loggedon = target_loggedon(module, target) + loggedon = target_loggedon(module, target, portal, port) if (login and loggedon) or (not login and not loggedon): result['changed'] |= False if login: @@ -368,6 +407,22 @@ def main(): result['changed'] |= True result['automatic_changed'] = True + if automatic_portal is not None: + isauto = target_isauto(module, target, portal, port) + if (automatic_portal and isauto) or (not automatic_portal and not isauto): + result['changed'] |= False + result['automatic_portal_changed'] = False + elif not check: + if automatic_portal: + target_setauto(module, target, portal, port) + else: + target_setmanual(module, target, portal, port) + result['changed'] |= True + result['automatic_portal_changed'] = True + else: + result['changed'] |= True + result['automatic_portal_changed'] = True + module.exit_json(**result) diff --git a/plugins/modules/system/parted.py b/plugins/modules/system/parted.py index bbb8c1408b..3796cfc40b 100644 --- a/plugins/modules/system/parted.py +++ b/plugins/modules/system/parted.py @@ -100,7 +100,7 @@ options: fs_type: description: - If specified and the partition does not exist, will set filesystem type to given partition. - - Parameter optional, but see notes below about negative negative C(part_start) values. + - Parameter optional, but see notes below about negative C(part_start) values. type: str version_added: '0.2.0' resize: diff --git a/plugins/modules/system/puppet.py b/plugins/modules/system/puppet.py index 309da290d0..b83ef89aa5 100644 --- a/plugins/modules/system/puppet.py +++ b/plugins/modules/system/puppet.py @@ -54,7 +54,8 @@ options: logdest: description: - Where the puppet logs should go, if puppet apply is being used. - - C(all) will go to both C(stdout) and C(syslog). + - C(all) will go to both C(console) and C(syslog). + - C(stdout) will be deprecated and replaced by C(console). type: str choices: [ all, stdout, syslog ] default: stdout @@ -127,7 +128,7 @@ EXAMPLES = r''' community.general.puppet: noop: yes -- name: Run a manifest with debug, log to both syslog and stdout, specify module path +- name: Run a manifest with debug, log to both syslog and console, specify module path community.general.puppet: modulepath: /etc/puppet/modules:/opt/stack/puppet-modules:/usr/share/openstack-puppet/modules logdest: all @@ -269,7 +270,7 @@ def main(): if p['logdest'] == 'syslog': cmd += "--logdest syslog " if p['logdest'] == 'all': - cmd += " --logdest syslog --logdest stdout" + cmd += " --logdest syslog --logdest console" if p['modulepath']: cmd += "--modulepath='%s'" % p['modulepath'] if p['environment']: diff --git a/plugins/modules/system/ssh_config.py b/plugins/modules/system/ssh_config.py index 943f6b44fc..be177baaaf 100644 --- a/plugins/modules/system/ssh_config.py +++ b/plugins/modules/system/ssh_config.py @@ -209,6 +209,8 @@ class SSHConfig(): hosts_removed = [] hosts_added = [] + hosts_result = [host for host in hosts_result if host['host'] == self.host] + if hosts_result: for host in hosts_result: if state == 'absent': diff --git a/plugins/modules/system/xfconf.py b/plugins/modules/system/xfconf.py index f2975df050..dc560e7775 100644 --- a/plugins/modules/system/xfconf.py +++ b/plugins/modules/system/xfconf.py @@ -258,7 +258,7 @@ class XFConfProperty(CmdMixin, StateMixin, ModuleHelper): params = ['channel', 'property', {'create': True}] if self.vars.is_array: - params.append({'is_array': True}) + params.append('is_array') params.append({'values_and_types': (self.vars.value, value_type)}) if not self.module.check_mode: diff --git a/plugins/modules/web_infrastructure/jenkins_plugin.py b/plugins/modules/web_infrastructure/jenkins_plugin.py index c9946023ac..be335fcfd3 100644 --- a/plugins/modules/web_infrastructure/jenkins_plugin.py +++ b/plugins/modules/web_infrastructure/jenkins_plugin.py @@ -696,7 +696,8 @@ class JenkinsPlugin(object): self._get_url_data( url, msg_status="Plugin not found. %s" % url, - msg_exception="%s has failed." % msg) + msg_exception="%s has failed." % msg, + method="POST") def main(): diff --git a/plugins/modules/web_infrastructure/jira.py b/plugins/modules/web_infrastructure/jira.py index 6acf0c7f51..4c10974126 100644 --- a/plugins/modules/web_infrastructure/jira.py +++ b/plugins/modules/web_infrastructure/jira.py @@ -86,6 +86,25 @@ options: - The comment text to add. - Note that JIRA may not allow changing field values on specific transitions or states. + comment_visibility: + type: dict + description: + - Used to specify comment comment visibility. + - See U(https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-comments/#api-rest-api-2-issue-issueidorkey-comment-post) for details. + suboptions: + type: + description: + - Use type to specify which of the JIRA visibility restriction types will be used. + type: str + required: true + choices: [group, role] + value: + description: + - Use value to specify value corresponding to the type of visibility restriction. For example name of the group or role. + type: str + required: true + version_added: '3.2.0' + status: type: str required: false @@ -223,6 +242,18 @@ EXAMPLES = r""" operation: comment comment: A comment added by Ansible +- name: Comment on issue with restricted visibility + community.general.jira: + uri: '{{ server }}' + username: '{{ user }}' + password: '{{ pass }}' + issue: '{{ issue.meta.key }}' + operation: comment + comment: A comment added by Ansible + comment_visibility: + type: role + value: Developers + # Assign an existing issue using edit - name: Assign an issue using free-form fields community.general.jira: @@ -385,6 +416,10 @@ class JIRA(StateModuleHelper): issuetype=dict(type='str', ), issue=dict(type='str', aliases=['ticket']), comment=dict(type='str', ), + comment_visibility=dict(type='dict', options=dict( + type=dict(type='str', choices=['group', 'role'], required=True), + value=dict(type='str', required=True) + )), status=dict(type='str', ), assignee=dict(type='str', ), fields=dict(default={}, type='dict'), @@ -445,6 +480,10 @@ class JIRA(StateModuleHelper): data = { 'body': self.vars.comment } + # if comment_visibility is specified restrict visibility + if self.vars.comment_visibility is not None: + data['visibility'] = self.vars.comment_visibility + url = self.vars.restbase + '/issue/' + self.vars.issue + '/comment' self.vars.meta = self.post(url, data) diff --git a/scripts/inventory/abiquo.py b/scripts/inventory/abiquo.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/apache-libcloud.py b/scripts/inventory/apache-libcloud.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/apstra_aos.py b/scripts/inventory/apstra_aos.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/azure_rm.py b/scripts/inventory/azure_rm.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/brook.py b/scripts/inventory/brook.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/cloudforms.py b/scripts/inventory/cloudforms.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/cobbler.py b/scripts/inventory/cobbler.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/collins.py b/scripts/inventory/collins.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/consul_io.py b/scripts/inventory/consul_io.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/docker.py b/scripts/inventory/docker.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/fleet.py b/scripts/inventory/fleet.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/foreman.py b/scripts/inventory/foreman.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/freeipa.py b/scripts/inventory/freeipa.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/infoblox.py b/scripts/inventory/infoblox.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/jail.py b/scripts/inventory/jail.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/landscape.py b/scripts/inventory/landscape.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/linode.py b/scripts/inventory/linode.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/lxc_inventory.py b/scripts/inventory/lxc_inventory.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/lxd.py b/scripts/inventory/lxd.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/mdt_dynamic_inventory.py b/scripts/inventory/mdt_dynamic_inventory.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/nagios_livestatus.py b/scripts/inventory/nagios_livestatus.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/nagios_ndo.py b/scripts/inventory/nagios_ndo.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/nsot.py b/scripts/inventory/nsot.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/openshift.py b/scripts/inventory/openshift.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/openvz.py b/scripts/inventory/openvz.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/ovirt.py b/scripts/inventory/ovirt.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/ovirt4.py b/scripts/inventory/ovirt4.py old mode 100644 new mode 100755 index afff18dbdb..84b68a1258 --- a/scripts/inventory/ovirt4.py +++ b/scripts/inventory/ovirt4.py @@ -56,6 +56,7 @@ import sys from collections import defaultdict from ansible.module_utils.six.moves import configparser +from ansible.module_utils.six import PY2 import json @@ -106,14 +107,24 @@ def create_connection(): config_path = os.environ.get('OVIRT_INI_PATH', default_path) # Create parser and add ovirt section if it doesn't exist: - config = configparser.SafeConfigParser( - defaults={ - 'ovirt_url': os.environ.get('OVIRT_URL'), - 'ovirt_username': os.environ.get('OVIRT_USERNAME'), - 'ovirt_password': os.environ.get('OVIRT_PASSWORD'), - 'ovirt_ca_file': os.environ.get('OVIRT_CAFILE', ''), - } - ) + if PY2: + config = configparser.SafeConfigParser( + defaults={ + 'ovirt_url': os.environ.get('OVIRT_URL'), + 'ovirt_username': os.environ.get('OVIRT_USERNAME'), + 'ovirt_password': os.environ.get('OVIRT_PASSWORD'), + 'ovirt_ca_file': os.environ.get('OVIRT_CAFILE', ''), + }, allow_no_value=True + ) + else: + config = configparser.ConfigParser( + defaults={ + 'ovirt_url': os.environ.get('OVIRT_URL'), + 'ovirt_username': os.environ.get('OVIRT_USERNAME'), + 'ovirt_password': os.environ.get('OVIRT_PASSWORD'), + 'ovirt_ca_file': os.environ.get('OVIRT_CAFILE', ''), + }, allow_no_value=True + ) if not config.has_section('ovirt'): config.add_section('ovirt') config.read(config_path) diff --git a/scripts/inventory/packet_net.py b/scripts/inventory/packet_net.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/proxmox.py b/scripts/inventory/proxmox.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/rackhd.py b/scripts/inventory/rackhd.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/rax.py b/scripts/inventory/rax.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/rudder.py b/scripts/inventory/rudder.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/scaleway.py b/scripts/inventory/scaleway.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/serf.py b/scripts/inventory/serf.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/softlayer.py b/scripts/inventory/softlayer.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/spacewalk.py b/scripts/inventory/spacewalk.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/ssh_config.py b/scripts/inventory/ssh_config.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/stacki.py b/scripts/inventory/stacki.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/vagrant.py b/scripts/inventory/vagrant.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/vbox.py b/scripts/inventory/vbox.py old mode 100644 new mode 100755 diff --git a/scripts/inventory/zone.py b/scripts/inventory/zone.py old mode 100644 new mode 100755 diff --git a/scripts/vault/azure_vault.py b/scripts/vault/azure_vault.py old mode 100644 new mode 100755 diff --git a/scripts/vault/vault-keyring-client.py b/scripts/vault/vault-keyring-client.py old mode 100644 new mode 100755 diff --git a/scripts/vault/vault-keyring.py b/scripts/vault/vault-keyring.py old mode 100644 new mode 100755 diff --git a/shippable.yml b/shippable.yml deleted file mode 100644 index 7cbbdc24e7..0000000000 --- a/shippable.yml +++ /dev/null @@ -1,48 +0,0 @@ -language: python - -env: - matrix: - - T=none - -matrix: - exclude: - - env: T=none - include: - - env: T=devel/sanity/1 - - env: T=devel/sanity/2 - - env: T=devel/sanity/3 - - env: T=devel/sanity/4 - - - env: T=2.11/sanity/1 - - env: T=2.11/sanity/2 - - env: T=2.11/sanity/3 - - env: T=2.11/sanity/4 - - - env: T=2.10/sanity/1 - - env: T=2.10/sanity/2 - - env: T=2.10/sanity/3 - - env: T=2.10/sanity/4 - - - env: T=2.9/sanity/1 - - env: T=2.9/sanity/2 - - env: T=2.9/sanity/3 - - env: T=2.9/sanity/4 - -branches: - except: - - "*-patch-*" - - "revert-*-*" - - "patchback/backports/*" - -build: - ci: - - tests/utils/shippable/timing.sh tests/utils/shippable/shippable.sh $T - -integrations: - notifications: - - integrationName: email - type: email - on_success: never - on_failure: never - on_start: never - on_pull_request: never diff --git a/tests/config.yml b/tests/config.yml new file mode 100644 index 0000000000..ba0238e305 --- /dev/null +++ b/tests/config.yml @@ -0,0 +1,5 @@ +--- +# See template for more information: +# https://github.com/ansible/ansible/blob/devel/test/lib/ansible_test/config/config.yml +modules: + python_requires: default diff --git a/tests/integration/targets/archive/tasks/main.yml b/tests/integration/targets/archive/tasks/main.yml index 19a1f6af0c..761f9eb7b8 100644 --- a/tests/integration/targets/archive/tasks/main.yml +++ b/tests/integration/targets/archive/tasks/main.yml @@ -174,7 +174,7 @@ - name: Test that the file modes were changed assert: that: - - "archive_02_gz_stat.changed == False " + - archive_02_gz_stat is not changed - "archive_02_gz_stat.stat.mode == '0600'" - "'archived' in archive_bz2_result_02" - "{{ archive_bz2_result_02['archived']| length}} == 3" @@ -199,7 +199,7 @@ - name: Test that the file modes were changed assert: that: - - "archive_02_zip_stat.changed == False" + - archive_02_zip_stat is not changed - "archive_02_zip_stat.stat.mode == '0600'" - "'archived' in archive_zip_result_02" - "{{ archive_zip_result_02['archived']| length}} == 3" @@ -224,7 +224,7 @@ - name: Test that the file modes were changed assert: that: - - "archive_02_bz2_stat.changed == False" + - archive_02_bz2_stat is not changed - "archive_02_bz2_stat.stat.mode == '0600'" - "'archived' in archive_bz2_result_02" - "{{ archive_bz2_result_02['archived']| length}} == 3" @@ -248,7 +248,7 @@ - name: Test that the file modes were changed assert: that: - - "archive_02_xz_stat.changed == False" + - archive_02_xz_stat is not changed - "archive_02_xz_stat.stat.mode == '0600'" - "'archived' in archive_xz_result_02" - "{{ archive_xz_result_02['archived']| length}} == 3" @@ -294,7 +294,7 @@ - name: Assert that nonascii tests succeeded assert: that: - - "nonascii_result_0.changed == true" + - nonascii_result_0 is changed - "nonascii_stat0.stat.exists == true" - name: remove nonascii test @@ -315,7 +315,7 @@ - name: Assert that nonascii tests succeeded assert: that: - - "nonascii_result_1.changed == true" + - nonascii_result_1 is changed - "nonascii_stat_1.stat.exists == true" - name: remove nonascii test @@ -336,7 +336,7 @@ - name: Assert that nonascii tests succeeded assert: that: - - "nonascii_result_1.changed == true" + - nonascii_result_1 is changed - "nonascii_stat_1.stat.exists == true" - name: remove nonascii test @@ -357,12 +357,25 @@ - name: Assert that nonascii tests succeeded assert: that: - - "nonascii_result_2.changed == true" + - nonascii_result_2 is changed - "nonascii_stat_2.stat.exists == true" - name: remove nonascii test file: path="{{ output_dir }}/test-archive-nonascii-くらとみ.zip" state=absent +- name: Test exclusion_patterns option + archive: + path: "{{ output_dir }}/*.txt" + dest: "{{ output_dir }}/test-archive-exclustion-patterns.tgz" + exclusion_patterns: b?r.* + register: exclusion_patterns_result + +- name: Assert that exclusion_patterns only archives included files + assert: + that: + - exclusion_patterns_result is changed + - "'bar.txt' not in exclusion_patterns_result.archived" + - name: Remove backports.lzma if previously installed (pip) pip: name=backports.lzma state=absent when: backports_lzma_pip is changed diff --git a/tests/integration/targets/filesystem/defaults/main.yml b/tests/integration/targets/filesystem/defaults/main.yml index 764b98b6ba..15ef85aa0e 100644 --- a/tests/integration/targets/filesystem/defaults/main.yml +++ b/tests/integration/targets/filesystem/defaults/main.yml @@ -17,7 +17,9 @@ tested_filesystems: ext2: {fssize: 10, grow: True} xfs: {fssize: 20, grow: False} # grow requires a mounted filesystem btrfs: {fssize: 150, grow: False} # grow not implemented + reiserfs: {fssize: 33, grow: False} # grow not implemented vfat: {fssize: 20, grow: True} ocfs2: {fssize: '{{ ocfs2_fssize }}', grow: False} # grow not implemented f2fs: {fssize: '{{ f2fs_fssize|default(60) }}', grow: 'f2fs_version is version("1.10.0", ">=")'} lvm: {fssize: 20, grow: True} + swap: {fssize: 10, grow: False} # grow not implemented diff --git a/tests/integration/targets/filesystem/tasks/create_device.yml b/tests/integration/targets/filesystem/tasks/create_device.yml index e49861e7ca..30fd62e33a 100644 --- a/tests/integration/targets/filesystem/tasks/create_device.yml +++ b/tests/integration/targets/filesystem/tasks/create_device.yml @@ -1,6 +1,9 @@ --- - name: 'Create a "disk" file' - command: 'dd if=/dev/zero of={{ image_file }} bs=1M count={{ fssize }}' + community.general.filesize: + path: '{{ image_file }}' + size: '{{ fssize }}M' + force: true - vars: dev: '{{ image_file }}' @@ -8,26 +11,29 @@ - when: fstype == 'lvm' block: - name: 'Create a loop device for LVM' - command: 'losetup --show -f {{ dev }}' + ansible.builtin.command: + cmd: 'losetup --show -f {{ dev }}' register: loop_device_cmd - - set_fact: + - name: 'Switch to loop device target for further tasks' + ansible.builtin.set_fact: dev: "{{ loop_device_cmd.stdout }}" - include_tasks: '{{ action }}.yml' always: - name: 'Detach loop device used for LVM' - command: 'losetup -d {{ dev }}' - args: + ansible.builtin.command: + cmd: 'losetup -d {{ dev }}' removes: '{{ dev }}' when: fstype == 'lvm' - name: 'Clean correct device for LVM' - set_fact: + ansible.builtin.set_fact: dev: '{{ image_file }}' when: fstype == 'lvm' - - file: + - name: 'Remove disk image file' + ansible.builtin.file: name: '{{ image_file }}' state: absent diff --git a/tests/integration/targets/filesystem/tasks/create_fs.yml b/tests/integration/targets/filesystem/tasks/create_fs.yml index 688a4462db..de1a9f18a0 100644 --- a/tests/integration/targets/filesystem/tasks/create_fs.yml +++ b/tests/integration/targets/filesystem/tasks/create_fs.yml @@ -1,43 +1,58 @@ -- name: filesystem creation - filesystem: +--- +- name: "Create filesystem" + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' register: fs_result -- assert: +- name: "Assert that results are as expected" + ansible.builtin.assert: that: - 'fs_result is changed' - 'fs_result is success' -- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' +- name: "Get UUID of created filesystem" + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid - name: "Check that filesystem isn't created if force isn't used" - filesystem: + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' register: fs2_result -- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' +- name: "Get UUID of the filesystem" + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid2 -- assert: +- name: "Assert that filesystem UUID is not changed" + ansible.builtin.assert: that: - - 'not (fs2_result is changed)' + - 'fs2_result is not changed' - 'fs2_result is success' - 'uuid.stdout == uuid2.stdout' -- name: Check that filesystem is recreated if force is used - filesystem: +- name: "Check that filesystem is recreated if force is used" + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' force: yes register: fs3_result -- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' +- name: "Get UUID of the new filesystem" + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid3 -- assert: +- name: "Assert that filesystem UUID is changed" + # libblkid gets no UUID at all for this fstype on FreeBSD + when: not (ansible_system == 'FreeBSD' and fstype == 'reiserfs') + ansible.builtin.assert: that: - 'fs3_result is changed' - 'fs3_result is success' @@ -46,24 +61,31 @@ - when: 'grow|bool and (fstype != "vfat" or resize_vfat)' block: - - name: increase fake device - shell: 'dd if=/dev/zero bs=1M count=1 >> {{ image_file }}' + - name: "Increase fake device" + community.general.filesize: + path: '{{ image_file }}' + size: '{{ fssize | int + 1 }}M' - - name: Resize loop device for LVM - command: losetup -c {{ dev }} + - name: "Resize loop device for LVM" + ansible.builtin.command: + cmd: 'losetup -c {{ dev }}' when: fstype == 'lvm' - - name: Expand filesystem - filesystem: + - name: "Expand filesystem" + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' resizefs: yes register: fs4_result - - command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + - name: "Get UUID of the filesystem" + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid4 - - assert: + - name: "Assert that filesystem UUID is not changed" + ansible.builtin.assert: that: - 'fs4_result is changed' - 'fs4_result is success' @@ -74,14 +96,15 @@ (fstype == "xfs" and ansible_system == "Linux" and ansible_distribution not in ["CentOS", "Ubuntu"]) block: - - name: Check that resizefs does nothing if device size is not changed - filesystem: + - name: "Check that resizefs does nothing if device size is not changed" + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' resizefs: yes register: fs5_result - - assert: + - name: "Assert that the state did not change" + ansible.builtin.assert: that: - 'fs5_result is not changed' - 'fs5_result is succeeded' diff --git a/tests/integration/targets/filesystem/tasks/main.yml b/tests/integration/targets/filesystem/tasks/main.yml index 44e8c49f61..d836c8a15d 100644 --- a/tests/integration/targets/filesystem/tasks/main.yml +++ b/tests/integration/targets/filesystem/tasks/main.yml @@ -4,9 +4,9 @@ # and should not be used as examples of how to write Ansible roles # #################################################################### -- debug: +- ansible.builtin.debug: msg: '{{ role_name }}' -- debug: +- ansible.builtin.debug: msg: '{{ role_path|basename }}' - import_tasks: setup.yml @@ -27,29 +27,35 @@ grow: '{{ item.0.value.grow }}' action: '{{ item.1 }}' when: - - 'not (item.0.key == "btrfs" and ansible_system == "FreeBSD")' # btrfs not available on FreeBSD - # On Ubuntu trusty, blkid is unable to identify filesystem smaller than 256Mo, see - # https://www.kernel.org/pub/linux/utils/util-linux/v2.21/v2.21-ChangeLog - # https://anonscm.debian.org/cgit/collab-maint/pkg-util-linux.git/commit/?id=04f7020eadf31efc731558df92daa0a1c336c46c - - 'not (item.0.key == "btrfs" and (ansible_distribution == "Ubuntu" and ansible_distribution_release == "trusty"))' - - 'not (item.0.key == "btrfs" and (ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version("8", ">=")))' - - 'not (item.0.key == "lvm" and ansible_system == "FreeBSD")' # LVM not available on FreeBSD - - 'not (item.0.key == "lvm" and ansible_virtualization_type in ["docker", "container", "containerd"])' # Tests use losetup which can not be used inside unprivileged container - - 'not (item.0.key == "ocfs2" and ansible_os_family != "Debian")' # ocfs2 only available on Debian based distributions - - 'not (item.0.key == "f2fs" and ansible_system == "FreeBSD")' - # f2fs-tools package not available with RHEL/CentOS - - 'not (item.0.key == "f2fs" and ansible_distribution in ["CentOS", "RedHat"])' - # On Ubuntu trusty, blkid (2.20.1) is unable to identify F2FS filesystem. blkid handles F2FS since v2.23, see: - # https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.23/v2.23-ReleaseNotes - - 'not (item.0.key == "f2fs" and ansible_distribution == "Ubuntu" and ansible_distribution_version is version("14.04", "<="))' - - 'not (item.1 == "overwrite_another_fs" and ansible_system == "FreeBSD")' + # FreeBSD limited support + # Not available: btrfs, lvm, f2fs, ocfs2 + # All BSD systems use swap fs, but only Linux needs mkswap + # Supported: ext2/3/4 (e2fsprogs), xfs (xfsprogs), reiserfs (progsreiserfs), vfat + - 'not (ansible_system == "FreeBSD" and item.0.key in ["btrfs", "f2fs", "swap", "lvm", "ocfs2"])' + # Available on FreeBSD but not on testbed (util-linux conflicts with e2fsprogs): wipefs, mkfs.minix + - 'not (ansible_system == "FreeBSD" and item.1 in ["overwrite_another_fs", "remove_fs"])' + + # Other limitations and corner cases + + # f2fs-tools and reiserfs-utils packages not available with RHEL/CentOS on CI + - 'not (ansible_distribution in ["CentOS", "RedHat"] and item.0.key in ["f2fs", "reiserfs"])' + - 'not (ansible_os_family == "RedHat" and ansible_distribution_major_version is version("8", ">=") and + item.0.key == "btrfs")' + # ocfs2 only available on Debian based distributions + - 'not (item.0.key == "ocfs2" and ansible_os_family != "Debian")' + # Tests use losetup which can not be used inside unprivileged container + - 'not (item.0.key == "lvm" and ansible_virtualization_type in ["docker", "container", "containerd"])' - - 'not (item.1 == "remove_fs" and ansible_system == "FreeBSD")' # util-linux not available on FreeBSD # On CentOS 6 shippable containers, wipefs seems unable to remove vfat signatures - - 'not (item.1 == "remove_fs" and item.0.key == "vfat" and ansible_distribution == "CentOS" and - ansible_distribution_version is version("7.0", "<"))' + - 'not (ansible_distribution == "CentOS" and ansible_distribution_version is version("7.0", "<") and + item.1 == "remove_fs" and item.0.key == "vfat")' + # On same systems, mkfs.minix (unhandled by the module) can't find the device/file + - 'not (ansible_distribution == "CentOS" and ansible_distribution_version is version("7.0", "<") and + item.1 == "overwrite_another_fs")' # The xfsprogs package on newer versions of OpenSUSE (15+) require Python 3, we skip this on our Python 2 container # OpenSUSE 42.3 Python2 and the other py3 containers are not affected so we will continue to run that - - 'not (item.0.key == "xfs" and ansible_os_family == "Suse" and ansible_python.version.major == 2 and ansible_distribution_major_version|int != 42)' + - 'not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and + item.0.key == "xfs" and ansible_python.version.major == 2)' + loop: "{{ query('dict', tested_filesystems)|product(['create_fs', 'overwrite_another_fs', 'remove_fs'])|list }}" diff --git a/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml b/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml index 671d9b0bea..4bf92836bb 100644 --- a/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml +++ b/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml @@ -1,40 +1,55 @@ --- - name: 'Recreate "disk" file' - command: 'dd if=/dev/zero of={{ image_file }} bs=1M count={{ fssize }}' + community.general.filesize: + path: '{{ image_file }}' + size: '{{ fssize }}M' + force: true -- name: 'Create a swap filesystem' - command: 'mkswap {{ dev }}' +- name: 'Create a minix filesystem' + ansible.builtin.command: + cmd: 'mkfs.minix {{ dev }}' -- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' +- name: 'Get UUID of the new filesystem' + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid - name: "Check that an existing filesystem (not handled by this module) isn't overwritten when force isn't used" - filesystem: + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' register: fs_result ignore_errors: True -- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' +- name: 'Get UUID of the filesystem' + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid2 -- assert: +- name: 'Assert that module failed and filesystem UUID is not changed' + ansible.builtin.assert: that: - 'fs_result is failed' - 'uuid.stdout == uuid2.stdout' - name: "Check that an existing filesystem (not handled by this module) is overwritten when force is used" - filesystem: + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' force: yes register: fs_result2 -- command: 'blkid -c /dev/null -o value -s UUID {{ dev }}' +- name: 'Get UUID of the new filesystem' + ansible.builtin.command: + cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' + changed_when: false register: uuid3 -- assert: +- name: 'Assert that module succeeded and filesystem UUID is changed' + ansible.builtin.assert: that: - - 'fs_result2 is successful' + - 'fs_result2 is success' - 'fs_result2 is changed' - 'uuid2.stdout != uuid3.stdout' diff --git a/tests/integration/targets/filesystem/tasks/remove_fs.yml b/tests/integration/targets/filesystem/tasks/remove_fs.yml index 7d1ca2a19c..338d439d60 100644 --- a/tests/integration/targets/filesystem/tasks/remove_fs.yml +++ b/tests/integration/targets/filesystem/tasks/remove_fs.yml @@ -1,98 +1,98 @@ --- # We assume 'create_fs' tests have passed. -- name: filesystem creation - filesystem: +- name: "Create filesystem" + community.general.filesystem: dev: '{{ dev }}' fstype: '{{ fstype }}' -- name: get filesystem UUID with 'blkid' - command: +- name: "Get filesystem UUID with 'blkid'" + ansible.builtin.command: cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' changed_when: false register: blkid_ref -- name: Assert that a filesystem exists on top of the device - assert: +- name: "Assert that a filesystem exists on top of the device" + ansible.builtin.assert: that: - blkid_ref.stdout | length > 0 # Test check_mode first -- name: filesystem removal (check mode) - filesystem: +- name: "Remove filesystem (check mode)" + community.general.filesystem: dev: '{{ dev }}' state: absent register: wipefs check_mode: yes -- name: get filesystem UUID with 'blkid' (should remain the same) - command: +- name: "Get filesystem UUID with 'blkid' (should remain the same)" + ansible.builtin.command: cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' changed_when: false register: blkid -- name: Assert that the state changed but the filesystem still exists - assert: +- name: "Assert that the state changed but the filesystem still exists" + ansible.builtin.assert: that: - wipefs is changed - blkid.stdout == blkid_ref.stdout # Do it -- name: filesystem removal - filesystem: +- name: "Remove filesystem" + community.general.filesystem: dev: '{{ dev }}' state: absent register: wipefs -- name: get filesystem UUID with 'blkid' (should be empty) - command: +- name: "Get filesystem UUID with 'blkid' (should be empty)" + ansible.builtin.command: cmd: 'blkid -c /dev/null -o value -s UUID {{ dev }}' changed_when: false failed_when: false register: blkid -- name: Assert that the state changed and the device has no filesystem - assert: +- name: "Assert that the state changed and the device has no filesystem" + ansible.builtin.assert: that: - wipefs is changed - blkid.stdout | length == 0 - blkid.rc == 2 # Do it again -- name: filesystem removal (idempotency) - filesystem: +- name: "Remove filesystem (idempotency)" + community.general.filesystem: dev: '{{ dev }}' state: absent register: wipefs -- name: Assert that the state did not change - assert: +- name: "Assert that the state did not change" + ansible.builtin.assert: that: - wipefs is not changed # and again -- name: filesystem removal (idempotency, check mode) - filesystem: +- name: "Remove filesystem (idempotency, check mode)" + community.general.filesystem: dev: '{{ dev }}' state: absent register: wipefs check_mode: yes -- name: Assert that the state did not change - assert: +- name: "Assert that the state did not change" + ansible.builtin.assert: that: - wipefs is not changed # By the way, test removal of a filesystem on unexistent device -- name: filesystem removal (unexistent device) - filesystem: +- name: "Remove filesystem (unexistent device)" + community.general.filesystem: dev: '/dev/unexistent_device' state: absent register: wipefs -- name: Assert that the state did not change - assert: +- name: "Assert that the state did not change" + ansible.builtin.assert: that: - wipefs is not changed diff --git a/tests/integration/targets/filesystem/tasks/setup.yml b/tests/integration/targets/filesystem/tasks/setup.yml index 82fe7c54e6..9ca4b983d0 100644 --- a/tests/integration/targets/filesystem/tasks/setup.yml +++ b/tests/integration/targets/filesystem/tasks/setup.yml @@ -1,6 +1,9 @@ --- -- name: install filesystem tools - package: +# By installing e2fsprogs on FreeBSD, we get a usable blkid command, but this +# package conflicts with util-linux, that provides blkid too, but also wipefs +# (required for filesystem state=absent). +- name: "Install filesystem tools" + ansible.builtin.package: name: '{{ item }}' state: present # xfsprogs on OpenSUSE requires Python 3, skip this for our newer Py2 OpenSUSE builds @@ -9,86 +12,134 @@ - e2fsprogs - xfsprogs -- block: - - name: install btrfs progs - package: - name: btrfs-progs - state: present - when: - - ansible_os_family != 'Suse' - - not (ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('16.04', '<=')) - - ansible_system != "FreeBSD" - - not (ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('8', '>=')) +- name: "Install btrfs progs" + ansible.builtin.package: + name: btrfs-progs + state: present + when: + - ansible_os_family != 'Suse' + - not (ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('16.04', '<=')) + - ansible_system != "FreeBSD" + - not (ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('8', '>=')) - - name: install btrfs progs (Ubuntu <= 16.04) - package: - name: btrfs-tools - state: present - when: ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('16.04', '<=') +- name: "Install btrfs tools (Ubuntu <= 16.04)" + ansible.builtin.package: + name: btrfs-tools + state: present + when: + - ansible_distribution == 'Ubuntu' + - ansible_distribution_version is version('16.04', '<=') - - name: install btrfs progs (OpenSuse) - package: - name: '{{ item }}' - state: present - when: ansible_os_family == 'Suse' - with_items: - - python{{ ansible_python.version.major }}-xml - - btrfsprogs +- name: "Install btrfs progs (OpenSuse)" + ansible.builtin.package: + name: '{{ item }}' + state: present + when: ansible_os_family == 'Suse' + with_items: + - python{{ ansible_python.version.major }}-xml + - btrfsprogs - - name: install ocfs2 (Debian) - package: - name: ocfs2-tools - state: present - when: ansible_os_family == 'Debian' +- name: "Install reiserfs utils (Fedora)" + ansible.builtin.package: + name: reiserfs-utils + state: present + when: + - ansible_distribution == 'Fedora' - - when: - - ansible_os_family != 'RedHat' or ansible_distribution == 'Fedora' - - ansible_distribution != 'Ubuntu' or ansible_distribution_version is version('16.04', '>=') - - ansible_system != "FreeBSD" - block: - - name: install f2fs - package: - name: f2fs-tools - state: present +- name: "Install reiserfs (OpenSuse)" + ansible.builtin.package: + name: reiserfs + state: present + when: + - ansible_os_family == 'Suse' - - name: fetch f2fs version - command: mkfs.f2fs /dev/null - ignore_errors: yes - register: mkfs_f2fs +- name: "Install reiserfs progs (Debian and more)" + ansible.builtin.package: + name: reiserfsprogs + state: present + when: + - ansible_system == 'Linux' + - ansible_os_family not in ['Suse', 'RedHat'] - - set_fact: - f2fs_version: '{{ mkfs_f2fs.stdout | regex_search("F2FS-tools: mkfs.f2fs Ver:.*") | regex_replace("F2FS-tools: mkfs.f2fs Ver: ([0-9.]+) .*", "\1") }}' +- name: "Install reiserfs progs (FreeBSD)" + ansible.builtin.package: + name: progsreiserfs + state: present + when: + - ansible_system == 'FreeBSD' - - name: install dosfstools and lvm2 (Linux) - package: - name: '{{ item }}' - with_items: - - dosfstools - - lvm2 - when: ansible_system == 'Linux' +- name: "Install ocfs2 (Debian)" + ansible.builtin.package: + name: ocfs2-tools + state: present + when: ansible_os_family == 'Debian' -- block: - - name: install fatresize - package: - name: fatresize - state: present - - command: fatresize --help - register: fatresize - - set_fact: - fatresize_version: '{{ fatresize.stdout_lines[0] | regex_search("[0-9]+\.[0-9]+\.[0-9]+") }}' +- name: "Install f2fs tools and get version" + when: + - ansible_os_family != 'RedHat' or ansible_distribution == 'Fedora' + - ansible_distribution != 'Ubuntu' or ansible_distribution_version is version('16.04', '>=') + - ansible_system != "FreeBSD" + block: + - name: "Install f2fs tools" + ansible.builtin.package: + name: f2fs-tools + state: present + + - name: "Fetch f2fs version" + ansible.builtin.command: + cmd: mkfs.f2fs /dev/null + changed_when: false + ignore_errors: true + register: mkfs_f2fs + + - name: "Record f2fs_version" + ansible.builtin.set_fact: + f2fs_version: '{{ mkfs_f2fs.stdout + | regex_search("F2FS-tools: mkfs.f2fs Ver:.*") + | regex_replace("F2FS-tools: mkfs.f2fs Ver: ([0-9.]+) .*", "\1") }}' + +- name: "Install dosfstools and lvm2 (Linux)" + ansible.builtin.package: + name: '{{ item }}' + with_items: + - dosfstools + - lvm2 + when: ansible_system == 'Linux' + +- name: "Install fatresize and get version" when: - ansible_system == 'Linux' - ansible_os_family != 'Suse' - ansible_os_family != 'RedHat' or (ansible_distribution == 'CentOS' and ansible_distribution_version is version('7.0', '==')) + block: + - name: "Install fatresize" + ansible.builtin.package: + name: fatresize + state: present -- command: mke2fs -V + - name: "Fetch fatresize version" + ansible.builtin.command: + cmd: fatresize --help + changed_when: false + register: fatresize + + - name: "Record fatresize_version" + ansible.builtin.set_fact: + fatresize_version: '{{ fatresize.stdout_lines[0] | regex_search("[0-9]+\.[0-9]+\.[0-9]+") }}' + +- name: "Fetch e2fsprogs version" + ansible.builtin.command: + cmd: mke2fs -V + changed_when: false register: mke2fs -- set_fact: +- name: "Record e2fsprogs_version" + ansible.builtin.set_fact: # mke2fs 1.43.6 (29-Aug-2017) e2fsprogs_version: '{{ mke2fs.stderr_lines[0] | regex_search("[0-9]{1,2}\.[0-9]{1,2}(\.[0-9]{1,2})?") }}' -- set_fact: +- name: "Set version-related facts to skip further tasks" + ansible.builtin.set_fact: # http://e2fsprogs.sourceforge.net/e2fsprogs-release.html#1.43 # Mke2fs no longer complains if the user tries to create a file system # using the entire block device. diff --git a/tests/integration/targets/filter_groupby/aliases b/tests/integration/targets/filter_groupby/aliases new file mode 100644 index 0000000000..6e79abdd02 --- /dev/null +++ b/tests/integration/targets/filter_groupby/aliases @@ -0,0 +1,2 @@ +shippable/posix/group4 +skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller diff --git a/tests/integration/targets/filter_groupby/tasks/main.yml b/tests/integration/targets/filter_groupby/tasks/main.yml new file mode 100644 index 0000000000..29036a3bc5 --- /dev/null +++ b/tests/integration/targets/filter_groupby/tasks/main.yml @@ -0,0 +1,45 @@ +--- +- name: Test functionality + assert: + that: + - list1 | community.general.groupby_as_dict('name') == dict1 + +- name: 'Test error: not a list' + set_fact: + test: "{{ list_no_list | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - result.msg == 'Input is not a sequence' + +- name: 'Test error: list element not a mapping' + set_fact: + test: "{{ list_no_dict | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - "result.msg == 'Sequence element #0 is not a mapping'" + +- name: 'Test error: list element does not have attribute' + set_fact: + test: "{{ list_no_attribute | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - "result.msg == 'Attribute not contained in element #1 of sequence'" + +- name: 'Test error: attribute collision' + set_fact: + test: "{{ list_collision | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - result.msg == "Multiple sequence entries have attribute value 'a'" diff --git a/tests/integration/targets/filter_groupby/vars/main.yml b/tests/integration/targets/filter_groupby/vars/main.yml new file mode 100644 index 0000000000..15d38a351a --- /dev/null +++ b/tests/integration/targets/filter_groupby/vars/main.yml @@ -0,0 +1,31 @@ +--- +list1: + - name: a + x: y + - name: b + z: 1 + +dict1: + a: + name: a + x: y + b: + name: b + z: 1 + +list_no_list: + a: + name: a + +list_no_dict: + - [] + - 1 + +list_no_attribute: + - name: a + foo: baz + - foo: bar + +list_collision: + - name: a + - name: a diff --git a/tests/integration/targets/flatpak/aliases b/tests/integration/targets/flatpak/aliases index 59e306f8b4..39291d435b 100644 --- a/tests/integration/targets/flatpak/aliases +++ b/tests/integration/targets/flatpak/aliases @@ -1,4 +1,4 @@ -unsupported +shippable/posix/group3 destructive skip/aix skip/freebsd @@ -6,4 +6,3 @@ skip/osx skip/macos skip/rhel needs/root -needs/privileged diff --git a/tests/integration/targets/flatpak/files/serve.py b/tests/integration/targets/flatpak/files/serve.py new file mode 100644 index 0000000000..d9ca2d17a5 --- /dev/null +++ b/tests/integration/targets/flatpak/files/serve.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import posixpath +import sys + +try: + from http.server import SimpleHTTPRequestHandler, HTTPServer + from urllib.parse import unquote +except ImportError: + from SimpleHTTPServer import SimpleHTTPRequestHandler + from BaseHTTPServer import HTTPServer + from urllib import unquote + + +# Argument parsing +if len(sys.argv) != 4: + print('Syntax: {0} '.format(sys.argv[0])) + sys.exit(-1) + +HOST, PORT, PATH = sys.argv[1:4] +PORT = int(PORT) + + +# The HTTP request handler +class Handler(SimpleHTTPRequestHandler): + def translate_path(self, path): + # Modified from Python 3.6's version of SimpleHTTPRequestHandler + # to support using another base directory than CWD. + + # abandon query parameters + path = path.split('?', 1)[0] + path = path.split('#', 1)[0] + # Don't forget explicit trailing slash when normalizing. Issue17324 + trailing_slash = path.rstrip().endswith('/') + try: + path = unquote(path, errors='surrogatepass') + except (UnicodeDecodeError, TypeError) as exc: + path = unquote(path) + path = posixpath.normpath(path) + words = path.split('/') + words = filter(None, words) + path = PATH + for word in words: + if os.path.dirname(word) or word in (os.curdir, os.pardir): + # Ignore components that are not a simple file/directory name + continue + path = os.path.join(path, word) + if trailing_slash: + path += '/' + return path + + +# Run simple HTTP server +httpd = HTTPServer((HOST, PORT), Handler) + +try: + httpd.serve_forever() +except KeyboardInterrupt: + pass + +httpd.server_close() diff --git a/tests/integration/targets/flatpak/meta/main.yml b/tests/integration/targets/flatpak/meta/main.yml index 07faa21776..314f77eba9 100644 --- a/tests/integration/targets/flatpak/meta/main.yml +++ b/tests/integration/targets/flatpak/meta/main.yml @@ -1,2 +1,3 @@ dependencies: - prepare_tests + - setup_flatpak_remote diff --git a/tests/integration/targets/flatpak/tasks/check_mode.yml b/tests/integration/targets/flatpak/tasks/check_mode.yml index 065f10dfa7..2270e0a9be 100644 --- a/tests/integration/targets/flatpak/tasks/check_mode.yml +++ b/tests/integration/targets/flatpak/tasks/check_mode.yml @@ -4,8 +4,8 @@ - name: Test addition of absent flatpak (check mode) flatpak: - name: org.gnome.Characters - remote: flathub + name: com.dummy.App1 + remote: dummy-remote state: present register: addition_result check_mode: true @@ -13,13 +13,13 @@ - name: Verify addition of absent flatpak test result (check mode) assert: that: - - "addition_result.changed == true" + - addition_result is changed msg: "Adding an absent flatpak shall mark module execution as changed" - name: Test non-existent idempotency of addition of absent flatpak (check mode) flatpak: - name: org.gnome.Characters - remote: flathub + name: com.dummy.App1 + remote: dummy-remote state: present register: double_addition_result check_mode: true @@ -27,7 +27,7 @@ - name: Verify non-existent idempotency of addition of absent flatpak test result (check mode) assert: that: - - "double_addition_result.changed == true" + - double_addition_result is changed msg: | Adding an absent flatpak a second time shall still mark module execution as changed in check mode @@ -36,7 +36,7 @@ - name: Test removal of absent flatpak check mode flatpak: - name: org.gnome.Characters + name: com.dummy.App1 state: absent register: removal_result check_mode: true @@ -44,15 +44,15 @@ - name: Verify removal of absent flatpak test result (check mode) assert: that: - - "removal_result.changed == false" + - removal_result is not changed msg: "Removing an absent flatpak shall mark module execution as not changed" # state=present with url on absent flatpak - name: Test addition of absent flatpak with url (check mode) flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref - remote: flathub + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote state: present register: url_addition_result check_mode: true @@ -60,13 +60,13 @@ - name: Verify addition of absent flatpak with url test result (check mode) assert: that: - - "url_addition_result.changed == true" + - url_addition_result is changed msg: "Adding an absent flatpak from URL shall mark module execution as changed" - name: Test non-existent idempotency of addition of absent flatpak with url (check mode) flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref - remote: flathub + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote state: present register: double_url_addition_result check_mode: true @@ -76,7 +76,7 @@ result (check mode) assert: that: - - "double_url_addition_result.changed == true" + - double_url_addition_result is changed msg: | Adding an absent flatpak from URL a second time shall still mark module execution as changed in check mode @@ -85,7 +85,7 @@ - name: Test removal of absent flatpak with url not doing anything (check mode) flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref state: absent register: url_removal_result check_mode: true @@ -93,18 +93,17 @@ - name: Verify removal of absent flatpak with url test result (check mode) assert: that: - - "url_removal_result.changed == false" + - url_removal_result is not changed msg: "Removing an absent flatpak shall mark module execution as not changed" - # - Tests with present flatpak ------------------------------------------------- # state=present on present flatpak - name: Test addition of present flatpak (check mode) flatpak: - name: org.gnome.Calculator - remote: flathub + name: com.dummy.App2 + remote: dummy-remote state: present register: addition_present_result check_mode: true @@ -112,14 +111,14 @@ - name: Verify addition test result of present flatpak (check mode) assert: that: - - "addition_present_result.changed == false" + - addition_present_result is not changed msg: "Adding an present flatpak shall mark module execution as not changed" # state=absent on present flatpak - name: Test removal of present flatpak (check mode) flatpak: - name: org.gnome.Calculator + name: com.dummy.App2 state: absent register: removal_present_result check_mode: true @@ -127,12 +126,12 @@ - name: Verify removal of present flatpak test result (check mode) assert: that: - - "removal_present_result.changed == true" + - removal_present_result is changed msg: "Removing a present flatpak shall mark module execution as changed" - name: Test non-existent idempotency of removal (check mode) flatpak: - name: org.gnome.Calculator + name: com.dummy.App2 state: absent register: double_removal_present_result check_mode: true @@ -140,7 +139,7 @@ - name: Verify non-existent idempotency of removal (check mode) assert: that: - - "double_removal_present_result.changed == true" + - double_removal_present_result is changed msg: | Removing a present flatpak a second time shall still mark module execution as changed in check mode @@ -149,8 +148,8 @@ - name: Test addition with url of present flatpak (check mode) flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Calculator.flatpakref - remote: flathub + name: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + remote: dummy-remote state: present register: url_addition_present_result check_mode: true @@ -158,14 +157,14 @@ - name: Verify addition with url of present flatpak test result (check mode) assert: that: - - "url_addition_present_result.changed == false" + - url_addition_present_result is not changed msg: "Adding a present flatpak from URL shall mark module execution as not changed" # state=absent with url on present flatpak - name: Test removal with url of present flatpak (check mode) flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Calculator.flatpakref + name: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref state: absent register: url_removal_present_result check_mode: true @@ -173,13 +172,13 @@ - name: Verify removal with url of present flatpak test result (check mode) assert: that: - - "url_removal_present_result.changed == true" + - url_removal_present_result is changed msg: "Removing an absent flatpak shall mark module execution as not changed" - name: Test non-existent idempotency of removal with url of present flatpak (check mode) flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Calculator.flatpakref - remote: flathub + name: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + remote: dummy-remote state: absent register: double_url_removal_present_result check_mode: true @@ -189,5 +188,5 @@ flatpak test result (check mode) assert: that: - - "double_url_removal_present_result.changed == true" + - double_url_removal_present_result is changed msg: Removing an absent flatpak a second time shall still mark module execution as changed diff --git a/tests/integration/targets/flatpak/tasks/main.yml b/tests/integration/targets/flatpak/tasks/main.yml index 45f9ecd501..68d41d2efe 100644 --- a/tests/integration/targets/flatpak/tasks/main.yml +++ b/tests/integration/targets/flatpak/tasks/main.yml @@ -30,8 +30,8 @@ - name: Test executable override flatpak: - name: org.gnome.Characters - remote: flathub + name: com.dummy.App1 + remote: dummy-remote state: present executable: nothing-that-exists ignore_errors: true @@ -40,8 +40,8 @@ - name: Verify executable override test result assert: that: - - "executable_override_result.failed == true" - - "executable_override_result.changed == false" + - executable_override_result is failed + - executable_override_result is not changed msg: "Specifying non-existing executable shall fail module execution" - import_tasks: check_mode.yml @@ -57,5 +57,20 @@ vars: method: system + always: + + - name: Check HTTP server status + async_status: + jid: "{{ webserver_status.ansible_job_id }}" + ignore_errors: true + + - name: List processes + command: ps aux + + - name: Stop HTTP server + command: >- + pkill -f -- '{{ remote_tmp_dir }}/serve.py' + when: | - ansible_distribution in ('Fedora', 'Ubuntu') + ansible_distribution == 'Fedora' or + ansible_distribution == 'Ubuntu' and not ansible_distribution_major_version | int < 16 diff --git a/tests/integration/targets/flatpak/tasks/setup.yml b/tests/integration/targets/flatpak/tasks/setup.yml index 2dfa33a0b1..98b07cd480 100644 --- a/tests/integration/targets/flatpak/tasks/setup.yml +++ b/tests/integration/targets/flatpak/tasks/setup.yml @@ -4,32 +4,58 @@ state: present become: true when: ansible_distribution == 'Fedora' + - block: - name: Activate flatpak ppa on Ubuntu apt_repository: repo: ppa:alexlarsson/flatpak state: present mode: '0644' + when: ansible_lsb.major_release | int < 18 + - name: Install flatpak package on Ubuntu apt: name: flatpak state: present - become: true + when: ansible_distribution == 'Ubuntu' -- name: Enable flathub for user + +- name: Install dummy remote for user flatpak_remote: - name: flathub + name: dummy-remote state: present - flatpakrepo_url: https://dl.flathub.org/repo/flathub.flatpakrepo + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo method: user -- name: Enable flathub for system + +- name: Install dummy remote for system flatpak_remote: - name: flathub + name: dummy-remote state: present - flatpakrepo_url: https://dl.flathub.org/repo/flathub.flatpakrepo + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo method: system + +- name: Remove (if necessary) flatpak for testing check mode on absent flatpak + flatpak: + name: com.dummy.App1 + remote: dummy-remote + state: absent + no_dependencies: true + - name: Add flatpak for testing check mode on present flatpak flatpak: - name: org.gnome.Calculator - remote: flathub + name: com.dummy.App2 + remote: dummy-remote state: present + no_dependencies: true + +- name: Copy HTTP server + copy: + src: serve.py + dest: '{{ remote_tmp_dir }}/serve.py' + mode: '0755' + +- name: Start HTTP server + command: '{{ remote_tmp_dir }}/serve.py 127.0.0.1 8000 /tmp/flatpak/' + async: 120 + poll: 0 + register: webserver_status diff --git a/tests/integration/targets/flatpak/tasks/test.yml b/tests/integration/targets/flatpak/tasks/test.yml index 20d864a84d..7442e4b468 100644 --- a/tests/integration/targets/flatpak/tasks/test.yml +++ b/tests/integration/targets/flatpak/tasks/test.yml @@ -2,114 +2,140 @@ - name: Test addition - {{ method }} flatpak: - name: org.gnome.Characters - remote: flathub + name: com.dummy.App1 + remote: dummy-remote state: present method: "{{ method }}" + no_dependencies: true register: addition_result - name: Verify addition test result - {{ method }} assert: that: - - "addition_result.changed == true" + - addition_result is changed msg: "state=present shall add flatpak when absent" - name: Test idempotency of addition - {{ method }} flatpak: - name: org.gnome.Characters - remote: flathub + name: com.dummy.App1 + remote: dummy-remote state: present method: "{{ method }}" + no_dependencies: true register: double_addition_result - name: Verify idempotency of addition test result - {{ method }} assert: that: - - "double_addition_result.changed == false" + - double_addition_result is not changed msg: "state=present shall not do anything when flatpak is already present" # state=absent - name: Test removal - {{ method }} flatpak: - name: org.gnome.Characters + name: com.dummy.App1 state: absent method: "{{ method }}" + no_dependencies: true register: removal_result - name: Verify removal test result - {{ method }} assert: that: - - "removal_result.changed == true" + - removal_result is changed msg: "state=absent shall remove flatpak when present" - name: Test idempotency of removal - {{ method }} flatpak: - name: org.gnome.Characters + name: com.dummy.App1 state: absent method: "{{ method }}" + no_dependencies: true register: double_removal_result - name: Verify idempotency of removal test result - {{ method }} assert: that: - - "double_removal_result.changed == false" + - double_removal_result is not changed msg: "state=absent shall not do anything when flatpak is not present" # state=present with url as name - name: Test addition with url - {{ method }} flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref - remote: flathub + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote state: present method: "{{ method }}" + no_dependencies: true register: url_addition_result - name: Verify addition test result - {{ method }} assert: that: - - "url_addition_result.changed == true" + - url_addition_result is changed msg: "state=present with url as name shall add flatpak when absent" - name: Test idempotency of addition with url - {{ method }} flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref - remote: flathub + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote state: present method: "{{ method }}" + no_dependencies: true register: double_url_addition_result - name: Verify idempotency of addition with url test result - {{ method }} assert: that: - - "double_url_addition_result.changed == false" + - double_url_addition_result is not changed msg: "state=present with url as name shall not do anything when flatpak is already present" # state=absent with url as name - name: Test removal with url - {{ method }} flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref state: absent method: "{{ method }}" + no_dependencies: true register: url_removal_result + ignore_errors: true -- name: Verify removal test result - {{ method }} +- name: Verify removal test result failed - {{ method }} + # It looks like flatpak has a bug when the hostname contains a port. If this is the case, it emits + # the following message, which we check for. If another error happens, we fail. + # Upstream issue: https://github.com/flatpak/flatpak/issues/4307 + # (The second message happens with Ubuntu 18.04.) assert: that: - - "url_removal_result.changed == true" - msg: "state=absent with url as name shall remove flatpak when present" + - >- + url_removal_result.msg in [ + "error: Invalid branch 127.0.0.1:8000: Branch can't contain :", + "error: Invalid id http:: Name can't contain :", + ] + when: url_removal_result is failed -- name: Test idempotency of removal with url - {{ method }} - flatpak: - name: https://flathub.org/repo/appstream/org.gnome.Characters.flatpakref - state: absent - method: "{{ method }}" - register: double_url_removal_result +- when: url_removal_result is not failed + block: -- name: Verify idempotency of removal with url test result - {{ method }} - assert: - that: - - "double_url_removal_result.changed == false" - msg: "state=absent with url as name shall not do anything when flatpak is not present" + - name: Verify removal test result - {{ method }} + assert: + that: + - url_removal_result is changed + msg: "state=absent with url as name shall remove flatpak when present" + + - name: Test idempotency of removal with url - {{ method }} + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: absent + method: "{{ method }}" + no_dependencies: true + register: double_url_removal_result + + - name: Verify idempotency of removal with url test result - {{ method }} + assert: + that: + - double_url_removal_result is not changed + msg: "state=absent with url as name shall not do anything when flatpak is not present" diff --git a/tests/integration/targets/flatpak_remote/aliases b/tests/integration/targets/flatpak_remote/aliases index 3623baa5c2..39291d435b 100644 --- a/tests/integration/targets/flatpak_remote/aliases +++ b/tests/integration/targets/flatpak_remote/aliases @@ -6,4 +6,3 @@ skip/osx skip/macos skip/rhel needs/root -disabled # FIXME diff --git a/tests/integration/targets/flatpak_remote/tasks/check_mode.yml b/tests/integration/targets/flatpak_remote/tasks/check_mode.yml index 7ce89a8c15..1f4def86d9 100644 --- a/tests/integration/targets/flatpak_remote/tasks/check_mode.yml +++ b/tests/integration/targets/flatpak_remote/tasks/check_mode.yml @@ -13,7 +13,7 @@ - name: Verify addition of absent flatpak remote test result (check mode) assert: that: - - "addition_result.changed == true" + - addition_result is changed msg: "Adding an absent flatpak remote shall mark module execution as changed" - name: Test non-existent idempotency of addition of absent flatpak remote (check mode) @@ -29,7 +29,7 @@ test result (check mode) assert: that: - - "double_addition_result.changed == true" + - double_addition_result is changed msg: | Adding an absent flatpak remote a second time shall still mark module execution as changed in check mode @@ -46,7 +46,7 @@ - name: Verify removal of absent flatpak remote test result (check mode) assert: that: - - "removal_result.changed == false" + - removal_result is not changed msg: "Removing an absent flatpak remote shall mark module execution as not changed" @@ -65,7 +65,7 @@ - name: Verify addition of present flatpak remote test result (check mode) assert: that: - - "addition_result.changed == false" + - addition_result is not changed msg: "Adding a present flatpak remote shall mark module execution as not changed" # state=absent @@ -80,7 +80,7 @@ - name: Verify removal of present flatpak remote test result (check mode) assert: that: - - "removal_result.changed == true" + - removal_result is changed msg: "Removing a present flatpak remote shall mark module execution as changed" - name: Test non-existent idempotency of removal of present flatpak remote (check mode) @@ -95,7 +95,7 @@ test result (check mode) assert: that: - - "double_removal_result.changed == true" + - double_removal_result is changed msg: | Removing a present flatpak remote a second time shall still mark module execution as changed in check mode diff --git a/tests/integration/targets/flatpak_remote/tasks/main.yml b/tests/integration/targets/flatpak_remote/tasks/main.yml index aa2219e181..91fa7262df 100644 --- a/tests/integration/targets/flatpak_remote/tasks/main.yml +++ b/tests/integration/targets/flatpak_remote/tasks/main.yml @@ -40,8 +40,8 @@ - name: Verify executable override test result assert: that: - - "executable_override_result.failed == true" - - "executable_override_result.changed == false" + - executable_override_result is failed + - executable_override_result is not changed msg: "Specifying non-existing executable shall fail module execution" - import_tasks: check_mode.yml diff --git a/tests/integration/targets/flatpak_remote/tasks/test.yml b/tests/integration/targets/flatpak_remote/tasks/test.yml index 9570f623a1..66c43649b4 100644 --- a/tests/integration/targets/flatpak_remote/tasks/test.yml +++ b/tests/integration/targets/flatpak_remote/tasks/test.yml @@ -11,7 +11,7 @@ - name: Verify addition test result - {{ method }} assert: that: - - "addition_result.changed == true" + - addition_result is changed msg: "state=present shall add flatpak when absent" - name: Test idempotency of addition - {{ method }} @@ -25,7 +25,7 @@ - name: Verify idempotency of addition test result - {{ method }} assert: that: - - "double_addition_result.changed == false" + - double_addition_result is not changed msg: "state=present shall not do anything when flatpak is already present" - name: Test updating remote url does not do anything - {{ method }} @@ -39,7 +39,7 @@ - name: Verify updating remote url does not do anything - {{ method }} assert: that: - - "url_update_result.changed == false" + - url_update_result is not changed msg: "Trying to update the URL of an existing flatpak remote shall not do anything" @@ -55,7 +55,7 @@ - name: Verify removal test result - {{ method }} assert: that: - - "removal_result.changed == true" + - removal_result is changed msg: "state=absent shall remove flatpak when present" - name: Test idempotency of removal - {{ method }} @@ -68,5 +68,5 @@ - name: Verify idempotency of removal test result - {{ method }} assert: that: - - "double_removal_result.changed == false" + - double_removal_result is not changed msg: "state=absent shall not do anything when flatpak is not present" diff --git a/tests/integration/targets/git_config/tasks/get_set_no_state.yml b/tests/integration/targets/git_config/tasks/get_set_no_state.yml index 149a9b2d93..7e9714a75e 100644 --- a/tests/integration/targets/git_config/tasks/get_set_no_state.yml +++ b/tests/integration/targets/git_config/tasks/get_set_no_state.yml @@ -17,9 +17,9 @@ - name: assert set changed and value is correct assert: that: - - set_result.changed == true + - set_result is changed - set_result.diff.before == "\n" - set_result.diff.after == option_value + "\n" - - get_result.changed == false + - get_result is not changed - get_result.config_value == option_value ... diff --git a/tests/integration/targets/git_config/tasks/get_set_state_present.yml b/tests/integration/targets/git_config/tasks/get_set_state_present.yml index 59f3c9c0ee..52d986d633 100644 --- a/tests/integration/targets/git_config/tasks/get_set_state_present.yml +++ b/tests/integration/targets/git_config/tasks/get_set_state_present.yml @@ -19,9 +19,9 @@ - name: assert set changed and value is correct with state=present assert: that: - - set_result.changed == true + - set_result is changed - set_result.diff.before == "\n" - set_result.diff.after == option_value + "\n" - - get_result.changed == false + - get_result is not changed - get_result.config_value == option_value ... diff --git a/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml b/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml index 24ef292015..9eb4ca4034 100644 --- a/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml +++ b/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml @@ -18,7 +18,7 @@ - name: assert unset changed and deleted value assert: that: - - unset_result.changed == true + - unset_result is changed - unset_result.diff.before == option_value + "\n" - unset_result.diff.after == "\n" - get_result.config_value == '' diff --git a/tests/integration/targets/git_config/tasks/unset_check_mode.yml b/tests/integration/targets/git_config/tasks/unset_check_mode.yml index c8fe00c0b7..43b9905373 100644 --- a/tests/integration/targets/git_config/tasks/unset_check_mode.yml +++ b/tests/integration/targets/git_config/tasks/unset_check_mode.yml @@ -18,7 +18,7 @@ - name: assert unset changed but dit not delete value assert: that: - - unset_result.changed == true + - unset_result is changed - unset_result.diff.before == option_value + "\n" - unset_result.diff.after == "\n" - get_result.config_value == option_value diff --git a/tests/integration/targets/git_config/tasks/unset_no_value.yml b/tests/integration/targets/git_config/tasks/unset_no_value.yml index 71568e3aa4..5fb6b6bcb6 100644 --- a/tests/integration/targets/git_config/tasks/unset_no_value.yml +++ b/tests/integration/targets/git_config/tasks/unset_no_value.yml @@ -17,7 +17,7 @@ - name: assert unsetting didn't change assert: that: - - unset_result.changed == false + - unset_result is not changed - unset_result.msg == 'no setting to unset' - get_result.config_value == '' ... diff --git a/tests/integration/targets/git_config/tasks/unset_value.yml b/tests/integration/targets/git_config/tasks/unset_value.yml index a2308156aa..6dda37736e 100644 --- a/tests/integration/targets/git_config/tasks/unset_value.yml +++ b/tests/integration/targets/git_config/tasks/unset_value.yml @@ -17,7 +17,7 @@ - name: assert unset changed and deleted value assert: that: - - unset_result.changed == true + - unset_result is changed - unset_result.diff.before == option_value + "\n" - unset_result.diff.after == "\n" - get_result.config_value == '' diff --git a/tests/integration/targets/github_issue/tasks/main.yml b/tests/integration/targets/github_issue/tasks/main.yml index 24266128ae..7731a7a955 100644 --- a/tests/integration/targets/github_issue/tasks/main.yml +++ b/tests/integration/targets/github_issue/tasks/main.yml @@ -18,8 +18,8 @@ - assert: that: - - "{{ get_status_0002.changed == True }}" - - "{{ get_status_0002.issue_status == 'closed' }}" + - get_status_0002 is changed + - get_status_0002.issue_status == 'closed' - name: Check if GitHub issue is closed or not github_issue: @@ -32,6 +32,6 @@ - assert: that: - - "{{ get_status_0003.changed == False }}" - - "{{ get_status_0003.failed == True }}" - - "{{ 'Failed' in get_status_0003.msg }}" + - get_status_0003 is not changed + - get_status_0003 is failed + - "'Failed' in get_status_0003.msg" diff --git a/tests/integration/targets/gitlab_user/defaults/main.yml b/tests/integration/targets/gitlab_user/defaults/main.yml index a6755cf412..bbe016b0a8 100644 --- a/tests/integration/targets/gitlab_user/defaults/main.yml +++ b/tests/integration/targets/gitlab_user/defaults/main.yml @@ -1,3 +1,6 @@ gitlab_user: ansible_test_user gitlab_user_pass: Secr3tPassw00rd gitlab_user_email: root@localhost +gitlab_sshkey_name: ansibletest +gitlab_sshkey_file: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDI8GIMlrirf+zsvBpxnF0daykP6YEJ5wytZXhDGD2dZXg9Tln0KUSDgreT3FDgoabjlOmG1L/nhu6ML76WCsmc/wnVMlXlDlQpVJSQ2PCxGNs9WRW7Y/Pk6t9KtV/VSYr0LaPgLEU8VkffSUBJezbKa1cssjb4CmRRqcePRNYpgCXdK05TEgFvmXl9qIM8Domf1ak1PlbyMmi/MytzHmnVFzxgUKv5c0Mr+vguCi131gPdh3QSf5AHPLEoO9LcMfu2IO1zvl61wYfsJ0Wn2Fncw+tJQfUin0ffTFgUIsGqki04/YjXyWynjSwQf5Jym4BYM0i2zlDUyRxs4/Tfp4yvJFik42ambzjLK6poq+iCpQReeYih9WZUaZwUQe7zYWhTOuoV7ydsk8+kDRMPidF9K5zWkQnglGrOzdbTqnhxNpwHCg2eSRJ49kPYLOH76g8P7IQvl+zluG0o8Nndir1WcYil4D4CCBskM8WbmrElZH1CRyP/NQMNIf4hFMItTjk= ansible@ansible +gitlab_sshkey_expires_at: 2030-01-01T00:00:00.000Z diff --git a/tests/integration/targets/gitlab_user/tasks/main.yml b/tests/integration/targets/gitlab_user/tasks/main.yml index 6cbcd14c34..dddf7aaea8 100644 --- a/tests/integration/targets/gitlab_user/tasks/main.yml +++ b/tests/integration/targets/gitlab_user/tasks/main.yml @@ -56,7 +56,7 @@ - gitlab_user_state_again.user.is_admin == False -- name: Update User Test => Make User Admin +- name: Update User Test => Make User Admin gitlab_user: api_url: "{{ gitlab_host }}" email: "{{ gitlab_user_email }}" @@ -189,8 +189,8 @@ api_url: "{{ gitlab_host }}" validate_certs: False - # note: the only way to check if a password really is what it is expected - # to be is to use it for login, so we use it here instead of the + # note: the only way to check if a password really is what it is expected + # to be is to use it for login, so we use it here instead of the # default token assuming that a user can always change its own password api_username: "{{ gitlab_user }}" api_password: "{{ gitlab_user_pass }}" @@ -205,8 +205,8 @@ - name: Check PW setting return state assert: that: - # note: there is no way to determine if a password has changed or - # not, so it can only be always yellow or always green, we + # note: there is no way to determine if a password has changed or + # not, so it can only be always yellow or always green, we # decided for always green for now - gitlab_user_state is not changed @@ -248,3 +248,5 @@ assert: that: - gitlab_user_state is not changed + +- include_tasks: sshkey.yml diff --git a/tests/integration/targets/gitlab_user/tasks/sshkey.yml b/tests/integration/targets/gitlab_user/tasks/sshkey.yml new file mode 100644 index 0000000000..2d2067e74b --- /dev/null +++ b/tests/integration/targets/gitlab_user/tasks/sshkey.yml @@ -0,0 +1,134 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +- name: Create gitlab user with sshkey credentials + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + state: present + register: gitlab_user_sshkey + +- name: Check user has been created correctly + assert: + that: + - gitlab_user_sshkey is changed + +- name: Create gitlab user again + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + state: present + register: gitlab_user_sshkey_again + +- name: Check state is not changed + assert: + that: + - gitlab_user_sshkey_again is not changed + +- name: Add expires_at to an already created gitlab user with ssh key + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + sshkey_expires_at: "{{ gitlab_sshkey_expires_at }}" + state: present + register: gitlab_user_created_user_sshkey_expires_at + +- name: Check expires_at will not be added to a present ssh key + assert: + that: + - gitlab_user_created_user_sshkey_expires_at is not changed + +- name: Remove created gitlab user + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + validate_certs: false + state: absent + register: gitlab_user_sshkey_remove + +- name: Check user has been removed correctly + assert: + that: + - gitlab_user_sshkey_remove is changed + +- name: Create gitlab user with sshkey and expires_at + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + sshkey_expires_at: "{{ gitlab_sshkey_expires_at }}" + state: present + register: gitlab_user_sshkey_expires_at + +- name: Check user has been created correctly + assert: + that: + - gitlab_user_sshkey_expires_at is changed + +- name: Create gitlab user with sshkey and expires_at again + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + sshkey_expires_at: "{{ gitlab_sshkey_expires_at }}" + state: present + register: gitlab_user_sshkey_expires_at_again + +- name: Check state is not changed + assert: + that: + - gitlab_user_sshkey_expires_at_again is not changed + +- name: Remove created gitlab user + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + validate_certs: false + state: absent + register: gitlab_user_sshkey_expires_at_remove + +- name: Check user has been removed correctly + assert: + that: + - gitlab_user_sshkey_expires_at_remove is changed diff --git a/tests/integration/targets/hwc_ecs_instance/tasks/main.yml b/tests/integration/targets/hwc_ecs_instance/tasks/main.yml index 8c8ea2eb3d..4d36c11286 100644 --- a/tests/integration/targets/hwc_ecs_instance/tasks/main.yml +++ b/tests/integration/targets/hwc_ecs_instance/tasks/main.yml @@ -167,8 +167,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a instance (check mode) hwc_ecs_instance: @@ -277,8 +277,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a disk diff --git a/tests/integration/targets/hwc_evs_disk/tasks/main.yml b/tests/integration/targets/hwc_evs_disk/tasks/main.yml index 79e67d0dc9..e2380450cd 100644 --- a/tests/integration/targets/hwc_evs_disk/tasks/main.yml +++ b/tests/integration/targets/hwc_evs_disk/tasks/main.yml @@ -50,8 +50,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a disk (check mode) hwc_evs_disk: @@ -92,7 +92,7 @@ - name: assert changed is false assert: that: - - result.changed == false + - result is not changed # ---------------------------------------------------------------------------- - name: delete a disk that does not exist hwc_evs_disk: @@ -105,5 +105,5 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed diff --git a/tests/integration/targets/hwc_network_vpc/tasks/main.yml b/tests/integration/targets/hwc_network_vpc/tasks/main.yml index 5c01cf7ad8..e3b979d0b5 100644 --- a/tests/integration/targets/hwc_network_vpc/tasks/main.yml +++ b/tests/integration/targets/hwc_network_vpc/tasks/main.yml @@ -62,8 +62,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a vpc hwc_network_vpc: @@ -97,5 +97,5 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed diff --git a/tests/integration/targets/hwc_smn_topic/tasks/main.yml b/tests/integration/targets/hwc_smn_topic/tasks/main.yml index 180f8fad3e..a9879aea54 100644 --- a/tests/integration/targets/hwc_smn_topic/tasks/main.yml +++ b/tests/integration/targets/hwc_smn_topic/tasks/main.yml @@ -44,8 +44,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a smn topic hwc_smn_topic: @@ -77,5 +77,5 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed diff --git a/tests/integration/targets/hwc_vpc_eip/tasks/main.yml b/tests/integration/targets/hwc_vpc_eip/tasks/main.yml index 57de832418..bdf5d763a7 100644 --- a/tests/integration/targets/hwc_vpc_eip/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_eip/tasks/main.yml @@ -96,8 +96,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a eip (check mode) hwc_vpc_eip: @@ -159,8 +159,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a port diff --git a/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml b/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml index 2316a4b25c..cb6a15f750 100644 --- a/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml @@ -78,8 +78,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a peering connect (check mode) hwc_vpc_peering_connect: @@ -133,8 +133,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a vpc diff --git a/tests/integration/targets/hwc_vpc_port/tasks/main.yml b/tests/integration/targets/hwc_vpc_port/tasks/main.yml index b7f28360c1..00f5ae8b2e 100644 --- a/tests/integration/targets/hwc_vpc_port/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_port/tasks/main.yml @@ -69,8 +69,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a port (check mode) hwc_vpc_port: @@ -116,8 +116,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a subnet diff --git a/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml b/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml index efd6765c80..5531d575f8 100644 --- a/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml @@ -70,8 +70,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a private ip (check mode) hwc_vpc_private_ip: @@ -117,8 +117,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a subnet diff --git a/tests/integration/targets/hwc_vpc_route/tasks/main.yml b/tests/integration/targets/hwc_vpc_route/tasks/main.yml index b281000b7a..9c9c37e8c0 100644 --- a/tests/integration/targets/hwc_vpc_route/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_route/tasks/main.yml @@ -81,8 +81,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a route (check mode) hwc_vpc_route: @@ -127,8 +127,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a peering connect diff --git a/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml b/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml index 6b21f8b9a4..9f853ca8e7 100644 --- a/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml @@ -51,8 +51,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a security group (check mode) hwc_vpc_security_group: @@ -83,5 +83,5 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed diff --git a/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml b/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml index 2d774101bf..04213e7162 100644 --- a/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml @@ -85,8 +85,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a security group rule (check mode) hwc_vpc_security_group_rule: @@ -151,8 +151,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a security group diff --git a/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml b/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml index 3b3cf65478..c16ff85241 100644 --- a/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml +++ b/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml @@ -77,8 +77,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #---------------------------------------------------------- - name: delete a subnet (check mode) hwc_vpc_subnet: @@ -136,8 +136,8 @@ - name: assert changed is false assert: that: - - result.failed == 0 - - result.changed == false + - result is not failed + - result is not changed #--------------------------------------------------------- # Post-test teardown - name: delete a vpc diff --git a/tests/integration/targets/influxdb_user/tasks/tests.yml b/tests/integration/targets/influxdb_user/tasks/tests.yml index b980e29094..ad3396642b 100644 --- a/tests/integration/targets/influxdb_user/tasks/tests.yml +++ b/tests/integration/targets/influxdb_user/tasks/tests.yml @@ -13,7 +13,7 @@ - name: Check that admin user adding succeeds with a change assert: that: - - add_admin_user.changed == true + - add_admin_user is changed - name: Test add admin user block: @@ -24,7 +24,7 @@ - name: Check that admin user adding succeeds with a change assert: that: - - add_admin_user.changed == true + - add_admin_user is changed - name: Test add admin user idempotence block: @@ -35,7 +35,7 @@ - name: Check that admin user adding succeeds without a change assert: that: - - add_admin_user.changed == false + - add_admin_user is not changed - name: Enable authentication and restart service block: @@ -58,7 +58,7 @@ - name: Check that adding user with enabled authentication succeeds with a change assert: that: - - add_user_with_auth_enabled.changed == true + - add_user_with_auth_enabled is changed - name: Test add user when authentication enabled block: @@ -69,7 +69,7 @@ - name: Check that adding user with enabled authentication succeeds with a change assert: that: - - add_user_with_auth_enabled.changed == true + - add_user_with_auth_enabled is changed - name: Test add user when authentication enabled idempotence block: @@ -80,7 +80,7 @@ - name: Check that adding same user succeeds without a change assert: that: - - same_user.changed == false + - same_user is not changed - name: Test change user password in check mode block: @@ -92,7 +92,7 @@ - name: Check that password changing succeeds with a change assert: that: - - change_password.changed == true + - change_password is changed - name: Test change user password block: @@ -103,7 +103,7 @@ - name: Check that password changing succeeds with a change assert: that: - - change_password.changed == true + - change_password is changed - name: Test remove user in check mode block: @@ -115,7 +115,7 @@ - name: Check that removing user succeeds with a change assert: that: - - remove_user.changed == true + - remove_user is changed - name: Test remove user block: @@ -126,7 +126,7 @@ - name: Check that removing user succeeds with a change assert: that: - - remove_user.changed == true + - remove_user is changed - name: Test remove user idempotence block: @@ -137,4 +137,4 @@ - name: Check that removing user succeeds without a change assert: that: - - remove_user.changed == false + - remove_user is not changed diff --git a/tests/integration/targets/ini_file/tasks/main.yml b/tests/integration/targets/ini_file/tasks/main.yml index 2e84147c72..be5835669b 100644 --- a/tests/integration/targets/ini_file/tasks/main.yml +++ b/tests/integration/targets/ini_file/tasks/main.yml @@ -480,3 +480,37 @@ assert: that: - content15 == expected15 + +- name: Create starting ini file + copy: + # The content below is the following text file with BOM: + # [section1] + # var1=aaa + # var2=bbb + # [section2] + # var3=ccc + content: !!binary | + 77u/W3NlY3Rpb24xXQp2YXIxPWFhYQp2YXIyPWJiYgpbc2VjdGlvbjJdCnZhcjM9Y2NjCg== + dest: "{{ output_file }}" +- name: Test ini breakage + ini_file: + path: "{{ output_file }}" + section: section1 + option: var4 + value: 0 + +- name: read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: set expected content and get current ini file content + set_fact: + expected16: "[section1]\nvar1=aaa\nvar2=bbb\nvar4 = 0\n[section2]\nvar3=ccc\n" + content16: "{{ output_content.content | b64decode }}" +- debug: + var: content16 +- name: Verify content of ini file is as expected + assert: + that: + - content16 == expected16 diff --git a/tests/integration/targets/ipify_facts/tasks/main.yml b/tests/integration/targets/ipify_facts/tasks/main.yml index 4fbd5ab696..7b620ff9ec 100644 --- a/tests/integration/targets/ipify_facts/tasks/main.yml +++ b/tests/integration/targets/ipify_facts/tasks/main.yml @@ -41,6 +41,6 @@ - name: check if task was successful assert: that: - - "{{ external_ip.changed == false }}" - - "{{ external_ip['ansible_facts'] is defined }}" - - "{{ external_ip['ansible_facts']['ipify_public_ip'] is defined }}" + - external_ip is not changed + - external_ip.ansible_facts is defined + - external_ip.ansible_facts.ipify_public_ip is defined diff --git a/tests/integration/targets/iso_create/tasks/main.yml b/tests/integration/targets/iso_create/tasks/main.yml index de46276743..4a0df3b818 100644 --- a/tests/integration/targets/iso_create/tasks/main.yml +++ b/tests/integration/targets/iso_create/tasks/main.yml @@ -35,7 +35,7 @@ - debug: var=iso_file - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == False - name: Create iso file with a specified file @@ -54,7 +54,7 @@ - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == True - name: Create iso file with a specified file and folder @@ -74,10 +74,10 @@ - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == True -- name: Create iso file with volume identification string +- name: Create iso file with volume identification string iso_create: src_files: - "{{ role_path }}/files/test1.cfg" @@ -93,7 +93,7 @@ - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == True - name: Create iso file with Rock Ridge extention @@ -112,7 +112,7 @@ - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == True - name: Create iso file with Joliet extention @@ -131,7 +131,7 @@ - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == True - name: Create iso file with UDF enabled @@ -150,5 +150,5 @@ - assert: that: - - iso_result.changed == True + - iso_result is changed - iso_file.stat.exists == True diff --git a/tests/integration/targets/iso_extract/tasks/tests.yml b/tests/integration/targets/iso_extract/tasks/tests.yml index f9182ba6ae..18f22422ce 100644 --- a/tests/integration/targets/iso_extract/tasks/tests.yml +++ b/tests/integration/targets/iso_extract/tasks/tests.yml @@ -28,7 +28,7 @@ - assert: that: - - iso_extract_test0 is changed == true + - iso_extract_test0 is changed - name: Extract the iso again iso_extract: @@ -42,11 +42,11 @@ - name: Test iso_extract_test0_again (normal mode) assert: that: - - iso_extract_test0_again is changed == false + - iso_extract_test0_again is not changed when: not in_check_mode - name: Test iso_extract_test0_again (check-mode) assert: that: - - iso_extract_test0_again is changed == true + - iso_extract_test0_again is changed when: in_check_mode diff --git a/tests/integration/targets/java_cert/tasks/state_change.yml b/tests/integration/targets/java_cert/tasks/state_change.yml index 3c37fc6727..8cee41106f 100644 --- a/tests/integration/targets/java_cert/tasks/state_change.yml +++ b/tests/integration/targets/java_cert/tasks/state_change.yml @@ -4,52 +4,11 @@ args: creates: "{{ test_key_path }}" -- name: Create the test keystore - java_keystore: - name: placeholder - dest: "{{ test_keystore2_path }}" - password: "{{ test_keystore2_password }}" - private_key: "{{ lookup('file', '{{ test_key_path }}') }}" - certificate: "{{ lookup('file', '{{ test_cert_path }}') }}" - - name: Generate the self signed cert we will use for testing command: openssl req -x509 -newkey rsa:4096 -keyout '{{ test_key2_path }}' -out '{{ test_cert2_path }}' -days 365 -nodes -subj '/CN=localhost' args: creates: "{{ test_key2_path }}" -- name: | - Import the newly created certificate. This is our main test. - If the java_cert has been updated properly, then this task will report changed each time - since the module will be comparing the hash of the certificate instead of validating that the alias - simply exists - java_cert: - cert_alias: test_cert - cert_path: "{{ test_cert2_path }}" - keystore_path: "{{ test_keystore2_path }}" - keystore_pass: "{{ test_keystore2_password }}" - state: present - register: result_x509_changed - -- name: Verify the x509 status has changed - assert: - that: - - result_x509_changed is changed - -- name: | - We also want to make sure that the status doesnt change if we import the same cert - java_cert: - cert_alias: test_cert - cert_path: "{{ test_cert2_path }}" - keystore_path: "{{ test_keystore2_path }}" - keystore_pass: "{{ test_keystore2_password }}" - state: present - register: result_x509_succeeded - -- name: Verify the x509 status is ok - assert: - that: - - result_x509_succeeded is succeeded - - name: Create the pkcs12 archive from the test x509 cert command: > openssl pkcs12 @@ -70,6 +29,97 @@ -out {{ test_pkcs2_path }} -passout pass:"{{ test_keystore2_password }}" +- name: try to create the test keystore based on the just created pkcs12, keystore_create flag not enabled + java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + ignore_errors: true + register: result_x509_changed + +- name: Verify the x509 status is failed + assert: + that: + - result_x509_changed is failed + +- name: Create the test keystore based on the just created pkcs12 + java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + keystore_create: yes + +- name: try to import from pkcs12 a non existing alias + java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: non_existing_alias + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + keystore_create: yes + ignore_errors: yes + register: result_x509_changed + +- name: Verify the x509 status is failed + assert: + that: + - result_x509_changed is failed + +- name: import initial test certificate from file path + java_cert: + cert_alias: test_cert + cert_path: "{{ test_cert_path }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + keystore_create: yes + state: present + register: result_x509_changed + +- name: Verify the x509 status is changed + assert: + that: + - result_x509_changed is changed + +- name: | + Import the newly created certificate. This is our main test. + If the java_cert has been updated properly, then this task will report changed each time + since the module will be comparing the hash of the certificate instead of validating that the alias + simply exists + java_cert: + cert_alias: test_cert + cert_path: "{{ test_cert2_path }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + register: result_x509_changed + +- name: Verify the x509 status is changed + assert: + that: + - result_x509_changed is changed + +- name: | + We also want to make sure that the status doesnt change if we import the same cert + java_cert: + cert_alias: test_cert + cert_path: "{{ test_cert2_path }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + register: result_x509_succeeded + +- name: Verify the x509 status is ok + assert: + that: + - result_x509_succeeded is succeeded + - name: > Ensure the original pkcs12 cert is in the keystore java_cert: @@ -83,7 +133,7 @@ - name: | Perform the same test, but we will now be testing the pkcs12 functionality - If we add a different pkcs12 cert with the same alias, we should have a chnaged result, NOT the same + If we add a different pkcs12 cert with the same alias, we should have a changed result, NOT the same java_cert: cert_alias: test_pkcs12_cert pkcs12_alias: test_pkcs12_cert @@ -94,7 +144,7 @@ state: present register: result_pkcs12_changed -- name: Verify the pkcs12 status has changed +- name: Verify the pkcs12 status is changed assert: that: - result_pkcs12_changed is changed @@ -155,7 +205,7 @@ that: - result_x509_absent is changed -- name: Ensure we can remove the pkcs12 archive +- name: Ensure we can remove the certificate imported from pkcs12 archive java_cert: cert_alias: test_pkcs12_cert keystore_path: "{{ test_keystore2_path }}" diff --git a/tests/integration/targets/java_keystore/tasks/main.yml b/tests/integration/targets/java_keystore/tasks/main.yml index 358222aea8..b5f1f01624 100644 --- a/tests/integration/targets/java_keystore/tasks/main.yml +++ b/tests/integration/targets/java_keystore/tasks/main.yml @@ -9,12 +9,22 @@ - name: Include tasks to create ssl materials on the controller include_tasks: prepare.yml +- set_fact: + ssl_backends: ['openssl'] + +- set_fact: + ssl_backends: "{{ ssl_backends + ['cryptography'] }}" + when: cryptography_version.stdout is version('3.0', '>=') + - when: has_java_keytool block: - name: Include tasks to play with 'certificate' and 'private_key' contents include_tasks: tests.yml vars: remote_cert: false + loop: "{{ ssl_backends }}" + loop_control: + loop_var: ssl_backend - name: Include tasks to create ssl materials on the remote host include_tasks: prepare.yml @@ -23,3 +33,6 @@ include_tasks: tests.yml vars: remote_cert: true + loop: "{{ ssl_backends }}" + loop_control: + loop_var: ssl_backend diff --git a/tests/integration/targets/java_keystore/tasks/tests.yml b/tests/integration/targets/java_keystore/tasks/tests.yml index e0de1c6836..b892dd1d29 100644 --- a/tests/integration/targets/java_keystore/tasks/tests.yml +++ b/tests/integration/targets/java_keystore/tasks/tests.yml @@ -23,6 +23,7 @@ private_key_path: "{{ omit if not remote_cert else output_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.key' }}" private_key_passphrase: "{{ item.passphrase | d(omit) }}" password: changeit + ssl_backend: "{{ ssl_backend }}" loop: "{{ java_keystore_certs }}" check_mode: yes register: result_check diff --git a/tests/integration/targets/lookup_dependent/aliases b/tests/integration/targets/lookup_dependent/aliases new file mode 100644 index 0000000000..45489be80c --- /dev/null +++ b/tests/integration/targets/lookup_dependent/aliases @@ -0,0 +1,2 @@ +shippable/posix/group2 +skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller diff --git a/tests/integration/targets/lookup_dependent/tasks/main.yml b/tests/integration/targets/lookup_dependent/tasks/main.yml new file mode 100644 index 0000000000..0f1b8d34fb --- /dev/null +++ b/tests/integration/targets/lookup_dependent/tasks/main.yml @@ -0,0 +1,179 @@ +--- +- name: Test 1 + set_fact: + loop_result: >- + {{ + query('community.general.dependent', + dict(key1=[1, 2]), + dict(key2='[item.key1 + 3, item.key1 + 6]'), + dict(key3='[item.key1 + item.key2 * 10]')) + }} + +- name: Check result of Test 1 + assert: + that: + - loop_result == expected_result + vars: + expected_result: + - key1: 1 + key2: 4 + key3: 41 + - key1: 1 + key2: 7 + key3: 71 + - key1: 2 + key2: 5 + key3: 52 + - key1: 2 + key2: 8 + key3: 82 + +- name: Test 2 + set_fact: + loop_result: >- + {{ query('community.general.dependent', + dict([['a', [1, 2, 3]]]), + dict([['b', '[1, 2, 3, 4] if item.a == 1 else [2, 3, 4] if item.a == 2 else [3, 4]']])) }} + # The last expression could have been `range(item.a, 5)`, but that's not supported by all Jinja2 versions used in CI + +- name: Check result of Test 2 + assert: + that: + - loop_result == expected_result + vars: + expected_result: + - a: 1 + b: 1 + - a: 1 + b: 2 + - a: 1 + b: 3 + - a: 1 + b: 4 + - a: 2 + b: 2 + - a: 2 + b: 3 + - a: 2 + b: 4 + - a: 3 + b: 3 + - a: 3 + b: 4 + +- name: Test 3 + debug: + var: item + with_community.general.dependent: + - var1: + a: + - 1 + - 2 + b: + - 3 + - 4 + - var2: 'item.var1.value' + - var3: 'dependent_lookup_test[item.var1.key ~ "_" ~ item.var2]' + loop_control: + label: "{{ [item.var1.key, item.var2, item.var3] }}" + register: dependent + vars: + dependent_lookup_test: + a_1: + - A + - B + a_2: + - C + b_3: + - D + b_4: + - E + - F + - G + +- name: Check result of Test 3 + assert: + that: + - (dependent.results | length) == 7 + - dependent.results[0].item.var1.key == "a" + - dependent.results[0].item.var2 == 1 + - dependent.results[0].item.var3 == "A" + - dependent.results[1].item.var1.key == "a" + - dependent.results[1].item.var2 == 1 + - dependent.results[1].item.var3 == "B" + - dependent.results[2].item.var1.key == "a" + - dependent.results[2].item.var2 == 2 + - dependent.results[2].item.var3 == "C" + - dependent.results[3].item.var1.key == "b" + - dependent.results[3].item.var2 == 3 + - dependent.results[3].item.var3 == "D" + - dependent.results[4].item.var1.key == "b" + - dependent.results[4].item.var2 == 4 + - dependent.results[4].item.var3 == "E" + - dependent.results[5].item.var1.key == "b" + - dependent.results[5].item.var2 == 4 + - dependent.results[5].item.var3 == "F" + - dependent.results[6].item.var1.key == "b" + - dependent.results[6].item.var2 == 4 + - dependent.results[6].item.var3 == "G" + +- name: "Test 4: template failure" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - a: + - 1 + - 2 + - b: "[item.a + foo]" + ignore_errors: true + register: eval_error + +- name: Check result of Test 4 + assert: + that: + - eval_error is failed + - eval_error.msg.startswith("Caught \"'foo' is undefined\" while evaluating ") + +- name: "Test 5: same variable name reused" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - a: x + - b: x + ignore_errors: true + register: eval_error + +- name: Check result of Test 5 + assert: + that: + - eval_error is failed + - eval_error.msg.startswith("Caught \"'x' is undefined\" while evaluating ") + +- name: "Test 6: multi-value dict" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - a: x + b: x + ignore_errors: true + register: eval_error + +- name: Check result of Test 6 + assert: + that: + - eval_error is failed + - eval_error.msg == 'Parameter 0 must be a one-element dictionary, got 2 elements' + +- name: "Test 7: empty dict" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - {} + ignore_errors: true + register: eval_error + +- name: Check result of Test 7 + assert: + that: + - eval_error is failed + - eval_error.msg == 'Parameter 0 must be a one-element dictionary, got 0 elements' diff --git a/tests/integration/targets/lookup_passwordstore/tasks/tests.yml b/tests/integration/targets/lookup_passwordstore/tasks/tests.yml index aba5457c0a..e69ba5e572 100644 --- a/tests/integration/targets/lookup_passwordstore/tasks/tests.yml +++ b/tests/integration/targets/lookup_passwordstore/tasks/tests.yml @@ -61,6 +61,37 @@ that: - readpass == newpass +- name: Create a password using missing=create + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=create length=8') }}" + +- name: Fetch password from an existing file + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create') }}" + +- name: Verify password + assert: + that: + - readpass == newpass + +- name: Fetch password from existing file using missing=empty + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=empty') }}" + +- name: Verify password + assert: + that: + - readpass == newpass + +- name: Fetch password from non-existing file using missing=empty + set_fact: + readpass: "{{ query('community.general.passwordstore', 'test-missing-pass missing=empty') }}" + +- name: Verify password + assert: + that: + - readpass == [ none ] + # As inserting multiline passwords on the commandline would require something # like expect, simply create it by using default gpg on a file with the correct # structure. diff --git a/tests/integration/targets/lookup_random_pet/aliases b/tests/integration/targets/lookup_random_pet/aliases new file mode 100644 index 0000000000..bc987654d9 --- /dev/null +++ b/tests/integration/targets/lookup_random_pet/aliases @@ -0,0 +1,3 @@ +shippable/posix/group2 +skip/aix +skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller diff --git a/tests/integration/targets/lookup_random_pet/dependencies.yml b/tests/integration/targets/lookup_random_pet/dependencies.yml new file mode 100644 index 0000000000..b6b679d966 --- /dev/null +++ b/tests/integration/targets/lookup_random_pet/dependencies.yml @@ -0,0 +1,6 @@ +--- +- hosts: localhost + tasks: + - name: Install Petname Python package + pip: + name: petname \ No newline at end of file diff --git a/tests/integration/targets/lookup_random_pet/runme.sh b/tests/integration/targets/lookup_random_pet/runme.sh new file mode 100755 index 0000000000..afdff7bb9d --- /dev/null +++ b/tests/integration/targets/lookup_random_pet/runme.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +set -eux + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook dependencies.yml -v "$@" + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test.yml -v "$@" diff --git a/tests/integration/targets/lookup_random_pet/test.yml b/tests/integration/targets/lookup_random_pet/test.yml new file mode 100644 index 0000000000..1ab619d2f4 --- /dev/null +++ b/tests/integration/targets/lookup_random_pet/test.yml @@ -0,0 +1,25 @@ +- hosts: localhost + gather_facts: no + tasks: + - name: Call plugin + set_fact: + result1: "{{ query('community.general.random_pet', words=3) }}" + result2: "{{ query('community.general.random_pet', length=3) }}" + result3: "{{ query('community.general.random_pet', prefix='kubernetes') }}" + result4: "{{ query('community.general.random_pet', separator='_') }}" + result5: "{{ query('community.general.random_pet', words=2, length=6, prefix='kubernetes', separator='_') }}" + + - name: Check results + assert: + that: + - result1 | length == 1 + - result1[0].split('-') | length == 3 + - result2 | length == 1 + - result2[0].split('-')[0] | length <= 3 + - result3 | length == 1 + - result3[0].split('-')[0] == 'kubernetes' + - result4 | length == 1 + - result4[0].split('_') | length == 2 + - result5 | length == 1 + - result5[0].split('_') | length == 3 + - result5[0].split('_')[0] == 'kubernetes' diff --git a/tests/integration/targets/lookup_random_string/aliases b/tests/integration/targets/lookup_random_string/aliases new file mode 100644 index 0000000000..bc987654d9 --- /dev/null +++ b/tests/integration/targets/lookup_random_string/aliases @@ -0,0 +1,3 @@ +shippable/posix/group2 +skip/aix +skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller diff --git a/tests/integration/targets/lookup_random_string/runme.sh b/tests/integration/targets/lookup_random_string/runme.sh new file mode 100755 index 0000000000..8ed6373823 --- /dev/null +++ b/tests/integration/targets/lookup_random_string/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +set -eux + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test.yml -v "$@" diff --git a/tests/integration/targets/lookup_random_string/test.yml b/tests/integration/targets/lookup_random_string/test.yml new file mode 100644 index 0000000000..edbf9fd035 --- /dev/null +++ b/tests/integration/targets/lookup_random_string/test.yml @@ -0,0 +1,48 @@ +- hosts: localhost + gather_facts: no + tasks: + - name: Call plugin + set_fact: + result1: "{{ query('community.general.random_string') }}" + result2: "{{ query('community.general.random_string', length=0) }}" + result3: "{{ query('community.general.random_string', length=10) }}" + result4: "{{ query('community.general.random_string', length=-1) }}" + result5: "{{ query('community.general.random_string', override_special='_', min_special=1) }}" + result6: "{{ query('community.general.random_string', upper=false, special=false) }}" # lower case only + result7: "{{ query('community.general.random_string', lower=false, special=false) }}" # upper case only + result8: "{{ query('community.general.random_string', lower=false, upper=false, special=false) }}" # number only + result9: "{{ query('community.general.random_string', lower=false, upper=false, special=false, min_numeric=1, length=1) }}" # single digit only + result10: "{{ query('community.general.random_string', numbers=false, upper=false, special=false, min_lower=1, length=1) }}" # single lowercase character only + result11: "{{ query('community.general.random_string', base64=true, length=8) }}" + result12: "{{ query('community.general.random_string', upper=false, numbers=false, special=false) }}" # all lower case + result13: "{{ query('community.general.random_string', override_all='0', length=2) }}" + + - name: Raise error when impossible constraints are provided + set_fact: + impossible: "{{ query('community.general.random_string', upper=false, lower=false, special=false, numbers=false) }}" + ignore_errors: yes + register: impossible_result + + - name: Check results + assert: + that: + - result1[0] | length == 8 + - result2[0] | length == 0 + - result3[0] | length == 10 + - result4[0] | length == 0 + - result5[0] | length == 8 + - "'_' in result5[0]" + - result6[0] is lower + - result7[0] is upper + - result8[0] | regex_replace('^(\d+)$', '') == '' + - result9[0] | regex_replace('^(\d+)$', '') == '' + - result9[0] | length == 1 + - result10[0] | length == 1 + - result10[0] is lower + # if input string is not multiple of 3, base64 encoded string will be padded with = + - result11[0].endswith('=') + - result12[0] is lower + - result13[0] | length == 2 + - result13[0] == '00' + - impossible_result is failed + - "'Available characters cannot' in impossible_result.msg" diff --git a/tests/integration/targets/one_host/tasks/main.yml b/tests/integration/targets/one_host/tasks/main.yml index a3cea768af..7d38c2a890 100644 --- a/tests/integration/targets/one_host/tasks/main.yml +++ b/tests/integration/targets/one_host/tasks/main.yml @@ -177,7 +177,7 @@ - name: "assert test_{{test_number}} worked" assert: that: - - result.changed == false + - result is not changed # HOST DISABLEMENT diff --git a/tests/integration/targets/prepare_nuage_tests/tasks/main.yml b/tests/integration/targets/prepare_nuage_tests/tasks/main.yml deleted file mode 100644 index 2a902dc828..0000000000 --- a/tests/integration/targets/prepare_nuage_tests/tasks/main.yml +++ /dev/null @@ -1,24 +0,0 @@ -#################################################################### -# WARNING: These are designed specifically for Ansible tests # -# and should not be used as examples of how to write Ansible roles # -#################################################################### - -- block: - - name: Install Nuage VSD API Simulator - pip: - name: nuage-vsd-sim - - - name: Start Nuage VSD API Simulator - shell: "(cd /; nuage-vsd-sim >/dev/null 2>&1)" - async: 1800 - poll: 0 - - - name: Wait for API to be ready - uri: - url: http://localhost:5000 - register: api - delay: 3 - retries: 10 - until: api.status == 200 - - when: "ansible_python_version is version('2.7', '>=')" diff --git a/tests/integration/targets/proxmox/tasks/main.yml b/tests/integration/targets/proxmox/tasks/main.yml index 6301cb66ef..5954d3f11f 100644 --- a/tests/integration/targets/proxmox/tasks/main.yml +++ b/tests/integration/targets/proxmox/tasks/main.yml @@ -48,7 +48,7 @@ api_token_secret: "{{ api_token_secret | default(omit) }}" validate_certs: "{{ validate_certs }}" register: results - + - assert: that: - results is not changed @@ -226,6 +226,92 @@ - results_action_current.vmid == {{ vmid }} - results_action_current.msg == "VM test-instance with vmid = {{ vmid }} is running" +- name: VM add/change/delete NIC + tags: [ 'nic' ] + block: + - name: Add NIC to test VM + proxmox_nic: + api_host: "{{ api_host }}" + api_user: "{{ user }}@{{ domain }}" + api_password: "{{ api_password | default(omit) }}" + api_token_id: "{{ api_token_id | default(omit) }}" + api_token_secret: "{{ api_token_secret | default(omit) }}" + validate_certs: "{{ validate_certs }}" + vmid: "{{ vmid }}" + state: present + interface: net5 + bridge: vmbr0 + tag: 42 + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 updated on VM with vmid {{ vmid }}" + + - name: Update NIC no changes + proxmox_nic: + api_host: "{{ api_host }}" + api_user: "{{ user }}@{{ domain }}" + api_password: "{{ api_password | default(omit) }}" + api_token_id: "{{ api_token_id | default(omit) }}" + api_token_secret: "{{ api_token_secret | default(omit) }}" + validate_certs: "{{ validate_certs }}" + vmid: "{{ vmid }}" + state: present + interface: net5 + bridge: vmbr0 + tag: 42 + register: results + + - assert: + that: + - results is not changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 unchanged on VM with vmid {{ vmid }}" + + - name: Update NIC with changes + proxmox_nic: + api_host: "{{ api_host }}" + api_user: "{{ user }}@{{ domain }}" + api_password: "{{ api_password | default(omit) }}" + api_token_id: "{{ api_token_id | default(omit) }}" + api_token_secret: "{{ api_token_secret | default(omit) }}" + validate_certs: "{{ validate_certs }}" + vmid: "{{ vmid }}" + state: present + interface: net5 + bridge: vmbr0 + tag: 24 + firewall: True + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 updated on VM with vmid {{ vmid }}" + + - name: Delete NIC + proxmox_nic: + api_host: "{{ api_host }}" + api_user: "{{ user }}@{{ domain }}" + api_password: "{{ api_password | default(omit) }}" + api_token_id: "{{ api_token_id | default(omit) }}" + api_token_secret: "{{ api_token_secret | default(omit) }}" + validate_certs: "{{ validate_certs }}" + vmid: "{{ vmid }}" + state: absent + interface: net5 + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 deleted on VM with vmid {{ vmid }}" + - name: VM stop tags: [ 'stop' ] block: diff --git a/tests/integration/targets/setup_flatpak_remote/create-repo.sh b/tests/integration/targets/setup_flatpak_remote/create-repo.sh new file mode 100755 index 0000000000..4ece76ccfc --- /dev/null +++ b/tests/integration/targets/setup_flatpak_remote/create-repo.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -eux + +# Delete traces from last run +rm -rf appdir* dummy-repo.gpg gpg hello.sh repo + +# Create GPG key +mkdir -p gpg +chmod 0700 gpg +gpg --homedir gpg --batch --passphrase '' --quick-gen-key test@dummy.com future-default default 10y +KEY_ID=$(gpg --homedir=gpg --list-keys --with-colons test@dummy.com | grep fpr: | head -1 | cut -d ':' -f 10) +gpg --homedir=gpg --export "${KEY_ID}" > dummy-repo.gpg +BASE64_PUBLIC_KEY=$(base64 dummy-repo.gpg | tr -d '\n') + +# Install dependencies +flatpak install -y --system flathub org.freedesktop.Platform//1.6 org.freedesktop.Sdk//1.6 + +# Add individual flatpaks +echo $'#!/bin/sh\necho hello world' > hello.sh + +for NUM in 1 2; do + flatpak build-init appdir${NUM} com.dummy.App${NUM} org.freedesktop.Sdk org.freedesktop.Platform 1.6; + flatpak build appdir${NUM} mkdir /app/bin; + flatpak build appdir${NUM} install --mode=750 hello.sh /app/bin; + flatpak build-finish --command=hello.sh appdir${NUM} + + flatpak build-export repo appdir${NUM} stable + + cat > repo/com.dummy.App${NUM}.flatpakref < repo/dummy-repo.flatpakrepo <=') block: - name: Setup testjail include: setup-testjail.yml @@ -316,3 +325,13 @@ - not sysrc_value_absent_idempotent.changed - "'sysrc_delim=\"t1,t2\"' in sysrc_delim_content.stdout_lines" - "'sysrc_delim_delete' not in sysrc_delim_content.stdout_lines" + always: + - name: Restore /etc/rc.conf + copy: + content: "{{ cached_etc_rcconf_content }}" + dest: /etc/rc.conf + + - name: Restore /boot/loader.conf + copy: + content: "{{ cached_boot_loaderconf_content }}" + dest: /boot/loader.conf \ No newline at end of file diff --git a/tests/integration/targets/sysrc/tasks/setup-testjail.yml b/tests/integration/targets/sysrc/tasks/setup-testjail.yml index 9bd15320ae..e75957d19f 100644 --- a/tests/integration/targets/sysrc/tasks/setup-testjail.yml +++ b/tests/integration/targets/sysrc/tasks/setup-testjail.yml @@ -17,12 +17,19 @@ pkgng: name: ezjail +- name: Configure ezjail to use http + when: ansible_distribution_version is version('11.01', '>') + lineinfile: + dest: /usr/local/etc/ezjail.conf + regexp: ^ezjail_ftphost + line: ezjail_ftphost=http://ftp.freebsd.org + - name: Configure ezjail to use archive for old freebsd releases when: ansible_distribution_version is version('11.01', '<=') lineinfile: dest: /usr/local/etc/ezjail.conf regexp: ^ezjail_ftphost - line: ezjail_ftphost=ftp-archive.freebsd.org + line: ezjail_ftphost=http://ftp-archive.freebsd.org - name: Start ezjail ignore_errors: yes diff --git a/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml b/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml index 8ad91501c3..d89c29ae27 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml @@ -24,6 +24,6 @@ - name: Test expected result assert: that: - - add_children_elements_unicode.changed == true - - comparison.changed == false # identical + - add_children_elements_unicode is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-children-elements-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-add-children-elements.yml b/tests/integration/targets/xml/tasks/test-add-children-elements.yml index 8d9b06866d..3c439c7ac2 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-elements.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-elements.yml @@ -24,6 +24,6 @@ - name: Test expected result assert: that: - - add_children_elements.changed == true - - comparison.changed == false # identical + - add_children_elements is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml b/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml index e062de8d14..818fdf09b9 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml @@ -23,6 +23,6 @@ - name: Test expected result assert: that: - - add_children_from_groupvars.changed == true - - comparison.changed == false # identical + - add_children_from_groupvars is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-children-from-groupvars.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml b/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml index 2d42e2d54e..479052ebdd 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml @@ -28,5 +28,5 @@ - name: Test expected result assert: that: - - add_children_insertafter.changed == true - - comparison.changed == false # identical + - add_children_insertafter is changed + - comparison is not changed # identical diff --git a/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml b/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml index 8550f12cf7..9839d7cc91 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml @@ -28,5 +28,5 @@ - name: Test expected result assert: that: - - add_children_insertbefore.changed == true - - comparison.changed == false # identical + - add_children_insertbefore is changed + - comparison is not changed # identical diff --git a/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml b/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml index d4a2329f69..585157c970 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml @@ -26,6 +26,6 @@ - name: Test expected result assert: that: - - add_children_with_attributes_unicode.changed == true - - comparison.changed == false # identical + - add_children_with_attributes_unicode is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-children-with-attributes-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml b/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml index 91e92637fc..c3704801d9 100644 --- a/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml +++ b/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml @@ -29,7 +29,7 @@ - name: Test expected result assert: that: - - add_children_with_attributes.changed == true - - comparison.changed == false # identical + - add_children_with_attributes is changed + - comparison is not changed # identical when: lxml_predictable_attribute_order #command: diff -u {{ role_path }}/results/test-add-children-with-attributes.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml b/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml index db674ba4fc..6166cd46b9 100644 --- a/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml +++ b/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml @@ -108,7 +108,7 @@ - name: Test expected result assert: that: - - comparison.changed == false # identical + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-element-implicitly.yml /tmp/ansible-xml-beers-implicit.xml diff --git a/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml b/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml index 25eca47f5b..2cac73e65c 100644 --- a/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml +++ b/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml @@ -21,12 +21,12 @@ src: results/test-add-namespaced-children-elements.xml dest: /tmp/ansible-xml-namespaced-beers.xml check_mode: yes - diff: yes + diff: yes register: comparison - name: Test expected result assert: that: - - add_namespaced_children_elements.changed == true - - comparison.changed == false # identical + - add_namespaced_children_elements is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-namespaced-children-elements.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-children-elements-xml.yml b/tests/integration/targets/xml/tasks/test-children-elements-xml.yml index e63100c47c..6b50d819c3 100644 --- a/tests/integration/targets/xml/tasks/test-children-elements-xml.yml +++ b/tests/integration/targets/xml/tasks/test-children-elements-xml.yml @@ -25,6 +25,6 @@ - name: Test expected result assert: that: - - children_elements.changed == true - - comparison.changed == false # identical + - children_elements is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-count-unicode.yml b/tests/integration/targets/xml/tasks/test-count-unicode.yml index 47a806bf98..a9a462b5da 100644 --- a/tests/integration/targets/xml/tasks/test-count-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-count-unicode.yml @@ -15,5 +15,5 @@ - name: Test expected result assert: that: - - beers.changed == false + - beers is not changed - beers.count == 2 diff --git a/tests/integration/targets/xml/tasks/test-count.yml b/tests/integration/targets/xml/tasks/test-count.yml index cbc97e323c..b8a21870f7 100644 --- a/tests/integration/targets/xml/tasks/test-count.yml +++ b/tests/integration/targets/xml/tasks/test-count.yml @@ -15,5 +15,5 @@ - name: Test expected result assert: that: - - beers.changed == false + - beers is not changed - beers.count == 3 diff --git a/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml b/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml index 73ae96674f..718f12d640 100644 --- a/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml @@ -15,7 +15,7 @@ - name: Test expected result assert: that: - - get_element_attribute.changed == false + - get_element_attribute is not changed - get_element_attribute.matches[0]['rating'] is defined and get_element_attribute.matches[0]['rating']['subjective'] == 'да' - name: Get element text @@ -28,5 +28,5 @@ - name: Test expected result assert: that: - - get_element_text.changed == false + - get_element_text is not changed - get_element_text.matches[0]['rating'] == 'десять' diff --git a/tests/integration/targets/xml/tasks/test-get-element-content.yml b/tests/integration/targets/xml/tasks/test-get-element-content.yml index 4a40b42dcf..d38aa70d95 100644 --- a/tests/integration/targets/xml/tasks/test-get-element-content.yml +++ b/tests/integration/targets/xml/tasks/test-get-element-content.yml @@ -15,7 +15,7 @@ - name: Test expected result assert: that: - - get_element_attribute.changed == false + - get_element_attribute is not changed - get_element_attribute.matches[0]['rating'] is defined - get_element_attribute.matches[0]['rating']['subjective'] == 'true' @@ -43,5 +43,5 @@ - name: Test expected result assert: that: - - get_element_text.changed == false + - get_element_text is not changed - get_element_text.matches[0]['rating'] == '10' diff --git a/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml b/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml index 3f24b0ac84..07a71f9153 100644 --- a/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml +++ b/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml @@ -18,5 +18,5 @@ - name: Test expected result assert: that: - - module_output.changed == false - - module_output.failed == true + - module_output is not changed + - module_output is failed diff --git a/tests/integration/targets/xml/tasks/test-pretty-print-only.yml b/tests/integration/targets/xml/tasks/test-pretty-print-only.yml index 7c0f7d5fd6..16fcf629c5 100644 --- a/tests/integration/targets/xml/tasks/test-pretty-print-only.yml +++ b/tests/integration/targets/xml/tasks/test-pretty-print-only.yml @@ -24,6 +24,6 @@ - name: Test expected result assert: that: - - pretty_print_only.changed == true - - comparison.changed == false # identical + - pretty_print_only is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-pretty-print.yml b/tests/integration/targets/xml/tasks/test-pretty-print.yml index 88b618b25d..fd47ff3d82 100644 --- a/tests/integration/targets/xml/tasks/test-pretty-print.yml +++ b/tests/integration/targets/xml/tasks/test-pretty-print.yml @@ -25,6 +25,6 @@ - name: Test expected result assert: that: - - pretty_print.changed == true - - comparison.changed == false # identical + - pretty_print is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml b/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml index d09dee405c..fbd73237f1 100644 --- a/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml +++ b/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml @@ -23,6 +23,6 @@ - name: Test expected result assert: that: - - remove_attribute.changed == false - - comparison.changed == false # identical + - remove_attribute is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-attribute.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-attribute.yml b/tests/integration/targets/xml/tasks/test-remove-attribute.yml index 9aa395e666..52b5214213 100644 --- a/tests/integration/targets/xml/tasks/test-remove-attribute.yml +++ b/tests/integration/targets/xml/tasks/test-remove-attribute.yml @@ -23,6 +23,6 @@ - name: Test expected result assert: that: - - remove_attribute.changed == true - - comparison.changed == false # identical + - remove_attribute is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-attribute.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml b/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml index 2debc80d51..e548bfabf8 100644 --- a/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml +++ b/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml @@ -23,6 +23,6 @@ - name: Test expected result assert: that: - - remove_element.changed == false - - comparison.changed == false # identical + - remove_element is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-element.yml b/tests/integration/targets/xml/tasks/test-remove-element.yml index f2e20ea220..092ca3e033 100644 --- a/tests/integration/targets/xml/tasks/test-remove-element.yml +++ b/tests/integration/targets/xml/tasks/test-remove-element.yml @@ -23,6 +23,6 @@ - name: Test expected result assert: that: - - remove_element.changed == true - - comparison.changed == false # identical + - remove_element is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml b/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml index 291536d3bf..19c14dec8d 100644 --- a/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml +++ b/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml @@ -28,6 +28,6 @@ - name: Test expected result assert: that: - - remove_namespaced_attribute.changed == false - - comparison.changed == false # identical + - remove_namespaced_attribute is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-namespaced-attribute.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml b/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml index a7ccdac4e3..9e54911ba5 100644 --- a/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml +++ b/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml @@ -28,6 +28,6 @@ - name: Test expected result assert: that: - - remove_namespaced_attribute.changed == true - - comparison.changed == false # identical + - remove_namespaced_attribute is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-namespaced-attribute.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml b/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml index b1938e45b7..b96f2a7819 100644 --- a/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml +++ b/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml @@ -28,6 +28,6 @@ - name: Test expected result assert: that: - - remove_namespaced_element.changed == false - - comparison.changed == false # identical + - remove_namespaced_element is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml b/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml index be78af6803..660baa9840 100644 --- a/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml +++ b/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml @@ -28,6 +28,6 @@ - name: Test expected result assert: that: - - remove_namespaced_element.changed == true - - comparison.changed == false # identical + - remove_namespaced_element is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml b/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml index dabf72a1b7..b72d502f12 100644 --- a/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml @@ -24,6 +24,6 @@ - name: Test expected result assert: that: - - set_attribute_value_unicode.changed == true - - comparison.changed == false # identical + - set_attribute_value_unicode is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-attribute-value-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-attribute-value.yml b/tests/integration/targets/xml/tasks/test-set-attribute-value.yml index 2aa39fe22f..6a2aa6c511 100644 --- a/tests/integration/targets/xml/tasks/test-set-attribute-value.yml +++ b/tests/integration/targets/xml/tasks/test-set-attribute-value.yml @@ -24,6 +24,6 @@ - name: Test expected result assert: that: - - set_attribute_value.changed == true - - comparison.changed == false # identical + - set_attribute_value is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-attribute-value.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml b/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml index 3e2c0adb6f..7fa926e879 100644 --- a/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml +++ b/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml @@ -47,8 +47,8 @@ - name: Test expected result assert: that: - - set_children_elements_level.changed == true - - comparison.changed == false # identical + - set_children_elements_level is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-children-elements-level.xml /tmp/ansible-xml-beers.xml @@ -70,5 +70,5 @@ - name: Test expected result assert: that: - - set_children_again.changed == false - - comparison.changed == false # identical + - set_children_again is not changed + - comparison is not changed # identical diff --git a/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml b/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml index 240b894ac7..3cc25cd999 100644 --- a/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml @@ -25,8 +25,8 @@ - name: Test expected result assert: that: - - set_children_elements_unicode.changed == true - - comparison.changed == false # identical + - set_children_elements_unicode is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml @@ -41,6 +41,6 @@ - name: Test expected result assert: that: - - set_children_again.changed == false - - comparison.changed == false # identical + - set_children_again is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-children-elements.yml b/tests/integration/targets/xml/tasks/test-set-children-elements.yml index 7b0f3247ad..7c305ead74 100644 --- a/tests/integration/targets/xml/tasks/test-set-children-elements.yml +++ b/tests/integration/targets/xml/tasks/test-set-children-elements.yml @@ -25,8 +25,8 @@ - name: Test expected result assert: that: - - set_children_elements.changed == true - - comparison.changed == false # identical + - set_children_elements is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml @@ -48,6 +48,6 @@ - name: Test expected result assert: that: - - set_children_again.changed == false - - comparison.changed == false # identical + - set_children_again is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml b/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml index 5814803cb7..4575d5e75f 100644 --- a/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml +++ b/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml @@ -23,6 +23,6 @@ - name: Test expected result assert: that: - - set_element_value_empty.changed == true - - comparison.changed == false # identical + - set_element_value_empty is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-element-value-empty.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml b/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml index c3a40b7d93..139087fcd9 100644 --- a/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml +++ b/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml @@ -37,7 +37,7 @@ - name: Test expected result assert: that: - - set_element_first_run.changed == true - - set_element_second_run.changed == false - - comparison.changed == false # identical + - set_element_first_run is changed + - set_element_second_run is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-element-value-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-element-value.yml b/tests/integration/targets/xml/tasks/test-set-element-value.yml index dbd070f139..2f845e949b 100644 --- a/tests/integration/targets/xml/tasks/test-set-element-value.yml +++ b/tests/integration/targets/xml/tasks/test-set-element-value.yml @@ -37,7 +37,7 @@ - name: Test expected result assert: that: - - set_element_first_run.changed == true - - set_element_second_run.changed == false - - comparison.changed == false # identical + - set_element_first_run is changed + - set_element_second_run is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-element-value.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml b/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml index e0086efe3a..2ba83a8330 100644 --- a/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml +++ b/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml @@ -29,6 +29,6 @@ - name: Test expected result assert: that: - - set_namespaced_attribute_value.changed == true - - comparison.changed == false # identical + - set_namespaced_attribute_value is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-set-namespaced-attribute-value.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml b/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml index 8e66e70eeb..6204c8c74d 100644 --- a/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml +++ b/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml @@ -52,6 +52,6 @@ - name: Test expected result assert: that: - - set_children_again.changed == false # idempotency - - set_namespaced_attribute_value.changed == true - - comparison.changed == false # identical + - set_children_again is not changed # idempotency + - set_namespaced_attribute_value is changed + - comparison is not changed # identical diff --git a/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml b/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml index f77d7537e9..cf6a8a7eb0 100644 --- a/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml +++ b/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml @@ -41,6 +41,6 @@ - name: Test expected result assert: that: - - set_element_first_run.changed == true - - set_element_second_run.changed == false - - comparison.changed == false # identical + - set_element_first_run is changed + - set_element_second_run is not changed + - comparison is not changed # identical diff --git a/tests/integration/targets/xml/tasks/test-xmlstring.yml b/tests/integration/targets/xml/tasks/test-xmlstring.yml index 4620d984fa..82781fa94d 100644 --- a/tests/integration/targets/xml/tasks/test-xmlstring.yml +++ b/tests/integration/targets/xml/tasks/test-xmlstring.yml @@ -25,8 +25,8 @@ - name: Test expected result assert: that: - - xmlresponse.changed == false - - comparison.changed == false # identical + - xmlresponse is not changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml @@ -49,8 +49,8 @@ - name: Test expected result assert: that: - - xmlresponse.changed == true - - comparison.changed == false # identical + - xmlresponse is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml @@ -63,7 +63,7 @@ add_children: - beer: Old Rasputin register: xmlresponse_modification - + - name: Compare to expected result copy: content: '{{ xmlresponse_modification.xmlstring }}' @@ -76,6 +76,6 @@ - name: Test expected result assert: that: - - xmlresponse_modification.changed == true - - comparison.changed == false # identical + - xmlresponse_modification is changed + - comparison is not changed # identical #command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml diff --git a/tests/integration/targets/yum_versionlock/tasks/main.yml b/tests/integration/targets/yum_versionlock/tasks/main.yml index dda5a11bf0..4084bdcb91 100644 --- a/tests/integration/targets/yum_versionlock/tasks/main.yml +++ b/tests/integration/targets/yum_versionlock/tasks/main.yml @@ -23,7 +23,8 @@ state: present register: lock_all_packages - - name: Update all packages + # This should fail when it needs user interaction and missing -y is on purpose. + - name: Update all packages (not really) command: yum update --setopt=obsoletes=0 register: update_all_locked_packages changed_when: @@ -59,4 +60,4 @@ state: absent when: yum_versionlock_install is changed when: (ansible_distribution in ['CentOS', 'RedHat'] and ansible_distribution_major_version is version('7', '>=')) or - (ansible_distribution == 'Fedora') + (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('33', '<=')) diff --git a/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml b/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml index 0290fa4da2..4490ddca7d 100644 --- a/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml +++ b/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml @@ -125,3 +125,61 @@ priority: 100 auto_import_keys: true state: "present" + +- name: add a repo by releasever + community.general.zypper_repository: + name: releaseverrepo + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_$releasever/ + state: present + register: add_repo + +- name: add a repo by releasever again + community.general.zypper_repository: + name: releaseverrepo + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_$releasever/ + state: present + register: add_repo_again + +- assert: + that: + - add_repo is changed + - add_repo_again is not changed + +- name: remove added repo + community.general.zypper_repository: + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_{{ ansible_distribution_version }}/ + state: absent + register: remove_repo + +- assert: + that: + - remove_repo is changed + +- name: add a repo by basearch + community.general.zypper_repository: + name: basearchrepo + repo: https://packagecloud.io/netdata/netdata/opensuse/13.2/$basearch + state: present + register: add_repo + +- name: add a repo by basearch again + community.general.zypper_repository: + name: basearchrepo + repo: https://packagecloud.io/netdata/netdata/opensuse/13.2/$basearch + state: present + register: add_repo_again + +- assert: + that: + - add_repo is changed + - add_repo_again is not changed + +- name: remove added repo + community.general.zypper_repository: + repo: https://packagecloud.io/netdata/netdata/opensuse/13.2/x86_64 + state: absent + register: remove_repo + +- assert: + that: + - remove_repo is changed diff --git a/tests/sanity/extra/extra-docs.json b/tests/sanity/extra/extra-docs.json new file mode 100644 index 0000000000..a62ef37e63 --- /dev/null +++ b/tests/sanity/extra/extra-docs.json @@ -0,0 +1,10 @@ +{ + "include_symlinks": false, + "prefixes": [ + "docs/docsite/" + ], + "output": "path-line-column-message", + "requirements": [ + "antsibull" + ] +} diff --git a/tests/sanity/extra/extra-docs.py b/tests/sanity/extra/extra-docs.py new file mode 100755 index 0000000000..f4b7f59d3c --- /dev/null +++ b/tests/sanity/extra/extra-docs.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +"""Check extra collection docs with antsibull-lint.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys +import subprocess + + +def main(): + """Main entry point.""" + if not os.path.isdir(os.path.join('docs', 'docsite')): + return + p = subprocess.run(['antsibull-lint', 'collection-docs', '.'], check=False) + if p.returncode not in (0, 3): + print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode)) + + +if __name__ == '__main__': + main() diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 7beedfa206..6a58e6b20a 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -3,26 +3,15 @@ plugins/module_utils/compat/ipaddress.py no-assert plugins/module_utils/compat/ipaddress.py no-unicode-literals plugins/module_utils/_mount.py future-import-boilerplate plugins/module_utils/_mount.py metaclass-boilerplate -plugins/modules/cloud/linode/linode.py validate-modules:parameter-list-no-elements -plugins/modules/cloud/linode/linode.py validate-modules:parameter-type-not-in-doc -plugins/modules/cloud/linode/linode.py validate-modules:undocumented-parameter plugins/modules/cloud/lxc/lxc_container.py use-argspec-type-path plugins/modules/cloud/lxc/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/cloud/misc/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/cloud/online/online_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/online/online_user_info.py validate-modules:return-syntax-error plugins/modules/cloud/rackspace/rax.py use-argspec-type-path # fix needed plugins/modules/cloud/rackspace/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/cloud/rackspace/rax_files_objects.py use-argspec-type-path plugins/modules/cloud/rackspace/rax_mon_notification_plan.py validate-modules:parameter-list-no-elements plugins/modules/cloud/rackspace/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/cloud/scaleway/scaleway_image_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_ip_info.py validate-modules:return-syntax-error plugins/modules/cloud/scaleway/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_security_group_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_snapshot_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_volume_info.py validate-modules:return-syntax-error plugins/modules/cloud/smartos/vmadm.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/smartos/vmadm.py validate-modules:undocumented-parameter plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements @@ -53,9 +42,6 @@ plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_tags.py validate-modules:parameter-state-invalid-choice -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:doc-default-does-not-match-spec -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:parameter-type-not-in-doc -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:undocumented-parameter plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid plugins/modules/system/gconftool2.py validate-modules:parameter-state-invalid-choice plugins/modules/system/iptables_state.py validate-modules:undocumented-parameter diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 80975cf389..69f021b9c6 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -2,26 +2,15 @@ plugins/module_utils/compat/ipaddress.py no-assert plugins/module_utils/compat/ipaddress.py no-unicode-literals plugins/module_utils/_mount.py future-import-boilerplate plugins/module_utils/_mount.py metaclass-boilerplate -plugins/modules/cloud/linode/linode.py validate-modules:parameter-list-no-elements -plugins/modules/cloud/linode/linode.py validate-modules:parameter-type-not-in-doc -plugins/modules/cloud/linode/linode.py validate-modules:undocumented-parameter plugins/modules/cloud/lxc/lxc_container.py use-argspec-type-path plugins/modules/cloud/lxc/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/cloud/misc/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/cloud/online/online_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/online/online_user_info.py validate-modules:return-syntax-error plugins/modules/cloud/rackspace/rax.py use-argspec-type-path # fix needed plugins/modules/cloud/rackspace/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/cloud/rackspace/rax_files_objects.py use-argspec-type-path plugins/modules/cloud/rackspace/rax_mon_notification_plan.py validate-modules:parameter-list-no-elements plugins/modules/cloud/rackspace/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/cloud/scaleway/scaleway_image_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_ip_info.py validate-modules:return-syntax-error plugins/modules/cloud/scaleway/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_security_group_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_snapshot_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_volume_info.py validate-modules:return-syntax-error plugins/modules/cloud/smartos/vmadm.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/smartos/vmadm.py validate-modules:undocumented-parameter plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements @@ -52,9 +41,6 @@ plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_tags.py validate-modules:parameter-state-invalid-choice -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:doc-default-does-not-match-spec -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:parameter-type-not-in-doc -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:undocumented-parameter plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid plugins/modules/system/gconftool2.py validate-modules:parameter-state-invalid-choice plugins/modules/system/iptables_state.py validate-modules:undocumented-parameter diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index 80975cf389..2af0d25f6e 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -2,26 +2,15 @@ plugins/module_utils/compat/ipaddress.py no-assert plugins/module_utils/compat/ipaddress.py no-unicode-literals plugins/module_utils/_mount.py future-import-boilerplate plugins/module_utils/_mount.py metaclass-boilerplate -plugins/modules/cloud/linode/linode.py validate-modules:parameter-list-no-elements -plugins/modules/cloud/linode/linode.py validate-modules:parameter-type-not-in-doc -plugins/modules/cloud/linode/linode.py validate-modules:undocumented-parameter plugins/modules/cloud/lxc/lxc_container.py use-argspec-type-path plugins/modules/cloud/lxc/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/cloud/misc/rhevm.py validate-modules:parameter-state-invalid-choice -plugins/modules/cloud/online/online_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/online/online_user_info.py validate-modules:return-syntax-error plugins/modules/cloud/rackspace/rax.py use-argspec-type-path # fix needed plugins/modules/cloud/rackspace/rax_files.py validate-modules:parameter-state-invalid-choice plugins/modules/cloud/rackspace/rax_files_objects.py use-argspec-type-path plugins/modules/cloud/rackspace/rax_mon_notification_plan.py validate-modules:parameter-list-no-elements plugins/modules/cloud/rackspace/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/cloud/scaleway/scaleway_image_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_ip_info.py validate-modules:return-syntax-error plugins/modules/cloud/scaleway/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_security_group_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_snapshot_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_volume_info.py validate-modules:return-syntax-error plugins/modules/cloud/smartos/vmadm.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/smartos/vmadm.py validate-modules:undocumented-parameter plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements @@ -52,9 +41,6 @@ plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_tags.py validate-modules:parameter-state-invalid-choice -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:doc-default-does-not-match-spec -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:parameter-type-not-in-doc -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:undocumented-parameter plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid plugins/modules/system/gconftool2.py validate-modules:parameter-state-invalid-choice plugins/modules/system/iptables_state.py validate-modules:undocumented-parameter @@ -69,7 +55,5 @@ plugins/modules/system/ssh_config.py use-argspec-type-path # Required since modu plugins/modules/system/xfconf.py validate-modules:parameter-state-invalid-choice plugins/modules/system/xfconf.py validate-modules:return-syntax-error plugins/modules/web_infrastructure/jenkins_plugin.py use-argspec-type-path -tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.6 # django generated code -tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.7 # django generated code tests/utils/shippable/check_matrix.py replace-urlopen tests/utils/shippable/timing.py shebang diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 36a0c3e08e..06aff8d41c 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -3,22 +3,12 @@ plugins/module_utils/compat/ipaddress.py no-assert plugins/module_utils/compat/ipaddress.py no-unicode-literals plugins/module_utils/_mount.py future-import-boilerplate plugins/module_utils/_mount.py metaclass-boilerplate -plugins/modules/cloud/linode/linode.py validate-modules:parameter-type-not-in-doc -plugins/modules/cloud/linode/linode.py validate-modules:undocumented-parameter plugins/modules/cloud/lxc/lxc_container.py use-argspec-type-path plugins/modules/cloud/lxc/lxc_container.py validate-modules:use-run-command-not-popen -plugins/modules/cloud/online/online_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/online/online_user_info.py validate-modules:return-syntax-error plugins/modules/cloud/rackspace/rax.py use-argspec-type-path plugins/modules/cloud/rackspace/rax_files_objects.py use-argspec-type-path plugins/modules/cloud/rackspace/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values -plugins/modules/cloud/scaleway/scaleway_image_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_ip_info.py validate-modules:return-syntax-error plugins/modules/cloud/scaleway/scaleway_organization_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_security_group_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_server_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_snapshot_info.py validate-modules:return-syntax-error -plugins/modules/cloud/scaleway/scaleway_volume_info.py validate-modules:return-syntax-error plugins/modules/cloud/smartos/vmadm.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/smartos/vmadm.py validate-modules:undocumented-parameter plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc @@ -42,9 +32,38 @@ plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:doc-default-does-not-match-spec -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:parameter-type-not-in-doc -plugins/modules/remote_management/stacki/stacki_host.py validate-modules:undocumented-parameter +plugins/modules/net_tools/nios/nios_a_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_a_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_aaaa_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_aaaa_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_cname_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_cname_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_dns_view.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_dns_view.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_fixed_address.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_fixed_address.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_host_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_host_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_member.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_member.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_mx_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_mx_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_naptr_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_naptr_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_network.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_network.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_network_view.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_network_view.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_nsgroup.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_nsgroup.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_ptr_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_ptr_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_srv_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_srv_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_txt_record.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_txt_record.py validate-modules:invalid-documentation +plugins/modules/net_tools/nios/nios_zone.py validate-modules:deprecation-mismatch +plugins/modules/net_tools/nios/nios_zone.py validate-modules:invalid-documentation plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid plugins/modules/system/iptables_state.py validate-modules:undocumented-parameter plugins/modules/system/launchd.py use-argspec-type-path # False positive diff --git a/tests/unit/plugins/become/test_sudosu.py b/tests/unit/plugins/become/test_sudosu.py index 4e5c998f09..6adf200d8e 100644 --- a/tests/unit/plugins/become/test_sudosu.py +++ b/tests/unit/plugins/become/test_sudosu.py @@ -10,36 +10,41 @@ __metaclass__ = type import re from ansible import context -from ansible.playbook.play_context import PlayContext -from ansible.plugins.loader import become_loader + +from .helper import call_become_plugin def test_sudosu(mocker, parser, reset_cli_args): options = parser.parse_args([]) context._init_global_context(options) - play_context = PlayContext() default_cmd = "/bin/foo" default_exe = "/bin/bash" sudo_exe = 'sudo' sudo_flags = '-H -s -n' - cmd = play_context.make_become_cmd(cmd=default_cmd, executable=default_exe) - assert cmd == default_cmd - success = 'BECOME-SUCCESS-.+?' - play_context.become = True - play_context.become_user = 'foo' - play_context.set_become_plugin(become_loader.get('community.general.sudosu')) - play_context.become_flags = sudo_flags - cmd = play_context.make_become_cmd(cmd=default_cmd, executable=default_exe) - - assert (re.match("""%s %s su -l %s %s -c 'echo %s; %s'""" % (sudo_exe, sudo_flags, play_context.become_user, + task = { + 'become_user': 'foo', + 'become_method': 'community.general.sudosu', + 'become_flags': sudo_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s su -l %s %s -c 'echo %s; %s'""" % (sudo_exe, sudo_flags, task['become_user'], default_exe, success, default_cmd), cmd) is not None) - play_context.become_pass = 'testpass' - cmd = play_context.make_become_cmd(cmd=default_cmd, executable=default_exe) + task = { + 'become_user': 'foo', + 'become_method': 'community.general.sudosu', + 'become_flags': sudo_flags, + 'become_pass': 'testpass', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) assert (re.match("""%s %s -p "%s" su -l %s %s -c 'echo %s; %s'""" % (sudo_exe, sudo_flags.replace('-n', ''), - r"\[sudo via ansible, key=.+?\] password:", play_context.become_user, + r"\[sudo via ansible, key=.+?\] password:", task['become_user'], default_exe, success, default_cmd), cmd) is not None) diff --git a/tests/unit/plugins/cache/test_redis.py b/tests/unit/plugins/cache/test_redis.py index e665826769..ee7e1f7913 100644 --- a/tests/unit/plugins/cache/test_redis.py +++ b/tests/unit/plugins/cache/test_redis.py @@ -23,10 +23,23 @@ import pytest pytest.importorskip('redis') +from ansible import constants as C from ansible.plugins.loader import cache_loader +from ansible.release import __version__ as ansible_version from ansible_collections.community.general.plugins.cache.redis import CacheModule as RedisCache def test_redis_cachemodule(): # The _uri option is required for the redis plugin - assert isinstance(cache_loader.get('community.general.redis', **{'_uri': '127.0.0.1:6379:1'}), RedisCache) + connection = '127.0.0.1:6379:1' + if ansible_version.startswith('2.9.'): + C.CACHE_PLUGIN_CONNECTION = connection + assert isinstance(cache_loader.get('community.general.redis', **{'_uri': connection}), RedisCache) + + +def test_redis_cachemodule(): + # The _uri option is required for the redis plugin + connection = '[::1]:6379:1' + if ansible_version.startswith('2.9.'): + C.CACHE_PLUGIN_CONNECTION = connection + assert isinstance(cache_loader.get('community.general.redis', **{'_uri': connection}), RedisCache) diff --git a/tests/unit/plugins/lookup/test_dependent.py b/tests/unit/plugins/lookup/test_dependent.py new file mode 100644 index 0000000000..f2a31ff4b6 --- /dev/null +++ b/tests/unit/plugins/lookup/test_dependent.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# (c) 2020-2021, Felix Fontein +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +from ansible_collections.community.internal_test_tools.tests.unit.compat.unittest import TestCase +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import ( + MagicMock, +) + +from ansible.plugins.loader import lookup_loader + + +class TestLookupModule(TestCase): + def setUp(self): + templar = MagicMock() + templar._loader = None + self.lookup = lookup_loader.get("community.general.dependent", templar=templar) + + def test_empty(self): + self.assertListEqual(self.lookup.run([], None), []) + + def test_simple(self): + self.assertListEqual( + self.lookup.run( + [ + {'a': '[1, 2]'}, + {'b': '[item.a + 3, item.a + 6]'}, + {'c': '[item.a + item.b * 10]'}, + ], + {}, + ), + [ + {'a': 1, 'b': 4, 'c': 41}, + {'a': 1, 'b': 7, 'c': 71}, + {'a': 2, 'b': 5, 'c': 52}, + {'a': 2, 'b': 8, 'c': 82}, + ], + ) diff --git a/tests/unit/plugins/module_utils/test_module_helper.py b/tests/unit/plugins/module_utils/test_module_helper.py index 6f77ca7662..6452784182 100644 --- a/tests/unit/plugins/module_utils/test_module_helper.py +++ b/tests/unit/plugins/module_utils/test_module_helper.py @@ -6,12 +6,10 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from collections import namedtuple - import pytest from ansible_collections.community.general.plugins.module_utils.module_helper import ( - ArgFormat, DependencyCtxMgr, ModuleHelper, VarMeta, cause_changes + ArgFormat, DependencyCtxMgr, VarMeta, VarDict, cause_changes ) @@ -144,7 +142,7 @@ def test_variable_meta_diff(): def test_vardict(): - vd = ModuleHelper.VarDict() + vd = VarDict() vd.set('a', 123) assert vd['a'] == 123 assert vd.a == 123 diff --git a/plugins/action/__init__.py b/tests/unit/plugins/modules/database/saphana/__init__.py similarity index 100% rename from plugins/action/__init__.py rename to tests/unit/plugins/modules/database/saphana/__init__.py diff --git a/tests/unit/plugins/modules/database/saphana/test_hana_query.py b/tests/unit/plugins/modules/database/saphana/test_hana_query.py new file mode 100644 index 0000000000..4d158c028e --- /dev/null +++ b/tests/unit/plugins/modules/database/saphana/test_hana_query.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Rainer Leber (@rainerleber) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.community.general.plugins.modules import hana_query +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible.module_utils import basic + + +def get_bin_path(*args, **kwargs): + """Function to return path of hdbsql""" + return "/usr/sap/HDB/HDB01/exe/hdbsql" + + +class Testhana_query(ModuleTestCase): + """Main class for testing hana_query module.""" + + def setUp(self): + """Setup.""" + super(Testhana_query, self).setUp() + self.module = hana_query + self.mock_get_bin_path = patch.object(basic.AnsibleModule, 'get_bin_path', get_bin_path) + self.mock_get_bin_path.start() + self.addCleanup(self.mock_get_bin_path.stop) # ensure that the patching is 'undone' + + def tearDown(self): + """Teardown.""" + super(Testhana_query, self).tearDown() + + def test_without_required_parameters(self): + """Failure must occurs when all parameters are missing.""" + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + self.module.main() + + def test_hana_query(self): + """Check that result is processed.""" + set_module_args({ + 'sid': "HDB", + 'instance': "01", + 'encrypted': False, + 'host': "localhost", + 'user': "SYSTEM", + 'password': "1234Qwer", + 'database': "HDB", + 'query': "SELECT * FROM users;" + }) + with patch.object(basic.AnsibleModule, 'run_command') as run_command: + run_command.return_value = 0, 'username,name\n testuser,test user \n myuser, my user \n', '' + with self.assertRaises(AnsibleExitJson) as result: + hana_query.main() + self.assertEqual(result.exception.args[0]['query_result'], [[ + {'username': 'testuser', 'name': 'test user'}, + {'username': 'myuser', 'name': 'my user'}, + ]]) + self.assertEqual(run_command.call_count, 1) diff --git a/plugins/become/__init__.py b/tests/unit/plugins/modules/files/__init__.py similarity index 100% rename from plugins/become/__init__.py rename to tests/unit/plugins/modules/files/__init__.py diff --git a/tests/unit/plugins/modules/files/test_sapcar_extract.py b/tests/unit/plugins/modules/files/test_sapcar_extract.py new file mode 100644 index 0000000000..05946e8217 --- /dev/null +++ b/tests/unit/plugins/modules/files/test_sapcar_extract.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Rainer Leber (@rainerleber) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.community.general.plugins.modules.files import sapcar_extract +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible.module_utils import basic + + +def get_bin_path(*args, **kwargs): + """Function to return path of SAPCAR""" + return "/tmp/sapcar" + + +class Testsapcar_extract(ModuleTestCase): + """Main class for testing sapcar_extract module.""" + + def setUp(self): + """Setup.""" + super(Testsapcar_extract, self).setUp() + self.module = sapcar_extract + self.mock_get_bin_path = patch.object(basic.AnsibleModule, 'get_bin_path', get_bin_path) + self.mock_get_bin_path.start() + self.addCleanup(self.mock_get_bin_path.stop) # ensure that the patching is 'undone' + + def tearDown(self): + """Teardown.""" + super(Testsapcar_extract, self).tearDown() + + def test_without_required_parameters(self): + """Failure must occurs when all parameters are missing.""" + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + self.module.main() + + def test_sapcar_extract(self): + """Check that result is changed.""" + set_module_args({ + 'path': "/tmp/HANA_CLIENT_REV2_00_053_00_LINUX_X86_64.SAR", + 'dest': "/tmp/test2", + 'binary_path': "/tmp/sapcar" + }) + with patch.object(basic.AnsibleModule, 'run_command') as run_command: + run_command.return_value = 0, '', '' # successful execution, no output + with self.assertRaises(AnsibleExitJson) as result: + sapcar_extract.main() + self.assertTrue(result.exception.args[0]['changed']) + self.assertEqual(run_command.call_count, 1) diff --git a/tests/unit/plugins/modules/net_tools/test_nmcli.py b/tests/unit/plugins/modules/net_tools/test_nmcli.py index 8d830bcf19..5b3f96937b 100644 --- a/tests/unit/plugins/modules/net_tools/test_nmcli.py +++ b/tests/unit/plugins/modules/net_tools/test_nmcli.py @@ -95,8 +95,12 @@ connection.autoconnect: yes ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no """ TESTCASE_GENERIC_DNS4_SEARCH = [ @@ -120,10 +124,14 @@ connection.autoconnect: yes ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv4.dns-search: search.redhat.com ipv6.dns-search: search6.redhat.com ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no """ TESTCASE_GENERIC_ZONE = [ @@ -147,8 +155,12 @@ connection.zone: external ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no """ TESTCASE_BOND = [ @@ -172,8 +184,12 @@ connection.autoconnect: yes ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no bond.options: mode=active-backup,primary=non_existent_primary """ @@ -184,6 +200,7 @@ TESTCASE_BRIDGE = [ 'ifname': 'br0_non_existant', 'ip4': '10.10.10.10/24', 'gw4': '10.10.10.1', + 'mac': '52:54:00:ab:cd:ef', 'maxage': 100, 'stp': True, 'state': 'present', @@ -198,8 +215,13 @@ connection.autoconnect: yes ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +bridge.mac-address: 52:54:00:AB:CD:EF bridge.stp: yes bridge.max-age: 100 bridge.ageing-time: 300 @@ -223,6 +245,7 @@ TESTCASE_BRIDGE_SLAVE_SHOW_OUTPUT = """\ connection.id: non_existent_nw_device connection.interface-name: br0_non_existant connection.autoconnect: yes +connection.slave-type: bridge ipv4.never-default: no bridge-port.path-cost: 100 bridge-port.hairpin-mode: yes @@ -249,8 +272,12 @@ connection.autoconnect: yes ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no vlan.id: 10 """ @@ -340,8 +367,12 @@ connection.autoconnect: yes 802-3-ethernet.mtu: auto ipv4.method: auto ipv4.dhcp-client-id: 00:11:22:AA:BB:CC:DD +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no """ TESTCASE_ETHERNET_STATIC = [ @@ -365,9 +396,13 @@ connection.autoconnect: yes ipv4.method: manual ipv4.addresses: 10.10.10.10/24 ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no ipv4.never-default: no ipv4.dns: 1.1.1.1,8.8.8.8 ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no """ diff --git a/tests/unit/plugins/modules/notification/test_discord.py b/tests/unit/plugins/modules/notification/test_discord.py new file mode 100644 index 0000000000..257b0d4dab --- /dev/null +++ b/tests/unit/plugins/modules/notification/test_discord.py @@ -0,0 +1,103 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import pytest +from ansible_collections.community.general.tests.unit.compat.mock import Mock, patch +from ansible_collections.community.general.plugins.modules.notification import discord +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class TestDiscordModule(ModuleTestCase): + + def setUp(self): + super(TestDiscordModule, self).setUp() + self.module = discord + + def tearDown(self): + super(TestDiscordModule, self).tearDown() + + @pytest.fixture + def fetch_url_mock(self, mocker): + return mocker.patch('ansible.module_utils.notification.discord.fetch_url') + + def test_without_parameters(self): + """Failure if no parameters set""" + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + self.module.main() + + def test_without_content(self): + """Failure if content and embeds both are missing""" + set_module_args({ + 'webhook_id': 'xxx', + 'webhook_token': 'xxx' + }) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_successful_message(self): + """Test a basic message successfully.""" + set_module_args({ + 'webhook_id': 'xxx', + 'webhook_token': 'xxx', + 'content': 'test' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 204, 'msg': 'OK (0 bytes)'}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['content'] == "test" + + def test_message_with_username(self): + """Test a message with username set successfully.""" + set_module_args({ + 'webhook_id': 'xxx', + 'webhook_token': 'xxx', + 'content': 'test', + 'username': 'Ansible Bot' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 204, 'msg': 'OK (0 bytes)'}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['username'] == "Ansible Bot" + assert call_data['content'] == "test" + + def test_failed_message(self): + """Test failure because webhook id is wrong.""" + + set_module_args({ + 'webhook_id': 'wrong', + 'webhook_token': 'xxx', + 'content': 'test' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'HTTP Error 404: Not Found', 'body': '{"message": "Unknown Webhook", "code": 10015}'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_failed_message_without_body(self): + """Test failure with empty response body.""" + + set_module_args({ + 'webhook_id': 'wrong', + 'webhook_token': 'xxx', + 'content': 'test' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'HTTP Error 404: Not Found'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() diff --git a/tests/unit/plugins/modules/packaging/language/test_cpanm.py b/tests/unit/plugins/modules/packaging/language/test_cpanm.py index fd52fc1cc9..10a2955019 100644 --- a/tests/unit/plugins/modules/packaging/language/test_cpanm.py +++ b/tests/unit/plugins/modules/packaging/language/test_cpanm.py @@ -38,7 +38,7 @@ TEST_CASES = [ ), ( ['/testbin/cpanm', 'Dancer'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err ), ], @@ -65,7 +65,7 @@ TEST_CASES = [ 'id': 'install_dancer', 'run_command.calls': [( ['/testbin/cpanm', 'Dancer'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -77,7 +77,7 @@ TEST_CASES = [ 'id': 'install_distribution_file_compatibility', 'run_command.calls': [( ['/testbin/cpanm', 'MIYAGAWA/Plack-0.99_05.tar.gz'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -89,7 +89,7 @@ TEST_CASES = [ 'id': 'install_distribution_file', 'run_command.calls': [( ['/testbin/cpanm', 'MIYAGAWA/Plack-0.99_05.tar.gz'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -101,7 +101,7 @@ TEST_CASES = [ 'id': 'install_into_locallib', 'run_command.calls': [( ['/testbin/cpanm', '--local-lib', '/srv/webapps/my_app/extlib', 'Dancer'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -113,7 +113,7 @@ TEST_CASES = [ 'id': 'install_from_local_directory', 'run_command.calls': [( ['/testbin/cpanm', '/srv/webapps/my_app/src/'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -125,7 +125,7 @@ TEST_CASES = [ 'id': 'install_into_locallib_no_unit_testing', 'run_command.calls': [( ['/testbin/cpanm', '--notest', '--local-lib', '/srv/webapps/my_app/extlib', 'Dancer'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -137,7 +137,7 @@ TEST_CASES = [ 'id': 'install_from_mirror', 'run_command.calls': [( ['/testbin/cpanm', '--mirror', 'http://cpan.cpantesters.org/', 'Dancer'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -158,7 +158,7 @@ TEST_CASES = [ 'id': 'install_minversion_implicit', 'run_command.calls': [( ['/testbin/cpanm', 'Dancer~1.0'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -170,7 +170,7 @@ TEST_CASES = [ 'id': 'install_minversion_explicit', 'run_command.calls': [( ['/testbin/cpanm', 'Dancer~1.5'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -182,7 +182,7 @@ TEST_CASES = [ 'id': 'install_specific_version', 'run_command.calls': [( ['/testbin/cpanm', 'Dancer@1.7'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -215,7 +215,7 @@ TEST_CASES = [ 'id': 'install_specific_version_from_git_url_explicit', 'run_command.calls': [( ['/testbin/cpanm', 'git://github.com/plack/Plack.git@1.7'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, @@ -228,7 +228,7 @@ TEST_CASES = [ 'id': 'install_specific_version_from_git_url_implicit', 'run_command.calls': [( ['/testbin/cpanm', 'git://github.com/plack/Plack.git@2.5'], - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': True}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True}, (0, '', '',), # output rc, out, err )], 'changed': True, diff --git a/tests/unit/plugins/modules/packaging/os/test_pacman_key.py b/tests/unit/plugins/modules/packaging/os/test_pacman_key.py new file mode 100644 index 0000000000..757fee4e87 --- /dev/null +++ b/tests/unit/plugins/modules/packaging/os/test_pacman_key.py @@ -0,0 +1,576 @@ +# Copyright: (c) 2019, George Rawlinson +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.modules.packaging.os import pacman_key +import pytest +import json + +# path used for mocking get_bin_path() +MOCK_BIN_PATH = '/mocked/path' + +# Key ID used for tests +TESTING_KEYID = '14F26682D0916CDD81E37B6D61B7B526D98F0353' +TESTING_KEYFILE_PATH = '/tmp/pubkey.asc' + +# gpg --{show,list}-key output (key present) +GPG_SHOWKEY_OUTPUT = '''tru::1:1616373715:0:3:1:5 +pub:-:4096:1:61B7B526D98F0353:1437155332:::-:::scSC::::::23::0: +fpr:::::::::14F26682D0916CDD81E37B6D61B7B526D98F0353: +uid:-::::1437155332::E57D1F9BFF3B404F9F30333629369B08DF5E2161::Mozilla Software Releases ::::::::::0: +sub:e:4096:1:1C69C4E55E9905DB:1437155572:1500227572:::::s::::::23: +fpr:::::::::F2EF4E6E6AE75B95F11F1EB51C69C4E55E9905DB: +sub:e:4096:1:BBBEBDBB24C6F355:1498143157:1561215157:::::s::::::23: +fpr:::::::::DCEAC5D96135B91C4EA672ABBBBEBDBB24C6F355: +sub:e:4096:1:F1A6668FBB7D572E:1559247338:1622319338:::::s::::::23: +fpr:::::::::097B313077AE62A02F84DA4DF1A6668FBB7D572E:''' + +# gpg --{show,list}-key output (key absent) +GPG_NOKEY_OUTPUT = '''gpg: error reading key: No public key +tru::1:1616373715:0:3:1:5''' + +# pacman-key output (successful invocation) +PACMAN_KEY_SUCCESS = '''==> Updating trust database... +gpg: next trustdb check due at 2021-08-02''' + +# expected command for gpg --list-keys KEYID +RUN_CMD_LISTKEYS = [ + MOCK_BIN_PATH, + '--with-colons', + '--batch', + '--no-tty', + '--no-default-keyring', + '--keyring=/etc/pacman.d/gnupg/pubring.gpg', + '--list-keys', + TESTING_KEYID, +] + +# expected command for gpg --show-keys KEYFILE +RUN_CMD_SHOW_KEYFILE = [ + MOCK_BIN_PATH, + '--with-colons', + '--with-fingerprint', + '--batch', + '--no-tty', + '--show-keys', + TESTING_KEYFILE_PATH, +] + +# expected command for pacman-key --lsign-key KEYID +RUN_CMD_LSIGN_KEY = [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--lsign-key', + TESTING_KEYID, +] + + +TESTCASES = [ + # + # invalid user input + # + # state: present, id: absent + [ + { + 'state': 'present', + }, + { + 'id': 'param_missing_id', + 'msg': 'missing required arguments: id', + 'failed': True, + }, + ], + # state: present, required parameters: missing + [ + { + 'state': 'present', + 'id': '0xDOESNTMATTER', + }, + { + 'id': 'param_missing_method', + 'msg': 'state is present but any of the following are missing: data, file, url, keyserver', + 'failed': True, + }, + ], + # state: present, id: invalid (not full-length) + [ + { + 'id': '0xDOESNTMATTER', + 'data': 'FAKEDATA', + }, + { + 'id': 'param_id_not_full', + 'msg': 'key ID is not full-length: DOESNTMATTER', + 'failed': True, + }, + ], + # state: present, id: invalid (not hexadecimal) + [ + { + 'state': 'present', + 'id': '01234567890ABCDE01234567890ABCDE1234567M', + 'data': 'FAKEDATA', + }, + { + 'id': 'param_id_not_hex', + 'msg': 'key ID is not hexadecimal: 01234567890ABCDE01234567890ABCDE1234567M', + 'failed': True, + }, + ], + # state: absent, id: absent + [ + { + 'state': 'absent', + }, + { + 'id': 'param_absent_state_missing_id', + 'msg': 'missing required arguments: id', + 'failed': True, + }, + ], + # + # check mode + # + # state & key present + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'FAKEDATA', + '_ansible_check_mode': True, + }, + { + 'id': 'checkmode_state_and_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ], + 'changed': False, + }, + ], + # state present, key absent + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'FAKEDATA', + '_ansible_check_mode': True, + }, + { + 'id': 'checkmode_state_present_key_absent', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ], + 'changed': True, + }, + ], + # state & key absent + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + '_ansible_check_mode': True, + }, + { + 'id': 'checkmode_state_and_key_absent', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ], + 'changed': False, + }, + ], + # state absent, key present + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + '_ansible_check_mode': True, + }, + { + 'id': 'check_mode_state_absent_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ], + 'changed': True, + }, + ], + # + # normal operation + # + # state & key present + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'FAKEDATA', + }, + { + 'id': 'state_and_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ], + 'changed': False, + }, + ], + # state absent, key present + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + }, + { + 'id': 'state_absent_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--delete', + TESTING_KEYID, + ], + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'changed': True, + }, + ], + # state & key absent + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + }, + { + 'id': 'state_and_key_absent', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ], + 'changed': False, + }, + ], + # state: present, key: absent, method: file + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'file': TESTING_KEYFILE_PATH, + }, + { + 'id': 'state_present_key_absent_method_file', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + RUN_CMD_SHOW_KEYFILE, + {'check_rc': True}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--add', + '/tmp/pubkey.asc', + ], + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ( + RUN_CMD_LSIGN_KEY, + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'changed': True, + }, + ], + # state: present, key: absent, method: file + # failure: keyid & keyfile don't match + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'file': TESTING_KEYFILE_PATH, + }, + { + 'id': 'state_present_key_absent_verify_failed', + 'msg': 'key ID does not match. expected 14F26682D0916CDD81E37B6D61B7B526D98F0353, got 14F26682D0916CDD81E37B6D61B7B526D98F0354', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + RUN_CMD_SHOW_KEYFILE, + {'check_rc': True}, + ( + 0, + GPG_SHOWKEY_OUTPUT.replace('61B7B526D98F0353', '61B7B526D98F0354'), + '', + ), + ), + ], + 'failed': True, + }, + ], + # state: present, key: absent, method: keyserver + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'keyserver': 'pgp.mit.edu', + }, + { + 'id': 'state_present_key_absent_method_keyserver', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--keyserver', + 'pgp.mit.edu', + '--recv-keys', + TESTING_KEYID, + ], + {'check_rc': True}, + ( + 0, + ''' +gpg: key 0x61B7B526D98F0353: 32 signatures not checked due to missing keys +gpg: key 0x61B7B526D98F0353: public key "Mozilla Software Releases " imported +gpg: marginals needed: 3 completes needed: 1 trust model: pgp +gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u +gpg: Total number processed: 1 +gpg: imported: 1 +''', + '', + ), + ), + ( + RUN_CMD_LSIGN_KEY, + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'changed': True, + }, + ], + # state: present, key: absent, method: data + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'PGP_DATA', + }, + { + 'id': 'state_present_key_absent_method_data', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + RUN_CMD_SHOW_KEYFILE, + {'check_rc': True}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--add', + '/tmp/pubkey.asc', + ], + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ( + RUN_CMD_LSIGN_KEY, + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'save_key_output': TESTING_KEYFILE_PATH, + 'changed': True, + }, + ], +] + + +@pytest.fixture +def patch_get_bin_path(mocker): + get_bin_path = mocker.patch.object( + AnsibleModule, + 'get_bin_path', + return_value=MOCK_BIN_PATH, + ) + + +@pytest.mark.parametrize( + 'patch_ansible_module, expected', + TESTCASES, + ids=[item[1]['id'] for item in TESTCASES], + indirect=['patch_ansible_module'] +) +@pytest.mark.usefixtures('patch_ansible_module') +def test_operation(mocker, capfd, patch_get_bin_path, expected): + # patch run_command invocations with mock data + if 'run_command.calls' in expected: + mock_run_command = mocker.patch.object( + AnsibleModule, + 'run_command', + side_effect=[item[2] for item in expected['run_command.calls']], + ) + + # patch save_key invocations with mock data + if 'save_key_output' in expected: + mock_save_key = mocker.patch.object( + pacman_key.PacmanKey, + 'save_key', + return_value=expected['save_key_output'], + ) + + # invoke module + with pytest.raises(SystemExit): + pacman_key.main() + + # capture std{out,err} + out, err = capfd.readouterr() + results = json.loads(out) + + # assertion time! + if 'msg' in expected: + assert results['msg'] == expected['msg'] + if 'changed' in expected: + assert results['changed'] == expected['changed'] + if 'failed' in expected: + assert results['failed'] == expected['failed'] + + if 'run_command.calls' in expected: + assert AnsibleModule.run_command.call_count == len(expected['run_command.calls']) + call_args_list = [(item[0][0], item[1]) for item in AnsibleModule.run_command.call_args_list] + expected_call_args_list = [(item[0], item[1]) for item in expected['run_command.calls']] + assert call_args_list == expected_call_args_list diff --git a/tests/unit/plugins/modules/packaging/os/test_rhsm_release.py b/tests/unit/plugins/modules/packaging/os/test_rhsm_release.py index a75ec69448..98db6e2840 100644 --- a/tests/unit/plugins/modules/packaging/os/test_rhsm_release.py +++ b/tests/unit/plugins/modules/packaging/os/test_rhsm_release.py @@ -125,13 +125,12 @@ class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase): def test_release_matcher(self): # throw a few values at the release matcher -- only sane_values should match - sane_values = ['1Server', '10Server', '1.10', '10.0'] + sane_values = ['1Server', '1Client', '10Server', '1.10', '10.0', '9'] insane_values = [ '6server', # lowercase 's' '100Server', # excessively long 'x' component - '100.0', # excessively long 'x' component - '6.100', # excessively long 'y' component '100.100', # excessively long 'x' and 'y' components + '+.-', # illegal characters ] matches = self.module.release_matcher.findall(' '.join(sane_values + insane_values)) diff --git a/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_user.py b/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_user.py index 4a47654a8c..5722854e17 100644 --- a/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_user.py +++ b/tests/unit/plugins/modules/source_control/gitlab/test_gitlab_user.py @@ -144,7 +144,8 @@ class TestGitlabUser(GitlabModuleTestCase): 'name': "Public key", 'file': "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJe" "jgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4" - "soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="}) + "soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + 'expires_at': ""}) self.assertEqual(rvalue, False) rvalue = self.moduleUtil.addSshKeyToUser(user, { @@ -153,7 +154,8 @@ class TestGitlabUser(GitlabModuleTestCase): "dRuSuA5zszUJzYPPUSRAX3BCgTqLqYx//UuVncK7YqLVSbbwjKR2Ez5lISgCnVfLVEXzwhv+" "xawxKWmI7hJ5S0tOv6MJ+IxyTa4xcKwJTwB86z22n9fVOQeJTR2dSOH1WJrf0PvRk+KVNY2j" "TiGHTi9AIjLnyD/jWRpOgtdfkLRc8EzAWrWlgNmH2WOKBw6za0az6XoG75obUdFVdW3qcD0x" - "c809OHLi7FDf+E7U4wiZJCFuUizMeXyuK/SkaE1aee4Qp5R4dxTR4TP9M1XAYkf+kF0W9srZ+mhF069XD/zhUPJsvwEF"}) + "c809OHLi7FDf+E7U4wiZJCFuUizMeXyuK/SkaE1aee4Qp5R4dxTR4TP9M1XAYkf+kF0W9srZ+mhF069XD/zhUPJsvwEF", + 'expires_at': "2027-01-01"}) self.assertEqual(rvalue, True) @with_httmock(resp_get_group) diff --git a/tests/unit/plugins/modules/system/test_java_keystore.py b/tests/unit/plugins/modules/system/test_java_keystore.py index ec14b3734d..7d582a3e99 100644 --- a/tests/unit/plugins/modules/system/test_java_keystore.py +++ b/tests/unit/plugins/modules/system/test_java_keystore.py @@ -14,7 +14,7 @@ from ansible_collections.community.general.tests.unit.plugins.modules.utils impo from ansible_collections.community.general.tests.unit.compat.mock import patch from ansible_collections.community.general.tests.unit.compat.mock import Mock from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.modules.system.java_keystore import create_jks, cert_changed, ArgumentSpec +from ansible_collections.community.general.plugins.modules.system.java_keystore import JavaKeystore, ArgumentSpec class TestCreateJavaKeystore(ModuleTestCase): @@ -28,14 +28,16 @@ class TestCreateJavaKeystore(ModuleTestCase): self.spec = ArgumentSpec() self.mock_create_file = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.create_file') self.mock_create_path = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.create_path') - self.mock_run_commands = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.run_commands') + self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command') + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') self.mock_os_path_exists = patch('os.path.exists', side_effect=lambda path: True if path == '/path/to/keystore.jks' else orig_exists(path)) self.mock_selinux_context = patch('ansible.module_utils.basic.AnsibleModule.selinux_context', side_effect=lambda path: ['unconfined_u', 'object_r', 'user_home_t', 's0']) self.mock_is_special_selinux_path = patch('ansible.module_utils.basic.AnsibleModule.is_special_selinux_path', side_effect=lambda path: (False, None)) - self.run_commands = self.mock_run_commands.start() + self.run_command = self.mock_run_command.start() + self.get_bin_path = self.mock_get_bin_path.start() self.create_file = self.mock_create_file.start() self.create_path = self.mock_create_path.start() self.selinux_context = self.mock_selinux_context.start() @@ -47,7 +49,8 @@ class TestCreateJavaKeystore(ModuleTestCase): super(TestCreateJavaKeystore, self).tearDown() self.mock_create_file.stop() self.mock_create_path.stop() - self.mock_run_commands.stop() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() self.mock_selinux_context.stop() self.mock_is_special_selinux_path.stop() self.mock_os_path_exists.stop() @@ -57,7 +60,38 @@ class TestCreateJavaKeystore(ModuleTestCase): certificate='cert-foo', private_key='private-foo', dest='/path/to/keystore.jks', - name='foo', + name='test', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + + with patch('os.remove', return_value=True): + self.create_path.side_effect = ['/tmp/tmpgrzm2ah7'] + self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp', ''] + self.run_command.side_effect = [(0, '', ''), (0, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + assert jks.create() == { + 'changed': True, + 'cmd': ["keytool", "-importkeystore", + "-destkeystore", "/path/to/keystore.jks", + "-srckeystore", "/tmp/tmpgrzm2ah7", "-srcstoretype", "pkcs12", "-alias", "test", + "-noprompt"], + 'msg': '', + 'rc': 0 + } + + def test_create_jks_keypass_fail_export_pkcs12(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + private_key_passphrase='passphrase-foo', + dest='/path/to/keystore.jks', + name='test', password='changeit' )) @@ -67,44 +101,15 @@ class TestCreateJavaKeystore(ModuleTestCase): ) module.exit_json = Mock() - - with patch('os.remove', return_value=True): - self.create_path.side_effect = ['/tmp/tmpgrzm2ah7'] - self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp'] - self.run_commands.side_effect = [(0, '', ''), (0, '', '')] - create_jks(module, "test", "openssl", "keytool", "/path/to/keystore.jks", "changeit", "") - module.exit_json.assert_called_once_with( - changed=True, - cmd=["keytool", "-importkeystore", - "-destkeystore", "/path/to/keystore.jks", - "-srckeystore", "/tmp/tmpgrzm2ah7", "-srcstoretype", "pkcs12", "-alias", "test", - "-deststorepass:env", "STOREPASS", "-srcstorepass:env", "STOREPASS", "-noprompt"], - msg='', - rc=0 - ) - - def test_create_jks_keypass_fail_export_pkcs12(self): - set_module_args(dict( - certificate='cert-foo', - private_key='private-foo', - private_key_passphrase='passphrase-foo', - dest='/path/to/keystore.jks', - name='foo', - password='changeit' - )) - - module = AnsibleModule( - argument_spec=self.spec.argument_spec, - supports_check_mode=self.spec.supports_check_mode - ) - module.fail_json = Mock() with patch('os.remove', return_value=True): self.create_path.side_effect = ['/tmp/tmp1cyp12xa'] - self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c'] - self.run_commands.side_effect = [(1, '', ''), (0, '', '')] - create_jks(module, "test", "openssl", "keytool", "/path/to/keystore.jks", "changeit", "passphrase-foo") + self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c', ''] + self.run_command.side_effect = [(1, '', ''), (0, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.create() module.fail_json.assert_called_once_with( cmd=["openssl", "pkcs12", "-export", "-name", "test", "-in", "/tmp/tmpvalcrt32", @@ -121,7 +126,7 @@ class TestCreateJavaKeystore(ModuleTestCase): certificate='cert-foo', private_key='private-foo', dest='/path/to/keystore.jks', - name='foo', + name='test', password='changeit' )) @@ -130,13 +135,16 @@ class TestCreateJavaKeystore(ModuleTestCase): supports_check_mode=self.spec.supports_check_mode ) + module.exit_json = Mock() module.fail_json = Mock() with patch('os.remove', return_value=True): self.create_path.side_effect = ['/tmp/tmp1cyp12xa'] - self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c'] - self.run_commands.side_effect = [(1, '', ''), (0, '', '')] - create_jks(module, "test", "openssl", "keytool", "/path/to/keystore.jks", "changeit", "") + self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c', ''] + self.run_command.side_effect = [(1, '', ''), (0, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.create() module.fail_json.assert_called_once_with( cmd=["openssl", "pkcs12", "-export", "-name", "test", "-in", "/tmp/tmpvalcrt32", @@ -152,7 +160,7 @@ class TestCreateJavaKeystore(ModuleTestCase): certificate='cert-foo', private_key='private-foo', dest='/path/to/keystore.jks', - name='foo', + name='test', password='changeit' )) @@ -161,18 +169,21 @@ class TestCreateJavaKeystore(ModuleTestCase): supports_check_mode=self.spec.supports_check_mode ) + module.exit_json = Mock() module.fail_json = Mock() with patch('os.remove', return_value=True): self.create_path.side_effect = ['/tmp/tmpgrzm2ah7'] - self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp'] - self.run_commands.side_effect = [(0, '', ''), (1, '', '')] - create_jks(module, "test", "openssl", "keytool", "/path/to/keystore.jks", "changeit", "") + self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp', ''] + self.run_command.side_effect = [(0, '', ''), (1, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.create() module.fail_json.assert_called_once_with( cmd=["keytool", "-importkeystore", "-destkeystore", "/path/to/keystore.jks", "-srckeystore", "/tmp/tmpgrzm2ah7", "-srcstoretype", "pkcs12", "-alias", "test", - "-deststorepass:env", "STOREPASS", "-srcstorepass:env", "STOREPASS", "-noprompt"], + "-noprompt"], msg='', rc=1 ) @@ -186,15 +197,18 @@ class TestCertChanged(ModuleTestCase): super(TestCertChanged, self).setUp() self.spec = ArgumentSpec() self.mock_create_file = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.create_file') - self.mock_run_commands = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.run_commands') - self.run_commands = self.mock_run_commands.start() + self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command') + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + self.run_command = self.mock_run_command.start() self.create_file = self.mock_create_file.start() + self.get_bin_path = self.mock_get_bin_path.start() def tearDown(self): """Teardown.""" super(TestCertChanged, self).tearDown() self.mock_create_file.stop() - self.mock_run_commands.stop() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() def test_cert_unchanged_same_fingerprint(self): set_module_args(dict( @@ -211,9 +225,11 @@ class TestCertChanged(ModuleTestCase): ) with patch('os.remove', return_value=True): - self.create_file.side_effect = ['/tmp/placeholder'] - self.run_commands.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: abcd:1234:efgh', '')] - result = cert_changed(module, "openssl", "keytool", "/path/to/keystore.jks", "changeit", 'foo') + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: abcd:1234:efgh', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + result = jks.cert_changed() self.assertFalse(result, 'Fingerprint is identical') def test_cert_changed_fingerprint_mismatch(self): @@ -231,9 +247,11 @@ class TestCertChanged(ModuleTestCase): ) with patch('os.remove', return_value=True): - self.create_file.side_effect = ['/tmp/placeholder'] - self.run_commands.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: wxyz:9876:stuv', '')] - result = cert_changed(module, "openssl", "keytool", "/path/to/keystore.jks", "changeit", 'foo') + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: wxyz:9876:stuv', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + result = jks.cert_changed() self.assertTrue(result, 'Fingerprint mismatch') def test_cert_changed_fail_alias_does_not_exist(self): @@ -251,10 +269,12 @@ class TestCertChanged(ModuleTestCase): ) with patch('os.remove', return_value=True): - self.create_file.side_effect = ['/tmp/placeholder'] - self.run_commands.side_effect = [(0, 'foo=abcd:1234:efgh', ''), - (1, 'keytool error: java.lang.Exception: Alias does not exist', '')] - result = cert_changed(module, "openssl", "keytool", "/path/to/keystore.jks", "changeit", 'foo') + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), + (1, 'keytool error: java.lang.Exception: Alias does not exist', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + result = jks.cert_changed() self.assertTrue(result, 'Alias mismatch detected') def test_cert_changed_password_mismatch(self): @@ -272,10 +292,12 @@ class TestCertChanged(ModuleTestCase): ) with patch('os.remove', return_value=True): - self.create_file.side_effect = ['/tmp/placeholder'] - self.run_commands.side_effect = [(0, 'foo=abcd:1234:efgh', ''), - (1, 'keytool error: java.io.IOException: Keystore password was incorrect', '')] - result = cert_changed(module, "openssl", "keytool", "/path/to/keystore.jks", "changeit", 'foo') + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), + (1, 'keytool error: java.io.IOException: Keystore password was incorrect', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + result = jks.cert_changed() self.assertTrue(result, 'Password mismatch detected') def test_cert_changed_fail_read_cert(self): @@ -292,12 +314,15 @@ class TestCertChanged(ModuleTestCase): supports_check_mode=self.spec.supports_check_mode ) + module.exit_json = Mock() module.fail_json = Mock() with patch('os.remove', return_value=True): - self.create_file.side_effect = ['/tmp/tmpdj6bvvme'] - self.run_commands.side_effect = [(1, '', 'Oops'), (0, 'SHA256: wxyz:9876:stuv', '')] - cert_changed(module, "openssl", "keytool", "/path/to/keystore.jks", "changeit", 'foo') + self.create_file.side_effect = ['/tmp/tmpdj6bvvme', ''] + self.run_command.side_effect = [(1, '', 'Oops'), (0, 'SHA256: wxyz:9876:stuv', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.cert_changed() module.fail_json.assert_called_once_with( cmd=["openssl", "x509", "-noout", "-in", "/tmp/tmpdj6bvvme", "-fingerprint", "-sha256"], msg='', @@ -319,14 +344,17 @@ class TestCertChanged(ModuleTestCase): supports_check_mode=self.spec.supports_check_mode ) + module.exit_json = Mock() module.fail_json = Mock(return_value=True) with patch('os.remove', return_value=True): - self.create_file.side_effect = ['/tmp/placeholder'] - self.run_commands.side_effect = [(0, 'foo: wxyz:9876:stuv', ''), (1, '', 'Oops')] - cert_changed(module, "openssl", "keytool", "/path/to/keystore.jks", "changeit", 'foo') + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo: wxyz:9876:stuv', ''), (1, '', 'Oops')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.cert_changed() module.fail_json.assert_called_with( - cmd=["keytool", "-list", "-alias", "foo", "-keystore", "/path/to/keystore.jks", "-storepass:env", "STOREPASS", "-v"], + cmd=["keytool", "-list", "-alias", "foo", "-keystore", "/path/to/keystore.jks", "-v"], msg='', err='Oops', rc=1 diff --git a/tests/unit/plugins/modules/system/test_xfconf.py b/tests/unit/plugins/modules/system/test_xfconf.py index 1002952ce3..d8c9a30a9a 100644 --- a/tests/unit/plugins/modules/system/test_xfconf.py +++ b/tests/unit/plugins/modules/system/test_xfconf.py @@ -21,7 +21,7 @@ def patch_xfconf(mocker): """ Function used for mocking some parts of redhat_subscribtion module """ - mocker.patch('ansible_collections.community.general.plugins.module_utils.module_helper.AnsibleModule.get_bin_path', + mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path', return_value='/testbin/xfconf-query') @@ -49,7 +49,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '100\n', '',), ), @@ -69,7 +69,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/i_dont_exist'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (1, '', 'Property "/general/i_dont_exist" does not exist on channel "xfwm4".\n',), ), @@ -89,7 +89,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, 'Value is an array with 3 items:\n\nMain\nWork\nTmp\n', '',), ), @@ -109,7 +109,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/use_compositing'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, 'true', '',), ), @@ -129,7 +129,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/use_compositing'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, 'false', '',), ), @@ -155,7 +155,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '100\n', '',), ), @@ -164,7 +164,7 @@ TEST_CASES = [ ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity', '--create', '--type', 'int', '--set', '90'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '', '',), ), @@ -190,7 +190,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '90\n', '',), ), @@ -199,7 +199,7 @@ TEST_CASES = [ ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity', '--create', '--type', 'int', '--set', '90'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '', '',), ), @@ -225,7 +225,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, 'Value is an array with 3 items:\n\nMain\nWork\nTmp\n', '',), ), @@ -235,7 +235,7 @@ TEST_CASES = [ '--create', '--force-array', '--type', 'string', '--set', 'A', '--type', 'string', '--set', 'B', '--type', 'string', '--set', 'C'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '', '',), ), @@ -261,7 +261,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, 'Value is an array with 3 items:\n\nA\nB\nC\n', '',), ), @@ -271,7 +271,7 @@ TEST_CASES = [ '--create', '--force-array', '--type', 'string', '--set', 'A', '--type', 'string', '--set', 'B', '--type', 'string', '--set', 'C'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '', '',), ), @@ -295,7 +295,7 @@ TEST_CASES = [ # Calling of following command will be asserted ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, 'Value is an array with 3 items:\n\nA\nB\nC\n', '',), ), @@ -304,7 +304,7 @@ TEST_CASES = [ ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names', '--reset'], # Was return code checked? - {'environ_update': {'LANGUAGE': 'C'}, 'check_rc': False}, + {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False}, # Mock of returned code, stdout and stderr (0, '', '',), ), @@ -332,7 +332,7 @@ def test_xfconf(mocker, capfd, patch_xfconf, testcase): # 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.module_helper.AnsibleModule.run_command', + 'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command', side_effect=call_results) # Try to run test case diff --git a/tests/utils/shippable/shippable.sh b/tests/utils/shippable/shippable.sh index f239e86975..f70aa11380 100755 --- a/tests/utils/shippable/shippable.sh +++ b/tests/utils/shippable/shippable.sh @@ -181,7 +181,7 @@ function cleanup flags="${flags//=/,}" flags="${flags//[^a-zA-Z0-9_,]/_}" - bash <(curl -s https://codecov.io/bash) \ + bash <(curl -s https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh) \ -f "${file}" \ -F "${flags}" \ -n "${test}" \