Merge pull request #1 from cidrblock/to_dotbracket
Add get_path and to_paths lookup/filter pluginspull/2/head 0.0.01
commit
c846a94883
|
@ -24,9 +24,8 @@ jobs:
|
||||||
ansible:
|
ansible:
|
||||||
# It's important that Sanity is tested against all stable-X.Y branches
|
# It's important that Sanity is tested against all stable-X.Y branches
|
||||||
# Testing against `devel` may fail as new tests are added.
|
# Testing against `devel` may fail as new tests are added.
|
||||||
# - stable-2.9 # Only if your collection supports Ansible 2.9
|
- stable-2.9
|
||||||
- stable-2.10
|
- stable-2.10
|
||||||
- devel
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ jobs:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: ansible_collections/NAMESPACE/COLLECTION_NAME
|
path: ansible_collections/ansible/utils
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
@ -54,7 +53,7 @@ jobs:
|
||||||
# and all python versions ansible supports.
|
# and all python versions ansible supports.
|
||||||
- name: Run sanity tests
|
- name: Run sanity tests
|
||||||
run: ansible-test sanity --docker -v --color
|
run: ansible-test sanity --docker -v --color
|
||||||
working-directory: ./ansible_collections/NAMESPACE/COLLECTION_NAME
|
working-directory: ./ansible_collections/ansible/utils
|
||||||
|
|
||||||
###
|
###
|
||||||
# Unit tests (OPTIONAL)
|
# Unit tests (OPTIONAL)
|
||||||
|
@ -69,11 +68,9 @@ jobs:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
ansible:
|
ansible:
|
||||||
# - stable-2.9 # Only if your collection supports Ansible 2.9
|
- stable-2.9
|
||||||
- stable-2.10
|
- stable-2.10
|
||||||
- devel
|
|
||||||
python:
|
python:
|
||||||
- 2.6
|
|
||||||
- 2.7
|
- 2.7
|
||||||
- 3.5
|
- 3.5
|
||||||
- 3.6
|
- 3.6
|
||||||
|
@ -88,7 +85,7 @@ jobs:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: ansible_collections/NAMESPACE/COLLECTION_NAME
|
path: ansible_collections/ansible/utils
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.ansible }}
|
- name: Set up Python ${{ matrix.ansible }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
@ -100,30 +97,27 @@ jobs:
|
||||||
|
|
||||||
# OPTIONAL If your unit test requires Python libraries from other collections
|
# OPTIONAL If your unit test requires Python libraries from other collections
|
||||||
# Install them like this
|
# Install them like this
|
||||||
- name: Install collection dependencies
|
# - name: Install collection dependencies
|
||||||
run: ansible-galaxy collection install ansible.netcommon -p .
|
# run: ansible-galaxy collection install ansible.netcommon -p .
|
||||||
|
|
||||||
# Run the unit tests
|
# Run the unit tests
|
||||||
- name: Run unit test
|
- name: Run unit test
|
||||||
run: ansible-test units -v --color --python ${{ matrix.python }} --docker --coverage
|
run: ansible-test units -v --color --python ${{ matrix.python }} --docker --coverage
|
||||||
working-directory: ./ansible_collections/NAMESPACE/COLLECTION_NAME
|
working-directory: ./ansible_collections/ansible/utils
|
||||||
|
|
||||||
# ansible-test support producing code coverage date
|
# ansible-test support producing code coverage date
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
run: ansible-test coverage xml -v --requirements --group-by command --group-by version
|
run: ansible-test coverage xml -v --requirements --group-by command --group-by version
|
||||||
working-directory: ./ansible_collections/NAMESPACE/COLLECTION_NAME
|
working-directory: ./ansible_collections/ansible/utils
|
||||||
|
|
||||||
# See the reports at https://codecov.io/gh/ansible_collections/GITHUBORG/REPONAME
|
# See the reports at https://codecov.io/gh/ansible_collections/GITHUBORG/REPONAME
|
||||||
- uses: codecov/codecov-action@v1
|
- uses: codecov/codecov-action@v1
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
|
|
||||||
###
|
##
|
||||||
# Integration tests (RECOMMENDED)
|
# Integration tests (RECOMMENDED)
|
||||||
#
|
|
||||||
# https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html
|
# https://docs.ansible.com/ansible/latest/dev_guide/testing_integration.html
|
||||||
|
|
||||||
|
|
||||||
# If the application you are testing is available as a docker container and you want to test
|
# If the application you are testing is available as a docker container and you want to test
|
||||||
# multiple versions see the following for an example:
|
# multiple versions see the following for an example:
|
||||||
# https://github.com/ansible-collections/community.zabbix/tree/master/.github/workflows
|
# https://github.com/ansible-collections/community.zabbix/tree/master/.github/workflows
|
||||||
|
@ -135,11 +129,9 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ansible:
|
ansible:
|
||||||
# - stable-2.9 # Only if your collection supports Ansible 2.9
|
- stable-2.9
|
||||||
- stable-2.10
|
- stable-2.10
|
||||||
- devel
|
|
||||||
python:
|
python:
|
||||||
- 2.6
|
|
||||||
- 2.7
|
- 2.7
|
||||||
- 3.5
|
- 3.5
|
||||||
- 3.6
|
- 3.6
|
||||||
|
@ -154,7 +146,7 @@ jobs:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: ansible_collections/NAMESPACE/COLLECTION_NAME
|
path: ansible_collections/ansible/utils
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.ansible }}
|
- name: Set up Python ${{ matrix.ansible }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
@ -166,20 +158,46 @@ jobs:
|
||||||
|
|
||||||
# OPTIONAL If your integration test requires Python libraries or modules from other collections
|
# OPTIONAL If your integration test requires Python libraries or modules from other collections
|
||||||
# Install them like this
|
# Install them like this
|
||||||
- name: Install collection dependencies
|
# - name: Install collection dependencies
|
||||||
run: ansible-galaxy collection install ansible.netcommon -p .
|
# run: ansible-galaxy collection install ansible.netcommon -p .
|
||||||
|
|
||||||
# Run the integration tests
|
# Run the integration tests
|
||||||
- name: Run integration test
|
- name: Run integration test
|
||||||
run: ansible-test integration -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker --coverage
|
run: ansible-test integration -v --color --retry-on-error --continue-on-error --diff --python ${{ matrix.python }} --docker --coverage
|
||||||
working-directory: ./ansible_collections/NAMESPACE/COLLECTION_NAME
|
working-directory: ./ansible_collections/ansible/utils
|
||||||
|
|
||||||
# ansible-test support producing code coverage date
|
# ansible-test support producing code coverage date
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
run: ansible-test coverage xml -v --requirements --group-by command --group-by version
|
run: ansible-test coverage xml -v --requirements --group-by command --group-by version
|
||||||
working-directory: ./ansible_collections/NAMESPACE/COLLECTION_NAME
|
working-directory: ./ansible_collections/ansible/utils
|
||||||
|
|
||||||
# See the reports at https://codecov.io/gh/ansible_collections/GITHUBORG/REPONAME
|
# See the reports at https://codecov.io/gh/ansible_collections/GITHUBORG/REPONAME
|
||||||
- uses: codecov/codecov-action@v1
|
- uses: codecov/codecov-action@v1
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Formatting with black
|
||||||
|
# Require all code be formatted with black
|
||||||
|
# with line length 79
|
||||||
|
|
||||||
|
black:
|
||||||
|
name: Black (formatting)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: ansible_collections/ansible/utils
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
|
||||||
|
- name: Install Black
|
||||||
|
run: pip install black
|
||||||
|
|
||||||
|
- name: Run black --check
|
||||||
|
run: black -l79 --diff --check ansible_collections/ansible/utils
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: Publish
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- created
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
name: Publish to galaxy
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
|
||||||
|
- name: Install ansible-base
|
||||||
|
run: pip install ansible-base
|
||||||
|
|
||||||
|
- name: Update the galaxy.yml with the release
|
||||||
|
env:
|
||||||
|
VERSION: ${{ github.event.release.tag_name }}
|
||||||
|
run: |
|
||||||
|
sed 's/{{ version }}/'$VERSION'/' galaxy.yml
|
||||||
|
cat galaxy.yml
|
||||||
|
|
||||||
|
# - name: Build and publish
|
||||||
|
# env:
|
||||||
|
# ANSIBLE_GALAXY_API_KEY: ${{ secrets.ANSIBLE_GALAXY_API_KEY }}
|
||||||
|
# run: |
|
||||||
|
# ansible-galaxy collection build
|
||||||
|
# ansible-galaxy collection publish *.tar.gz --api-key $ANSIBLE_GALAXY_API_KEY
|
80
README.md
80
README.md
|
@ -1,41 +1,76 @@
|
||||||
# collection_template
|
|
||||||
You can build a new repository for an Ansible Collection using this template by following [Creating a repository from a template](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template). This README.md contains recommended headings for your collection README.md, with comments describing what each section should contain. Once you have created your collection repository, delete this paragraph and the title above it from your README.md.
|
|
||||||
|
|
||||||
# Foo Collection
|
|
||||||
<!-- Add CI and code coverage badges here. Samples included below. -->
|
|
||||||
[](https://github.com/ansible-collections/REPONAMEHERE/actions) [](https://codecov.io/gh/ansible-collections/REPONAMEHERE)
|
|
||||||
|
|
||||||
<!-- Describe the collection and why a user would want to use it. What does the collection do? -->
|
# Ansible Utilities Collection
|
||||||
|
[](https://dashboard.zuul.ansible.com/t/ansible/builds?project=ansible-collections%2Fansible.utils) <!--[](https://codecov.io/gh/ansible-collections/ansible.utils)-->
|
||||||
|
|
||||||
## Tested with Ansible
|
The Ansible ``ansible.utils`` collection includes FIXME
|
||||||
|
|
||||||
<!-- List the versions of Ansible the collection has been tested with. Must match what is in galaxy.yml. -->
|
<!--start requires_ansible-->
|
||||||
|
## Ansible version compatibility
|
||||||
|
|
||||||
## External requirements
|
This collection has been tested against following Ansible versions: **>=2.9.10,<2.11**.
|
||||||
|
|
||||||
<!-- List any external resources the collection depends on, for example minimum versions of an OS, libraries, or utilities. Do not list other Ansible collections here. -->
|
Plugins and modules within a collection may be tested with only specific Ansible versions.
|
||||||
|
A collection may contain metadata that identifies these versions.
|
||||||
### Supported connections
|
PEP440 is the schema used to describe the versions of Ansible.
|
||||||
<!-- Optional. If your collection supports only specific connection types (such as HTTPAPI, netconf, or others), list them here. -->
|
<!--end requires_ansible-->
|
||||||
|
|
||||||
## Included content
|
## Included content
|
||||||
|
|
||||||
<!-- Galaxy will eventually list the module docs within the UI, but until that is ready, you may need to either describe your plugins etc here, or point to an external docsite to cover that information. -->
|
<!--start collection content-->
|
||||||
|
### Filter plugins
|
||||||
|
Name | Description
|
||||||
|
--- | ---
|
||||||
|
ansible.utils.get_path|Get the value within a variable using a path. [See examples](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.get_path_lookup.rst)
|
||||||
|
ansible.utils.to_paths|Convert complex objects to paths. [See examples](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_paths_lookup.rst)
|
||||||
|
|
||||||
|
### Lookup plugins
|
||||||
|
Name | Description
|
||||||
|
--- | ---
|
||||||
|
[ansible.utils.get_path](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.get_path_lookup.rst)|Retrieve the value in a variable using a path
|
||||||
|
[ansible.utils.to_paths](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_paths_lookup.rst)|Flatten a complex object into a dictionary of paths and values
|
||||||
|
|
||||||
|
<!--end collection content-->
|
||||||
|
|
||||||
|
## Installing this collection
|
||||||
|
|
||||||
|
You can install the ``ansible.utils`` collection with the Ansible Galaxy CLI:
|
||||||
|
|
||||||
|
ansible-galaxy collection install ansible.utils
|
||||||
|
|
||||||
|
You can also include it in a `requirements.yml` file and install it with `ansible-galaxy collection install -r requirements.yml`, using the format:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
collections:
|
||||||
|
- name: ansible.utils
|
||||||
|
```
|
||||||
## Using this collection
|
## Using this collection
|
||||||
|
|
||||||
<!--Include some quick examples that cover the most common use cases for your collection content. -->
|
The most common use case for this collection is FIXME
|
||||||
|
|
||||||
See [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details.
|
|
||||||
|
**NOTE**: For Ansible 2.9, you may not see deprecation warnings when you run your playbooks with this collection. Use this documentation to track when a module is deprecated.
|
||||||
|
|
||||||
|
### See Also:
|
||||||
|
|
||||||
|
* [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details.
|
||||||
|
|
||||||
## Contributing to this collection
|
## Contributing to this collection
|
||||||
|
|
||||||
<!--Describe how the community can contribute to your collection. At a minimum, include how and where users can create issues to report problems or request features for this collection. List contribution requirements, including preferred workflows and necessary testing, so you can benefit from community PRs. If you are following general Ansible contributor guidelines, you can link to - [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html). -->
|
We welcome community contributions to this collection. If you find problems, please open an issue or create a PR against the [ansible.utils collection repository](https://github.com/ansible-collections/ansible.utils). See [Contributing to Ansible-maintained collections](https://docs.ansible.com/ansible/devel/community/contributing_maintained_collections.html#contributing-maintained-collections) for complete details.
|
||||||
|
|
||||||
|
See the [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html) for details on contributing to Ansible.
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
This collection follows the Ansible project's
|
||||||
|
[Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html).
|
||||||
|
Please read and familiarize yourself with this document.
|
||||||
|
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
<!--Add a link to a changelog.md file or an external docsite to cover this information. -->
|
||||||
See the [changelog](https://github.com/ansible-collections/REPONAMEHERE/tree/main/CHANGELOG.rst).
|
Release notes are available [here](https://github.com/ansible-collections/ansible.utils/blob/main/changelogs/CHANGELOG.rst)
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
|
@ -43,20 +78,13 @@ See the [changelog](https://github.com/ansible-collections/REPONAMEHERE/tree/mai
|
||||||
|
|
||||||
## More information
|
## More information
|
||||||
|
|
||||||
<!-- List out where the user can find additional information, such as working group meeting times, slack/IRC channels, or documentation for the product this collection automates. At a minimum, link to: -->
|
|
||||||
|
|
||||||
- [Ansible Collection overview](https://github.com/ansible-collections/overview)
|
- [Ansible Collection overview](https://github.com/ansible-collections/overview)
|
||||||
- [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html)
|
- [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html)
|
||||||
- [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html)
|
- [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html)
|
||||||
- [Ansible Collections Checklist](https://github.com/ansible-collections/overview/blob/master/collection_requirements.rst)
|
|
||||||
- [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html)
|
- [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html)
|
||||||
- [The Bullhorn (the Ansible Contributor newsletter)](https://us19.campaign-archive.com/home/?u=56d874e027110e35dea0e03c1&id=d6635f5420)
|
|
||||||
- [Changes impacting Contributors](https://github.com/ansible-collections/overview/issues/45)
|
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
<!-- Include the appropriate license information here and a pointer to the full licensing details. If the collection contains modules migrated from the ansible/ansible repo, you must use the same license that existed in the ansible/ansible repo. See the GNU license example below. -->
|
|
||||||
|
|
||||||
GNU General Public License v3.0 or later.
|
GNU General Public License v3.0 or later.
|
||||||
|
|
||||||
See [LICENSE](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text.
|
See [LICENSE](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text.
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
.. _ansible.utils.get_path_lookup:
|
||||||
|
|
||||||
|
|
||||||
|
**********************
|
||||||
|
ansible.utils.get_path
|
||||||
|
**********************
|
||||||
|
|
||||||
|
**Retrieve the value in a variable using a path**
|
||||||
|
|
||||||
|
|
||||||
|
Version added: 1.0
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 1
|
||||||
|
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
--------
|
||||||
|
- Use a ``path`` to retreive a nested value from a ``var``
|
||||||
|
- ``get_path`` is also available as a ``filter_plugin`` for convenience
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table border=0 cellpadding=0 class="documentation-table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="1">Parameter</th>
|
||||||
|
<th>Choices/<font color="blue">Defaults</font></th>
|
||||||
|
<th>Configuration</th>
|
||||||
|
<th width="100%">Comments</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>_terms</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">-</span>
|
||||||
|
/ <span style="color: red">required</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>The values below provided in the order <code>var</code>, <code>path</code>, <code>wantlist=</code>.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>path</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">string</span>
|
||||||
|
/ <span style="color: red">required</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>The <code>path</code> in the <code>var</code> to retrieve the value of. The <code>path</code> needs to a be a valid jinja path</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>var</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">raw</span>
|
||||||
|
/ <span style="color: red">required</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>The variable from which the value should be extraced</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>wantlist</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">boolean</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||||
|
<li>no</li>
|
||||||
|
<li>yes</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>If set to <code>True</code>, the return value will always be a list This can also be accomplished using <code>query</code> or <code>q</code> instead of <code>lookup</code> <a href='https://docs.ansible.com/ansible/latest/plugins/lookup.html'>https://docs.ansible.com/ansible/latest/plugins/lookup.html</a></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. code-block:: yaml+jinja
|
||||||
|
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
e:
|
||||||
|
- True
|
||||||
|
- False
|
||||||
|
|
||||||
|
- name: Retrieve a value deep inside a using a path
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.get_path', a, path) }}"
|
||||||
|
as_filter: "{{ a|ansible.utils.get_path(path) }}"
|
||||||
|
vars:
|
||||||
|
path: b.c.d[0]
|
||||||
|
|
||||||
|
# TASK [ansible.builtin.set_fact] *************************************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter: '0'
|
||||||
|
# as_lookup: '0'
|
||||||
|
|
||||||
|
|
||||||
|
#### Working with hostvars
|
||||||
|
|
||||||
|
- name: Retrieve a value deep inside all of the host's vars
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.get_path', look_in, look_for) }}"
|
||||||
|
as_filter: "{{ look_in|ansible.utils.get_path(look_for) }}"
|
||||||
|
vars:
|
||||||
|
look_in: "{{ hostvars[inventory_hostname] }}"
|
||||||
|
look_for: a.b.c.d[0]
|
||||||
|
|
||||||
|
# TASK [Retrieve a value deep inside all of the host's vars] **********
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter: '0'
|
||||||
|
# as_lookup: '0'
|
||||||
|
|
||||||
|
|
||||||
|
#### Used alongside ansible.utils.to_paths
|
||||||
|
|
||||||
|
- name: Get the paths for the object
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
paths: "{{ a|ansible.utils.to_paths(prepend='a') }}"
|
||||||
|
|
||||||
|
- name: Retrieve the value of each path from vars
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "The value of path {{ path }} in vars is {{ value }}"
|
||||||
|
loop: "{{ paths.keys()|list }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
vars:
|
||||||
|
path: "{{ item }}"
|
||||||
|
value: "{{ vars|ansible.utils.get_path(item) }}"
|
||||||
|
|
||||||
|
# TASK [Get the paths for the object] *********************************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# paths:
|
||||||
|
# a.b.c.d[0]: 0
|
||||||
|
# a.b.c.d[1]: 1
|
||||||
|
# a.b.c.e[0]: true
|
||||||
|
# a.b.c.e[1]: false
|
||||||
|
|
||||||
|
# TASK [Retrieve the value of each path from vars] ********************
|
||||||
|
# ok: [nxos101] => (item=a.b.c.d[0]) =>
|
||||||
|
# msg: The value of path a.b.c.d[0] in vars is 0
|
||||||
|
# ok: [nxos101] => (item=a.b.c.d[1]) =>
|
||||||
|
# msg: The value of path a.b.c.d[1] in vars is 1
|
||||||
|
# ok: [nxos101] => (item=a.b.c.e[0]) =>
|
||||||
|
# msg: The value of path a.b.c.e[0] in vars is True
|
||||||
|
# ok: [nxos101] => (item=a.b.c.e[1]) =>
|
||||||
|
# msg: The value of path a.b.c.e[1] in vars is False
|
||||||
|
|
||||||
|
|
||||||
|
#### Working with complex structures
|
||||||
|
|
||||||
|
- name: Retrieve the current interface config
|
||||||
|
cisco.nxos.nxos_interfaces:
|
||||||
|
state: gathered
|
||||||
|
register: interfaces
|
||||||
|
|
||||||
|
- name: Get the description of several interfaces
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ rekeyed|ansible.utils.get_path(item) }}"
|
||||||
|
vars:
|
||||||
|
rekeyed:
|
||||||
|
by_name: "{{ interfaces.gathered|ansible.builtin.rekey_on_member('name') }}"
|
||||||
|
loop:
|
||||||
|
- by_name['Ethernet1/1'].description
|
||||||
|
- by_name['Ethernet1/2'].description
|
||||||
|
|
||||||
|
# TASK [Get the description of several interfaces] ********************
|
||||||
|
# ok: [nxos101] => (item=by_name['Ethernet1/1'].description) =>
|
||||||
|
# msg: Configured by Ansible
|
||||||
|
# ok: [nxos101] => (item=by_name['Ethernet1/2'].description) =>
|
||||||
|
# msg: Configured by Ansible Network
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Return Values
|
||||||
|
-------------
|
||||||
|
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this lookup:
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table border=0 cellpadding=0 class="documentation-table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="1">Key</th>
|
||||||
|
<th>Returned</th>
|
||||||
|
<th width="100%">Description</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||||
|
<b>_raw</b>
|
||||||
|
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">-</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<div>One or more zero-based indicies of the matching list items</div>
|
||||||
|
<div>See <code>wantlist</code> if a list is always required</div>
|
||||||
|
<br/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
|
||||||
|
Status
|
||||||
|
------
|
||||||
|
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- Bradley Thornton (@cidrblock)
|
||||||
|
|
||||||
|
|
||||||
|
.. hint::
|
||||||
|
Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
|
|
@ -0,0 +1,253 @@
|
||||||
|
.. _ansible.utils.to_paths_lookup:
|
||||||
|
|
||||||
|
|
||||||
|
**********************
|
||||||
|
ansible.utils.to_paths
|
||||||
|
**********************
|
||||||
|
|
||||||
|
**Flatten a complex object into a dictionary of paths and values**
|
||||||
|
|
||||||
|
|
||||||
|
Version added: 1.0
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
:depth: 1
|
||||||
|
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
--------
|
||||||
|
- Flatten a complex object into a dictionary of paths and values.
|
||||||
|
- Paths are dot delimited whenever possible
|
||||||
|
- Brakets are used for list indicies and keys that contain special characters
|
||||||
|
- ``to_paths`` is also available as a filter plugin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table border=0 cellpadding=0 class="documentation-table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="1">Parameter</th>
|
||||||
|
<th>Choices/<font color="blue">Defaults</font></th>
|
||||||
|
<th>Configuration</th>
|
||||||
|
<th width="100%">Comments</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>_terms</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">-</span>
|
||||||
|
/ <span style="color: red">required</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>The values below provided in the order <code>var</code>, <code>prepend=</code>, <code>wantlist=</code>.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>prepend</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">string</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>Prepend each path entry. Useful to add the initial <code>var</code> name.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>var</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">raw</span>
|
||||||
|
/ <span style="color: red">required</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>The value of <code>var</code> will be will be used.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="parameter-"></div>
|
||||||
|
<b>wantlist</b>
|
||||||
|
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">boolean</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul style="margin: 0; padding: 0"><b>Choices:</b>
|
||||||
|
<li>no</li>
|
||||||
|
<li>yes</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>If set to <code>True</code>, the return value will always be a list. This can also be accomplished using <code>query</code> or <code>q</code> instead of <code>lookup</code>. <a href='https://docs.ansible.com/ansible/latest/plugins/lookup.html'>https://docs.ansible.com/ansible/latest/plugins/lookup.html</a></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. code-block:: yaml+jinja
|
||||||
|
|
||||||
|
#### Simple examples
|
||||||
|
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
e:
|
||||||
|
- True
|
||||||
|
- False
|
||||||
|
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.to_paths', a) }}"
|
||||||
|
as_filter: "{{ a|ansible.utils.to_paths }}"
|
||||||
|
|
||||||
|
# TASK [set_fact] *****************************************************
|
||||||
|
# task path: /home/brad/github/dotbracket/site.yaml:17
|
||||||
|
# ok: [localhost] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter:
|
||||||
|
# b.c.d[0]: 0
|
||||||
|
# b.c.d[1]: 1
|
||||||
|
# b.c.e[0]: true
|
||||||
|
# b.c.e[1]: false
|
||||||
|
# as_lookup:
|
||||||
|
# b.c.d[0]: 0
|
||||||
|
# b.c.d[1]: 1
|
||||||
|
# b.c.e[0]: true
|
||||||
|
# b.c.e[1]: false
|
||||||
|
|
||||||
|
- name: Use prepend to add the initial variable name
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.to_paths', a, prepend=('a')) }}"
|
||||||
|
as_filter: "{{ a|ansible.utils.to_paths(prepend='a') }}"
|
||||||
|
|
||||||
|
# TASK [Use prepend to add the initial variable name] *****************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter:
|
||||||
|
# a.b.c.d[0]: 0
|
||||||
|
# a.b.c.d[1]: 1
|
||||||
|
# a.b.c.e[0]: true
|
||||||
|
# a.b.c.e[1]: false
|
||||||
|
# as_lookup:
|
||||||
|
# a.b.c.d[0]: 0
|
||||||
|
# a.b.c.d[1]: 1
|
||||||
|
# a.b.c.e[0]: true
|
||||||
|
# a.b.c.e[1]: false
|
||||||
|
|
||||||
|
|
||||||
|
#### Using a complex object
|
||||||
|
|
||||||
|
- name: Make an API call
|
||||||
|
uri:
|
||||||
|
url: "https://nxos101/restconf/data/openconfig-interfaces:interfaces"
|
||||||
|
headers:
|
||||||
|
accept: "application/yang.data+json"
|
||||||
|
url_password: password
|
||||||
|
url_username: admin
|
||||||
|
validate_certs: False
|
||||||
|
register: result
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Flatten the complex object
|
||||||
|
set_fact:
|
||||||
|
flattened: "{{ result.json|ansible.utils.to_paths }}"
|
||||||
|
|
||||||
|
# TASK [Flatten the complex object] ********************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# flattened:
|
||||||
|
# interfaces.interface[0].config.enabled: 'true'
|
||||||
|
# interfaces.interface[0].config.mtu: '1500'
|
||||||
|
# interfaces.interface[0].config.name: eth1/71
|
||||||
|
# interfaces.interface[0].config.type: ethernetCsmacd
|
||||||
|
# interfaces.interface[0].ethernet.config['auto-negotiate']: 'true'
|
||||||
|
# interfaces.interface[0].ethernet.state.counters['in-crc-errors']: '0'
|
||||||
|
# interfaces.interface[0].ethernet.state.counters['in-fragment-frames']: '0'
|
||||||
|
# interfaces.interface[0].ethernet.state.counters['in-jabber-frames']: '0'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Return Values
|
||||||
|
-------------
|
||||||
|
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this lookup:
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table border=0 cellpadding=0 class="documentation-table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="1">Key</th>
|
||||||
|
<th>Returned</th>
|
||||||
|
<th width="100%">Description</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="1">
|
||||||
|
<div class="ansibleOptionAnchor" id="return-"></div>
|
||||||
|
<b>_raw</b>
|
||||||
|
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
|
||||||
|
<div style="font-size: small">
|
||||||
|
<span style="color: purple">-</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<div>A dictionary of key value pairs</div>
|
||||||
|
<div>The key is the path</div>
|
||||||
|
<div>The value is the value</div>
|
||||||
|
<br/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
|
||||||
|
Status
|
||||||
|
------
|
||||||
|
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- Bradley Thornton (@cidrblock)
|
||||||
|
|
||||||
|
|
||||||
|
.. hint::
|
||||||
|
Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
|
34
galaxy.yml
34
galaxy.yml
|
@ -1,23 +1,13 @@
|
||||||
# See https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html
|
---
|
||||||
|
|
||||||
namespace: community
|
|
||||||
name: FIXME
|
|
||||||
version: 0.1.0
|
|
||||||
readme: README.md
|
|
||||||
authors:
|
authors:
|
||||||
- YOUR NAME (github.com/YOURGITHUB)
|
- Ansible Community
|
||||||
description: null
|
license_file: LICENSE
|
||||||
license_file: COPYING
|
name: utils
|
||||||
tags:
|
namespace: ansible
|
||||||
# tags so people can search for collections https://galaxy.ansible.com/search
|
description: Ansible Collection with utilities to ease the management, manipulation, and validation of data within a playbook
|
||||||
# tags are all lower-case, no spaces, no dashes.
|
readme: README.md
|
||||||
- example1
|
repository: https://github.com/ansible-collections/ansible.utils
|
||||||
- example2
|
tags: [networking, security, cloud, utilities, data, validation]
|
||||||
repository: https://github.com/ansible-collections/community.REPO_NAME
|
# this will be updated by a gh action prior to push
|
||||||
#documentation: https://github.com/ansible-collection-migration/community.REPO_NAME/tree/main/docs
|
# do not modify, see /github/workflows/publish.yml
|
||||||
homepage: https://github.com/ansible-collections/community.REPO_NAME
|
version: 0.0.0
|
||||||
issues: https://github.com/ansible-collections/community.REPO_NAME/issues
|
|
||||||
build_ignore:
|
|
||||||
# https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#ignoring-files-and-folders
|
|
||||||
- .gitignore
|
|
||||||
- changelogs/.plugin-cache.yaml
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
---
|
---
|
||||||
requires_ansible: '>=2.9.10'
|
requires_ansible: '>=2.9.10,<2.11'
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 Red Hat
|
||||||
|
# GNU General Public License v3.0+
|
||||||
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
flatten a complex object to dot bracket notation
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
from ansible.errors import AnsibleFilterError
|
||||||
|
from ansible.module_utils.common._collections_compat import (
|
||||||
|
Mapping,
|
||||||
|
MutableMapping,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ansible_collections.ansible.utils.plugins.module_utils.common.path import (
|
||||||
|
to_paths,
|
||||||
|
get_path,
|
||||||
|
)
|
||||||
|
from jinja2.filters import environmentfilter
|
||||||
|
|
||||||
|
|
||||||
|
def _to_paths(*args, **kwargs):
|
||||||
|
"""Convert objects to paths. [See examples](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.to_paths_lookup.rst)"""
|
||||||
|
return to_paths(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@environmentfilter
|
||||||
|
def _get_path(*args, **kwargs):
|
||||||
|
"""Get value using path. [See examples](https://github.com/ansible-collections/ansible.utils/blob/main/docs/ansible.utils.get_path_lookup.rst)"""
|
||||||
|
kwargs["environment"] = args[0]
|
||||||
|
args = args[1:]
|
||||||
|
return get_path(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
""" path filters """
|
||||||
|
|
||||||
|
def filters(self):
|
||||||
|
return {"to_paths": _to_paths, "get_path": _get_path}
|
|
@ -0,0 +1,174 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 Red Hat
|
||||||
|
# GNU General Public License v3.0+
|
||||||
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
The get_path lookup plugin
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
lookup: get_path
|
||||||
|
author: Bradley Thornton (@cidrblock)
|
||||||
|
version_added: "1.0"
|
||||||
|
short_description: Retrieve the value in a variable using a path
|
||||||
|
description:
|
||||||
|
- Use a C(path) to retreive a nested value from a C(var)
|
||||||
|
- C(get_path) is also available as a C(filter_plugin) for convenience
|
||||||
|
options:
|
||||||
|
_terms:
|
||||||
|
description: The values below provided in the order C(var), C(path), C(wantlist=).
|
||||||
|
required: True
|
||||||
|
var:
|
||||||
|
description: The variable from which the value should be extraced
|
||||||
|
type: raw
|
||||||
|
required: True
|
||||||
|
path:
|
||||||
|
description: >
|
||||||
|
The C(path) in the C(var) to retrieve the value of.
|
||||||
|
The C(path) needs to a be a valid jinja path
|
||||||
|
type: str
|
||||||
|
required: True
|
||||||
|
wantlist:
|
||||||
|
description: >
|
||||||
|
If set to C(True), the return value will always be a list
|
||||||
|
This can also be accomplished using C(query) or C(q) instead of C(lookup)
|
||||||
|
U(https://docs.ansible.com/ansible/latest/plugins/lookup.html)
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
notes:
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
e:
|
||||||
|
- True
|
||||||
|
- False
|
||||||
|
|
||||||
|
- name: Retrieve a value deep inside a using a path
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.get_path', a, path) }}"
|
||||||
|
as_filter: "{{ a|ansible.utils.get_path(path) }}"
|
||||||
|
vars:
|
||||||
|
path: b.c.d[0]
|
||||||
|
|
||||||
|
# TASK [ansible.builtin.set_fact] *************************************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter: '0'
|
||||||
|
# as_lookup: '0'
|
||||||
|
|
||||||
|
|
||||||
|
#### Working with hostvars
|
||||||
|
|
||||||
|
- name: Retrieve a value deep inside all of the host's vars
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.get_path', look_in, look_for) }}"
|
||||||
|
as_filter: "{{ look_in|ansible.utils.get_path(look_for) }}"
|
||||||
|
vars:
|
||||||
|
look_in: "{{ hostvars[inventory_hostname] }}"
|
||||||
|
look_for: a.b.c.d[0]
|
||||||
|
|
||||||
|
# TASK [Retrieve a value deep inside all of the host's vars] **********
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter: '0'
|
||||||
|
# as_lookup: '0'
|
||||||
|
|
||||||
|
|
||||||
|
#### Used alongside ansible.utils.to_paths
|
||||||
|
|
||||||
|
- name: Get the paths for the object
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
paths: "{{ a|ansible.utils.to_paths(prepend='a') }}"
|
||||||
|
|
||||||
|
- name: Retrieve the value of each path from vars
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "The value of path {{ path }} in vars is {{ value }}"
|
||||||
|
loop: "{{ paths.keys()|list }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item }}"
|
||||||
|
vars:
|
||||||
|
path: "{{ item }}"
|
||||||
|
value: "{{ vars|ansible.utils.get_path(item) }}"
|
||||||
|
|
||||||
|
# TASK [Get the paths for the object] *********************************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# paths:
|
||||||
|
# a.b.c.d[0]: 0
|
||||||
|
# a.b.c.d[1]: 1
|
||||||
|
# a.b.c.e[0]: true
|
||||||
|
# a.b.c.e[1]: false
|
||||||
|
|
||||||
|
# TASK [Retrieve the value of each path from vars] ********************
|
||||||
|
# ok: [nxos101] => (item=a.b.c.d[0]) =>
|
||||||
|
# msg: The value of path a.b.c.d[0] in vars is 0
|
||||||
|
# ok: [nxos101] => (item=a.b.c.d[1]) =>
|
||||||
|
# msg: The value of path a.b.c.d[1] in vars is 1
|
||||||
|
# ok: [nxos101] => (item=a.b.c.e[0]) =>
|
||||||
|
# msg: The value of path a.b.c.e[0] in vars is True
|
||||||
|
# ok: [nxos101] => (item=a.b.c.e[1]) =>
|
||||||
|
# msg: The value of path a.b.c.e[1] in vars is False
|
||||||
|
|
||||||
|
|
||||||
|
#### Working with complex structures
|
||||||
|
|
||||||
|
- name: Retrieve the current interface config
|
||||||
|
cisco.nxos.nxos_interfaces:
|
||||||
|
state: gathered
|
||||||
|
register: interfaces
|
||||||
|
|
||||||
|
- name: Get the description of several interfaces
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ rekeyed|ansible.utils.get_path(item) }}"
|
||||||
|
vars:
|
||||||
|
rekeyed:
|
||||||
|
by_name: "{{ interfaces.gathered|ansible.builtin.rekey_on_member('name') }}"
|
||||||
|
loop:
|
||||||
|
- by_name['Ethernet1/1'].description
|
||||||
|
- by_name['Ethernet1/2'].description
|
||||||
|
|
||||||
|
# TASK [Get the description of several interfaces] ********************
|
||||||
|
# ok: [nxos101] => (item=by_name['Ethernet1/1'].description) =>
|
||||||
|
# msg: Configured by Ansible
|
||||||
|
# ok: [nxos101] => (item=by_name['Ethernet1/2'].description) =>
|
||||||
|
# msg: Configured by Ansible Network
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
_raw:
|
||||||
|
description:
|
||||||
|
- One or more zero-based indicies of the matching list items
|
||||||
|
- See C(wantlist) if a list is always required
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ansible.plugins.lookup import LookupBase
|
||||||
|
from ansible_collections.ansible.utils.plugins.module_utils.common.path import (
|
||||||
|
get_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LookupModule(LookupBase):
|
||||||
|
def run(self, terms, variables, **kwargs):
|
||||||
|
kwargs["environment"] = self._templar.environment
|
||||||
|
if isinstance(terms, dict):
|
||||||
|
terms.update(kwargs)
|
||||||
|
res = get_path(**terms)
|
||||||
|
else:
|
||||||
|
res = get_path(*terms, **kwargs)
|
||||||
|
if not isinstance(res, list):
|
||||||
|
return [res]
|
||||||
|
return res
|
|
@ -0,0 +1,157 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 Red Hat
|
||||||
|
# GNU General Public License v3.0+
|
||||||
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
The to_paths lookup plugin
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
lookup: to_paths
|
||||||
|
author: Bradley Thornton (@cidrblock)
|
||||||
|
version_added: "1.0"
|
||||||
|
short_description: Flatten a complex object into a dictionary of paths and values
|
||||||
|
description:
|
||||||
|
- Flatten a complex object into a dictionary of paths and values.
|
||||||
|
- Paths are dot delimited whenever possible
|
||||||
|
- Brakets are used for list indicies and keys that contain special characters
|
||||||
|
- C(to_paths) is also available as a filter plugin
|
||||||
|
options:
|
||||||
|
_terms:
|
||||||
|
description: The values below provided in the order C(var), C(prepend=), C(wantlist=).
|
||||||
|
required: True
|
||||||
|
var:
|
||||||
|
description: The value of C(var) will be will be used.
|
||||||
|
type: raw
|
||||||
|
required: True
|
||||||
|
prepend:
|
||||||
|
description: Prepend each path entry. Useful to add the initial C(var) name.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
wantlist:
|
||||||
|
description: >
|
||||||
|
If set to C(True), the return value will always be a list.
|
||||||
|
This can also be accomplished using C(query) or C(q) instead of C(lookup).
|
||||||
|
U(https://docs.ansible.com/ansible/latest/plugins/lookup.html)
|
||||||
|
type: bool
|
||||||
|
|
||||||
|
notes:
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = r"""
|
||||||
|
|
||||||
|
#### Simple examples
|
||||||
|
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
e:
|
||||||
|
- True
|
||||||
|
- False
|
||||||
|
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.to_paths', a) }}"
|
||||||
|
as_filter: "{{ a|ansible.utils.to_paths }}"
|
||||||
|
|
||||||
|
# TASK [set_fact] *****************************************************
|
||||||
|
# task path: /home/brad/github/dotbracket/site.yaml:17
|
||||||
|
# ok: [localhost] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter:
|
||||||
|
# b.c.d[0]: 0
|
||||||
|
# b.c.d[1]: 1
|
||||||
|
# b.c.e[0]: true
|
||||||
|
# b.c.e[1]: false
|
||||||
|
# as_lookup:
|
||||||
|
# b.c.d[0]: 0
|
||||||
|
# b.c.d[1]: 1
|
||||||
|
# b.c.e[0]: true
|
||||||
|
# b.c.e[1]: false
|
||||||
|
|
||||||
|
- name: Use prepend to add the initial variable name
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
as_lookup: "{{ lookup('ansible.utils.to_paths', a, prepend=('a')) }}"
|
||||||
|
as_filter: "{{ a|ansible.utils.to_paths(prepend='a') }}"
|
||||||
|
|
||||||
|
# TASK [Use prepend to add the initial variable name] *****************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# as_filter:
|
||||||
|
# a.b.c.d[0]: 0
|
||||||
|
# a.b.c.d[1]: 1
|
||||||
|
# a.b.c.e[0]: true
|
||||||
|
# a.b.c.e[1]: false
|
||||||
|
# as_lookup:
|
||||||
|
# a.b.c.d[0]: 0
|
||||||
|
# a.b.c.d[1]: 1
|
||||||
|
# a.b.c.e[0]: true
|
||||||
|
# a.b.c.e[1]: false
|
||||||
|
|
||||||
|
|
||||||
|
#### Using a complex object
|
||||||
|
|
||||||
|
- name: Make an API call
|
||||||
|
uri:
|
||||||
|
url: "https://nxos101/restconf/data/openconfig-interfaces:interfaces"
|
||||||
|
headers:
|
||||||
|
accept: "application/yang.data+json"
|
||||||
|
url_password: password
|
||||||
|
url_username: admin
|
||||||
|
validate_certs: False
|
||||||
|
register: result
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Flatten the complex object
|
||||||
|
set_fact:
|
||||||
|
flattened: "{{ result.json|ansible.utils.to_paths }}"
|
||||||
|
|
||||||
|
# TASK [Flatten the complex object] ********************
|
||||||
|
# ok: [nxos101] => changed=false
|
||||||
|
# ansible_facts:
|
||||||
|
# flattened:
|
||||||
|
# interfaces.interface[0].config.enabled: 'true'
|
||||||
|
# interfaces.interface[0].config.mtu: '1500'
|
||||||
|
# interfaces.interface[0].config.name: eth1/71
|
||||||
|
# interfaces.interface[0].config.type: ethernetCsmacd
|
||||||
|
# interfaces.interface[0].ethernet.config['auto-negotiate']: 'true'
|
||||||
|
# interfaces.interface[0].ethernet.state.counters['in-crc-errors']: '0'
|
||||||
|
# interfaces.interface[0].ethernet.state.counters['in-fragment-frames']: '0'
|
||||||
|
# interfaces.interface[0].ethernet.state.counters['in-jabber-frames']: '0'
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
_raw:
|
||||||
|
description:
|
||||||
|
- A dictionary of key value pairs
|
||||||
|
- The key is the path
|
||||||
|
- The value is the value
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ansible.plugins.lookup import LookupBase
|
||||||
|
from ansible_collections.ansible.utils.plugins.module_utils.common.path import (
|
||||||
|
to_paths,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LookupModule(LookupBase):
|
||||||
|
def run(self, terms, variables, **kwargs):
|
||||||
|
if isinstance(terms, dict):
|
||||||
|
terms.update(kwargs)
|
||||||
|
res = to_paths(**terms)
|
||||||
|
else:
|
||||||
|
res = to_paths(*terms, **kwargs)
|
||||||
|
if not isinstance(res, list):
|
||||||
|
return [res]
|
||||||
|
return res
|
|
@ -0,0 +1,76 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 Red Hat
|
||||||
|
# GNU General Public License v3.0+
|
||||||
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
flatten a complex object to dot bracket notation
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import re
|
||||||
|
from ansible.module_utils.common._collections_compat import (
|
||||||
|
Mapping,
|
||||||
|
MutableMapping,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Note, this file can only be used on the control node
|
||||||
|
# where ansible is installed
|
||||||
|
# limit imports to filter and lookup plugins
|
||||||
|
try:
|
||||||
|
from ansible.errors import AnsibleError
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_path(var, path, environment, wantlist=False):
|
||||||
|
"""Get the value of a path within an object
|
||||||
|
|
||||||
|
:param var: The var from which the value is retrieved
|
||||||
|
:type var: should be dict or list, but jinja can sort that out
|
||||||
|
:param path: The path to get
|
||||||
|
:type path: should be a string but jinja can sort that out
|
||||||
|
:param environment: The jinja Environment
|
||||||
|
:type environment: Environment
|
||||||
|
:return: The result of the jinja evaluation
|
||||||
|
:rtype: any
|
||||||
|
"""
|
||||||
|
string_to_variable = "{{ %s }}" % path
|
||||||
|
result = environment.from_string(string_to_variable).render(**var)
|
||||||
|
if wantlist:
|
||||||
|
return list(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def to_paths(var, prepend=False, wantlist=False):
|
||||||
|
if prepend:
|
||||||
|
if not isinstance(prepend, str):
|
||||||
|
raise AnsibleError("The value of 'prepend' must be a string.")
|
||||||
|
var = {prepend: var}
|
||||||
|
|
||||||
|
out = {}
|
||||||
|
|
||||||
|
def flatten(data, name=""):
|
||||||
|
if isinstance(data, (dict, Mapping, MutableMapping)):
|
||||||
|
for key, val in data.items():
|
||||||
|
if name:
|
||||||
|
if re.match("^[a-zA-Z_][a-zA-Z0-9_]*$", key):
|
||||||
|
nname = name + ".{key}".format(key=key)
|
||||||
|
else:
|
||||||
|
nname = name + "['{key}']".format(key=key)
|
||||||
|
else:
|
||||||
|
nname = key
|
||||||
|
flatten(val, nname)
|
||||||
|
elif isinstance(data, list):
|
||||||
|
for idx, val in enumerate(data):
|
||||||
|
flatten(val, "{name}[{idx}]".format(name=name, idx=idx))
|
||||||
|
else:
|
||||||
|
out[name] = data
|
||||||
|
|
||||||
|
flatten(var)
|
||||||
|
if wantlist:
|
||||||
|
return [out]
|
||||||
|
return out
|
|
@ -0,0 +1,58 @@
|
||||||
|
- set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
|
||||||
|
- name: Simple test filter and lookup
|
||||||
|
assert:
|
||||||
|
that: "{{ item.result == item.expected }}"
|
||||||
|
loop:
|
||||||
|
- result: "{{ vars|ansible.utils.get_path('a') }}"
|
||||||
|
expected: "{{ a }}"
|
||||||
|
- result: "{{ a|ansible.utils.get_path('b') }}"
|
||||||
|
expected: "{{ a.b }}"
|
||||||
|
- result: "{{ a|ansible.utils.get_path('b.c') }}"
|
||||||
|
expected: "{{ a.b.c }}"
|
||||||
|
- result: "{{ a|ansible.utils.get_path('b.c.d') }}"
|
||||||
|
expected: "{{ a.b.c.d }}"
|
||||||
|
- result: "{{ a|ansible.utils.get_path('b.c.d[0]') }}"
|
||||||
|
expected: "{{ a.b.c.d[0] }}"
|
||||||
|
- result: "{{ a|ansible.utils.get_path('b.c.d[1]') }}"
|
||||||
|
expected: "{{ a.b.c.d[1] }}"
|
||||||
|
- result: "{{ a|ansible.utils.get_path('b[\"c\"]') }}"
|
||||||
|
expected: "{{ a.b.c }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', vars, 'a') }}"
|
||||||
|
expected: "{{ a }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', a, 'b') }}"
|
||||||
|
expected: "{{ a.b }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', a, 'b.c') }}"
|
||||||
|
expected: "{{ a.b.c }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', a, 'b.c.d') }}"
|
||||||
|
expected: "{{ a.b.c.d }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', a, 'b.c.d[0]') }}"
|
||||||
|
expected: "{{ a.b.c.d[0] }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', a, 'b.c.d[1]') }}"
|
||||||
|
expected: "{{ a.b.c.d[1] }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', a, 'b[\"c\"]') }}"
|
||||||
|
expected: "{{ a.b.c }}"
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
|
||||||
|
- name: Simple test filter and lookup w/ wantlist
|
||||||
|
assert:
|
||||||
|
that: "{{ item.result == item.expected }}"
|
||||||
|
loop:
|
||||||
|
- result: "{{ vars|ansible.utils.get_path('a.b.c.d[0]', wantlist=True) }}"
|
||||||
|
expected:
|
||||||
|
- "{{ a.b.c.d[0] }}"
|
||||||
|
- result: "{{ lookup('ansible.utils.get_path', vars, 'a.b.c.d[0]', wantlist=True) }}"
|
||||||
|
expected:
|
||||||
|
- "{{ a.b.c.d[0] }}"
|
|
@ -0,0 +1,52 @@
|
||||||
|
- set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
|
||||||
|
- name: Test filter and lookup plugin, simple and prepend
|
||||||
|
assert:
|
||||||
|
that: "{{ item.result == item.expected }}"
|
||||||
|
loop:
|
||||||
|
- result: "{{ a|ansible.utils.to_paths }}"
|
||||||
|
expected:
|
||||||
|
b.c.d[0]: 0
|
||||||
|
b.c.d[1]: 1
|
||||||
|
- result: "{{ lookup('ansible.utils.to_paths', a) }}"
|
||||||
|
expected:
|
||||||
|
b.c.d[0]: 0
|
||||||
|
b.c.d[1]: 1
|
||||||
|
- result: "{{ a|ansible.utils.to_paths(prepend='a') }}"
|
||||||
|
expected:
|
||||||
|
a.b.c.d[0]: 0
|
||||||
|
a.b.c.d[1]: 1
|
||||||
|
- result: "{{ lookup('ansible.utils.to_paths', a, prepend='a') }}"
|
||||||
|
expected:
|
||||||
|
a.b.c.d[0]: 0
|
||||||
|
a.b.c.d[1]: 1
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
a:
|
||||||
|
b:
|
||||||
|
c:
|
||||||
|
d:
|
||||||
|
- 0
|
||||||
|
|
||||||
|
- name: Test filter and lookup plugin, wantlist and prepend
|
||||||
|
assert:
|
||||||
|
that: "{{ item.result == item.expected }}"
|
||||||
|
loop:
|
||||||
|
- result: "{{ a|ansible.utils.to_paths(wantlist=True) }}"
|
||||||
|
expected:
|
||||||
|
- b.c.d[0]: 0
|
||||||
|
- result: "{{ lookup('ansible.utils.to_paths', a, wantlist=True) }}"
|
||||||
|
expected:
|
||||||
|
- b.c.d[0]: 0
|
||||||
|
- result: "{{ a|ansible.utils.to_paths(wantlist=True, prepend='a') }}"
|
||||||
|
expected:
|
||||||
|
- a.b.c.d[0]: 0
|
||||||
|
- result: "{{ lookup('ansible.utils.to_paths', a, wantlist=True, prepend='a') }}"
|
||||||
|
expected:
|
||||||
|
- a.b.c.d[0]: 0
|
|
@ -0,0 +1 @@
|
||||||
|
plugins/module_utils/common/path.py pylint:ansible-bad-module-import # file's use is limited to filter and lookups on control node
|
|
@ -0,0 +1 @@
|
||||||
|
plugins/module_utils/common/path.py pylint:ansible-bad-module-import # file's use is limited to filter and lookups on control node
|
|
@ -0,0 +1 @@
|
||||||
|
plugins/module_utils/common/path.py pylint:ansible-bad-module-import # file's use is limited to filter and lookups on control node
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,93 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2020 Red Hat
|
||||||
|
# GNU General Public License v3.0+
|
||||||
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import json
|
||||||
|
import heapq
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
from ansible_collections.ansible.utils.plugins.module_utils.common.path import (
|
||||||
|
get_path,
|
||||||
|
to_paths,
|
||||||
|
)
|
||||||
|
from ansible.template import Templar
|
||||||
|
|
||||||
|
|
||||||
|
class TestPathUtils(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self._environment = Templar(loader=None).environment
|
||||||
|
|
||||||
|
def test_get_path_pass(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
path = "a.b.c.d[0]"
|
||||||
|
result = get_path(var, path, environment=self._environment)
|
||||||
|
expected = "0"
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_get_path_pass_wantlist(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
path = "a.b.c.d[0]"
|
||||||
|
result = get_path(
|
||||||
|
var, path, environment=self._environment, wantlist=True
|
||||||
|
)
|
||||||
|
expected = ["0"]
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_get_path_fail(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
path = "a.b.e"
|
||||||
|
expected = "dict object' has no attribute 'e'"
|
||||||
|
with self.assertRaises(Exception) as exc:
|
||||||
|
get_path(var, path, environment=self._environment)
|
||||||
|
self.assertIn(expected, str(exc.exception))
|
||||||
|
|
||||||
|
def test_to_paths(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
expected = {"a.b.c.d[0]": 0, "a.b.c.d[1]": 1}
|
||||||
|
result = to_paths(var)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_to_paths_wantlist(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
expected = [{"a.b.c.d[0]": 0, "a.b.c.d[1]": 1}]
|
||||||
|
result = to_paths(var, wantlist=True)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_to_paths_special_char(self):
|
||||||
|
var = {"a": {"b": {"c": {"Eth1/1": True}}}}
|
||||||
|
expected = [{"a.b.c['Eth1/1']": True}]
|
||||||
|
result = to_paths(var, wantlist=True)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_to_paths_prepend(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
expected = [{"var.a.b.c.d[0]": 0, "var.a.b.c.d[1]": 1}]
|
||||||
|
result = to_paths(var, wantlist=True, prepend="var")
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_to_paths_prepend_fail(self):
|
||||||
|
var = {"a": {"b": {"c": {"d": [0, 1]}}}}
|
||||||
|
expected = "must be a string"
|
||||||
|
with self.assertRaises(Exception) as exc:
|
||||||
|
to_paths(var, wantlist=True, prepend=5)
|
||||||
|
self.assertIn(expected, str(exc.exception))
|
||||||
|
|
||||||
|
def test_roundtrip_large(self):
|
||||||
|
"""Test the 1000 longest keys, otherwise this takes a _really_ long time"""
|
||||||
|
big_json_path = os.path.join(
|
||||||
|
os.path.dirname(__file__), "fixtures", "large.json"
|
||||||
|
)
|
||||||
|
with open(big_json_path) as fhand:
|
||||||
|
big_json = fhand.read()
|
||||||
|
var = json.loads(big_json)
|
||||||
|
paths = to_paths(var)
|
||||||
|
to_tests = heapq.nlargest(1000, list(paths.keys()), key=len)
|
||||||
|
for to_test in to_tests:
|
||||||
|
gotten = get_path(var, to_test, environment=self._environment)
|
||||||
|
self.assertEqual(gotten, paths[to_test])
|
Loading…
Reference in New Issue