fix
This commit is contained in:
parent
f9712b45b0
commit
d2b3f8d612
100 changed files with 4216 additions and 5001 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,4 +1,4 @@
|
|||
# ---> Ansible
|
||||
*.retry
|
||||
.ansible/
|
||||
|
||||
collections/
|
||||
|
|
|
|||
18
ansible.cfg
18
ansible.cfg
|
|
@ -1,12 +1,14 @@
|
|||
[defaults]
|
||||
roles_path = roles
|
||||
collections_paths = collections
|
||||
filter_plugins = filter_plugins
|
||||
retry_files_enabled = False
|
||||
stdout_callback = yaml
|
||||
interpreter_python = auto_silent
|
||||
host_key_checking = False
|
||||
deprecation_warnings=False
|
||||
roles_path = roles
|
||||
collections_paths = collections
|
||||
filter_plugins = filter_plugins
|
||||
retry_files_enabled = False
|
||||
interpreter_python = auto_silent
|
||||
host_key_checking = False
|
||||
result_format = yaml
|
||||
stdout_callback = default
|
||||
callback_result_format = yaml
|
||||
deprecation_warnings = False
|
||||
|
||||
[ssh_connection]
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
|
||||
|
|
|
|||
|
|
@ -2,61 +2,28 @@
|
|||
name: "CI"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.run_id }}-${{ github.event_name }}
|
||||
group: ${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on: # yamllint disable-line rule:truthy
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
sonar:
|
||||
name: SonarCloud
|
||||
uses: ansible/ansible-content-actions/.github/workflows/sonarcloud.yaml@main
|
||||
with:
|
||||
python_version: "3.12"
|
||||
test_command: "pytest tests/unit -v --cov-report xml --cov=./ -o 'addopts='"
|
||||
secrets:
|
||||
SONAR_TOKEN: ${{ secrets.ANSIBLE_COLLECTIONS_ORG_SONAR_TOKEN_CICD_BOT }}
|
||||
|
||||
changelog:
|
||||
uses: ansible/ansible-content-actions/.github/workflows/changelog.yaml@main
|
||||
if: github.event_name == 'pull_request_target'
|
||||
|
||||
if: github.event_name == 'pull_request'
|
||||
ansible-lint:
|
||||
uses: ansible/ansible-content-actions/.github/workflows/ansible_lint.yaml@main
|
||||
|
||||
sanity:
|
||||
uses: ansible/ansible-content-actions/.github/workflows/sanity.yaml@main
|
||||
|
||||
unit-galaxy:
|
||||
uses: ansible/ansible-content-actions/.github/workflows/unit.yaml@main
|
||||
|
||||
integration:
|
||||
uses: ansible/ansible-content-actions/.github/workflows/integration.yaml@main
|
||||
|
||||
report-status:
|
||||
if: ${{ always() && github.event_name == 'schedule' }}
|
||||
needs:
|
||||
- changelog
|
||||
- ansible-lint
|
||||
- sanity
|
||||
- unit-galaxy
|
||||
- integration
|
||||
uses: ansible/ansible-content-actions/.github/workflows/upload_upstream_results.yaml@main
|
||||
with:
|
||||
job_context: ${{ toJSON(needs) }}
|
||||
component_name: ${{ github.event.repository.name }}
|
||||
UPLOAD_USER: ${{ vars.PDE_ORG_RESULTS_AGGREGATOR_UPLOAD_USER }}
|
||||
UPLOAD_URL: ${{ vars.PDE_ORG_RESULTS_AGGREGATOR_UPLOAD_URL }}
|
||||
secrets:
|
||||
UPLOAD_PASSWORD: ${{ secrets.PDE_ORG_RESULTS_UPLOAD_PASSWORD }}
|
||||
|
||||
all_green:
|
||||
if: ${{ always() }}
|
||||
needs:
|
||||
|
|
@ -65,8 +32,6 @@ jobs:
|
|||
- unit-galaxy
|
||||
- ansible-lint
|
||||
- integration
|
||||
- sonar
|
||||
- report-status
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: >-
|
||||
|
|
@ -76,7 +41,5 @@ jobs:
|
|||
'${{ needs.integration.result }}',
|
||||
'${{ needs.sanity.result }}',
|
||||
'${{ needs.unit-galaxy.result }}',
|
||||
'${{ needs.ansible-lint.result }}',
|
||||
'${{ needs.sonar.result }}',
|
||||
'${{ needs.report-status.result }}'
|
||||
'${{ needs.ansible-lint.result }}'
|
||||
])"
|
||||
|
|
|
|||
|
|
@ -135,5 +135,4 @@ dmypy.json
|
|||
.pyre/
|
||||
|
||||
# ide
|
||||
.tests/integration/inventory
|
||||
.vscode/
|
||||
.vscode
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
repos:
|
||||
- repo: https://github.com/ansible-network/collection_prep
|
||||
rev: da78082ea59b03ad16cd7dbee267f53e8ff50bb5
|
||||
rev: 1.1.1
|
||||
hooks:
|
||||
# - id: autoversion # removed as being handled by GHA push and release drafter
|
||||
- id: update-docs
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: debug-statements
|
||||
|
|
@ -16,7 +16,7 @@ repos:
|
|||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v3.2.0
|
||||
rev: v3.1.0
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
|
||||
|
|
@ -29,18 +29,18 @@ repos:
|
|||
- prettier-plugin-toml
|
||||
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 6.0.1
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
name: Sort import statements using isort
|
||||
args: ["--filter-files"]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 25.1.0
|
||||
rev: 23.12.1
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 7.3.0
|
||||
rev: 7.0.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
|
|
|||
|
|
@ -4,90 +4,6 @@ Ansible Utils Collection Release Notes
|
|||
|
||||
.. contents:: Topics
|
||||
|
||||
v6.0.2
|
||||
======
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- cidr_merge - Fix filter failing when used inside a Jinja2 macro called with ``with context`` by unwrapping Ansible lazy template lists before validation.
|
||||
- cli_parse - Honor ttp_results.results flat_list in TTP parser so output is a single-level list instead of double-wrapped (https://github.com/ansible-collections/ansible.utils/issues/402).
|
||||
- ipaddress_utils - Support Python 3.14+ by using the public ``version`` attribute instead of the removed private ``_version`` on ``ipaddress`` network objects (bpo-118710).
|
||||
- update_fact - Use task_vars at top-level instead of the deprecated ``vars`` key for compatibility with ansible-core 2.24 (ansible/ansible issue
|
||||
|
||||
v6.0.1
|
||||
======
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add a cleanup step that removes empty {} and [] values from lists in keep_keys_from_dict_n_list()
|
||||
|
||||
Documentation Changes
|
||||
---------------------
|
||||
|
||||
- Fix the description of the reduce_on_network filter.
|
||||
- Fix the module name in ipmath filter.
|
||||
|
||||
v6.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
With this release, the minimum required version of `ansible-core` for this collection is `2.16.0`. The last version known to be compatible with `ansible-core` versions below `2.16` is v5.1.2.
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- Bumping `requires_ansible` to `>=2.16.0`, since previous ansible-core versions are EoL now.
|
||||
|
||||
v5.1.2
|
||||
======
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- keep_keys - Fixes keep_keys filter to retain the entire node when a key match occurs, rather than just the leaf node values.
|
||||
|
||||
v5.1.1
|
||||
======
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- keep_keys - Fixes issue where all keys are removed when data is passed in as a dict.
|
||||
|
||||
v5.1.0
|
||||
======
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Allows the cli_parse module to find parser.template_path inside roles or collections when a path relative to the role/collection directory is provided.
|
||||
- Fix cli_parse module to require a connection.
|
||||
- Previously, the ansible.utils.ipcut filter only supported IPv6 addresses, leading to confusing error messages when used with IPv4 addresses. This fix ensures that the filter now appropriately handles both IPv4 and IPv6 addresses.
|
||||
- Removed conditional check for deprecated ansible.netcommon.cli_parse from ansible.utils.cli_parse
|
||||
- The from_xml filter returns a python dictionary instead of a json string.
|
||||
|
||||
Documentation Changes
|
||||
---------------------
|
||||
|
||||
- Add a wildcard mask/hostmask documentation to ipaddr filter doc page to obtain an IP address's wildcard mask/hostmask.
|
||||
|
||||
v5.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
With this release, the minimum required version of `ansible-core` for this collection is `2.15.0`. The last version known to be compatible with `ansible-core` versions below `2.15` is v4.1.0.
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- Bumping `requires_ansible` to `>=2.15.0`, since previous ansible-core versions are EoL now.
|
||||
|
||||
v4.1.0
|
||||
======
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
"collection_info": {
|
||||
"namespace": "ansible",
|
||||
"name": "utils",
|
||||
"version": "6.0.2",
|
||||
"version": "4.1.0",
|
||||
"authors": [
|
||||
"Ansible Community"
|
||||
],
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
"name": "FILES.json",
|
||||
"ftype": "file",
|
||||
"chksum_type": "sha256",
|
||||
"chksum_sha256": "19dcf67b45414d5687c1e9811735ab46014a9a7fd8b1f04fbfd1ea26e2baf98f",
|
||||
"chksum_sha256": "364b8a7ea619abce47e2b659b41dd8442aaf031f027a38888c9f0c48ee30181c",
|
||||
"format": 1
|
||||
},
|
||||
"format": 1
|
||||
|
|
|
|||
|
|
@ -1,36 +1,16 @@
|
|||
# Ansible Utilities Collection
|
||||
[](https://codecov.io/gh/ansible-collections/ansible.utils)
|
||||
[](https://github.com/ansible-collections/ansible.utils/actions/workflows/tests.yml)
|
||||
|
||||
The Ansible ``ansible.utils`` collection includes a variety of plugins that aid in the management, manipulation and visibility of data for the Ansible playbook developer.
|
||||
|
||||
## Support
|
||||
|
||||
As a Red Hat Ansible [Certified Content](https://catalog.redhat.com/software/search?target_platforms=Red%20Hat%20Ansible%20Automation%20Platform), this collection is entitled to [support](https://access.redhat.com/support/) through [Ansible Automation Platform](https://www.redhat.com/en/technologies/management/ansible) (AAP).
|
||||
|
||||
If a support case cannot be opened with Red Hat and the collection has been obtained either from [Galaxy](https://galaxy.ansible.com/ui/) or [GitHub](https://github.com/ansible-collections/ansible.utils), there is community support available at no charge.
|
||||
|
||||
You can join us on [#network:ansible.com](https://matrix.to/#/#network:ansible.com) room or the [Ansible Forum Network Working Group](https://forum.ansible.com/g/network-wg).
|
||||
|
||||
For more information you can check the communication section below.
|
||||
|
||||
## Communication
|
||||
|
||||
* Join the Ansible forum:
|
||||
* [Get Help](https://forum.ansible.com/c/help/6): get help or help others.
|
||||
* [Posts tagged with 'network'](https://forum.ansible.com/tag/network): subscribe to participate in collection-related conversations.```
|
||||
* [Ansible Network Automation Working Group](https://forum.ansible.com/g/network-wg/): by joining the team you will automatically get subscribed to the posts tagged with [network](https://forum.ansible.com/tags/network).
|
||||
* [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts.
|
||||
* [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events.
|
||||
|
||||
* The Ansible [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn): used to announce releases and important changes.
|
||||
|
||||
For more information about communication, see the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
|
||||
|
||||
<!--start requires_ansible-->
|
||||
## Ansible version compatibility
|
||||
|
||||
This collection has been tested against the following Ansible versions: **>=2.16.0**.
|
||||
This collection has been tested against following Ansible versions: **>=2.14.0**.
|
||||
|
||||
For collections that support Ansible 2.9, please ensure you update your `network_os` to use the
|
||||
fully qualified collection name (for example, `cisco.ios.ios`).
|
||||
Plugins and modules within a collection may be tested with only specific Ansible versions.
|
||||
A collection may contain metadata that identifies these versions.
|
||||
PEP440 is the schema used to describe the versions of Ansible.
|
||||
|
|
@ -172,7 +152,7 @@ Please read and familiarize yourself with this document.
|
|||
|
||||
## Release notes
|
||||
<!--Add a link to a changelog.md file or an external docsite to cover this information. -->
|
||||
Release notes are available [here](CHANGELOG.rst)
|
||||
Release notes are available [here](https://github.com/ansible-collections/ansible.utils/blob/main/changelogs/CHANGELOG.rst)
|
||||
For automated release announcements refer [here](https://twitter.com/AnsibleContent).
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,3 +2,5 @@
|
|||
# see https://docs.openstack.org/infra/bindep/ for additional information.
|
||||
|
||||
gcc-c++ [doc test platform:rpm]
|
||||
python3-devel [test platform:rpm]
|
||||
python3 [test platform:rpm]
|
||||
|
|
|
|||
|
|
@ -436,105 +436,3 @@ releases:
|
|||
fragments:
|
||||
- ipaddress_is_global_fallback.yaml
|
||||
release_date: "2024-04-15"
|
||||
5.0.0:
|
||||
changes:
|
||||
major_changes:
|
||||
- Bumping `requires_ansible` to `>=2.15.0`, since previous ansible-core versions
|
||||
are EoL now.
|
||||
release_summary:
|
||||
With this release, the minimum required version of `ansible-core`
|
||||
for this collection is `2.15.0`. The last version known to be compatible with
|
||||
`ansible-core` versions below `2.15` is v4.1.0.
|
||||
fragments:
|
||||
- bump_215.yaml
|
||||
release_date: "2024-06-10"
|
||||
5.1.0:
|
||||
changes:
|
||||
doc_changes:
|
||||
- Add a wildcard mask/hostmask documentation to ipaddr filter doc page to obtain
|
||||
an IP address's wildcard mask/hostmask.
|
||||
minor_changes:
|
||||
- Allows the cli_parse module to find parser.template_path inside roles or collections
|
||||
when a path relative to the role/collection directory is provided.
|
||||
- Fix cli_parse module to require a connection.
|
||||
- Previously, the ansible.utils.ipcut filter only supported IPv6 addresses,
|
||||
leading to confusing error messages when used with IPv4 addresses. This fix
|
||||
ensures that the filter now appropriately handles both IPv4 and IPv6 addresses.
|
||||
- Removed conditional check for deprecated ansible.netcommon.cli_parse from
|
||||
ansible.utils.cli_parse
|
||||
- The from_xml filter returns a python dictionary instead of a json string.
|
||||
fragments:
|
||||
- 200.yaml
|
||||
- 203.yaml
|
||||
- 204.yaml
|
||||
- 324.yaml
|
||||
- 358_ipcut.yaml
|
||||
- add_template_path.yaml
|
||||
- fix_cli_parse.yaml
|
||||
- fix_from_xml.yaml
|
||||
- todo_condition.yml
|
||||
release_date: "2024-08-05"
|
||||
5.1.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- keep_keys - Fixes issue where all keys are removed when data is passed in
|
||||
as a dict.
|
||||
fragments:
|
||||
- 0-readme.yml
|
||||
- keep_keys.yaml
|
||||
release_date: "2024-09-05"
|
||||
5.1.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- keep_keys - Fixes keep_keys filter to retain the entire node when a key match
|
||||
occurs, rather than just the leaf node values.
|
||||
fragments:
|
||||
- keep_keys_greedy.yaml
|
||||
release_date: "2024-09-30"
|
||||
6.0.0:
|
||||
changes:
|
||||
major_changes:
|
||||
- Bumping `requires_ansible` to `>=2.16.0`, since previous ansible-core versions
|
||||
are EoL now.
|
||||
release_summary:
|
||||
With this release, the minimum required version of `ansible-core`
|
||||
for this collection is `2.16.0`. The last version known to be compatible with
|
||||
`ansible-core` versions below `2.16` is v5.1.2.
|
||||
fragments:
|
||||
- bump_216.yaml
|
||||
- ipaddr.yaml
|
||||
- test_ansibleundefined.yaml
|
||||
release_date: "2025-04-08"
|
||||
6.0.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Add a cleanup step that removes empty {} and [] values from lists in keep_keys_from_dict_n_list()
|
||||
doc_changes:
|
||||
- Fix the description of the reduce_on_network filter.
|
||||
- Fix the module name in ipmath filter.
|
||||
fragments:
|
||||
- fix_data_tagging.yaml
|
||||
- fix_description.yaml
|
||||
- fix_keep_keys_list_retention.yaml
|
||||
- fix_module_name.yaml
|
||||
- fix_sanity.yaml
|
||||
release_date: "2025-12-31"
|
||||
6.0.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- cidr_merge - Fix filter failing when used inside a Jinja2 macro called with
|
||||
``with context`` by unwrapping Ansible lazy template lists before validation.
|
||||
- cli_parse - Honor ttp_results.results flat_list in TTP parser so output is
|
||||
a single-level list instead of double-wrapped (https://github.com/ansible-collections/ansible.utils/issues/402).
|
||||
- ipaddress_utils - Support Python 3.14+ by using the public ``version`` attribute
|
||||
instead of the removed private ``_version`` on ``ipaddress`` network objects
|
||||
(bpo-118710).
|
||||
- update_fact - Use task_vars at top-level instead of the deprecated ``vars``
|
||||
key for compatibility with ansible-core 2.24 (ansible/ansible issue
|
||||
fragments:
|
||||
- 402-flat_list.yml
|
||||
- 426-update_fact_task_vars.yml
|
||||
- cidr_merge-macro_context.yml
|
||||
- fix_deprecations.yaml
|
||||
- ipaddress_utils-python314.yml
|
||||
release_date: "2026-04-09"
|
||||
|
|
|
|||
|
|
@ -19,16 +19,9 @@ Synopsis
|
|||
--------
|
||||
- This filter is designed to return the input value if a query is True, and False if a query is False
|
||||
- This way it can be easily used in chained filters
|
||||
- For more details on how to use this plugin, please refer to `<docsite/rst/filters_ipaddr.rst>`_
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
The below requirements are needed on the local Ansible controller node that executes this filter.
|
||||
|
||||
- netaddr>=0.10.1
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -84,32 +84,32 @@ Examples
|
|||
# Ipmath filter plugin with different arthmetic.
|
||||
# Get the next fifth address based on an IP address
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.5' | ansible.utils.ipmath(5) }}"
|
||||
msg: "{{ '192.168.1.5' | ansible.netcommon.ipmath(5) }}"
|
||||
|
||||
# Get the tenth previous address based on an IP address
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.5' | ansible.utils.ipmath(-10) }}"
|
||||
msg: "{{ '192.168.1.5' | ansible.netcommon.ipmath(-10) }}"
|
||||
|
||||
# Get the next fifth address using CIDR notation
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.1/24' | ansible.utils.ipmath(5) }}"
|
||||
msg: "{{ '192.168.1.1/24' | ansible.netcommon.ipmath(5) }}"
|
||||
|
||||
# Get the previous fifth address using CIDR notation
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.6/24' | ansible.utils.ipmath(-5) }}"
|
||||
msg: "{{ '192.168.1.6/24' | ansible.netcommon.ipmath(-5) }}"
|
||||
|
||||
# Get the previous tenth address using cidr notation
|
||||
# It returns a address of the previous network range
|
||||
- debug:
|
||||
msg: "{{ '192.168.2.6/24' | ansible.utils.ipmath(-10) }}"
|
||||
msg: "{{ '192.168.2.6/24' | ansible.netcommon.ipmath(-10) }}"
|
||||
|
||||
# Get the next tenth address in IPv6
|
||||
- debug:
|
||||
msg: "{{ '2001::1' | ansible.utils.ipmath(10) }}"
|
||||
msg: "{{ '2001::1' | ansible.netcommon.ipmath(10) }}"
|
||||
|
||||
# Get the previous tenth address in IPv6
|
||||
- debug:
|
||||
msg: "{{ '2001::5' | ansible.utils.ipmath(-10) }}"
|
||||
msg: "{{ '2001::5' | ansible.netcommon.ipmath(-10) }}"
|
||||
|
||||
# TASK [debug] **********************************************************************************************************
|
||||
# ok: [localhost] => {
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ Common return values are documented `here <https://docs.ansible.com/ansible/late
|
|||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<div>Returns the filtered list of addresses belonging to the network.</div>
|
||||
<div>Returns whether an address or a network passed as argument is in a network.</div>
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -195,9 +195,9 @@ Examples
|
|||
update_list: []
|
||||
update:
|
||||
- path: addresses[{{ idx }}].network
|
||||
value: "{{ item['raw'] | ansible.utils.ipaddr('network') }}"
|
||||
value: "{{ item['raw'] | ansible.netcommon.ipaddr('network') }}"
|
||||
- path: addresses[{{ idx }}].prefix
|
||||
value: "{{ item['raw'] | ansible.utils.ipaddr('prefix') }}"
|
||||
value: "{{ item['raw'] | ansible.netcommon.ipaddr('prefix') }}"
|
||||
|
||||
- debug:
|
||||
var: update_list
|
||||
|
|
|
|||
|
|
@ -340,15 +340,6 @@ If needed, you can extract subnet and prefix information from the 'host/prefix'
|
|||
# {{ host_prefix | ansible.utils.ipaddr('host/prefix') | ansible.utils.ipaddr('prefix') }}
|
||||
[64, 24]
|
||||
|
||||
To get the wildcard mask from host_prefix
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
wildcard {{ host_prefix | ansible.utils.ipaddr('hostmask')}}
|
||||
|
||||
# from host_prefix '192.0.2.0/24' following will be generated
|
||||
wildcard 0.0.0.255
|
||||
|
||||
Converting subnet masks to CIDR notation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
@ -857,3 +848,7 @@ the filter ``slaac()`` generates an IPv6 address for a given network and a MAC A
|
|||
Playbook organization by roles
|
||||
:ref:`playbooks_best_practices`
|
||||
Tips and tricks for playbooks
|
||||
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
|
||||
Have a question? Stop by the google group!
|
||||
:ref:`communication_irc`
|
||||
How to join Ansible chat channels
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
requires_ansible: ">=2.16.0"
|
||||
requires_ansible: ">=2.14.0"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import json
|
|||
from importlib import import_module
|
||||
|
||||
from ansible.errors import AnsibleActionFail
|
||||
from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
from ansible.module_utils.connection import Connection
|
||||
from ansible.module_utils.connection import ConnectionError as AnsibleConnectionError
|
||||
from ansible.plugins.action import ActionBase
|
||||
|
|
@ -37,7 +37,7 @@ ARGSPEC_CONDITIONALS = {
|
|||
class ActionModule(ActionBase):
|
||||
"""action module"""
|
||||
|
||||
_requires_connection = True
|
||||
_requires_connection = False
|
||||
|
||||
PARSER_CLS_NAME = "CliParser"
|
||||
|
||||
|
|
@ -100,6 +100,19 @@ class ActionModule(ActionBase):
|
|||
"""
|
||||
requested_parser = self._task.args.get("parser").get("name")
|
||||
cref = dict(zip(["corg", "cname", "plugin"], requested_parser.split(".")))
|
||||
if cref["cname"] == "netcommon" and cref["plugin"] in [
|
||||
"json",
|
||||
"textfsm",
|
||||
"ttp",
|
||||
"xml",
|
||||
]:
|
||||
cref["cname"] = "utils"
|
||||
msg = (
|
||||
"Use 'ansible.utils.{plugin}' for parser name instead of '{requested_parser}'."
|
||||
" This feature will be removed from 'ansible.netcommon' collection in a release"
|
||||
" after 2022-11-01".format(plugin=cref["plugin"], requested_parser=requested_parser)
|
||||
)
|
||||
self._display.warning(msg)
|
||||
|
||||
parserlib = "ansible_collections.{corg}.{cname}.plugins.sub_plugins.cli_parser.{plugin}_parser".format(
|
||||
**cref,
|
||||
|
|
@ -113,6 +126,33 @@ class ActionModule(ActionBase):
|
|||
)
|
||||
return parser
|
||||
except Exception as exc:
|
||||
# TODO: The condition is added to support old sub-plugin structure.
|
||||
# Remove the if condition after ansible.netcommon.cli_parse module is removed
|
||||
# from ansible.netcommon collection
|
||||
if cref["cname"] == "netcommon" and cref["plugin"] in [
|
||||
"native",
|
||||
"content_templates",
|
||||
"ntc",
|
||||
"pyats",
|
||||
]:
|
||||
parserlib = (
|
||||
"ansible_collections.{corg}.{cname}.plugins.cli_parsers.{plugin}_parser".format(
|
||||
**cref,
|
||||
)
|
||||
)
|
||||
try:
|
||||
parsercls = getattr(import_module(parserlib), self.PARSER_CLS_NAME)
|
||||
parser = parsercls(
|
||||
task_args=self._task.args,
|
||||
task_vars=task_vars,
|
||||
debug=self._debug,
|
||||
)
|
||||
return parser
|
||||
except Exception as exc:
|
||||
self._result["failed"] = True
|
||||
self._result["msg"] = "Error loading parser: {err}".format(err=to_native(exc))
|
||||
return None
|
||||
|
||||
self._result["failed"] = True
|
||||
self._result["msg"] = "Error loading parser: {err}".format(err=to_native(exc))
|
||||
return None
|
||||
|
|
@ -165,11 +205,8 @@ class ActionModule(ActionBase):
|
|||
cmd_as_fname = self._task.args.get("parser").get("command").replace(" ", "_")
|
||||
fname = "{os}_{cmd}.{ext}".format(os=oper_sys, cmd=cmd_as_fname, ext=template_extension)
|
||||
source = self._find_needle("templates", fname)
|
||||
else:
|
||||
source = self._task.args.get("parser").get("template_path")
|
||||
source = self._find_needle("templates", source)
|
||||
self._debug("template_path in task args updated to {source}".format(source=source))
|
||||
self._task.args["parser"]["template_path"] = source
|
||||
self._debug("template_path in task args updated to {source}".format(source=source))
|
||||
self._task.args["parser"]["template_path"] = source
|
||||
|
||||
def _get_template_contents(self):
|
||||
"""Retrieve the contents of the parser template
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import re
|
|||
|
||||
from importlib import import_module
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.plugins.action import ActionBase
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
|
|
|
|||
|
|
@ -11,10 +11,9 @@ __metaclass__ = type
|
|||
import ast
|
||||
import re
|
||||
|
||||
from collections.abc import MutableMapping, MutableSequence
|
||||
|
||||
from ansible.errors import AnsibleActionFail
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common._collections_compat import MutableMapping, MutableSequence
|
||||
from ansible.plugins.action import ActionBase
|
||||
from jinja2 import Template, TemplateSyntaxError
|
||||
|
||||
|
|
@ -163,30 +162,23 @@ class ActionModule(ActionBase):
|
|||
self._result["changed"] = False
|
||||
self._check_argspec()
|
||||
results = set()
|
||||
full_replaces = set() # keys that were fully replaced (no path)
|
||||
self._ensure_valid_jinja()
|
||||
# Use task_vars (top-level) instead of task_vars["vars"] to avoid the
|
||||
# deprecated internal "vars" dictionary (ansible-core 2.24, issue #426).
|
||||
for entry in self._task.args["updates"]:
|
||||
parts = self._field_split(entry["path"])
|
||||
obj, path = parts[0], parts[1:]
|
||||
results.add(obj)
|
||||
if obj not in task_vars:
|
||||
if obj not in task_vars["vars"]:
|
||||
msg = "'{obj}' was not found in the current facts.".format(obj=obj)
|
||||
raise AnsibleActionFail(msg)
|
||||
retrieved = task_vars.get(obj)
|
||||
retrieved = task_vars["vars"].get(obj)
|
||||
if path:
|
||||
self.set_value(retrieved, path, entry["value"])
|
||||
else:
|
||||
if retrieved != entry["value"]:
|
||||
self._result.setdefault("ansible_facts", {})[obj] = entry["value"]
|
||||
full_replaces.add(obj)
|
||||
if task_vars["vars"][obj] != entry["value"]:
|
||||
task_vars["vars"][obj] = entry["value"]
|
||||
self._result["changed"] = True
|
||||
|
||||
for key in results:
|
||||
if key in full_replaces:
|
||||
value = self._result.get("ansible_facts", {}).get(key)
|
||||
else:
|
||||
value = task_vars.get(key)
|
||||
value = task_vars["vars"].get(key)
|
||||
self._result[key] = value
|
||||
return self._result
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from __future__ import absolute_import, division, print_function
|
|||
__metaclass__ = type
|
||||
|
||||
from ansible.errors import AnsibleActionFail, AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.plugins.action import ActionBase
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
|
|
|
|||
|
|
@ -129,26 +129,12 @@ RETURN = """
|
|||
"""
|
||||
|
||||
|
||||
def _unwrap_lazy_list(value):
|
||||
"""
|
||||
If value is an Ansible lazy template list, return a list of raw elements without
|
||||
triggering templar.template() resolution. This avoids Template.__new__() errors
|
||||
when the filter is used inside a Jinja2 macro called with ``with context``.
|
||||
"""
|
||||
if value is not None and hasattr(value, "_yield_non_lazy_list_items"):
|
||||
return list(value._yield_non_lazy_list_items())
|
||||
return value
|
||||
|
||||
|
||||
@pass_environment
|
||||
def _cidr_merge(*args, **kwargs):
|
||||
"""Merge CIDRs; compatible with pipe syntax (value | cidr_merge)."""
|
||||
"""Convert the given data from json to xml."""
|
||||
keys = ["value", "action"]
|
||||
data = dict(zip(keys, args[1:]))
|
||||
data.update(kwargs)
|
||||
# Unwrap lazy list when present to avoid resolution bug in macro context (no template.j2 change).
|
||||
if "value" in data:
|
||||
data["value"] = _unwrap_lazy_list(data["value"])
|
||||
aav = AnsibleArgSpecValidator(data=data, schema=DOCUMENTATION, name="cidr_merge")
|
||||
valid, errors, updated_data = aav.validate()
|
||||
if not valid:
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ RETURN = """
|
|||
- Returns diff between before and after facts.
|
||||
"""
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
AnsibleArgSpecValidator,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ DOCUMENTATION = """
|
|||
description:
|
||||
- This filter is designed to return the input value if a query is True, and False if a query is False
|
||||
- This way it can be easily used in chained filters
|
||||
- For more details on how to use this plugin, please refer to `<docsite/rst/filters_ipaddr.rst>`_
|
||||
options:
|
||||
value:
|
||||
description:
|
||||
|
|
@ -76,8 +75,6 @@ DOCUMENTATION = """
|
|||
type: str
|
||||
description: type of filter. example ipaddr, ipv4, ipv6, ipwrap
|
||||
notes:
|
||||
requirements:
|
||||
- netaddr>=0.10.1
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
|
|
|
|||
|
|
@ -105,15 +105,10 @@ def _ipcut(*args, **kwargs):
|
|||
|
||||
|
||||
def ipcut(value, amount):
|
||||
ipv6_oct = []
|
||||
try:
|
||||
ip = netaddr.IPAddress(value)
|
||||
if ip.version == 6:
|
||||
ip_bits = ip.bits().replace(":", "")
|
||||
elif ip.version == 4:
|
||||
ip_bits = ip.bits().replace(".", "")
|
||||
else:
|
||||
msg = "Unknown IP Address Version: {0}".format(ip.version)
|
||||
raise AnsibleFilterError(msg)
|
||||
ipv6address = ip.bits().replace(":", "")
|
||||
except (netaddr.AddrFormatError, ValueError):
|
||||
msg = "You must pass a valid IP address; {0} is invalid".format(value)
|
||||
raise AnsibleFilterError(msg)
|
||||
|
|
@ -125,27 +120,20 @@ def ipcut(value, amount):
|
|||
raise AnsibleFilterError(msg)
|
||||
else:
|
||||
if amount < 0:
|
||||
ipsub = ip_bits[amount:]
|
||||
ipsub = ipv6address[amount:]
|
||||
else:
|
||||
ipsub = ip_bits[0:amount]
|
||||
ipsub = ipv6address[0:amount]
|
||||
|
||||
if ip.version == 6:
|
||||
ipv4_oct = []
|
||||
for i in range(0, len(ipsub), 16):
|
||||
oct_sub = i + 16
|
||||
ipv4_oct.append(
|
||||
hex(int(ipsub[i:oct_sub], 2)).replace("0x", ""),
|
||||
)
|
||||
result = str(":".join(ipv4_oct))
|
||||
else: # ip.version == 4:
|
||||
ipv4_oct = []
|
||||
for i in range(0, len(ipsub), 8):
|
||||
oct_sub = i + 8
|
||||
ipv4_oct.append(
|
||||
str(int(ipsub[i:oct_sub], 2)),
|
||||
)
|
||||
result = str(".".join(ipv4_oct))
|
||||
return result
|
||||
ipsubfinal = []
|
||||
|
||||
for i in range(0, len(ipsub), 16):
|
||||
oct_sub = i + 16
|
||||
ipsubfinal.append(ipsub[i:oct_sub])
|
||||
|
||||
for i in ipsubfinal:
|
||||
x = hex(int(i, 2))
|
||||
ipv6_oct.append(x.replace("0x", ""))
|
||||
return str(":".join(ipv6_oct))
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
|
|
|
|||
|
|
@ -63,32 +63,32 @@ EXAMPLES = r"""
|
|||
# Ipmath filter plugin with different arthmetic.
|
||||
# Get the next fifth address based on an IP address
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.5' | ansible.utils.ipmath(5) }}"
|
||||
msg: "{{ '192.168.1.5' | ansible.netcommon.ipmath(5) }}"
|
||||
|
||||
# Get the tenth previous address based on an IP address
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.5' | ansible.utils.ipmath(-10) }}"
|
||||
msg: "{{ '192.168.1.5' | ansible.netcommon.ipmath(-10) }}"
|
||||
|
||||
# Get the next fifth address using CIDR notation
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.1/24' | ansible.utils.ipmath(5) }}"
|
||||
msg: "{{ '192.168.1.1/24' | ansible.netcommon.ipmath(5) }}"
|
||||
|
||||
# Get the previous fifth address using CIDR notation
|
||||
- debug:
|
||||
msg: "{{ '192.168.1.6/24' | ansible.utils.ipmath(-5) }}"
|
||||
msg: "{{ '192.168.1.6/24' | ansible.netcommon.ipmath(-5) }}"
|
||||
|
||||
# Get the previous tenth address using cidr notation
|
||||
# It returns a address of the previous network range
|
||||
- debug:
|
||||
msg: "{{ '192.168.2.6/24' | ansible.utils.ipmath(-10) }}"
|
||||
msg: "{{ '192.168.2.6/24' | ansible.netcommon.ipmath(-10) }}"
|
||||
|
||||
# Get the next tenth address in IPv6
|
||||
- debug:
|
||||
msg: "{{ '2001::1' | ansible.utils.ipmath(10) }}"
|
||||
msg: "{{ '2001::1' | ansible.netcommon.ipmath(10) }}"
|
||||
|
||||
# Get the previous tenth address in IPv6
|
||||
- debug:
|
||||
msg: "{{ '2001::5' | ansible.utils.ipmath(-10) }}"
|
||||
msg: "{{ '2001::5' | ansible.netcommon.ipmath(-10) }}"
|
||||
|
||||
# TASK [debug] **********************************************************************************************************
|
||||
# ok: [localhost] => {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from ansible_collections.ansible.utils.plugins.plugin_utils.base.ipaddr_utils im
|
|||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -118,23 +118,17 @@ def next_nth_usable(value, offset):
|
|||
Returns the next nth usable ip within a network described by value.
|
||||
"""
|
||||
try:
|
||||
v = None
|
||||
vtype = ipaddr(value, "type")
|
||||
if vtype == "address":
|
||||
v = ipaddr(value, "cidr")
|
||||
elif vtype == "network":
|
||||
v = ipaddr(value, "subnet")
|
||||
|
||||
if v is not None:
|
||||
v = netaddr.IPNetwork(v)
|
||||
else:
|
||||
return False
|
||||
v = netaddr.IPNetwork(v)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
if not isinstance(offset, int):
|
||||
raise AnsibleFilterError("Must pass in an integer")
|
||||
|
||||
if v.size > 1:
|
||||
first_usable, last_usable = _first_last(v)
|
||||
nth_ip = int(netaddr.IPAddress(int(v.ip) + offset))
|
||||
|
|
|
|||
|
|
@ -117,18 +117,13 @@ def previous_nth_usable(value, offset):
|
|||
Returns the previous nth usable ip within a network described by value.
|
||||
"""
|
||||
try:
|
||||
v = None
|
||||
vtype = ipaddr(value, "type")
|
||||
if vtype == "address":
|
||||
v = ipaddr(value, "cidr")
|
||||
elif vtype == "network":
|
||||
v = ipaddr(value, "subnet")
|
||||
|
||||
if v is not None:
|
||||
v = netaddr.IPNetwork(v)
|
||||
else:
|
||||
return False
|
||||
|
||||
v = netaddr.IPNetwork(v)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ RETURN = """
|
|||
data:
|
||||
type: bool
|
||||
description:
|
||||
- Returns the filtered list of addresses belonging to the network.
|
||||
- Returns whether an address or a network passed as argument is in a network.
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,6 @@ from ansible_collections.ansible.utils.plugins.plugin_utils.base.utils import _v
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
name: usable_range
|
||||
author: Priyam Sahoo (@priyamsahoo)
|
||||
|
|
@ -152,6 +148,10 @@ RETURN = """
|
|||
- List of usable IP addresses under the key C(usable_ips)
|
||||
"""
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.six import ensure_text
|
||||
|
||||
|
||||
@_need_ipaddress
|
||||
def _usable_range(ip):
|
||||
|
|
@ -162,11 +162,11 @@ def _usable_range(ip):
|
|||
|
||||
try:
|
||||
if ip_network(ip).version == 4:
|
||||
ips = [to_text(usable_ips) for usable_ips in IPv4Network(to_text(ip))]
|
||||
no_of_ips = IPv4Network(to_text(ip)).num_addresses
|
||||
ips = [to_text(usable_ips) for usable_ips in IPv4Network(ensure_text(ip))]
|
||||
no_of_ips = IPv4Network(ensure_text(ip)).num_addresses
|
||||
if ip_network(ip).version == 6:
|
||||
ips = [to_text(usable_ips) for usable_ips in IPv6Network(to_text(ip))]
|
||||
no_of_ips = IPv6Network(to_text(ip)).num_addresses
|
||||
ips = [to_text(usable_ips) for usable_ips in IPv6Network(ensure_text(ip))]
|
||||
no_of_ips = IPv6Network(ensure_text(ip)).num_addresses
|
||||
|
||||
except Exception as e:
|
||||
raise AnsibleFilterError(
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ RETURN = """
|
|||
"""
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleFilterError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
check_argspec,
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ RETURN = """
|
|||
"""
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleLookupError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ __metaclass__ = type
|
|||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.utils import dict_merge
|
||||
|
||||
|
|
@ -174,7 +175,7 @@ class AnsibleArgSpecValidator:
|
|||
:type temp_schema: dict
|
||||
"""
|
||||
options_obj = doc_obj.get("options")
|
||||
for okey, ovalue in options_obj.items():
|
||||
for okey, ovalue in iteritems(options_obj):
|
||||
temp_schema[okey] = {}
|
||||
for metakey in list(ovalue):
|
||||
if metakey == "suboptions":
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ __metaclass__ = type
|
|||
|
||||
import re
|
||||
|
||||
from collections.abc import Mapping, MutableMapping
|
||||
from ansible.module_utils.common._collections_compat import Mapping, MutableMapping
|
||||
|
||||
|
||||
def to_paths(var, prepend, wantlist):
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
from collections.abc import Mapping
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
def sort_list(val):
|
||||
if isinstance(val, list):
|
||||
|
|
@ -55,7 +57,7 @@ def dict_merge(base, other):
|
|||
|
||||
combined = dict()
|
||||
|
||||
for key, value in deepcopy(base).items():
|
||||
for key, value in iteritems(deepcopy(base)):
|
||||
if isinstance(value, dict):
|
||||
if key in other:
|
||||
item = other.get(key)
|
||||
|
|
|
|||
|
|
@ -144,9 +144,9 @@ EXAMPLES = r"""
|
|||
update_list: []
|
||||
update:
|
||||
- path: addresses[{{ idx }}].network
|
||||
value: "{{ item['raw'] | ansible.utils.ipaddr('network') }}"
|
||||
value: "{{ item['raw'] | ansible.netcommon.ipaddr('network') }}"
|
||||
- path: addresses[{{ idx }}].prefix
|
||||
value: "{{ item['raw'] | ansible.utils.ipaddr('prefix') }}"
|
||||
value: "{{ item['raw'] | ansible.netcommon.ipaddr('prefix') }}"
|
||||
|
||||
- debug:
|
||||
var: update_list
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"""
|
||||
The base class for cli_parsers
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from functools import wraps
|
|||
from ansible import errors
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.six import ensure_text
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
check_argspec,
|
||||
|
|
@ -38,7 +38,7 @@ def ip_network(ip):
|
|||
if not HAS_IPADDRESS:
|
||||
raise AnsibleError(missing_required_lib("ipaddress"))
|
||||
|
||||
return ipaddress.ip_network(to_text(ip))
|
||||
return ipaddress.ip_network(ensure_text(ip))
|
||||
|
||||
|
||||
def ip_address(ip):
|
||||
|
|
@ -47,7 +47,7 @@ def ip_address(ip):
|
|||
if not HAS_IPADDRESS:
|
||||
raise AnsibleError(missing_required_lib("ipaddress"))
|
||||
|
||||
return ipaddress.ip_address(to_text(ip))
|
||||
return ipaddress.ip_address(ensure_text(ip))
|
||||
|
||||
|
||||
def _need_ipaddress(func):
|
||||
|
|
@ -60,29 +60,15 @@ def _need_ipaddress(func):
|
|||
return wrapper
|
||||
|
||||
|
||||
def _get_network_version(network):
|
||||
"""
|
||||
Python 3.14 changes the _version attribute to version, so we have to try both.
|
||||
"""
|
||||
if hasattr(network, "_version"):
|
||||
return network._version
|
||||
return network.version
|
||||
|
||||
|
||||
def _is_subnet_of(network_a, network_b):
|
||||
"""
|
||||
Return True if network_a is a subnet of network_b (same logic as ipaddress).
|
||||
Uses the public .version attribute for compatibility with Python 3.14+ where
|
||||
the private _version was removed (see bpo-118710 / cpython@c530ce1).
|
||||
"""
|
||||
try:
|
||||
if _get_network_version(network_a) != _get_network_version(network_b):
|
||||
if network_a._version != network_b._version:
|
||||
return False
|
||||
return (
|
||||
network_b.network_address <= network_a.network_address
|
||||
and network_b.broadcast_address >= network_a.broadcast_address
|
||||
)
|
||||
except (AttributeError, TypeError):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
"""
|
||||
The base class for validator
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
|
|
@ -12,7 +11,8 @@ import os
|
|||
from importlib import import_module
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_native, to_text
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
check_argspec,
|
||||
|
|
@ -74,7 +74,7 @@ class ValidateBase(object):
|
|||
if not options:
|
||||
return None
|
||||
|
||||
for option_name, option_value in options.items():
|
||||
for option_name, option_value in iteritems(options):
|
||||
option_var_name_list = option_value.get("vars", [])
|
||||
option_env_name_list = option_value.get("env", [])
|
||||
|
||||
|
|
@ -181,10 +181,10 @@ def _load_validator(engine, data, criteria, plugin_vars=None, cls_name="Validate
|
|||
return validator, result
|
||||
except Exception as exc:
|
||||
result["failed"] = True
|
||||
result["msg"] = (
|
||||
"For engine '{engine}' error loading the corresponding validate plugin: {err}".format(
|
||||
engine=engine,
|
||||
err=to_native(exc),
|
||||
)
|
||||
result[
|
||||
"msg"
|
||||
] = "For engine '{engine}' error loading the corresponding validate plugin: {err}".format(
|
||||
engine=engine,
|
||||
err=to_native(exc),
|
||||
)
|
||||
return None, result
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
|
||||
|
||||
|
|
@ -44,7 +46,7 @@ def from_xml(data, engine):
|
|||
if not HAS_XMLTODICT:
|
||||
_raise_error("Missing required library xmltodict")
|
||||
try:
|
||||
res = xmltodict.parse(data, dict_constructor=dict)
|
||||
res = json.dumps(xmltodict.parse(data))
|
||||
except Exception:
|
||||
_raise_error("Input Xml is not valid")
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ __metaclass__ = type
|
|||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.six import integer_types, string_types
|
||||
from jinja2.exceptions import TemplateSyntaxError
|
||||
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ def index_of(
|
|||
if result:
|
||||
res.append(idx)
|
||||
|
||||
elif isinstance(key, (str, int, bool)):
|
||||
elif isinstance(key, (string_types, integer_types, bool)):
|
||||
if not all(isinstance(entry, dict) for entry in data):
|
||||
all_tipes = [type(_to_well_known_type(entry)).__name__ for entry in data]
|
||||
msg = (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2021 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
|
|
@ -30,34 +31,37 @@ def _raise_error(msg):
|
|||
def keep_keys_from_dict_n_list(data, target, matching_parameter):
|
||||
if isinstance(data, list):
|
||||
list_data = [keep_keys_from_dict_n_list(each, target, matching_parameter) for each in data]
|
||||
return [r for r in list_data if r not in ([], {}, None)]
|
||||
return list_data
|
||||
if isinstance(data, dict):
|
||||
keep = {}
|
||||
for k, val in data.items():
|
||||
key_matches = any(
|
||||
(
|
||||
k == t
|
||||
if matching_parameter == "equality"
|
||||
else (
|
||||
(re.match(t, k) is not None)
|
||||
if matching_parameter == "regex"
|
||||
else (
|
||||
k.startswith(t)
|
||||
if matching_parameter == "starts_with"
|
||||
else k.endswith(t)
|
||||
)
|
||||
)
|
||||
)
|
||||
for t in target
|
||||
)
|
||||
if key_matches:
|
||||
keep[k] = val
|
||||
elif isinstance(val, (list, dict)):
|
||||
nested_keep = keep_keys_from_dict_n_list(val, target, matching_parameter)
|
||||
if nested_keep not in ([], {}, None):
|
||||
keep[k] = nested_keep
|
||||
for key in target:
|
||||
match = None
|
||||
if not isinstance(val, (list, dict)):
|
||||
if matching_parameter == "regex":
|
||||
match = re.match(key, k)
|
||||
if match:
|
||||
keep[k] = val
|
||||
elif matching_parameter == "starts_with":
|
||||
if k.startswith(key):
|
||||
keep[k], match = val, True
|
||||
elif matching_parameter == "ends_with":
|
||||
if k.endswith(key):
|
||||
keep[k], match = val, True
|
||||
else:
|
||||
if k == key:
|
||||
keep[k], match = val, True
|
||||
else:
|
||||
list_data = keep_keys_from_dict_n_list(val, target, matching_parameter)
|
||||
if isinstance(list_data, list) and not match:
|
||||
list_data = [r for r in list_data if r not in ([], {})]
|
||||
if all(isinstance(s, str) for s in list_data):
|
||||
continue
|
||||
if list_data in ([], {}):
|
||||
continue
|
||||
keep[k] = list_data
|
||||
return keep
|
||||
return None
|
||||
return data
|
||||
|
||||
|
||||
def keep_keys(data, target, matching_parameter="equality"):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ json parser
|
|||
|
||||
This is the json parser for use with the cli_parse module and action plugin
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
|
|
@ -38,7 +37,8 @@ EXAMPLES = r"""
|
|||
|
||||
import json
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.cli_parser import CliParserBase
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ class CliParser(CliParserBase):
|
|||
"""
|
||||
text = self._task_args.get("text")
|
||||
try:
|
||||
if not isinstance(text, str):
|
||||
if not isinstance(text, string_types):
|
||||
text = json.dumps(text)
|
||||
parsed = json.loads(text)
|
||||
except Exception as exc:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ textfsm parser
|
|||
This is the textfsm parser for use with the cli_parse module and action plugin
|
||||
https://github.com/google/textfsm
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
|
|
@ -40,8 +39,8 @@ EXAMPLES = r"""
|
|||
|
||||
import os
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.cli_parser import CliParserBase
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ ttp parser
|
|||
This is the ttp parser for use with the cli_parse module and action plugin
|
||||
https://github.com/dmulyalin/ttp
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
|
|
@ -40,8 +39,8 @@ EXAMPLES = r"""
|
|||
|
||||
import os
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.cli_parser import CliParserBase
|
||||
|
||||
|
|
@ -119,23 +118,7 @@ class CliParser(CliParserBase):
|
|||
if parser_param.get("vars")
|
||||
else {}
|
||||
)
|
||||
# TTP's result() uses keyword "structure", but we accept "results" from users
|
||||
result_kwargs = dict(ttp_results)
|
||||
if "results" in result_kwargs:
|
||||
result_kwargs["structure"] = result_kwargs.pop("results")
|
||||
results = parser.result(**result_kwargs)
|
||||
# When flat_list is requested, flatten one level if we got [[...]]
|
||||
requested_flat = (
|
||||
ttp_results.get("results") == "flat_list"
|
||||
or ttp_results.get("structure") == "flat_list"
|
||||
)
|
||||
if (
|
||||
requested_flat
|
||||
and isinstance(results, list)
|
||||
and len(results) == 1
|
||||
and isinstance(results[0], list)
|
||||
):
|
||||
results = results[0]
|
||||
results = parser.result(**ttp_results)
|
||||
except Exception as exc:
|
||||
msg = "Template Text Parser returned an error while parsing. Error: {err}"
|
||||
return {"errors": [msg.format(err=to_native(exc))]}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ xml parser
|
|||
This is the xml parser for use with the cli_parse module and action plugin
|
||||
https://github.com/martinblech/xmltodict
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
|
|
@ -38,8 +37,8 @@ EXAMPLES = r"""
|
|||
register: nxos_xml_text
|
||||
"""
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.cli_parser import CliParserBase
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from __future__ import absolute_import, division, print_function
|
|||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
author: Katherine Case (@Qalthos)
|
||||
author: Nathaniel Case (@Qalthos)
|
||||
name: config
|
||||
short_description: Define configurable options for configuration validate plugin
|
||||
description:
|
||||
|
|
@ -51,7 +51,8 @@ import re
|
|||
from io import StringIO
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.utils import to_list
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.validate import ValidateBase
|
||||
|
|
@ -87,7 +88,7 @@ class Validate(ValidateBase):
|
|||
"""
|
||||
|
||||
try:
|
||||
if isinstance(self._criteria, str):
|
||||
if isinstance(self._criteria, string_types):
|
||||
self._criteria = yaml.load(StringIO(self._criteria), Loader=SafeLoader)
|
||||
except yaml.parser.ParserError as exc:
|
||||
msg = (
|
||||
|
|
|
|||
|
|
@ -54,8 +54,9 @@ DOCUMENTATION = """
|
|||
import json
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.utils.display import Display
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.utils import to_list
|
||||
|
|
@ -137,7 +138,7 @@ class Validate(ValidateBase):
|
|||
:return: None: In case all arguments passed are valid
|
||||
"""
|
||||
try:
|
||||
if isinstance(self._data, str):
|
||||
if isinstance(self._data, string_types):
|
||||
self._data = json.loads(self._data)
|
||||
else:
|
||||
self._data = json.loads(json.dumps(self._data))
|
||||
|
|
@ -154,7 +155,7 @@ class Validate(ValidateBase):
|
|||
try:
|
||||
criteria = []
|
||||
for item in to_list(self._criteria):
|
||||
if isinstance(self._criteria, str):
|
||||
if isinstance(self._criteria, string_types):
|
||||
criteria.append(json.loads(item))
|
||||
else:
|
||||
criteria.append(json.loads(json.dumps(item)))
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ RETURN = """
|
|||
"""
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
||||
check_argspec,
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
# Complete documentation with many more options at:
|
||||
# https://docs.sonarqube.org/latest/analysis/analysis-parameters/
|
||||
|
||||
## The unique project identifier. This is mandatory.
|
||||
# Do not duplicate or reuse!
|
||||
# Available characters: [a-zA-Z0-9_:\.\-]
|
||||
# Must have least one non-digit.
|
||||
# Recommended format: <group>:<project>
|
||||
sonar.projectKey=ansible-collections_ansible.utils
|
||||
|
||||
sonar.organization=ansible-collections
|
||||
|
||||
# Customize what paths to scan. Default is .
|
||||
sonar.sources=plugins
|
||||
sonar.inclusions=plugins/**/*.py
|
||||
|
||||
# Verbose name of project displayed in WUI. Default is set to the projectKey. This field is optional.
|
||||
sonar.projectName=ansible.utils
|
||||
|
||||
# Version of project. This field is optional.
|
||||
#sonar.projectVersion=1.0
|
||||
|
||||
# Exclude manifests directory as these are pulled in programmatically, or are partial files to be applied via kustomize
|
||||
# Exclude cli from scanning until we can add tests
|
||||
sonar.exclusions=tests/**
|
||||
|
||||
# Define test directories
|
||||
sonar.tests=tests/unit
|
||||
sonar.test.inclusions=tests/unit/**
|
||||
|
||||
# Tell sonar scanner where coverage files exist
|
||||
# Note: coverage.xml is created by pytest-cov in Code Coverage workflow
|
||||
sonar.python.coverage.reportPaths=coverage.xml
|
||||
|
||||
# Python versions used in CI (matching coverage workflow)
|
||||
sonar.python.version=3.12
|
||||
|
||||
# Ignore code dupe for the migrations
|
||||
# sonar.cpd.exclusions=
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
# Unit test runner
|
||||
pytest-ansible
|
||||
pytest-xdist
|
||||
typing-extensions
|
||||
|
||||
# For integration tests
|
||||
pexpect
|
||||
git+https://github.com/ansible-network/pytest-ansible-network-integration.git
|
||||
ipaddress ; python_version < '3.0'
|
||||
netaddr==0.10.1
|
||||
textfsm
|
||||
ttp
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
modules:
|
||||
python_requires: ">=3.10"
|
||||
python_requires: ">=3.9"
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#
|
||||
# Capture any deprecation warnings of e.g. ansible.module_utils for pytest
|
||||
#
|
||||
import warnings
|
||||
|
||||
import ansible.utils.display
|
||||
|
||||
|
||||
ansible.utils.display.Display().deprecated = lambda msg, *args, **kwargs: warnings.warn(
|
||||
msg,
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
|
@ -16,4 +16,4 @@
|
|||
with_items:
|
||||
- "{{ nxos_json_text['parsed'] is defined }}"
|
||||
- "{{ nxos_json_text['parsed'][0][0][0]['admin_state'] is defined }}"
|
||||
# - "{{ nxos_json_text['parsed'] == nxos_json_text_parsed }}"
|
||||
- "{{ nxos_json_text['parsed'] == nxos_json_text_parsed }}"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
- name: Set fact
|
||||
ansible.builtin.set_fact:
|
||||
nxos_ttp_text_parsed: "{{ lookup('ansible.builtin.file', role_path ~ '/output/nxos_show_interface_ttp_parsed.json') | from_json }}"
|
||||
nxos_ttp_text_parsed: "{{ lookup('ansible.builtin.file', '{{ role_path }}/output/nxos_show_interface_ttp_parsed.json') | from_json }}"
|
||||
|
||||
- name: "Pass text and template_path {{ parser }}"
|
||||
ansible.utils.cli_parse:
|
||||
text: "{{ lookup('ansible.builtin.file', role_path ~ '/files/nxos_show_interface.txt') }}"
|
||||
text: "{{ lookup('ansible.builtin.file', '{{ role_path }}/files/nxos_show_interface.txt') }}"
|
||||
parser:
|
||||
name: ansible.utils.ttp
|
||||
template_path: "{{ role_path }}/templates/nxos_show_interface.ttp"
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
ansible.builtin.assert:
|
||||
that:
|
||||
- "{{ POpqMQoJWTiDpEW is defined }}"
|
||||
- "{{ (nxos_ttp_text['parsed'][0][0] | selectattr('interface', 'search', 'mgmt0') | list | length) > 0 }}"
|
||||
- nxos_ttp_text['parsed'][0][0] | selectattr('interface', 'search', 'mgmt0') | list | length
|
||||
- "{{ nxos_ttp_text['parsed'] == nxos_ttp_text_parsed }}"
|
||||
|
||||
- name: "Pass text and custom variable {{ parser }}"
|
||||
|
|
@ -51,4 +51,4 @@
|
|||
ansible.builtin.assert:
|
||||
that: item
|
||||
with_items:
|
||||
- "{{ ((nxos_ttp_results['parsed'][0] | from_yaml)[0] | selectattr('interface', 'search', 'mgmt0') | list | length) > 0 }}"
|
||||
- "{{ (nxos_ttp_results['parsed'][0] | from_yaml)[0] | selectattr('interface', 'search', 'mgmt0') | list | length }}"
|
||||
|
|
|
|||
|
|
@ -19,12 +19,10 @@
|
|||
|
||||
- name: Integration tests with and without default engine as xmltodict and
|
||||
ansible.builtin.assert:
|
||||
that: "{{ output == test_item.test }}"
|
||||
that: "{{ output == item.test }}"
|
||||
loop:
|
||||
- test: "{{ data | ansible.utils.from_xml() }}"
|
||||
- test: "{{ data | ansible.utils.from_xml('xmltodict') }}"
|
||||
loop_control:
|
||||
loop_var: test_item
|
||||
|
||||
- name: Setup invalid xml as input to ansible.utils.from_xml.
|
||||
ansible.builtin.set_fact:
|
||||
|
|
|
|||
|
|
@ -52,15 +52,15 @@
|
|||
# ok: [sw01] => (item=2) =>
|
||||
# msg: 3 is > than 1
|
||||
|
||||
# - name: Find numbers greater than 1, using with
|
||||
# ansible.builtin.debug:
|
||||
# msg: "{{ data[item] }} is {{ params.test }} than {{ params.value }}"
|
||||
# with_ansible.utils.index_of: "{{ params }}"
|
||||
# vars:
|
||||
# params:
|
||||
# data: "{{ data }}"
|
||||
# test: ">"
|
||||
# value: 1
|
||||
- name: Find numbers greater than 1, using with
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ data[item] }} is {{ params.test }} than {{ params.value }}"
|
||||
with_ansible.utils.index_of: "{{ params }}"
|
||||
vars:
|
||||
params:
|
||||
data: "{{ data }}"
|
||||
test: ">"
|
||||
value: 1
|
||||
|
||||
# TASK [Find numbers greater than 1, using with] *****************************
|
||||
# ok: [nxos101] => (item=1) =>
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@
|
|||
# - test: "{{ lookup('ansible.utils.index_of', complex.a, 'integer') }}"
|
||||
# result: "3"
|
||||
|
||||
# - test: "{{ complex.b | ansible.utils.index_of('==', 1, 'b1') }}"
|
||||
# result: 0
|
||||
# - test: "{{ lookup('ansible.utils.index_of', complex.b, '==', 1, 'b1') }}"
|
||||
# result: 0
|
||||
- test: "{{ complex.b | ansible.utils.index_of('==', 1, 'b1') }}"
|
||||
result: "0"
|
||||
- test: "{{ lookup('ansible.utils.index_of', complex.b, '==', 1, 'b1') }}"
|
||||
result: "0"
|
||||
|
||||
- test: "{{ complex.c.c1 | ansible.utils.index_of('!=', 'c') }}"
|
||||
result: [0, 1]
|
||||
|
|
@ -75,15 +75,35 @@
|
|||
ansible.builtin.assert:
|
||||
that: "{{ item.test == item.result }}"
|
||||
loop:
|
||||
- test: "{{ complex.a.b.c.d | ansible.utils.index_of('eq', 'ansible', 'e1') }}"
|
||||
result: "0"
|
||||
- test: "{{ lookup('ansible.utils.index_of', complex.a.b.c.d, 'eq', 'ansible', 'e1') }}"
|
||||
result: "0"
|
||||
- test: "{{ complex.a.b.c.d | ansible.utils.index_of('eq', 'ansible', 'e1', wantlist=true) }}"
|
||||
result: [0]
|
||||
- test: "{{ lookup('ansible.utils.index_of', complex.a.b.c.d, 'eq', 'ansible', 'e1', wantlist=true) }}"
|
||||
result: [0]
|
||||
# - name: Test a missing key in the list of dictionaries
|
||||
# ansible.builtin.assert:
|
||||
# that: "{{ item.test == item.result }}"
|
||||
# loop:
|
||||
# - test: "{{ complex.a.b.c.d | ansible.utils.index_of('eq', true, 'e2') }}"
|
||||
# result: 0
|
||||
# - test: "{{ lookup('ansible.utils.index_of', complex.a.b.c.d, 'eq', true, 'e2') }}"
|
||||
# result: 0
|
||||
|
||||
- name: Test a missing key in the list of dictionaries
|
||||
ansible.builtin.assert:
|
||||
that: "{{ item.test == item.result }}"
|
||||
loop:
|
||||
- test: "{{ complex.a.b.c.d | ansible.utils.index_of('eq', true, 'e2') }}"
|
||||
result: "0"
|
||||
- test: "{{ lookup('ansible.utils.index_of', complex.a.b.c.d, 'eq', true, 'e2') }}"
|
||||
result: "0"
|
||||
|
||||
- name: Test a missing key in the list of dictionaries, fail on missing
|
||||
ansible.builtin.assert:
|
||||
that: "{{ item.test == item.result }}"
|
||||
loop:
|
||||
- test: "{{ complex.a.b.c.d | ansible.utils.index_of('eq', true, 'e2', fail_on_missing=true) }}"
|
||||
result: "0"
|
||||
- test: "{{ lookup('ansible.utils.index_of', complex.a.b.c.d, 'eq', true, 'e2', fail_on_missing=true) }}"
|
||||
result: "0"
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- name: Ensure the previous test failed
|
||||
ansible.builtin.assert:
|
||||
that: "{{ result.failed and 'not found in' in result.msg }}"
|
||||
|
|
|
|||
|
|
@ -6,3 +6,11 @@
|
|||
- name: Assert result for next_nth_usable.
|
||||
ansible.builtin.assert:
|
||||
that: "{{ result1 == '192.168.122.3' }}"
|
||||
|
||||
- name: Next_nth_usable filter
|
||||
ansible.builtin.set_fact:
|
||||
result1: "{{ '192.168.122.254/24' | ansible.utils.next_nth_usable(2) }}"
|
||||
|
||||
- name: Assert result for ipv4.
|
||||
ansible.builtin.assert:
|
||||
that: "{{ result1 == '' }}"
|
||||
|
|
|
|||
|
|
@ -6,3 +6,11 @@
|
|||
- name: Assert result for previous_nth_usable.
|
||||
ansible.builtin.assert:
|
||||
that: "{{ result1 == '192.168.122.8' }}"
|
||||
|
||||
- name: Previous_nth_usable filter
|
||||
ansible.builtin.set_fact:
|
||||
result1: "{{ '192.168.122.1/24' | ansible.utils.previous_nth_usable(2) }}"
|
||||
|
||||
- name: Assert result for ipv4.
|
||||
ansible.builtin.assert:
|
||||
that: "{{ result1 == '' }}"
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
---
|
||||
- name: Include expected output data
|
||||
ansible.builtin.include_vars:
|
||||
file: complex.yaml
|
||||
|
||||
- name: Test keep_keys
|
||||
tags: keep_keys
|
||||
block:
|
||||
- name: Setup data as facts for complex keep_keys integration test l1
|
||||
ansible.builtin.set_fact:
|
||||
l1:
|
||||
- { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
|
||||
- name: Test keep_keys debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.keep_keys(target=['p1', 'p2']) }}"
|
||||
register: result
|
||||
|
||||
- name: Test keep_keys assert
|
||||
ansible.builtin.assert:
|
||||
that: result['msg'] == keep['l1']
|
||||
|
||||
- name: Debug l1 for starts_with 'p'
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.keep_keys(target=['p1', 'p2'], matching_parameter='starts_with') }}"
|
||||
register: result_l1_starts_with_p
|
||||
|
||||
- name: Assert result dicts l1 for starts_with 'p'
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_starts_with_p['msg'] == keep['l1_starts_with_p']
|
||||
|
||||
- name: Debug l1 for ends_with
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.keep_keys(target=['p1', 'p2'], matching_parameter='ends_with') }}"
|
||||
register: result_l1_ends_with
|
||||
|
||||
- name: Assert result dicts l1 for ends_with
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_ends_with['msg'] == keep['l1_ends_with']
|
||||
|
||||
- name: Debug l1 for ends_with '2'
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.keep_keys(target=['2'], matching_parameter='ends_with') }}"
|
||||
register: result_l1_ends_with_2
|
||||
|
||||
- name: Assert result dicts l1 for ends_with '2'
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_ends_with_2['msg'] == keep['l1_ends_with_2']
|
||||
|
||||
- name: Test keep_keys regex
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.keep_keys(target=['p1', 'p2'], matching_parameter='regex') }}"
|
||||
register: result_l1_regex
|
||||
|
||||
- name: Assert result dicts l1 for regex
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_regex['msg'] == keep['l1_regex']
|
||||
|
||||
- name: Test map keep_keys
|
||||
tags: map_keep_keys
|
||||
block:
|
||||
- name: Setup data as facts for complex keep integration test l2
|
||||
ansible.builtin.set_fact:
|
||||
l2:
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
|
||||
- name: Test map keep_keys debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l2 | map('ansible.utils.keep_keys', target=['p1', 'p2']) | list }}"
|
||||
register: result
|
||||
|
||||
- name: Test map keep_keys assert
|
||||
ansible.builtin.assert:
|
||||
that: result['msg'] == keep['l2']
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
- name: Assert result dicts
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- keep_values['starts_with'] == result['msg']
|
||||
- keep['starts_with'] == result['msg']
|
||||
|
||||
- name: Setup data as facts for equivalent
|
||||
ansible.builtin.set_fact:
|
||||
|
|
@ -70,54 +70,3 @@
|
|||
ansible.builtin.assert:
|
||||
that:
|
||||
- keep_default['default'] == result['msg']
|
||||
|
||||
- name: Setup data for multiple keys in dict
|
||||
ansible.builtin.set_fact:
|
||||
tomcat_data:
|
||||
tomcat:
|
||||
tomcat1:
|
||||
name: tomcat1
|
||||
tomcat2:
|
||||
name: tomcat2
|
||||
tomcat3:
|
||||
name: tomcat3
|
||||
tomcat3:
|
||||
name: tomcat3
|
||||
tomcat4:
|
||||
name: tomcat3
|
||||
tomcat3:
|
||||
tomcat3:
|
||||
name: tomcattest
|
||||
checktomcat3: stringinput
|
||||
tomcats_block:
|
||||
- tomcat1
|
||||
- tomcat2
|
||||
- name: Debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ tomcat_data['tomcat'] | ansible.utils.keep_keys(target=['tomcats_block']) }}"
|
||||
register: result
|
||||
|
||||
- name: Assert result dicts
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- keep_tomcat['tomcat'] == result['msg']
|
||||
|
||||
- name: Test to keep keys when initial node is matched
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ tomcat_data['tomcat'] | ansible.utils.keep_keys(target=['tomcat1', 'tomcat2']) }}"
|
||||
register: result
|
||||
|
||||
- name: Assert result dicts
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- keep_tomcat['greedy_values'] == result['msg']
|
||||
|
||||
- name: Test to keep keys to check if nested dict is matched
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ tomcat_data['tomcat'] | ansible.utils.keep_keys(target=['tomcat3']) }}"
|
||||
register: result
|
||||
|
||||
- name: Assert result dicts
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- keep_tomcat['nested_values'] == result['msg']
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
---
|
||||
keep:
|
||||
l1:
|
||||
- { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
l2:
|
||||
- - { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
- - { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
- - { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
l3:
|
||||
- { p1_key_o1: a, p2_key_o2: a, p3_key_o3: a }
|
||||
- { p1_key_o4: b, p2_key_o5: b, p3_key_o6: b }
|
||||
- { p1_key_o7: c, p2_key_o8: c, p3_key_o9: c }
|
||||
l1_starts_with_p:
|
||||
- { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
l1_ends_with:
|
||||
- { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
l1_ends_with_2:
|
||||
- { p2: a }
|
||||
- { p2: b }
|
||||
- { p2: c }
|
||||
l1_regex:
|
||||
- { p1: a, p2: a }
|
||||
- { p1: b, p2: b }
|
||||
- { p1: c, p2: c }
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
keep_values:
|
||||
keep:
|
||||
starts_with:
|
||||
- interface_name: eth0
|
||||
- interface_name: eth1
|
||||
|
|
@ -22,27 +22,3 @@ keep_default:
|
|||
is_enabled: true
|
||||
- interface_name: eth2
|
||||
is_enabled: false
|
||||
|
||||
keep_tomcat:
|
||||
tomcat:
|
||||
tomcats_block:
|
||||
- tomcat1
|
||||
- tomcat2
|
||||
greedy_values:
|
||||
tomcat1:
|
||||
name: tomcat1
|
||||
tomcat2:
|
||||
name: tomcat2
|
||||
tomcat3:
|
||||
name: tomcat3
|
||||
nested_values:
|
||||
tomcat2:
|
||||
tomcat3:
|
||||
name: tomcat3
|
||||
tomcat3:
|
||||
name: tomcat3
|
||||
tomcat4:
|
||||
tomcat3:
|
||||
tomcat3:
|
||||
name: tomcattest
|
||||
checktomcat3: stringinput
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
---
|
||||
- name: Include expected output data
|
||||
ansible.builtin.include_vars:
|
||||
file: complex.yaml
|
||||
|
||||
- name: Test for remove_keys
|
||||
vars:
|
||||
target:
|
||||
- { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
tags: remove_keys
|
||||
block:
|
||||
- name: Test remove_keys debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.remove_keys(target=['p1', 'p2']) }}"
|
||||
register: result_l1
|
||||
- name: Test remove_keys assert
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1["msg"] == target
|
||||
|
||||
- name: Test map remove_keys
|
||||
vars:
|
||||
target:
|
||||
- - { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
- - { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
- - { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
tags: remove_keys
|
||||
block:
|
||||
- name: Test remove_keys map debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l2 | map('ansible.utils.remove_keys', target=['p1', 'p2']) | list }}"
|
||||
register: result_l2
|
||||
- name: Convert to yaml
|
||||
ansible.builtin.debug:
|
||||
var: result_l2|to_yaml
|
||||
when: debug_test|d(false)|bool
|
||||
- name: Test remove_keys map assert
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l2["msg"] == target
|
||||
|
||||
- name: Test remove_keys starts_with
|
||||
vars:
|
||||
target:
|
||||
- { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
tags: remove_keys_starts_with_2
|
||||
block:
|
||||
- name: Test remove_keys starts_with debug msg
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.remove_keys(target=['p1', 'p2'], matching_parameter='starts_with') }}"
|
||||
register: result_3
|
||||
- name: Test remove_keys starts_with convert to yaml
|
||||
ansible.builtin.debug:
|
||||
var: result_3|to_yaml
|
||||
when: debug_test|d(false)|bool
|
||||
- name: Test remove_keys assert
|
||||
ansible.builtin.assert:
|
||||
that: result_3["msg"] == target
|
||||
|
||||
- name: Test remove_keys starts_with 'p'
|
||||
tags: remove_keys_starts_with_1
|
||||
block:
|
||||
- name: Test remove_keys starts_with 'p' debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.remove_keys(target=['p'], matching_parameter='starts_with') }}"
|
||||
register: result_4
|
||||
- name: Test remove_keys starts_with 'p' convert to yaml
|
||||
ansible.builtin.debug:
|
||||
var: result_4|to_yaml
|
||||
when: debug_test|d(false)|bool
|
||||
- name: Test remove_keys assert
|
||||
ansible.builtin.assert:
|
||||
that: result_4["msg"] == []
|
||||
|
||||
- name: Test remove_keys ends_with
|
||||
vars:
|
||||
target:
|
||||
- { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
tags: remove_keys_ends_with_2
|
||||
block:
|
||||
- name: Test remove_keys ends_with
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.remove_keys(target=['p1', 'p2'], matching_parameter='ends_with') }}"
|
||||
register: result_5
|
||||
- name: Test remove_keys ends_with conver to yaml
|
||||
ansible.builtin.debug:
|
||||
var: result_5|to_yaml
|
||||
when: debug_test|d(false)|bool
|
||||
- name: Test remove_keys ends_with assert
|
||||
ansible.builtin.assert:
|
||||
that: result_5["msg"] == target
|
||||
|
||||
- name: Test remove_keys ends_with '2'
|
||||
vars:
|
||||
target:
|
||||
- { p1: a, p3: a }
|
||||
- { p1: b, p3: b }
|
||||
- { p1: c, p3: c }
|
||||
tags: remove_keys_ends_with_1
|
||||
block:
|
||||
- name: Test remove_keys ends_with '2' debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.remove_keys(target=['2'], matching_parameter='ends_with') }}"
|
||||
register: result_6
|
||||
- name: Test remove_keys ends_with '2' debug convert to yaml
|
||||
ansible.builtin.debug:
|
||||
var: result_6|to_yaml
|
||||
when: debug_test|d(false)|bool
|
||||
- name: Test remove_keys ends_with '2' assert
|
||||
ansible.builtin.assert:
|
||||
that: result_6["msg"] == target
|
||||
|
||||
- name: Test remove_keys regex
|
||||
vars:
|
||||
target:
|
||||
- { p3: a }
|
||||
- { p3: b }
|
||||
- { p3: c }
|
||||
tags: remove_keys_regex
|
||||
block:
|
||||
- name: Test remove_keys regex debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.remove_keys(target=['p1', 'p2'], matching_parameter='regex') }}"
|
||||
register: result_7
|
||||
- name: Test remove_keys regex debug conver to yaml
|
||||
ansible.builtin.debug:
|
||||
var: result_7|to_yaml
|
||||
when: debug_test|d(false)|bool
|
||||
- name: Test remove_keys regex assert
|
||||
ansible.builtin.assert:
|
||||
that: result_7["msg"] == target
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
l1:
|
||||
- { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
l2:
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
l3:
|
||||
- { p1_key_o1: a, p2_key_o2: a, p3_key_o3: a }
|
||||
- { p1_key_o4: b, p2_key_o5: b, p3_key_o6: b }
|
||||
- { p1_key_o7: c, p2_key_o8: c, p3_key_o9: c }
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
---
|
||||
- name: Include expected output data
|
||||
ansible.builtin.include_vars:
|
||||
file: complex.yaml
|
||||
|
||||
- name: Block for l1 tests
|
||||
block:
|
||||
- name: Setup data as facts for complex replace integration test l1
|
||||
ansible.builtin.set_fact:
|
||||
l1:
|
||||
- { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
|
||||
- name: Debug l1
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.replace_keys(target=[{'before': 'p1', 'after': 'x1'}, {'before': 'p2', 'after': 'x2'}]) }}"
|
||||
register: result_l1
|
||||
|
||||
- name: Assert result dicts l1
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1['msg'] == replace['l1']
|
||||
|
||||
- name: Debug l1 for starts_with 'p'
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.replace_keys(target=[{'before': 'p', 'after': 'x'}], matching_parameter='starts_with') }}"
|
||||
register: result_l1_starts_with_p
|
||||
|
||||
- name: Assert result dicts l1 for starts_with 'p'
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_starts_with_p['msg'] == replace['l1_starts_with_p']
|
||||
|
||||
- name: Debug l1 for ends_with
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.replace_keys(target=[{'before': 'p1', 'after': 'x1'}, {'before': 'p2', 'after': 'x2'}], matching_parameter='ends_with') }}"
|
||||
register: result_l1_ends_with
|
||||
|
||||
- name: Assert result dicts l1 for ends_with
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_ends_with['msg'] == replace['l1_ends_with']
|
||||
|
||||
- name: Debug l1 for ends_with '2'
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l1 | ansible.utils.replace_keys(target=[{'before': '2', 'after': '9'}], matching_parameter='ends_with') }}"
|
||||
register: result_l1_ends_with_2
|
||||
|
||||
- name: Assert result dicts l1 for ends_with '2'
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l1_ends_with_2['msg'] == replace['l1_ends_with_2']
|
||||
|
||||
- name: Block for l2 tests
|
||||
block:
|
||||
- name: Setup data as facts for complex replace integration test l2
|
||||
ansible.builtin.set_fact:
|
||||
l2:
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
- - { p1: a, p2: a, p3: a }
|
||||
- { p1: b, p2: b, p3: b }
|
||||
- { p1: c, p2: c, p3: c }
|
||||
|
||||
- name: Debug l2
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l2 | map('ansible.utils.replace_keys', target=[{'before': 'p1', 'after': 'x1'}, {'before': 'p2', 'after': 'x2'}]) | list }}"
|
||||
register: result_l2
|
||||
|
||||
- name: Assert result dicts l2
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l2['msg'] == replace['l2']
|
||||
|
||||
- name: Block for l3 tests
|
||||
block:
|
||||
- name: Setup data as facts for replace integration test l3
|
||||
ansible.builtin.set_fact:
|
||||
l3:
|
||||
- { p1_key_o1: a, p2_key_o2: a, p3_key_o3: a }
|
||||
- { p1_key_o4: b, p2_key_o5: b, p3_key_o6: b }
|
||||
- { p1_key_o7: c, p2_key_o8: c, p3_key_o9: c }
|
||||
|
||||
- name: Debug l3
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l3 | ansible.utils.replace_keys(target=[{'before': 'p1_key_o1', 'after': 'p1_key_oX1'}, {'before': 'p2_key_o2', 'after': 'p2_key_oX2'}]) }}"
|
||||
register: result_l3
|
||||
|
||||
- name: Assert result dicts l3
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l3['msg'] == replace['l3']
|
||||
|
||||
- name: Debug l3 for starts_with
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l3 | ansible.utils.replace_keys(target=[{'before': 'p1', 'after': 'x1'}, {'before': 'p2', 'after': 'x2'}], matching_parameter='starts_with') }}"
|
||||
register: result_l3_starts_with
|
||||
|
||||
- name: Assert result dicts l3 for starts_with
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l3_starts_with['msg'] == replace['l3_starts_with']
|
||||
|
||||
- name: Debug l3 for ends_with
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l3 | ansible.utils.replace_keys(target=[{'before': 'o1', 'after': 'x1'}, {'before': 'o2', 'after': 'x2'}], matching_parameter='ends_with') }}"
|
||||
register: result_l3_ends_with
|
||||
|
||||
- name: Assert result dicts l3 for ends_with
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l3_ends_with['msg'] == replace['l3_ends_with']
|
||||
|
||||
- name: Debug l3 for regex
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ l3 | ansible.utils.replace_keys(target=[{'before': '^(.*)_key_o2$', 'after': 'XY_key_o2'}], matching_parameter='regex') }}"
|
||||
register: result_l3_regex
|
||||
|
||||
- name: Assert result dicts l3 for regex
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result_l3_regex['msg'] == replace['l3_regex']
|
||||
|
|
@ -25,10 +25,6 @@
|
|||
mtu: 600
|
||||
enabled: false
|
||||
|
||||
- name: Include expected output data
|
||||
ansible.builtin.include_vars:
|
||||
file: main.yaml
|
||||
|
||||
- name: Debug
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ data | ansible.utils.replace_keys(target=[{'before': 'interface_name', 'after': 'name'}]) }}"
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
replace:
|
||||
l1:
|
||||
- { "p3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p3": "b", "x1": "b", "x2": "b" }
|
||||
- { "p3": "c", "x1": "c", "x2": "c" }
|
||||
l2:
|
||||
- - { "p3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p3": "b", "x1": "b", "x2": "b" }
|
||||
- { "p3": "c", "x1": "c", "x2": "c" }
|
||||
- - { "p3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p3": "b", "x1": "b", "x2": "b" }
|
||||
- { "p3": "c", "x1": "c", "x2": "c" }
|
||||
- - { "p3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p3": "b", "x1": "b", "x2": "b" }
|
||||
- { "p3": "c", "x1": "c", "x2": "c" }
|
||||
l3:
|
||||
- { "p1_key_oX1": "a", "p2_key_oX2": "a", "p3_key_o3": "a" }
|
||||
- { "p1_key_o4": "b", "p2_key_o5": "b", "p3_key_o6": "b" }
|
||||
- { "p1_key_o7": "c", "p2_key_o8": "c", "p3_key_o9": "c" }
|
||||
l3_starts_with:
|
||||
- { "p3_key_o3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p3_key_o6": "b", "x1": "b", "x2": "b" }
|
||||
- { "p3_key_o9": "c", "x1": "c", "x2": "c" }
|
||||
l1_starts_with_p:
|
||||
- { "x": "a" }
|
||||
- { "x": "b" }
|
||||
- { "x": "c" }
|
||||
l1_ends_with:
|
||||
- { "p3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p3": "b", "x1": "b", "x2": "b" }
|
||||
- { "p3": "c", "x1": "c", "x2": "c" }
|
||||
l3_ends_with:
|
||||
- { "p3_key_o3": "a", "x1": "a", "x2": "a" }
|
||||
- { "p1_key_o4": "b", "p2_key_o5": "b", "p3_key_o6": "b" }
|
||||
- { "p1_key_o7": "c", "p2_key_o8": "c", "p3_key_o9": "c" }
|
||||
l1_ends_with_2:
|
||||
- { "9": "a", "p1": "a", "p3": "a" }
|
||||
- { "9": "b", "p1": "b", "p3": "b" }
|
||||
- { "9": "c", "p1": "c", "p3": "c" }
|
||||
l3_regex:
|
||||
- { "XY_key_o2": "a", "p1_key_o1": "a", "p3_key_o3": "a" }
|
||||
- { "p1_key_o4": "b", "p2_key_o5": "b", "p3_key_o6": "b" }
|
||||
- { "p1_key_o7": "c", "p2_key_o8": "c", "p3_key_o9": "c" }
|
||||
|
|
@ -63,11 +63,11 @@
|
|||
|
||||
- name: read data and criteria from file
|
||||
ansible.builtin.set_fact:
|
||||
data: "{{ lookup('ansible.builtin.file', 'data/show_interface.json') | from_json }}"
|
||||
oper_status_up_criteria: "{{ lookup('ansible.builtin.file', 'criteria/oper_status_up.json') | from_json }}"
|
||||
enabled_check_criteria: "{{ lookup('ansible.builtin.file', 'criteria/enabled_check.json') | from_json }}"
|
||||
crc_error_check_criteria: "{{ lookup('ansible.builtin.file', 'criteria/crc_error_check.json') | from_json }}"
|
||||
in_rate_check_criteria: "{{ lookup('ansible.builtin.file', 'criteria/in_rate_check.json') | from_json }}"
|
||||
data: "{{ lookup('ansible.builtin.file', 'data/show_interface.json') }}"
|
||||
oper_status_up_criteria: "{{ lookup('ansible.builtin.file', 'criteria/oper_status_up.json') }}"
|
||||
enabled_check_criteria: "{{ lookup('ansible.builtin.file', 'criteria/enabled_check.json') }}"
|
||||
crc_error_check_criteria: "{{ lookup('ansible.builtin.file', 'criteria/crc_error_check.json') }}"
|
||||
in_rate_check_criteria: "{{ lookup('ansible.builtin.file', 'criteria/in_rate_check.json') }}"
|
||||
|
||||
- name: validate data using jsonschema engine (invalid data read from file)
|
||||
ansible.builtin.set_fact:
|
||||
|
|
|
|||
|
|
@ -91,6 +91,12 @@
|
|||
ansible.builtin.set_fact:
|
||||
data_criteria_checks: "{{ lookup('ansible.utils.validate', data, [oper_status_up_criteria, enabled_check_criteria, crc_error_check_criteria], engine='ansible.utils.jsonschema') }}"
|
||||
|
||||
- ansible.builtin.assert:
|
||||
that:
|
||||
- "data_criteria_checks[0].data_path == 'GigabitEthernet0/0/0/0.oper_status'"
|
||||
- "data_criteria_checks[1].data_path == 'GigabitEthernet0/0/0/1.enabled'"
|
||||
- "data_criteria_checks[2].data_path == 'GigabitEthernet0/0/0/1.counters.in_crc_errors'"
|
||||
|
||||
- name: validate data using jsonschema engine (valid data read from file)
|
||||
ansible.builtin.set_fact:
|
||||
data_criteria_checks: "{{ lookup('ansible.utils.validate', data, in_rate_check_criteria, engine='ansible.utils.jsonschema') }}"
|
||||
|
|
|
|||
|
|
@ -110,14 +110,13 @@
|
|||
- ansible.builtin.assert:
|
||||
that:
|
||||
- "'errors' in result"
|
||||
- "'GigabitEthernet0/0/0/0.oper_status' in result['errors'][0]"
|
||||
- "'GigabitEthernet0/0/0/1.enabled' in result['errors'][1]"
|
||||
- "'GigabitEthernet0/0/0/1.counters.in_crc_errors' in result['errors'][2]"
|
||||
- "result['errors'][0].data_path == 'GigabitEthernet0/0/0/0.oper_status'"
|
||||
- "result['errors'][1].data_path == 'GigabitEthernet0/0/0/1.enabled'"
|
||||
- "result['errors'][2].data_path == 'GigabitEthernet0/0/0/1.counters.in_crc_errors'"
|
||||
- "'Validation errors were found' in result.msg"
|
||||
- "'patternProperties.^.*.properties.oper_status.pattern' in result.msg"
|
||||
- "'patternProperties.^.*.properties.enabled.enum' in result.msg"
|
||||
- "'patternProperties.^.*.properties.counters.properties.in_crc_errors.maximum' in result.msg"
|
||||
ignore_errors: true
|
||||
|
||||
- name: validate data using jsonschema engine (valid data read from file)
|
||||
ansible.utils.validate:
|
||||
|
|
|
|||
|
|
@ -89,14 +89,13 @@
|
|||
|
||||
- name: read data and criteria from file
|
||||
ansible.builtin.set_fact:
|
||||
data: "{{ lookup('ansible.builtin.file', 'data/test_format_checker.json') | from_json }}"
|
||||
criteria_1: "{{ lookup('ansible.builtin.file', 'criteria/format_checker.json') | from_json }}"
|
||||
data: "{{ lookup('ansible.builtin.file', 'data/test_format_checker.json') }}"
|
||||
criteria_1: "{{ lookup('ansible.builtin.file', 'criteria/format_checker.json') }}"
|
||||
|
||||
- name: validate data using jsonschema engine
|
||||
- name: validate data using jsonschema engine (valid data read from file)
|
||||
ansible.builtin.set_fact:
|
||||
is_data_valid: "{{ data is ansible.utils.validate(engine='ansible.utils.jsonschema', criteria=[criteria_1], draft='draft7') }}"
|
||||
|
||||
- name: assert the data is invalid
|
||||
ansible.builtin.assert:
|
||||
- ansible.builtin.assert:
|
||||
that:
|
||||
- "is_data_valid == true"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ __metaclass__ = type
|
|||
import os
|
||||
|
||||
from ansible.errors import AnsibleParserError
|
||||
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ from contextlib import contextmanager
|
|||
from io import BytesIO, StringIO
|
||||
from unittest import TestCase
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.six import PY3
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
@ -40,8 +41,11 @@ def swap_stdin_and_argv(stdin_data="", argv_data=tuple()):
|
|||
real_stdin = sys.stdin
|
||||
real_argv = sys.argv
|
||||
|
||||
fake_stream = StringIO(stdin_data)
|
||||
fake_stream.buffer = BytesIO(to_bytes(stdin_data))
|
||||
if PY3:
|
||||
fake_stream = StringIO(stdin_data)
|
||||
fake_stream.buffer = BytesIO(to_bytes(stdin_data))
|
||||
else:
|
||||
fake_stream = BytesIO(to_bytes(stdin_data))
|
||||
|
||||
try:
|
||||
sys.stdin = fake_stream
|
||||
|
|
@ -60,7 +64,10 @@ def swap_stdout():
|
|||
"""
|
||||
old_stdout = sys.stdout
|
||||
|
||||
fake_stream = StringIO()
|
||||
if PY3:
|
||||
fake_stream = StringIO()
|
||||
else:
|
||||
fake_stream = BytesIO()
|
||||
|
||||
try:
|
||||
sys.stdout = fake_stream
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.parsing.vault import VaultSecret
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import io
|
|||
|
||||
import yaml
|
||||
|
||||
from ansible.module_utils.six import PY3
|
||||
from ansible.parsing.yaml.dumper import AnsibleDumper
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
|
||||
|
|
@ -21,11 +22,17 @@ class YamlTestUtils(object):
|
|||
|
||||
def _dump_stream(self, obj, stream, dumper=None):
|
||||
"""Dump to a py2-unicode or py3-string stream."""
|
||||
return yaml.dump(obj, stream, Dumper=dumper)
|
||||
if PY3:
|
||||
return yaml.dump(obj, stream, Dumper=dumper)
|
||||
else:
|
||||
return yaml.dump(obj, stream, Dumper=dumper, encoding=None)
|
||||
|
||||
def _dump_string(self, obj, dumper=None):
|
||||
"""Dump to a py2-unicode or py3-string"""
|
||||
return yaml.dump(obj, Dumper=dumper)
|
||||
if PY3:
|
||||
return yaml.dump(obj, Dumper=dumper)
|
||||
else:
|
||||
return yaml.dump(obj, Dumper=dumper, encoding=None)
|
||||
|
||||
def _dump_load_cycle(self, obj):
|
||||
# Each pass though a dump or load revs the 'generation'
|
||||
|
|
@ -82,8 +89,22 @@ class YamlTestUtils(object):
|
|||
stream_obj_from_stream = io.StringIO()
|
||||
stream_obj_from_string = io.StringIO()
|
||||
|
||||
yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper)
|
||||
yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper)
|
||||
if PY3:
|
||||
yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper)
|
||||
yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper)
|
||||
else:
|
||||
yaml.dump(
|
||||
obj_from_stream,
|
||||
stream_obj_from_stream,
|
||||
Dumper=AnsibleDumper,
|
||||
encoding=None,
|
||||
)
|
||||
yaml.dump(
|
||||
obj_from_stream,
|
||||
stream_obj_from_string,
|
||||
Dumper=AnsibleDumper,
|
||||
encoding=None,
|
||||
)
|
||||
|
||||
yaml_string_stream_obj_from_stream = stream_obj_from_stream.getvalue()
|
||||
yaml_string_stream_obj_from_string = stream_obj_from_string.getvalue()
|
||||
|
|
@ -91,8 +112,20 @@ class YamlTestUtils(object):
|
|||
stream_obj_from_stream.seek(0)
|
||||
stream_obj_from_string.seek(0)
|
||||
|
||||
yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper)
|
||||
yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper)
|
||||
if PY3:
|
||||
yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper)
|
||||
yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper)
|
||||
else:
|
||||
yaml_string_obj_from_stream = yaml.dump(
|
||||
obj_from_stream,
|
||||
Dumper=AnsibleDumper,
|
||||
encoding=None,
|
||||
)
|
||||
yaml_string_obj_from_string = yaml.dump(
|
||||
obj_from_string,
|
||||
Dumper=AnsibleDumper,
|
||||
encoding=None,
|
||||
)
|
||||
|
||||
assert yaml_string == yaml_string_obj_from_stream
|
||||
assert yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
# 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
|
||||
|
||||
|
||||
|
|
@ -10,14 +11,14 @@ __metaclass__ = type
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
from jinja2 import Environment
|
||||
from ansible.template import Templar
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.get_path import get_path
|
||||
|
||||
|
||||
class TestGetPath(TestCase):
|
||||
def setUp(self):
|
||||
self._environment = Environment()
|
||||
self._environment = Templar(loader=None).environment
|
||||
|
||||
def test_get_path_pass(self):
|
||||
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||
|
|
@ -37,7 +38,6 @@ class TestGetPath(TestCase):
|
|||
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||
path = "a.b.e"
|
||||
expected = "dict object' has no attribute 'e'"
|
||||
try:
|
||||
with self.assertRaises(Exception) as exc:
|
||||
get_path(var, path, environment=self._environment, wantlist=False)
|
||||
except Exception as exc:
|
||||
self.assertIn(expected, str(exc))
|
||||
self.assertIn(expected, str(exc.exception))
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import os
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
from jinja2 import Environment
|
||||
from ansible.template import Templar
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.get_path import get_path
|
||||
from ansible_collections.ansible.utils.plugins.module_utils.common.to_paths import to_paths
|
||||
|
|
@ -23,7 +23,7 @@ from ansible_collections.ansible.utils.plugins.module_utils.common.to_paths impo
|
|||
|
||||
class TestToPaths(TestCase):
|
||||
def setUp(self):
|
||||
self._environment = Environment()
|
||||
self._environment = Templar(loader=None).environment
|
||||
|
||||
def test_to_paths(self):
|
||||
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||
|
|
|
|||
|
|
@ -137,23 +137,24 @@ class TestUpdate_Fact(TestCase):
|
|||
"""Check for a missing fact"""
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b.c", "value": 5}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars={})
|
||||
self._plugin.run(task_vars={"vars": {}})
|
||||
self.assertIn("'a' was not found in the current facts.", str(error.exception))
|
||||
|
||||
def test_run_simple(self):
|
||||
"""Confirm a valid argspec passes"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"] = 5
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b", "value": 5}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_multiple(self):
|
||||
"""Confirm multiple paths passes"""
|
||||
task_vars = {"a": {"b1": [1, 2, 3], "b2": {"c": "123", "d": False}}}
|
||||
expected = {"a": {"b1": [1, 2, 3, 4], "b2": {"c": 456, "d": True}}, "changed": True}
|
||||
task_vars = {"vars": {"a": {"b1": [1, 2, 3], "b2": {"c": "123", "d": False}}}}
|
||||
expected = {"a": {"b1": [1, 2, 3, 4], "b2": {"c": 456, "d": True}}}
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {
|
||||
"updates": [
|
||||
{"path": "a.b1.3", "value": 4},
|
||||
|
|
@ -166,60 +167,60 @@ class TestUpdate_Fact(TestCase):
|
|||
|
||||
def test_run_replace_in_list(self):
|
||||
"""Replace in list"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"][1] = 5
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b.1", "value": 5}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_append_to_list(self):
|
||||
"""Append to list"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"].append(4)
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b.3", "value": 4}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_bracket_single_quote(self):
|
||||
"""Bracket notation sigle quote"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"].append(4)
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a['b'][3]", "value": 4}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_bracket_double_quote(self):
|
||||
"""Bracket notation double quote"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"].append(4)
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": 'a["b"][3]', "value": 4}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_int_dict_keys(self):
|
||||
"""Integer dict keys"""
|
||||
task_vars = {"a": {0: [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {0: [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"][0][0] = 0
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a.0.0", "value": 0}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_int_as_string(self):
|
||||
"""Integer dict keys as string"""
|
||||
task_vars = {"a": {"0": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"0": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["0"][0] = 0
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": 'a["0"].0', "value": 0}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
|
@ -228,44 +229,46 @@ class TestUpdate_Fact(TestCase):
|
|||
"""Invalid path format"""
|
||||
self._plugin._task.args = {"updates": [{"path": "a.'b'", "value": 0}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars={})
|
||||
self._plugin.run(task_vars={"vars": {}})
|
||||
self.assertIn("malformed", str(error.exception))
|
||||
|
||||
def test_run_invalid_path_bracket_after_dot(self):
|
||||
"""Invalid path format"""
|
||||
self._plugin._task.args = {"updates": [{"path": "a.['b']", "value": 0}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars={})
|
||||
self._plugin.run(task_vars={"vars": {}})
|
||||
self.assertIn("malformed", str(error.exception))
|
||||
|
||||
def test_run_invalid_key_start_with_dot(self):
|
||||
"""Invalid key format"""
|
||||
self._plugin._task.args = {"updates": [{"path": ".abc", "value": 0}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars={})
|
||||
self._plugin.run(task_vars={"vars": {}})
|
||||
self.assertIn("malformed", str(error.exception))
|
||||
|
||||
def test_run_no_update_list(self):
|
||||
"""Confirm no change when same"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
expected["changed"] = False
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"] = [1, 2, 3]
|
||||
expected.update({"changed": False})
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b.0", "value": 1}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_no_update_dict(self):
|
||||
"""Confirm no change when same"""
|
||||
task_vars = {"a": {"b": [1, 2, 3]}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
expected["changed"] = False
|
||||
task_vars = {"vars": {"a": {"b": [1, 2, 3]}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["b"] = [1, 2, 3]
|
||||
expected.update({"changed": False})
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b", "value": [1, 2, 3]}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_missing_key(self):
|
||||
"""Confirm error when key not found"""
|
||||
task_vars = {"a": {"b": 1}}
|
||||
task_vars = {"vars": {"a": {"b": 1}}}
|
||||
self._plugin._task.args = {"updates": [{"path": "a.c.d", "value": 1}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars=task_vars)
|
||||
|
|
@ -273,7 +276,7 @@ class TestUpdate_Fact(TestCase):
|
|||
|
||||
def test_run_list_not_int(self):
|
||||
"""Confirm error when key not found"""
|
||||
task_vars = {"a": {"b": [1]}}
|
||||
task_vars = {"vars": {"a": {"b": [1]}}}
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b['0']", "value": 2}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars=task_vars)
|
||||
|
|
@ -281,7 +284,7 @@ class TestUpdate_Fact(TestCase):
|
|||
|
||||
def test_run_list_not_long(self):
|
||||
"""List not long enough"""
|
||||
task_vars = {"a": {"b": [0]}}
|
||||
task_vars = {"vars": {"a": {"b": [0]}}}
|
||||
self._plugin._task.args = {"updates": [{"path": "a.b.2", "value": 2}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars=task_vars)
|
||||
|
|
@ -300,23 +303,27 @@ class TestUpdate_Fact(TestCase):
|
|||
|
||||
def test_run_not_dotted_success_one(self):
|
||||
"""Test with a not dotted key"""
|
||||
task_vars = {"a": 0}
|
||||
expected = {"a": 1, "ansible_facts": {"a": 1}, "changed": True}
|
||||
task_vars = {"vars": {"a": 0}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"] = 1
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a", "value": 1}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_not_dotted_success_three(self):
|
||||
"""Test with a not dotted key longer"""
|
||||
task_vars = {"abc": 0}
|
||||
expected = {"abc": 1, "ansible_facts": {"abc": 1}, "changed": True}
|
||||
task_vars = {"vars": {"abc": 0}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["abc"] = 1
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "abc", "value": 1}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_not_dotted_fail_missing(self):
|
||||
"""Test with a not dotted key, missing"""
|
||||
task_vars = {"abc": 0}
|
||||
task_vars = {"vars": {"abc": 0}}
|
||||
self._plugin._task.args = {"updates": [{"path": "123", "value": 1}]}
|
||||
with self.assertRaises(Exception) as error:
|
||||
self._plugin.run(task_vars=task_vars)
|
||||
|
|
@ -324,19 +331,19 @@ class TestUpdate_Fact(TestCase):
|
|||
|
||||
def test_run_not_dotted_success_same(self):
|
||||
"""Test with a not dotted key, no change"""
|
||||
task_vars = {"a": 0}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
expected["changed"] = False
|
||||
task_vars = {"vars": {"a": 0}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected.update({"changed": False})
|
||||
self._plugin._task.args = {"updates": [{"path": "a", "value": 0}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_run_looks_like_a_bool(self):
|
||||
"""Test with a key that looks like a bool"""
|
||||
task_vars = {"a": {"True": 0}}
|
||||
expected = copy.deepcopy(task_vars)
|
||||
task_vars = {"vars": {"a": {"True": 0}}}
|
||||
expected = copy.deepcopy(task_vars["vars"])
|
||||
expected["a"]["True"] = 1
|
||||
expected["changed"] = True
|
||||
expected.update({"changed": True})
|
||||
self._plugin._task.args = {"updates": [{"path": "a['True']", "value": 1}]}
|
||||
result = self._plugin.run(task_vars=task_vars)
|
||||
self.assertEqual(result, expected)
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ VALID_DATA = {"name": "ansible", "email": "ansible@redhat.com"}
|
|||
IN_VALID_DATA = {"name": "ansible", "email": "redhatcom"}
|
||||
|
||||
CRITERIA_FORMAT_SUPPORT_CHECK = {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$schema": "https://json-schema.org/schema#",
|
||||
"type": "object",
|
||||
"properties": {"name": {"type": "string"}, "email": {"format": "email"}},
|
||||
"required": ["email"],
|
||||
|
|
|
|||
|
|
@ -22,12 +22,8 @@ VALID_DATA = (
|
|||
"<schemas><schema/></schemas></netconf-state>"
|
||||
)
|
||||
|
||||
OUTPUT = {
|
||||
"netconf-state": {
|
||||
"@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring",
|
||||
"schemas": {"schema": None},
|
||||
},
|
||||
}
|
||||
OUTPUT = """{"netconf-state": \
|
||||
{"@xmlns": "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "schemas": {"schema": null}}}"""
|
||||
|
||||
|
||||
class TestFromXml(TestCase):
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ from unittest import TestCase
|
|||
import pytest
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.template import AnsibleUndefined
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.filter.cidr_merge import cidr_merge
|
||||
from ansible_collections.ansible.utils.plugins.filter.ip4_hex import ip4_hex
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipaddr import _ipaddr
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipmath import ipmath
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipsubnet import ipsubnet
|
||||
from ansible_collections.ansible.utils.plugins.filter.network_in_network import network_in_network
|
||||
|
|
@ -63,6 +65,15 @@ class TestIpFilter(TestCase):
|
|||
self.assertEqual(cidr_merge(subnets), ["1.12.1.1/32", "1.12.1.255/32"])
|
||||
self.assertEqual(cidr_merge(subnets, "span"), "1.12.1.0/24")
|
||||
|
||||
def test_ipaddr_undefined_value(self):
|
||||
"""Check ipaddr filter undefined value"""
|
||||
args = ["", AnsibleUndefined(name="my_ip"), ""]
|
||||
with pytest.raises(
|
||||
AnsibleFilterError,
|
||||
match="Unrecognized type <<class 'ansible.template.AnsibleUndefined'>> for ipaddr filter <value>",
|
||||
):
|
||||
_ipaddr(*args)
|
||||
|
||||
def test_ipaddr_empty_query(self):
|
||||
self.assertEqual(ipaddr("192.0.2.230"), "192.0.2.230")
|
||||
self.assertEqual(ipaddr("192.0.2.230/30"), "192.0.2.230/30")
|
||||
|
|
|
|||
|
|
@ -21,30 +21,16 @@ class TestIpCut(TestCase):
|
|||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_get_last_X_bits_ipv6(self):
|
||||
def test_get_last_X_bits(self):
|
||||
"""Get last X bits of Ipv6 address"""
|
||||
|
||||
args = ["", "1234:4321:abcd:dcba::17", -80]
|
||||
result = _ipcut(*args)
|
||||
self.assertEqual(result, "dcba:0:0:0:17")
|
||||
|
||||
def test_get_first_X_bits_ipv6(self):
|
||||
def test_get_first_X_bits(self):
|
||||
"""Get first X bits of Ipv6 address"""
|
||||
|
||||
args = ["", "1234:4321:abcd:dcba::17", 64]
|
||||
result = _ipcut(*args)
|
||||
self.assertEqual(result, "1234:4321:abcd:dcba")
|
||||
|
||||
def test_get_last_X_bits_ipv4(self):
|
||||
"""Get last X bits of Ipv4 address"""
|
||||
|
||||
args = ["", "10.2.3.0", -16]
|
||||
result = _ipcut(*args)
|
||||
self.assertEqual(result, "3.0")
|
||||
|
||||
def test_get_first_X_bits_ipv4(self):
|
||||
"""Get first X bits of Ipv4 address"""
|
||||
|
||||
args = ["", "10.2.3.0", 24]
|
||||
result = _ipcut(*args)
|
||||
self.assertEqual(result, "10.2.3")
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ __metaclass__ = type
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
import pytest
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.template import AnsibleUndefined
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipv4 import _ipv4
|
||||
|
||||
|
||||
|
|
@ -40,6 +45,15 @@ class TestIp4(TestCase):
|
|||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_ipv4_undefined_value(self):
|
||||
"""Check ipv4 filter undefined value"""
|
||||
args = ["", AnsibleUndefined(name="my_ip"), ""]
|
||||
with pytest.raises(
|
||||
AnsibleFilterError,
|
||||
match="Unrecognized type <<class 'ansible.template.AnsibleUndefined'>> for ipv4 filter <value>",
|
||||
):
|
||||
_ipv4(*args)
|
||||
|
||||
def test_ipv4_filter_empty_query(self):
|
||||
"""Check ipv4 filter empty query"""
|
||||
args = ["", VALID_DATA, ""]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ __metaclass__ = type
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
import pytest
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.template import AnsibleUndefined
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipv6 import _ipv6
|
||||
|
||||
|
||||
|
|
@ -43,6 +48,15 @@ class TestIp6(TestCase):
|
|||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_ipv6_undefined_value(self):
|
||||
"""Check ipv6 filter undefined value"""
|
||||
args = ["", AnsibleUndefined(name="my_ip"), ""]
|
||||
with pytest.raises(
|
||||
AnsibleFilterError,
|
||||
match="Unrecognized type <<class 'ansible.template.AnsibleUndefined'>> for ipv6 filter <value>",
|
||||
):
|
||||
_ipv6(*args)
|
||||
|
||||
def test_ipv6_filter_empty_query(self):
|
||||
"""Check ipv6 filter empty query"""
|
||||
args = ["", VALID_DATA, ""]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ __metaclass__ = type
|
|||
|
||||
from unittest import TestCase
|
||||
|
||||
import pytest
|
||||
|
||||
from ansible.errors import AnsibleFilterError
|
||||
from ansible.template import AnsibleUndefined
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.filter.ipwrap import _ipwrap
|
||||
|
||||
|
||||
|
|
@ -45,6 +50,15 @@ class TestIpWrap(TestCase):
|
|||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_ipwrap_undefined_value(self):
|
||||
"""Check ipwrap filter undefined value"""
|
||||
args = ["", AnsibleUndefined(name="my_ip"), ""]
|
||||
with pytest.raises(
|
||||
AnsibleFilterError,
|
||||
match="Unrecognized type <<class 'ansible.template.AnsibleUndefined'>> for ipwrap filter <value>",
|
||||
):
|
||||
_ipwrap(*args)
|
||||
|
||||
def test_valid_data_list(self):
|
||||
"""Check passing valid argspec(list)"""
|
||||
args = ["", VALID_DATA, ""]
|
||||
|
|
|
|||
|
|
@ -230,29 +230,3 @@ class TestKeepKeys(TestCase):
|
|||
with self.assertRaises(AnsibleFilterError) as error:
|
||||
_keep_keys(*args)
|
||||
self.assertIn("Error when using plugin 'keep_keys'", str(error.exception))
|
||||
|
||||
def test_keep_filter_excludes_list_values(self):
|
||||
"""Test that keep_keys excludes keys with list values that are not in target."""
|
||||
data = [
|
||||
{
|
||||
"k0": "A",
|
||||
"k1": "B",
|
||||
"k2": [1],
|
||||
"k3": "foo",
|
||||
},
|
||||
{
|
||||
"k0": "A",
|
||||
"k1": "B",
|
||||
"k2": [2],
|
||||
"k3": "bar",
|
||||
},
|
||||
]
|
||||
target = ["k0", "k1"]
|
||||
output = [
|
||||
{"k0": "A", "k1": "B"},
|
||||
{"k0": "A", "k1": "B"},
|
||||
]
|
||||
args = ["", data, target]
|
||||
|
||||
result = _keep_keys(*args)
|
||||
self.assertEqual(result, output)
|
||||
|
|
|
|||
|
|
@ -9,29 +9,17 @@ from __future__ import absolute_import, division, print_function
|
|||
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from jinja2 import Environment
|
||||
from ansible.template import Templar
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.index_of import index_of
|
||||
|
||||
|
||||
def jinja_match(value, pattern):
|
||||
return re.match(pattern, value) is not None
|
||||
|
||||
|
||||
def jinja_search(value, pattern):
|
||||
return re.search(pattern, value) is not None
|
||||
|
||||
|
||||
class TestIndexOfFilter(TestCase):
|
||||
def setUp(self):
|
||||
env = Environment()
|
||||
env.tests["match"] = jinja_match
|
||||
env.tests["search"] = jinja_search
|
||||
self._tests = env.tests
|
||||
self._tests = Templar(loader=None).environment.tests
|
||||
|
||||
def test_fail_no_qualfier(self):
|
||||
obj, test, value = [1, 2], "@@", 1
|
||||
|
|
@ -112,14 +100,14 @@ class TestIndexOfFilter(TestCase):
|
|||
),
|
||||
(
|
||||
[{"a": "abc"}, {"a": "def"}, {"a": "ghi"}, {"a": "jkl"}],
|
||||
"match",
|
||||
"ansible.builtin.match",
|
||||
"^a",
|
||||
"a",
|
||||
0,
|
||||
),
|
||||
(
|
||||
[{"a": "abc"}, {"a": "def"}, {"a": "ghi"}, {"a": "jkl"}],
|
||||
"search",
|
||||
"ansible.builtin.search",
|
||||
"e",
|
||||
"a",
|
||||
1,
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
"""
|
||||
Unit tests for ipaddress_utils helpers.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.ipaddress_utils import (
|
||||
_is_subnet_of,
|
||||
ip_network,
|
||||
)
|
||||
|
||||
|
||||
class TestIsSubnetOf(TestCase):
|
||||
def test_ipv4_subnet_true(self):
|
||||
a = ip_network("10.1.1.0/24")
|
||||
b = ip_network("10.0.0.0/8")
|
||||
self.assertTrue(_is_subnet_of(a, b))
|
||||
|
||||
def test_ipv4_subnet_false_disjoint(self):
|
||||
a = ip_network("192.168.1.0/24")
|
||||
b = ip_network("10.0.0.0/8")
|
||||
self.assertFalse(_is_subnet_of(a, b))
|
||||
|
||||
def test_ipv4_subnet_false_reverse(self):
|
||||
a = ip_network("10.0.0.0/8")
|
||||
b = ip_network("10.1.1.0/24")
|
||||
self.assertFalse(_is_subnet_of(a, b))
|
||||
|
||||
def test_ipv4_same_network(self):
|
||||
n = ip_network("10.0.0.0/8")
|
||||
self.assertTrue(_is_subnet_of(n, n))
|
||||
|
||||
def test_ipv6_subnet_true(self):
|
||||
a = ip_network("2001:db8:0:1::/64")
|
||||
b = ip_network("2001:db8::/48")
|
||||
self.assertTrue(_is_subnet_of(a, b))
|
||||
|
||||
def test_ipv6_subnet_false(self):
|
||||
a = ip_network("2001:db8:2::/64")
|
||||
b = ip_network("2001:db8:1::/48")
|
||||
self.assertFalse(_is_subnet_of(a, b))
|
||||
|
||||
def test_mixed_ip_versions(self):
|
||||
a = ip_network("10.0.0.0/8")
|
||||
b = ip_network("2001:db8::/32")
|
||||
self.assertFalse(_is_subnet_of(a, b))
|
||||
|
||||
def test_invalid_arguments_return_false(self):
|
||||
self.assertFalse(_is_subnet_of(None, ip_network("10.0.0.0/8")))
|
||||
self.assertFalse(_is_subnet_of(ip_network("10.0.0.0/8"), None))
|
||||
|
|
@ -52,40 +52,6 @@ class TestTextfsmParser(TestCase):
|
|||
]
|
||||
self.assertEqual(result["parsed"][0][0], parsed_output)
|
||||
|
||||
def test_ttp_parser_flat_list(self):
|
||||
"""With ttp_results.results: flat_list, output is a single-level list of items."""
|
||||
nxos_cfg_path = os.path.join(os.path.dirname(__file__), "fixtures", "nxos_show_version.cfg")
|
||||
nxos_template_path = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"fixtures",
|
||||
"nxos_show_version.ttp",
|
||||
)
|
||||
|
||||
with open(nxos_cfg_path) as fhand:
|
||||
nxos_show_version_output = fhand.read()
|
||||
|
||||
task_args = {
|
||||
"text": nxos_show_version_output,
|
||||
"parser": {
|
||||
"name": "ansible.utils.ttp",
|
||||
"command": "show version",
|
||||
"template_path": nxos_template_path,
|
||||
"vars": {
|
||||
"ttp_results": {"results": "flat_list"},
|
||||
},
|
||||
},
|
||||
}
|
||||
parser = CliParser(task_args=task_args, task_vars=[], debug=False)
|
||||
|
||||
result = parser.parse()
|
||||
self.assertNotIn("errors", result)
|
||||
parsed = result["parsed"]
|
||||
self.assertIsInstance(parsed, list)
|
||||
self.assertGreaterEqual(len(parsed), 1)
|
||||
self.assertIsInstance(parsed[0], dict)
|
||||
self.assertIn("boot_image", parsed[0])
|
||||
self.assertIn("os", parsed[0])
|
||||
|
||||
def test_textfsm_parser_invalid_parser(self):
|
||||
fake_path = "/ /I hope this doesn't exist"
|
||||
task_args = {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ __metaclass__ = type
|
|||
import pytest
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.common.text.converters import to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
from ansible_collections.ansible.utils.plugins.plugin_utils.base.validate import _load_validator
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,9 @@
|
|||
[tox]
|
||||
ignore_base_python_conflict = true
|
||||
[ansible]
|
||||
skip =
|
||||
py3.7
|
||||
py3.8
|
||||
2.9
|
||||
2.10
|
||||
2.11
|
||||
2.12
|
||||
2.13
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,12 +1,12 @@
|
|||
---
|
||||
- name: Install and configure Certbot
|
||||
hosts: vm01-balancer
|
||||
hosts: router
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
certbot_install_method: package
|
||||
certbot_admin_email: admin@gigacoms.ru
|
||||
certbot_admin_email: mrwho@mrwho.ru
|
||||
certbot_auto_renew: true
|
||||
certbot_auto_renew_user: "{{ ansible_user | default(lookup('env', 'USER')) }}"
|
||||
certbot_auto_renew_hour: "5"
|
||||
|
|
@ -18,16 +18,17 @@
|
|||
certbot_delete_certs: false
|
||||
certbot_certs:
|
||||
- domains:
|
||||
- gigacoms.ru
|
||||
- "*.gigacoms.ru"
|
||||
- local.mrwho.ru
|
||||
- "*.mrwho.ru"
|
||||
- "*.local.mrwho.ru"
|
||||
certbot_testmode: false
|
||||
# Generate key with: tsig-keygen -a HMAC-SHA512 CertbotKey
|
||||
certbot_dns_plugin: rfc2136
|
||||
certbot_dns_target_server: 91.218.87.251
|
||||
certbot_dns_target_server:
|
||||
certbot_dns_target_server_port: 53
|
||||
certbot_dns_tsig_keyname: CertbotKey
|
||||
certbot_dns_key_secret: hbxr+SNi3kzzygeArFp/1xgribKLDLZEfRWWh/btH6jauMrcJ0oPyOEuP11d1Oq10HUJnTKnOtb8YlnxrM4HcA==
|
||||
certbot_dns_key_algorithm: HMAC-SHA512
|
||||
certbot_dns_tsig_keyname: certbot-key
|
||||
certbot_dns_key_secret: YCUScDPbF9bscPFB0mLzRr2/V54FB7tcrifAdrrnlBI=
|
||||
certbot_dns_key_algorithm: HMAC-SHA256
|
||||
|
||||
roles:
|
||||
- certbot-dns
|
||||
144
playbooks/mrwho/router-nginx.yml
Normal file
144
playbooks/mrwho/router-nginx.yml
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
---
|
||||
- name: Install balancer nginx
|
||||
hosts: router
|
||||
roles:
|
||||
- nginx
|
||||
# - firewall
|
||||
# - chrony
|
||||
|
||||
vars:
|
||||
###### NGINX Configuration ######
|
||||
nginx_worker_connections: 16384
|
||||
nginx_use_epoll: true
|
||||
nginx_multi_accept: "on"
|
||||
nginx_selinux: false
|
||||
pki_dir: /etc/nginx/certs
|
||||
nginx_webroot: /var/www
|
||||
local_http_port: 8080
|
||||
nginx_letsencrypt_managed: true
|
||||
letsencrypt_acme_install: true
|
||||
letsencrypt_acme_certs_dir: /etc/letsencrypt/live
|
||||
nginx_conf_snippets:
|
||||
- nginx-compression.conf
|
||||
- nginx-proxy-params.conf
|
||||
- nginx-server-ssl.conf
|
||||
# - nginx-websockets.conf
|
||||
# - nginx-browser-cache.conf
|
||||
# - letsencrypt-proxy.conf
|
||||
# - nginx-cors.conf
|
||||
nginx_streams:
|
||||
- stream: NTP
|
||||
listen_port: 123
|
||||
protocol: udp
|
||||
responses: 1
|
||||
timeout: 3
|
||||
backends:
|
||||
- 10.203.2.11:123
|
||||
- 10.203.2.12:123
|
||||
|
||||
nginx_virthosts:
|
||||
- virthost_name: git.local.mrwho.ru
|
||||
listen: "80"
|
||||
server_name: git.local.mrwho.ru
|
||||
server_aliases: git.mrwho.ru
|
||||
# index: index.php
|
||||
# error_page: /path_to_error_page.html
|
||||
ssl_enabled: true
|
||||
ssl_only: true
|
||||
ssl_letsencrypt_certs: "{{ nginx_letsencrypt_managed }}"
|
||||
# root: "{{ nginx_webroot }}"
|
||||
server_tokens: "off"
|
||||
proxy_standard_setup: true
|
||||
proxy_additional_options:
|
||||
- proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache:30m max_size=250m;
|
||||
locations:
|
||||
- location: /
|
||||
target: "http://172.20.21.105:3000"
|
||||
|
||||
###### Firewalld configuration ######
|
||||
firewall:
|
||||
# - previous: replaced # flush previous rules
|
||||
|
||||
- zone: internal
|
||||
state: enabled
|
||||
permanent: true
|
||||
service:
|
||||
- ssh
|
||||
- ntp
|
||||
- dns
|
||||
- http
|
||||
- https
|
||||
source:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
|
||||
- zone: internal
|
||||
state: disabled
|
||||
permanent: true
|
||||
service:
|
||||
- cockpit
|
||||
- dhcpv6-client
|
||||
- mdns
|
||||
- samba-client
|
||||
|
||||
- zone: public
|
||||
state: enabled
|
||||
permanent: true
|
||||
service:
|
||||
- ntp
|
||||
- dns
|
||||
- http
|
||||
- https
|
||||
|
||||
- zone: external
|
||||
state: disabled
|
||||
permanent: true
|
||||
service:
|
||||
- ssh
|
||||
- cockpit
|
||||
- dhcpv6-client
|
||||
- mdns
|
||||
- samba-client
|
||||
|
||||
- set_default_zone: internal
|
||||
state: enabled
|
||||
permanent: true
|
||||
|
||||
- interface: eth1
|
||||
zone: internal
|
||||
state: enabled
|
||||
permanent: true
|
||||
|
||||
- interface: eth2
|
||||
zone: internal
|
||||
state: enabled
|
||||
permanent: true
|
||||
|
||||
- interface: eth0
|
||||
zone: external
|
||||
state: enabled
|
||||
permanent: true
|
||||
|
||||
- interface: tun0
|
||||
zone: external
|
||||
state: enabled
|
||||
permanent: true
|
||||
|
||||
- interface: tun1
|
||||
zone: external
|
||||
state: enabled
|
||||
permanent: true
|
||||
|
||||
|
||||
###### chrony-client ######
|
||||
chrony_disable_ntpd: true
|
||||
chrony_enable: true
|
||||
chrony_log_enable: true
|
||||
chrony_log: measurements statistics tracking
|
||||
chrony_logdir: /var/log/chrony/
|
||||
chrony_ntp_servers:
|
||||
- 10.203.2.11
|
||||
- 10.203.2.12
|
||||
chrony_rtcsync: true
|
||||
chrony_timezone: Europe/Moscow
|
||||
|
|
@ -148,12 +148,18 @@
|
|||
ansible.builtin.file:
|
||||
path: /etc/systemd/system/nginx.service.d
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0755
|
||||
when: nginx_streams | default([]) | selectattr('transparent', 'defined') | selectattr('transparent') | list | length > 0
|
||||
|
||||
- name: Systemd override для nginx — CAP_NET_RAW (transparent proxy)
|
||||
ansible.builtin.template:
|
||||
src: nginx-systemd-override.j2
|
||||
dest: /etc/systemd/system/nginx.service.d/transparent.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
when: nginx_streams | default([]) | selectattr('transparent', 'defined') | selectattr('transparent') | list | length > 0
|
||||
notify:
|
||||
- Daemon reload
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@
|
|||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
# mode: "0755"
|
||||
mode: "0644"
|
||||
|
||||
- name: Install the nginx stream files
|
||||
ansible.builtin.template:
|
||||
src: nginx-stream.j2
|
||||
src: nginx-stream.conf.j2
|
||||
dest: /etc/nginx/stream.d/{{ item.stream }}.conf
|
||||
owner: root
|
||||
group: root
|
||||
# mode: "0444"
|
||||
mode: "0644"
|
||||
loop: "{{ nginx_streams }}"
|
||||
notify: Reload nginx
|
||||
|
|
|
|||
Loading…
Reference in a new issue